Sharepoint

PowerApps Functions

On 30/09/2022

PowerApps functions

Parse text to number

    Filter('Workflow Tasks'; ID = Value(txtId.Text))

Add datas (listItem)

    Patch(NewVoie;Defaults(NewVoie);{Num_x00e9_rovoie:"0"&LookUp(NewVoie;ID=1).Num_x00e9_rovoie}))

Update context, and forms datas

    SubmitForm(FormBeneficiaires);;ResetForm(FormBeneficiaires);; NewForm(FormBeneficiaires);; UpdateContext({showPopup:false});

        If(IsBlankOrError(SubmitForm(Form1)), Set(saveStatus, "An error occured" & Form1.Error), Set(saveStatus, "Operation succeded"))

Navigate to another form

    Navigate(Page_infos_enregistrements)

Get query string parameter and set a variable

    Set(InitiativeId; Param("ID"))

Getquerystringparam

 

Get a field from your datasource by ID

    First(Filter(Initiatives; ID=1)).Nom

 

And Or Not

Or(And(Radio1.Selected.Value=4; !IsBlank(txtComment.Text));Radio1.Selected.Value<4)

 

Update Lookup Field

Patch(
        ResultatAnalyses;
        First(//here item to update
            Filter(
                ResultatAnalyses;
                Affaire.Id = currentAffaire.ID And Analyse.Id = ThisItem.ID
            )
        );
        {
            Title: "notused";
            Commentaires: txtGalComment.Text;
            Gravite: Rating1.Value;
            Affaire: {//lookup field name
                Id: currentAffaire.ID;//id of lookup
                Value: LookUp(
                    Affaires;//list who contains lookup value
                    ID = currentAffaire.ID;//id of lookup
                    currentAffaire.Title//title of lookup value
                )
            }
        }
    )

Patch Choice

TypeIntervention: {Value: dtvTypeIntervention.Selected.Value}

Execute automate with json

'My workflow'.Run(
	JSON(
		{
			SolutionId: selectedSolution.ID,
			ImageContent: UploadedImage14.Image
		},
		JSONFormat.IncludeBinaryData
	)
);

Reg ex to get cleaned string

Clear(AttachmentsCollection);
ForAll(
      RenameColumns(DataCardValue91.Attachments, "Name", "Name1"),
      Collect(
             AttachmentsCollection,
             Name1
      )
);Set(Title1, First(AttachmentsCollection).Value);Set(FileName1, Concat( Split(First(AttachmentsCollection).Value, "" ), If( IsMatch(Result, "([^A-Za-z0-9\.\-])" ), "",Result ) ))

Save Form

SubmitForm(Form1);;If(!IsBlankOrError( Form1.Error); Notify("Une erreur est survenue lors de la sauvegarde " & Form1.Error; NotificationType.Error);Notify("La savegarde a réussi";NotificationType.Information);;Set(currentElement; Form1.LastSubmit))

 

Sort columns


Set(Month, Distinct(SortByColumns(CurrentMonthMails, "Year", Ascending, "Month", Ascending), Month))

Set date


Set(StartDate, DateAdd(DateTimeValue( Day(Today) &"/"& Month(Today) &"/"& Year(Today) &" 00:00:00"), -30));

Sum


Sum(Filter(CurrentMonthMails, Month = ThisItem.Result ), uniqMails)

 

SharePoint get by Rest List Role DefinitionBindings

On 28/10/2024


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;
}

// Function to get permissions for a SharePoint group
async function getSharePointPermissions(siteUrl, select) {

    // REST API endpoint to get group permissions
    const endpoint = `${siteUrl}`;

    // Fetch options with headers for authentication and response format
    const fetchOptions = {
        method: 'GET',
        headers: {
            'Accept': 'application/json;odata=verbose'
        }
    };

    console.log("endpoint", endpoint);
    // Send the asynchronous GET request to the REST API endpoint
    const response = await fetch(endpoint, fetchOptions);

    // Check if the response is OK (status code 200)
    if (!response.ok) {
        throw new Error(`Error fetching permissions: ${response.statusText}`);
    }

    // Parse the JSON response to extract permission data
    const data = await response.json();

    // Extract role assignments (permissions)
    const roleAssignments = data.d.results;
    console.log('roleAssignments', roleAssignments);
    console.log(JSON.stringify(roleAssignments));

    const D1 = removeSlasches(select, roleAssignments)
    const pattern2 = /\//g;
    console.log("json111", JSON.stringify(D1));
    const table = generateTableFromJson2(D1, select.replace(pattern2, ""));
    // Append the table to the container
    document.getElementById('tableContainer').appendChild(table);
}

// Usage example: Replace 'your-group-id' and 'your-site-url' with actual values
const select = "Member/Title,Member/Id,Member/LoginName,RoleDefinitionBindings";
let siteUrl = 'https://mySite.sharepoint.com/sites/Dev_wf';
siteUrl += `/_api/web/roleassignments/?$expand=RoleDefinitionBindings,Member&$select=${select}`; // Replace with the actual site URL
document.body.innerHTML = `<div id="tableContainer" class="table-wrapper"></div>`; await getSharePointPermissions(siteUrl, select); 

 

Permissions

Power Automate Add Field To ContentType

On 03/10/2024

Power Automate Add Field To ContentType (reference Microsoft)

Note the following restrictions on adding a field (column) to a content type using the REST service:

  • Site Columns cannot be added to a content type using the REST service.
  • You can add a field to a site content type only if the field already exists on the parent content type.
  • You can add a field to a content type associated with a list only if the field already exists on the list. To add a completely new field to a list content type, you have to first add it to the list and then add it to the content type in a separate call to the REST service.

Addfieldtocontenttype

Invalid - Must not be used. The value = 0.

Integer - Specifies that the field contains an integer value. The value = 1.

Text - Specifies that the field contains a single line of text. The value = 2.

Note - Specifies that the field contains multiple lines of text. The value = 3.

DateTime - Specifies that the field contains a date and time value or a date-only value. The value = 4.

Counter - Specifies that the field contains a monotonically increasing integer. The value = 5.

Choice - Specifies that the field contains a single value from a set of specified values. The value = 6.

Lookup - Specifies that the field is a lookup field. The value = 7.

Boolean - Specifies that the field contains a Boolean value. The value = 8.

Number - Specifies that the field contains a floating-point number value. The value = 9.

Currency - Specifies that the field contains a currency value. The value = 10.

URL - Specifies that the field contains a URI and an optional description of the URI. The value = 11.

Computed - Specifies that the field is a computed field. The value = 12.

Threading - Specifies that the field indicates the thread for a discussion item in a threaded view of a discussion board. The value = 13.

Guid - Specifies that the field contains a GUID value. The value = 14.

MultiChoice - Specifies that the field contains one or more values from a set of specified values. The value = 15.

GridChoice - Specifies that the field contains rating scale values for a survey list. The value = 16.

Calculated - Specifies that the field is a calculated field. The value = 17.

File - Specifies that the field contains the leaf name of a document as a value. The value = 18.

Attachments - Specifies that the field indicates whether the list item has attachments. The value = 19.

User - Specifies that the field contains one or more users and groups as values. The value = 20.

Recurrence - Specifies that the field indicates whether a meeting in a calendar list recurs. The value = 21.

CrossProjectLink - Specifies that the field contains a link between projects in a Meeting Workspace site. The value = 22.

ModStat - Specifies that the field indicates moderation status. The value = 23.

Error - Specifies that the type of the field was set to an invalid value. The value = 24.

ContentTypeId - Specifies that the field contains a content type identifier as a value. The value = 25.

PageSeparator - Specifies that the field separates questions in a survey list onto multiple pages. The value = 26.

ThreadIndex - Specifies that the field indicates the position of a discussion item in a threaded view of a discussion board. The value = 27.

WorkflowStatus - Specifies that the field indicates the status of a workflow instance on a list item. The value = 28.

AllDayEvent - Specifies that the field indicates whether a meeting in a calendar list is an all-day event. The value = 29.

WorkflowEventType - Specifies that the field contains the most recent event in a workflow instance. The value = 30.

MaxItems - Must not be used. The value = 31.

Power Apps Limit 500 WorkArround

On 25/09/2024

Power Apps Limit WorkArround 500

The column Id hit type is counter, we cannot use it to filter if we have more than 500 items with "<" ou ">"

But with created we can use delegation with > and <

You can build a pagination with date filtering

Powerappslimit500listApp loading

Settingsbutton next

Powerappsnext 1button previous

Powerappslimit back

Buttons

 

Get User Permissions By Mail And Javascript

On 09/09/2024

Get User Permissions By Mail And Javascript

 


let siteUrl = "https://mayTenant.sharepoint.com/sites/test"

async function getUserPermissions(siteUrl, userMail) {

    // REST API endpoint to get group permissions
    const endpoint = `${siteUrl}/_api/web/siteusers?$filter=Email eq '${userMail}'&$select=Id`;
    //const endpoint = `${siteUrl}/_api/web/siteusers?$filter=Email eq '${userMail}'&$select=LoginName`;

    // Fetch options with headers for authentication and response format
    const fetchOptions = {
        method: 'GET',
        headers: {
            'Accept': 'application/json;odata=verbose'
        }
    };

    console.log("endpoint", endpoint);
    // Send the asynchronous GET request to the REST API endpoint
    const response = await fetch(endpoint, fetchOptions);

    // Check if the response is OK (status code 200)
    if (!response.ok) {
        throw new Error(`Error fetching permissions: ${response.statusText}`);
    }

    // Parse the JSON response to extract permission data
    const data = await response.json();
    console.log("data", data);

    if (data.d.results.length === 0) {
        console.log("user not found", userMail);
    } else {

        const userId = `${data.d.results[0].Id}`;
        for (const user in data.d.results) {

            console.log("user", userMail);
        }
        let ret = "";
        const endpoint2 = `${siteUrl}/_api/web//RoleAssignments/GetByPrincipalId(${userId})/RoleDefinitionBindings`;
        // Send the asynchronous GET request to the REST API endpoint
        const response2 = await fetch(endpoint2, fetchOptions);
        const data2 = await response2.json();
        console.log("data2", data2);
        for (let i = 0; i < data2.d.results.length; i++) {
            ret += `User ${data2.d.results[0].Name} : ${data2.d.results[0].Description}\r\n`;
        }

        //by groups
        const getGroupsEndPoint = `${siteUrl}/_api/web/GetUserById(${userId})/groups?$select=Id,Title`;
        const response3 = await fetch(getGroupsEndPoint, fetchOptions);
        const data3 = await response3.json();
        console.log("data3", data3);

        for (let i = 0; i < data3.d.results.length; i++) {
            //get group permissions
            const endpoint4 = `${siteUrl}/_api/web//RoleAssignments/GetByPrincipalId(${data3.d.results[i].Id})/RoleDefinitionBindings`;
            const response4 = await fetch(endpoint4, fetchOptions);
            const data4 = await response4.json();
            console.log("data4", data4);
            for (let j = 0; j < data4.d.results.length; j++) {
                ret += `Group ${data3.d.results[i].Title} Id ${data3.d.results[i].Id} : ${data4.d.results[j].Name} : ${data4.d.results[j].Description}. ` + "\r\n";
            }
        }

        return `${ret}.`;
    }
    return null;
}


await getUserPermissions(siteUrl, "test-ext@test.com");

Permissions to Check:

Here are some common permissions encoded in the High and Low values:

  • ViewListItems: 0x00000001
  • AddListItems: 0x00000002
  • EditListItems: 0x00000004
  • DeleteListItems: 0x00000008
  • OpenItems: 0x00000010
  • ViewVersions: 0x00000020
  • CancelCheckout: 0x00000040
  • ManagePermissions: 0x00010000
  • ManageWeb: 0x00040000

Manage SharePoint/ Teams Permissions

On 09/09/2024

How to manage SharePoint / Teams permissions

In every Teams, in background there is a SharePoint site.

How to access SharePoint associated with Teams

Browse your Teams files

1.Clic on “Files” tab
2.Clic on three bullets
3.Select “Open in SharePoint”
01 accestoteams 1
Access SharePoint site permission management
1.Clic on SharePoint « Settings » icon
2.Click on « Site permissions » link
3.Click « advanced permissions settings »
02 linkpermissions

 

 

03 access permissions

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Check user / Office 365 group permission

On SharePoint site advanced permissions page you can check user / Office 365 group permissions

04 checkpermissions

 

 

 

 

 

 

 

 

 

 

 

 

 

 

SharePoint standard defaults groups

On SharePoint Site creation, by default 3 groups are created

1.Owners
1.Full control permission
2.Member
1.Read / Write permission
3.Read
1.Read permission
 

By default, all content on the SharePoint site inherits permissions from the parent SharePoint site.

Allow only a few members or group for a folder

You can set specific permissions in SharePoint / Teams for :

Document library
Sub site
SharePoint list
Folder
Document
 

For each person / groups, you can set permission level :

Full control
Read / write
Read / write / but not delete
Read

 

Allow only a few members or group for a folder

1.Clic on three bullets on target folder
2.Clic “Manage access”
3.Clic three bullets
4.Clic “Advanced settings”

04 checkpermissions

 

 

 

 

 

 

 

 

 

 

 

 

 

By defaut the folder inherits parent folder or library permission, to break permissions and apply new ones :

1.Clic « Stop inheriting permissions »
2.Select available groups / users (to remove them or edit their permissions)
3.Remove user / group
4.Edit user / group permission (for read => read write per exemple)
5.Grant permission to a new user or group
6.Re inherite permissions (reset) from parent folder or document library
7.Check specifc user ou group permissions

 

06 delepermissions

 

 

 

 

 

 

 

 

 

 

Create specific permissions (Read / write but not delete)

By default in SharePoint we have 5 permissions levels

You can clic on default permission level to affine them but

 it’s not a good practice

.

To add a new permission level (in site permissions)

1.Clic permissions levels
2.Clic “Add a Permission level”
3.Name your permission
4.Check you required permissions
1.But uncheck “delete items”

 

07 permissionslevels

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Share / give permission to external user

Sharing with external users governance is managed at Tenant level

By default a Teams “private channel” cannot be Shared with externals users / domain 

With public site or channel, your can Share your SharePoint site :

1.Clic on site settings icon
2.Clic on “Site permissions” link
3.Clic on “Add members”
4.Select “Share site only”
5.Type user mail
6.Select the user and define his permission (Read, Edit or full control)
7.Clic on Add button

 

08 shareexternal

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

vvvvvx

 

ccc

SharePoint List Users And Their Groups By JS to CSV

On 06/09/2024


let siteUrl = '';


async function getUsersGroups(siteUrl) {

    // REST API endpoint to get group permissions
    const endpoint = `${siteUrl}`;

    // Fetch options with headers for authentication and response format
    const fetchOptions = {
        method: 'GET',
        headers: {
            'Accept': 'application/json;odata=verbose'
        }
    };

    console.log("endpoint", endpoint);
    // Send the asynchronous GET request to the REST API endpoint
    const response = await fetch(endpoint, fetchOptions);

    // Check if the response is OK (status code 200)
    if (!response.ok) {
        throw new Error(`Error fetching permissions: ${response.statusText}`);
    }

    // Parse the JSON response to extract permission data
    const data = await response.json();
    console.log("data", data);
    let csv = "Title;Email;LoginName;Id;IsSiteAdmin;UserOnly;GroupTitle;GroupId;Owner;Description;AllowMembersEditMembership;AllowRequestToJoinLeave;AutoAcceptRequestToJoinLeave;GroupId;Limited\r\n";
    for (const user of data.d.results) {

        //debugger;
        if (user.Groups && user.Groups.results && user.Groups.results.length > 0) {
            csv += `${user.Title};${user.Email};${user.LoginName};${user.Id};${user.IsSiteAdmin};true;;;;;;;;;NoLimited\r\n`;
            for (const group of user.Groups.results) {
                let limited = "not"
                if (`${group.Title}`.indexOf("imited") > 0) {
                    limited = "Limited";
                }
                csv += `${user.Title};${user.Email};${user.LoginName};${user.Id};${user.IsSiteAdmin};group;${group.Title};${group.Id};${group.OwnerTitle};${group.Description};${group.AllowMembersEditMembership};${group.AllowRequestToJoinLeave};${group.AutoAcceptRequestToJoinLeave};${group.Id};${limited}\r\n`;
            }
        } else
            csv += `${user.Title};${user.Email};${user.LoginName};${user.Id};${user.IsSiteAdmin};noGroup;;;;;;;;;noGroup\r\n`;
    }
    console.log("csv", csv);
}

let query = `/_api/web/siteusers?$select=IsSiteAdmin,Email,Id,LoginName,Title&$expand=Groups`;
await getUsersGroups(siteUrl + query);

Restreindre les accès dans PowerApps

On 06/09/2024

Restreindre l'accès à certaines sections d'une application PowerApps en fonction des groupes SharePoint permet de s'assurer que seules les personnes ayant les bonnes autorisations peuvent accéder à certaines fonctionnalités ou à des informations sensibles. PowerApps permet d'utiliser des groupes SharePoint pour personnaliser l'interface utilisateur en fonction des autorisations des utilisateurs.

Voici les étapes pour configurer ces restrictions en fonction des groupes SharePoint :

 1. Utiliser une connexion à SharePoint pour vérifier les groupes
Tout d'abord, vous devez ajouter la liste des groupes SharePoint à PowerApps. Vous pouvez utiliser la source de données SharePoint pour récupérer les groupes auxquels un utilisateur appartient.

# Étapes :
- Créez une liste SharePoint qui contient les informations des groupes SharePoint, ou utilisez la connexion directe à votre site SharePoint.
- Ajoutez SharePoint en tant que source de données dans PowerApps :
  1. Dans PowerApps Studio, allez dans l'onglet Data.
  2. Cliquez sur Add data.
  3. Sélectionnez SharePoint et connectez-vous à votre site.
  4. Choisissez une liste ou connectez-vous directement à votre site.

 2. Utiliser la fonction `User()` pour identifier l'utilisateur courant
PowerApps fournit la fonction `User()` qui permet d'identifier l'utilisateur actuellement connecté et obtenir son adresse e-mail.

User().Email


 3. Récupérer les groupes SharePoint d'un utilisateur
Pour vérifier si un utilisateur fait partie d'un groupe spécifique, vous pouvez utiliser la liste des groupes ou une connexion à SharePoint pour filtrer les groupes auxquels il appartient.

# Exemple de méthode :
Si vous avez une liste SharePoint appelée GroupesSharePoint avec une colonne `Title` (nom du groupe) et une colonne `Email` (adresse e-mail des membres du groupe), vous pouvez utiliser cette liste pour vérifier l'appartenance à un groupe.


If(
    CountRows(
        Filter(GroupesSharePoint, Email = User().Email && Title = "NomDuGroupe")
    ) > 0,
    true,
    false
)

 


Cela retourne `true` si l'utilisateur fait partie du groupe "NomDuGroupe", et `false` sinon.

 4. Utiliser des conditions pour afficher/masquer des sections
Une fois que vous avez vérifié si un utilisateur appartient à un groupe spécifique, vous pouvez utiliser cette logique pour afficher ou masquer certaines sections ou contrôles de l'application PowerApps.

# Exemple :
Supposons que vous ayez une section de formulaire (par exemple un conteneur, une galerie, ou un bouton) que vous souhaitez rendre visible uniquement pour les membres du groupe "Administrateurs".

- Sélectionnez le contrôle que vous souhaitez masquer.
- Allez dans la propriété Visible du contrôle.
- Utilisez une expression conditionnelle basée sur l'appartenance au groupe SharePoint.


If(


    CountRows(
        Filter(GroupesSharePoint, Email = User().Email && Title = "Administrateurs")
    ) > 0,
    true,
    false
)

 


Cela rend le contrôle visible uniquement si l'utilisateur fait partie du groupe Administrateurs.

 5. Restreindre l'accès à plusieurs groupes
Si vous souhaitez accorder l'accès à plusieurs groupes, vous pouvez ajuster la logique pour vérifier si l'utilisateur appartient à l'un des groupes autorisés.

# Exemple :


If(


    CountRows(
        Filter(GroupesSharePoint, Email = User().Email && (Title = "Administrateurs" || Title = "Managers"))
    ) > 0,
    true,
    false
)

 


Ici, l'utilisateur peut voir la section s'il fait partie soit du groupe Administrateurs soit du groupe Managers.

 6. Alternatives sans liste SharePoint - Utiliser des groupes Office 365
Si vous ne voulez pas créer une liste SharePoint pour gérer les autorisations, vous pouvez également utiliser des groupes Office 365 auxquels les utilisateurs sont déjà associés. PowerApps peut récupérer les informations sur les groupes Office 365 à l’aide du connecteur Office 365 Groups.

# Étapes :
1. Ajoutez la source de données Office 365 Groups à PowerApps.
2. Utilisez la fonction `Office365Groups.ListGroupMemberships()` pour obtenir les groupes dont l'utilisateur fait partie.

# Exemple :

If(


    CountRows(
        Filter(Office365Groups.ListGroupMemberships().value, displayName = "NomDuGroupe" && userPrincipalName = User().Email)
    ) > 0,
    true,
    false
)

 


 7. Masquer des Éléments de Menu ou des Boutons
Vous pouvez également utiliser cette logique pour masquer des boutons d'action ou des éléments de menu spécifiques pour des groupes d’utilisateurs.

# Exemple pour un bouton :

If(


    CountRows(
        Filter(GroupesSharePoint, Email = User().Email && Title = "Editeurs")
    ) > 0,
    true,
    false
)

 


Cela masque ou affiche le bouton uniquement si l'utilisateur appartient au groupe Editeurs.

 Conclusion

En utilisant les listes SharePoint ou les groupes Office 365, vous pouvez facilement restreindre l'accès à certaines sections d'une application PowerApps en fonction des autorisations de l'utilisateur. Cela permet d'assurer un contrôle d'accès flexible et sécurisé sans dupliquer les informations d'authentification. En configurant les sections de votre application avec des contrôles conditionnels, vous pouvez personnaliser l'expérience utilisateur en fonction des rôles et des groupes définis dans SharePoint.

Optimisation de la connexion à des listes SharePoint de grande taille dans PowerApps

On 06/09/2024

L'optimisation de la connexion à des listes SharePoint de grande taille dans PowerApps est cruciale pour assurer des performances fluides, surtout lorsque vous travaillez avec un grand volume de données. Voici quelques bonnes pratiques et techniques pour optimiser cette connexion.

 1. Limiter les enregistrements récupérés avec des filtres

Lorsque vous travaillez avec des listes SharePoint volumineuses (plus de 5000 éléments), il est essentiel de limiter le nombre d’enregistrements récupérés à l’aide de filtres.

- Utilisez des filtres spécifiques dans votre source de données pour limiter le nombre d'enregistrements. PowerApps applique une limite de 500 enregistrements par défaut (modifiable jusqu’à 2000), donc réduire la quantité de données à charger via des filtres est un bon point de départ.
  
Exemple : Filtrer les éléments à récupérer en fonction d'une condition.

powerapps


Filter(SharePointList, Status = "Active")


 2. Utiliser les fonctions déléguées

PowerApps utilise un concept appelé délégation pour gérer les grandes quantités de données en SharePoint. Lorsqu'une opération est déléguée, elle est effectuée côté serveur, ce qui permet de récupérer uniquement les données pertinentes.

- Utiliser des fonctions déléguées comme `Filter()`, `Sort()`, `LookUp()` qui sont prises en charge par SharePoint.
  
Exemple : Déléguer une opération de filtrage.

 


Filter(SharePointList, Created > Today() - 30)

Cela récupérera uniquement les éléments créés au cours des 30 derniers jours, tout en déléguant l'opération au serveur SharePoint.

 

 3. Éviter les fonctions non déléguées

Certaines fonctions dans PowerApps ne prennent pas en charge la délégation, ce qui signifie que l'application devra d'abord récupérer toutes les données côté client avant de pouvoir traiter la requête, ce qui ralentit considérablement l'application.

- Exemple de fonctions non déléguées : `Len()`, `StartsWith()`, `Mid()`, etc. Évitez-les dans vos requêtes si vous travaillez avec des listes de grande taille.

Mauvais exemple (non délégué) :


Filter(SharePointList, Len(Title) > 10)


 4. Limiter les colonnes récupérées (fonction `ShowColumns`)

Si vous n'avez pas besoin de toutes les colonnes de la liste SharePoint, utilisez la fonction `ShowColumns()` pour ne récupérer que les colonnes nécessaires, réduisant ainsi la charge sur le réseau et la performance.

Exemple :


ShowColumns(SharePointList, "Title", "Status", "DueDate")


Cela récupère uniquement les colonnes `Title`, `Status`, et `DueDate` de votre liste SharePoint.

 5. Pagination pour charger les données par lot

Pour des listes de très grande taille, vous pouvez implémenter une pagination dans PowerApps, en chargeant les données par lots pour éviter de récupérer un trop grand volume de données d'un coup.

Exemple de pagination (en fonction d'un ID de liste) :


ClearCollect(colPagedItems, Filter(SharePointList, ID > Last(colPagedItems).ID))

Cela permet de récupérer les éléments par lots en fonction de l'ID et d'ajouter chaque lot aux données précédentes.

 6. Utiliser des index de colonnes SharePoint

SharePoint permet d’indexer les colonnes dans les listes pour améliorer la performance des requêtes. Si vous effectuez des filtres sur une colonne particulière dans PowerApps, assurez-vous que cette colonne est indexée dans SharePoint.

- Exemple de colonnes à indexer : Colonnes souvent filtrées comme `Created`, `Modified`, ou des colonnes personnalisées.

Pour ajouter un index, dans la liste SharePoint :
- Allez dans Paramètres de la liste > Colonnes indexées > Créer un index.

 7. Utiliser des collections temporaires

Lorsque vous travaillez avec des données statiques ou peu modifiées, vous pouvez stocker temporairement les données SharePoint dans des collections PowerApps pour éviter de faire plusieurs appels réseau.

Exemple :


ClearCollect(TempCollection, SharePointList)


Une fois les données chargées dans la collection `TempCollection`, vous pouvez manipuler ces données localement sans avoir à refaire des requêtes vers SharePoint.

 8. Éviter les contrôles de galerie chargés de manière excessive

Lorsque vous utilisez des galeries pour afficher des données provenant de SharePoint, essayez de limiter le nombre d’enregistrements chargés simultanément. Trop de données affichées à la fois peut entraîner des ralentissements, surtout si chaque élément de la galerie a plusieurs contrôles.

- Conseil : Affichez une quantité limitée d’éléments et ajoutez une pagination ou un bouton "Charger plus".

 9. Désactiver l’actualisation automatique

Par défaut, certaines actions ou modifications dans PowerApps provoquent une actualisation automatique des données. Cela peut entraîner des ralentissements si de grandes quantités de données sont régulièrement rafraîchies.

- Conseil : Désactivez l’actualisation automatique pour les sources de données non essentielles et effectuez des rafraîchissements manuels uniquement lorsque nécessaire.


Refresh(SharePointList)


 10. Utiliser Power Automate pour des tâches complexes

Si vous avez besoin de traiter de grandes quantités de données ou d’exécuter des tâches complexes, envisagez d’utiliser Power Automate (anciennement Microsoft Flow) pour traiter ces opérations en arrière-plan, puis renvoyer les résultats dans PowerApps.

Exemple : Utilisez Power Automate pour rechercher et filtrer des éléments dans une liste SharePoint volumineuse, puis retournez uniquement les données filtrées à PowerApps.

 

 Conclusion

En suivant ces bonnes pratiques, vous pouvez optimiser la connexion de vos applications PowerApps à des listes SharePoint de grande taille. L'utilisation de filtres délégués, la limitation des colonnes récupérées, l’indexation des colonnes, et la pagination vous permettront de maintenir des performances optimales tout en améliorant l'expérience utilisateur.

SharePoint Format Date In View With JSON

On 06/09/2024

Format date background color in your SharePoint list with JSON

Json dateformatting

 


{
    "$schema": "https://developer.microsoft.com/json-schemas/sp/v2/column-formatting.schema.json",
    "elmType": "div",
    "txtContent": "@currentField",
    "style": {
       "background-color": 
       {
          "operator": "?",
          "operands": [
             {
                "operator": ">=",/*if the date is greater than tomorrow  24x60x60x1000 = 86400000 = 1day    */
                "operands": [
                   "@currentField",
                   {
                      "operator": "+",
                      "operands": [
                         "@now",
                         86400000
                      ]
                   }
                ]
             },
             "red",
             {
                "operator": "?",
                "operands": [
                   {
                      "operator": "<=",/*if the date is less than today - 60 days*/
                      "operands": [
                         "@currentField",
                         {
                            "operator": "-",
                            "operands": [
                               "@now",
                               5184000000 /* 86400000 x 60 */
                            ]
                         }
                      ]
                   },
                   "yellow",
                   "transparent"
                ]
             }
          ]
       }
    }
  }

Gestion des droits dans SharePoint On line Bonnes Pratiques Conseils

On 06/09/2024

La gestion des droits et des autorisations dans SharePoint Online est un aspect crucial pour garantir la sécurité et l'efficacité de la collaboration au sein d'une organisation. Une bonne gestion des droits aide à maintenir la confidentialité des données, réduit le risque d’erreurs et garantit que les utilisateurs peuvent accéder aux informations dont ils ont besoin, sans compromettre la sécurité.

Voici un aperçu des bonnes pratiques et des mauvaises pratiques en matière de gestion des droits dans SharePoint Online.

 Bonnes Pratiques de Gestion des Droits dans SharePoint Online

 1. Utiliser les Groupes de Sécurité et de SharePoint
   - Bonnes pratiques : Utilisez des groupes SharePoint ou des groupes de sécurité pour gérer les autorisations au lieu d’attribuer des droits directement à des utilisateurs individuels. Les groupes facilitent l’administration des droits et permettent une gestion plus efficace des utilisateurs.
    - Exemple : Créer des groupes comme "Contributeurs", "Lecteurs", "Admins" pour les différents niveaux d’accès et affecter les utilisateurs à ces groupes.
   - Pourquoi ? : Il est plus facile de gérer les autorisations en fonction des rôles et des équipes. Vous pouvez ajouter ou retirer des utilisateurs des groupes sans devoir réviser chaque autorisation individuellement.

 2. Hériter des Autorisations
   - Bonnes pratiques : Par défaut, les sites, les bibliothèques de documents et les listes héritent des autorisations de leur site parent. Cela permet de garder les autorisations cohérentes et plus faciles à gérer. Rompez l'héritage uniquement lorsqu'il est absolument nécessaire.
   - Pourquoi ? : Limiter les ruptures d’héritage rend la gestion des autorisations plus simple et cohérente. Cela aide à réduire les erreurs liées à des autorisations mal configurées sur des objets individuels.

 3. Appliquer le Principe du Moindre Droit (Least Privilege)
   - Bonnes pratiques : Donnez aux utilisateurs uniquement les permissions dont ils ont besoin pour accomplir leur travail. Par exemple, si un utilisateur a seulement besoin de lire des documents, ne lui donnez pas des droits de modification.
   - Pourquoi ? : Cela réduit les risques d’erreurs humaines et améliore la sécurité en limitant l'accès inutile aux fonctionnalités ou aux données sensibles.

 4. Utiliser les Modèles de Permissions Standard
   - Bonnes pratiques : Utilisez les modèles de permissions par défaut fournis par SharePoint, tels que "Contributeur", "Lecteur", "Propriétaire", au lieu de créer des niveaux de permissions personnalisés. Les permissions standard sont bien connues et bien documentées, facilitant la gestion.
   - Pourquoi ? : Les modèles standards garantissent une bonne compréhension des rôles et réduisent la complexité de la gestion des autorisations.

 5. Effectuer des Audits et des Revues Périodiques des Permissions
   - Bonnes pratiques : Révisez régulièrement les autorisations sur les sites SharePoint pour s'assurer que les utilisateurs n'ont pas plus de permissions que nécessaire, et que les anciennes autorisations inutiles sont révoquées.
   - Pourquoi ? : Avec le temps, des utilisateurs peuvent accumuler des permissions inutiles, ce qui augmente le risque de fuites de données ou d’accès non autorisé.

 6. Utiliser la Gestion des Accès Conditionnels et Azure AD
   - Bonnes pratiques : Pour les entreprises utilisant Azure Active Directory, configurez des politiques d'accès conditionnel qui obligent l'utilisation de la Multi-Factor Authentication (MFA) et des restrictions d'accès basées sur l'emplacement.
   - Pourquoi ? : Ces pratiques renforcent la sécurité en s'assurant que seuls les utilisateurs autorisés, se connectant de manière sécurisée, ont accès à vos données SharePoint.

 7. Documenter les Modifications de Permissions
   - Bonnes pratiques : Chaque changement d'autorisation doit être documenté, en notant pourquoi il a été effectué, par qui et pour quelle durée. Cette documentation aide lors d'audits futurs ou de la résolution de problèmes.
   - Pourquoi ? : Cela facilite la gestion à long terme, permet de retracer les modifications en cas de problème et contribue à la conformité des données.

 

 Mauvaises Pratiques de Gestion des Droits dans SharePoint Online

 1. Donner des Permissions Directes à des Utilisateurs Individuels
   - Mauvaise pratique : Attribuer des droits directement à des utilisateurs au lieu d'utiliser des groupes.
   - Pourquoi c'est un problème ? : Gérer les autorisations devient difficile lorsque les utilisateurs quittent l'organisation ou changent de rôle. Cela rend également la gestion des droits plus complexe et propice aux erreurs.

 2. Rompre l'Héritage des Permissions Trop Souvent
   - Mauvaise pratique : Rompre l'héritage des autorisations à plusieurs niveaux (par exemple, sur des fichiers ou des sous-dossiers individuels).
   - Pourquoi c'est un problème ? : Cela complique énormément la gestion des permissions. Vous pouvez perdre le contrôle de qui a accès à quoi, et il devient facile de faire des erreurs en octroyant ou en révoquant des droits.

 3. Donner Trop de Permissions (Trop de Propriétaires)
   - Mauvaise pratique : Donner des droits de propriétaire ou des droits d'édition à trop de personnes.
   - Pourquoi c'est un problème ? : Les propriétaires peuvent modifier des paramètres critiques, ce qui peut affecter la structure du site et les permissions des autres utilisateurs. Cela augmente également le risque d’erreurs ou d’accès non autorisé à des informations sensibles.

 4. Ne Pas Révoquer les Permissions des Anciens Utilisateurs
   - Mauvaise pratique : Ne pas retirer les autorisations d’utilisateurs qui ne sont plus actifs dans le projet ou l'organisation.
   - Pourquoi c'est un problème ? : Des anciens utilisateurs peuvent encore avoir accès à des informations sensibles. Cela représente un risque de sécurité majeur, surtout si ces utilisateurs ne sont plus sous votre contrôle.

 5. Ne Pas Gérer les Permissions sur les Documents Sensibles
   - Mauvaise pratique : Donner des droits larges sur des documents sensibles sans les protéger spécifiquement (par exemple, via une bibliothèque dédiée avec des permissions restreintes).
   - Pourquoi c'est un problème ? : Cela peut conduire à des fuites de données, où des utilisateurs qui n’ont pas besoin d’accéder à des documents critiques y ont tout de même accès.

 6. Ne Pas Utiliser les Groupes O365/AAD pour Simplifier la Gestion
   - Mauvaise pratique : Ne pas intégrer les groupes Office 365/Azure Active Directory pour la gestion des droits SharePoint.
   - Pourquoi c'est un problème ? : Ne pas tirer parti des groupes centralisés complique la gestion des utilisateurs et peut entraîner des redondances ou des erreurs de permissions.

 7. Manque de Documentation sur les Modifications des Permissions
   - Mauvaise pratique : Modifier les autorisations sans documentation claire.
   - Pourquoi c'est un problème ? : En l'absence de documentation, il devient difficile de savoir qui a modifié quoi, pourquoi et quand. Cela complique les audits et rend le diagnostic des problèmes d’accès plus long.

 

 Conclusion

La gestion des droits dans SharePoint Online doit être effectuée avec soin pour assurer la sécurité et l'efficacité. En suivant les bonnes pratiques, vous pouvez éviter des problèmes tels que des accès non autorisés, des erreurs de configuration, et des difficultés de gestion à long terme. Évitez les mauvaises pratiques qui peuvent entraîner une confusion dans la gestion des permissions et augmenter le risque de failles de sécurité.

Une gestion rigoureuse des droits est cruciale pour protéger les informations sensibles et assurer que les utilisateurs accèdent seulement aux ressources nécessaires à leur travail.

Get SharePoint Lists With JS

On 05/09/2024

Display Sharepoint lists in an html table, and in a csv text content

// The content of the stylesheet
const styleSheetContent = `
    .cadre-table-scroll {
  display: inline-block;
  height: 100em;
  overflow-y: scroll;
    overflow-x: auto;
    white-space: nowrap;
}
.table-scroll thead th {
  position: sticky;
  top: 0;
}
.table-scroll tfoot td {
  position: sticky;
  bottom: 0;
}
 .table-scroll tbody {
    display: table;
    width: 100%;
}
`;

// 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;
}


async function getSharePointLists(query) {

    const endpoint = `${query}`;

    // Fetch options with headers for authentication and response format
    const fetchOptions = {
        method: 'GET',
        headers: {
            'Accept': 'application/json;odata=verbose'
        }
    };

    console.log("endpoint", endpoint);
    // Send the asynchronous GET request to the REST API endpoint
    const response = await fetch(endpoint, fetchOptions);

    // Check if the response is OK (status code 200)
    if (!response.ok) {
        throw new Error(`Error fetching permissions: ${response.statusText}`);
    }

    // Parse the JSON response to extract permission data
    const data = await response.json();
    let csv = "Id;Title;RootFolderr;BaseTemplate;ContentTypesEnabled;Created;ItemCount;EnableMinorVersions;EnableModeration;EnableVersioning;LastItemModifiedDate;NoCrawl;Hidden\r\n";
    // Extract lists
    const lists = data.d.results;
    console.log("lists", lists);
    createStyleElement("fdiStyle", styleSheetContent);
    let html = `<table class="cadre-table-scroll"><thead><tr>
    <th>Id</th>
    <th>Title</th>
    <th>RootFolderr/ServerRelativeUrl</th>
    <th>BaseTemplate</th>
    <th>ContentTypesEnabled</th>
    <th>Created</th>
    <th>ItemCount</th>
    <th>EnableMinorVersions</th>
    <th>EnableModeration</th>
    <th>EnableVersioning</th>
    <th>LastItemModifiedDate</th>
    <th>NoCrawl</th>
    <th>Hidden</th></tr><thead><tbody>
`;
    for (let i = 0; i < lists.length; i++) {
        const responseList = await fetch(`${lists[i].__metadata.id}?$select=HasUniqueRoleAssignments`, fetchOptions);
        const data = await responseList.json();
        //console.log("responseList", data);
csv += `${lists[i].Id};${lists[i].Title};${lists[i].RootFolder.ServerRelativeUrl};${lists[i].BaseTemplate};${lists[i].ContentTypesEnabled};${lists[i].Created};${lists[i].ItemCount};${lists[i].EnableMinorVersions};${lists[i].EnableModeration};${lists[i].EnableVersioning};${lists[i].LastItemModifiedDate};${lists[i].NoCrawl};${lists[i].Hidden}`;
csv += `\r\n`;
        if (lists[i].Title === "DO_NOT_DELETE_SPLIST_SITECOLLECTION_AGGREGATED_CONTENTTYPES")
            continue;
        lists[i].HasUniqueRoleAssignments = data.d.HasUniqueRoleAssignments;
        html += `<tr>
    <td>${lists[i].Id}</td>
    <td>${lists[i].Title}</td>
    <td>${lists[i].RootFolder.ServerRelativeUrl}</td>
    <td>${lists[i].BaseTemplate}</td>
    <td>${lists[i].ContentTypesEnabled}</td>
    <td>${lists[i].Created}</td>
    <td>${lists[i].ItemCount}</td>
    <td>${lists[i].EnableMinorVersions}</td>
    <td>${lists[i].EnableModeration}</td>
    <td>${lists[i].EnableVersioning}</td>
    <td>${lists[i].LastItemModifiedDate}</td>
    <td>${lists[i].NoCrawl}</td>
    <td>${lists[i].Hidden}</td>
</tr>`;
    }
    html += "</tbody></table>"
    document.body.innerHTML = html;
    console.log("datas", csv);

}

let req = "/_api/web/lists?$select=Id,Title,RootFolder/ServerRelativeUrl,ItemCount,BaseTemplate,ContentTypesEnabled,Created,EnableMinorVersions,EnableModeration,EnableVersioning,LastItemModifiedDate,NoCrawl,Hidden&$expand=RootFolder&$OrderBy=Hidden,Title";
let siteUrl = ";


getSharePointLists(siteUrl + req);

Get SharePoint Web Permissions

On 05/09/2024

Gets sharepoint roleassignements and display page, and createcsv text

 


// The content of the stylesheet
const styleSheetContent = '
    .container {
        display: grid;
        row-gap: 10px;
        /* 20px gap between rows */
        column-gap: 10px;
        /* 10px gap between columns */
        grid-template-columns: 1fr 1fr 1fr 1fr;
        padding-bottom: 5px;
    }
    .container2 {
        display: grid;
        row-gap: 10px;
        /* 20px gap between rows */
        column-gap: 10px;
        /* 10px gap between columns */
        grid-template-columns: 1fr 1fr 1fr;
        padding-bottom: 5px;
    }

    .container .header {
        text-align: center;
        border: 1px solid black;
    }

    .container .data {
        border: 1px solid black;
    }
';

// 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")[0];
            head.appendChild(style);
        } else {
            st.innerHTML = content;
        }
    }
    return style;
}



// Function to get permissions for a SharePoint group
async function getSharePointPermissions(siteUrl) {
  
    // REST API endpoint to get group permissions
    const endpoint = '${siteUrl}';

    // Fetch options with headers for authentication and response format
    const fetchOptions = {
      method: 'GET',
      headers: {
        'Accept': 'application/json;odata=verbose'
      }
    };

    console.log("endpoint", endpoint);
    // Send the asynchronous GET request to the REST API endpoint
    const response = await fetch(endpoint, fetchOptions);

    // Check if the response is OK (status code 200)
    if (!response.ok) {
      throw new Error('Error fetching permissions: ${response.statusText}');
    }

    // Parse the JSON response to extract permission data
    const data = await response.json();
    
    // Extract role assignments (permissions)
    const roleAssignments = data.d.results;

    console.log('roleAssignments: ', data.d.results);
    createStyleElement("fdiStyle", styleSheetContent);
    let repportCSV = 'Title;LoginName;Id;Role_Name;Role_Description;Role;Id';
    repportCSV += "\r\n";
    document.body.innerHTML = '<div class="container">
    <div class="header">Title</div>
    <div class="header">LoginName</div>
    <div class="header">Id</div>
    <div class="header">Role
        <div class="container2">
            <div class="data">Name</div>
            <div class="data">Description</div>
            <div class="data">Id</div>
        </div>
    </div>
</div>';

    // Report permissions
    roleAssignments.forEach(roleAssignment => {
      const member = roleAssignment.Member;
      const roleBindings = roleAssignment.RoleDefinitionBindings.results;
      repportCSV+= '${member.Title};${member.LoginName};${member.Id}';
      let div = '      
<div class="container">
    <div class="data">${member.Title}</div>
    <div class="data">${member.LoginName}</div>
    <div class="data">${member.Id}</div>';
      roleBindings.forEach(role => {
        repportCSV+= ';${role.Name};${role.Description};${role.Id}';
        repportCSV += "\r\n";
        div += '
        <div class="container2">
            <div class="data">${role.Name}</div>
            <div class="data">${role.Description}</div>
            <div class="data">${role.Id}</div>
        </div>
        ';
        console.log(' - Role: ${role.Name}');
      });
      div += "
"; document.body.innerHTML += div; }); console.log("datas", repportCSV); } // Usage example: 'your-site-url' with actual values let siteUrl = ''; siteUrl += '/_api/web/roleassignments/?$expand=RoleDefinitionBindings,Member&$select=Member/Title,Member/Id,Member/LoginName'; // Replace with the actual site URL getSharePointPermissions(siteUrl);
In HTML

Css Best Practices For SPFX React

On 04/09/2024

Créer des CSS responsives pour une solution SPFX (SharePoint Framework) avec React nécessite une approche structurée et l'utilisation de bonnes pratiques. Voici quelques recommandations pour vous aider à y parvenir :

1. Utiliser des Media Queries

Les media queries sont essentielles pour créer des designs responsives. Elles permettent d'appliquer des styles différents en fonction de la taille de l'écran.

@media (max-width: 600px) { .container { flex-direction: column; } }

2. Frameworks CSS Responsive

Utiliser des frameworks CSS comme Bootstrap, Tailwind CSS ou Material-UI peut simplifier la création de designs responsives. Ces frameworks offrent des classes utilitaires et des composants pré-stylisés qui sont déjà responsives.

3. Flexbox et Grid Layout

Utilisez Flexbox et CSS Grid pour créer des mises en page flexibles et responsives.

.container { display: flex; flex-wrap: wrap; } .item { flex: 1 1 100%; } @media (min-width: 600px) { .item { flex: 1 1 50%; } }

4. Variables CSS

Utilisez des variables CSS pour gérer les valeurs récurrentes comme les couleurs, les marges et les tailles de police.

:root { --primary-color: #3498db; --font-size: 16px; } .button { background-color: var(--primary-color); font-size: var(--font-size); }

5. Unités Relatives

Utilisez des unités relatives comme em, rem, % et vw/vh pour créer des designs qui s'adaptent aux différentes tailles d'écran.

.container { width: 90%; max-width: 1200px; margin: 0 auto; }

6. Mobile-First Approach

Adoptez une approche mobile-first en écrivant d'abord les styles pour les petits écrans, puis en utilisant des media queries pour ajuster les styles pour les écrans plus grands.

.container { display: flex; flex-direction: column; } @media (min-width: 600px) { .container { flex-direction: row; } }

7. Utiliser des Composants React Responsive

Il existe des bibliothèques comme react-responsive qui permettent de créer des composants React responsives.

import { useMediaQuery } from 'react-responsive'; const MyComponent = () => { const isMobile = useMediaQuery({ query: '(max-width: 600px)' }); return ( <div className={isMobile ? 'mobile-container' : 'desktop-container'}> {/* Your content here */} </div> ); };

8. Optimiser les Images

Utilisez des images responsives avec les attributs srcset et sizes pour servir des images de différentes tailles en fonction de la résolution de l'écran.

<img src="image.jpg" srcset="image-320w.jpg 320w, image-640w.jpg 640w" sizes="(max-width: 600px) 100vw, 50vw" alt="Responsive Image">

9. Utiliser des Outils de Développement

Utilisez des outils comme les DevTools de Chrome pour tester et déboguer vos designs responsives.

10. Documentation et Tests

Documentez vos styles et testez-les sur différents appareils et navigateurs pour vous assurer qu'ils fonctionnent correctement.

En suivant ces bonnes pratiques, vous pouvez créer des CSS responsives efficaces pour votre solution SPFX avec React, garantissant ainsi une expérience utilisateur optimale sur tous les appareils.

Reccueil Besoin Projet SharePoint

On 04/09/2024

Lors de la phase de recueil des besoins pour une application SharePoint, il est essentiel de poser des questions stratégiques pour bien comprendre les attentes du client, définir le périmètre du projet, et éviter les dépassements de budget ou de calendrier. Voici une liste de questions importantes à poser, organisées par catégorie, pour vous aider à affiner les besoins et rédiger des spécifications fonctionnelles précises.

1. Questions générales sur le projet

Quel est l'objectif principal de cette application ?

Quels problèmes ou besoins spécifiques cherchez-vous à résoudre avec cette application ?

Qui sont les principaux utilisateurs de l'application ?

Y a-t-il des applications ou des systèmes existants que l'application SharePoint devra remplacer ou compléter ?

Avez-vous des exemples de solutions similaires que vous avez utilisées ou que vous aimeriez imiter ?

2. Fonctionnalités principales

Quelles sont les fonctionnalités indispensables de l'application ?

Quelles sont les fonctionnalités secondaires ou souhaitables ?

Y a-t-il des flux de travail spécifiques que vous souhaitez automatiser ?

Quels types de contenu seront gérés (documents, listes, bibliothèques, etc.) ?

Comment souhaitez-vous que les utilisateurs interagissent avec l'application (interface utilisateur, expérience utilisateur) ?

3. Gestion des utilisateurs et permissions

Comment les utilisateurs seront-ils gérés ? Faut-il intégrer des groupes d'utilisateurs existants ?

Quelles permissions spécifiques doivent être définies pour différents types d'utilisateurs ?

Y a-t-il des rôles utilisateurs spécifiques à définir (administrateurs, contributeurs, lecteurs, etc.) ?

4. Intégration et interopérabilité

L'application devra-t-elle s'intégrer avec d'autres systèmes (ERP, CRM, etc.) ou services externes (API, services web) ?

Quels outils ou technologies internes existants doivent être compatibles avec cette application ?

Avez-vous des besoins spécifiques concernant l'importation ou l'exportation de données ?

5. Structure des données

Quelles sont les principales entités de données que l'application doit gérer ?

Comment les données seront-elles organisées (listes SharePoint, bibliothèques de documents, métadonnées) ?

Y a-t-il des exigences spécifiques en matière de recherche, de tri ou de filtrage des données ?

6. Design et expérience utilisateur

Avez-vous des attentes spécifiques en matière de design ou de branding (couleurs, logo, etc.) ?

Souhaitez-vous que l'application soit compatible avec des appareils mobiles ?

Quels sont les critères clés pour l'expérience utilisateur (simplicité, rapidité, accessibilité) ?

7. Performance et évolutivité

Quel est le nombre prévu d'utilisateurs simultanés ?

Quelle est la quantité estimée de contenu que l'application devra gérer (documents, données, etc.) ?

L'application doit-elle être évolutive pour accueillir une augmentation du nombre d'utilisateurs ou de contenu ?

8. Sécurité et conformité

Quelles sont les exigences de sécurité spécifiques pour cette application (chiffrement, audit, etc.) ?

Y a-t-il des réglementations ou des normes de conformité spécifiques à respecter (RGPD, ISO, etc.) ?

Faut-il prévoir des sauvegardes régulières ou des procédures de récupération en cas de sinistre ?

9. Maintenance et support

Qui sera responsable de la maintenance de l'application après son déploiement ?

Avez-vous des exigences pour le support technique (SLA, support 24/7, etc.) ?

Souhaitez-vous une formation pour les utilisateurs finaux ou les administrateurs ?

10. Périmètre et calendrier

Quelles sont les fonctionnalités ou parties du projet qui peuvent être reportées dans une phase ultérieure ?

Quel est le calendrier souhaité pour le déploiement de l'application ?

Y a-t-il des dates limites ou des jalons importants à respecter ?

11. Budget

Quel est le budget alloué pour ce projet ?

Y a-t-il des contraintes budgétaires qui pourraient affecter la portée ou les fonctionnalités de l'application ?

12. Risques et challenges

Voyez-vous des risques ou des défis spécifiques pour ce projet ?

Y a-t-il des points d'incertitude ou des zones grises dans les exigences actuelles ?

13. Feedback et révisions

Comment souhaitez-vous valider les différentes étapes du projet ?

À quelle fréquence souhaitez-vous des réunions ou des rapports d'avancement ?

Conclusion

Ces questions vous permettront de mieux comprendre les besoins du client, de clarifier les attentes, de définir un périmètre clair et de rédiger des spécifications fonctionnelles précises. Assurez-vous de bien documenter les réponses et de valider les points critiques avec le client pour éviter tout malentendu ou ajustement coûteux en cours de projet.

SharePoint Restore File Version

On 03/06/2024

/_api/web/getlist('/sites/MySite/MyDocLib')/items(341)/File/versions/restoreByLabel(versionlabel='5.0')

Csom query All Items More than 5000

On 28/05/2024



$url = "https://test.sharepoint.com/sites/monSitte"
# Connect-PnPOnline -Url $url -Interactive
Clear-Host
$listTitle = "EarthStation_Authorisations"
$list = Get-PnPList -Identity "Antenna_Model_Ressources" -ThrowExceptionIfListNotFound

$ctx = Get-PnPContext
$page = $null
$pageNumber = 0;
$rowLimit = 3000
$datas = @();

#get first item
$reqFirst = "<View Scope='RecursiveAll'>
<Query>
    <ViewFields>
        <FieldRef Name='ID' />
    </ViewFields>
    <Where>
    </Where>
    <OrderBy>
        <FieldRef Name='ID' Ascending='TRUE' />
    </OrderBy>
</Query>
<RowLimit>1</RowLimit>
</View>"
$spqQuery = New-Object Microsoft.SharePoint.Client.CamlQuery # $req123 #
$spqQuery.ViewXml = $reqFirst #
$itemki = $list.GetItems($spqQuery); 
$ctx.Load($itemki)
$ctx.ExecuteQuery();

$itemId = $itemki[0].ID

# get last item
$reqFirst = "<View Scope='RecursiveAll'>
<Query>
    <ViewFields>
        <FieldRef Name='ID' />
    </ViewFields>
    <Where>
    </Where>
    <OrderBy>
        <FieldRef Name='ID' Ascending='FALSE' />
    </OrderBy>
</Query>
<RowLimit>1</RowLimit>
</View>"
$spqQuery = New-Object Microsoft.SharePoint.Client.CamlQuery # $req123 #
$spqQuery.ViewXml = $reqFirst #
$itemki = $list.GetItems($spqQuery); 
$ctx.Load($itemki)
$ctx.ExecuteQuery();

$max = $itemki[0].ID

$startDate = Get-Date
. ..\common.ps1
Do {
    $stringBuilder = New-Object System.Text.StringBuilder
  
    
  
    $stringBuilder.Append("<View Scope='RecursiveAll'>") | Out-Null
   
    $stringBuilder.Append("<Query><Where><And><And><Geq><FieldRef Name='Modified' /><Value Type='DateTime'>1900-12-01T19:49:00Z</Value></Geq><Gt><FieldRef Name='ID' /><Value Type='Counter'>$($itemId)</Value></Gt></And><Leq><FieldRef Name='ID' /><Value Type='Counter'>$($itemId + $rowLimit)</Value></Leq></And></Where>") | Out-Null
    # $stringBuilder.Append("<Query><Where><And><And><Geq><FieldRef Name='Modified' /><Value Type='DateTime'>1900-12-01T19:49:00Z</Value></Geq><Gt><FieldRef Name='ID' /><Value Type='Counter'>$($itemId)</Value></Gt></And><Leq><FieldRef Name='ID' /><Value Type='Counter'>$($itemId + $rowLimit)</Value></Leq></And></Where>") | Out-Null
    $stringBuilder.Append("<OrderBy><FieldRef Name='ID' Ascending='TRUE' /></OrderBy>") | Out-Null
    $stringBuilder.Append("</Query>") | Out-Null
   
    $stringBuilder.Append("<ViewFields>") | Out-Null
    $stringBuilder.Append("<FieldRef Name='Title' />") | Out-Null
    $stringBuilder.Append("<FieldRef Name='FileLeafRef' />") | Out-Null
    $stringBuilder.Append("<FieldRef Name='FileRef' />") | Out-Null
    $stringBuilder.Append("<FieldRef Name='Modified' />") | Out-Null
    $stringBuilder.Append("<FieldRef Name='Created' />") | Out-Null
    $stringBuilder.Append("<FieldRef Name='ID' />") | Out-Null
    $stringBuilder.Append("<FieldRef Name='Editor' />") | Out-Null
    $stringBuilder.Append("</ViewFields>") | Out-Null
   
    $stringBuilder.Append("<RowLimit Paged='TRUE'>$($rowLimit)</RowLimit>") | Out-Null
    $stringBuilder.Append("</View>") | Out-Null
     
    $req123 = Get-Content -Path ".\req\req1.xml" -Encoding:UTF8
    $spqQuery = New-Object Microsoft.SharePoint.Client.CamlQuery # $req123 #
    $spqQuery.ViewXml = $stringBuilder.ToString();
    $spqQuery.ListItemCollectionPosition = $page


    $pageNumber ++;

    $itemki = $list.GetItems($spqQuery); 
    $spqQuery.ListItemCollectionPosition = $itemki.ListItemCollectionPosition

    try {
        $ctx.Load($itemki)
        $ctx.ExecuteQuery();
    }
    catch {
        <#Do this if a terminating exception happens#>
        Write-Host "$($_)"
        Write-Host "$($_)"
        exit 0
    }


    Write-Host "################## PAGE " $($page.PagingInfo) " #########################"
    Write-Host "processing query results. Recs: $($itemki.Count) itemId $($itemId) max $($max)"
    
    $Counter = 0;
    foreach ($item in $itemki) {
        # Write-Host "$($item["ID"]) title pageNumber '$($pageNumber)' : $($item["Title"])"
        $datas += [PSCustomObject]@{
            "FileLeafRef" = $item["FileLeafRef"]
            "Title"       = $item["Title"]
            "FileRef"     = $item["FileRef"]
            "Modified"    = $item["Modified"]
            "Created"    = $item["Created"]
            "Editor"    = $item["Editor"].Email
        }
        $itemId = $item.ID
    }

    $page = $itemki.ListItemCollectionPosition
    # $itemId += $rowLimit
}   
Until($itemId -ge $max) 
Write-Host "$($listTitle) list.ItemCount $($list.ItemCount)" 
$datas | Export-Csv -Path ".\datas\$($listTitle)_7.csv" -Encoding utf8 -NoTypeInformation -Delimiter ";" -Append
Write-Host "$(AddNiceTimeSpan -start $startDate)"

SharePoint Copy Files To Library

On 27/05/2024


{
    "siteUrl": "https://tenant.sharepoint.com/sites/fdiSandBox/",
    "targetFolder": "/Shared%20Documents/test",
    "sourceFolder": "C:\\temp\\myFolderr",
    "checkIfFileExists": true,
    "forceOverWrite": false,
    "includeChildFolders": true,
    "copyDates": false,
    "copyAuthor": false,
    "lofFileName": "copyfiles",
    "forbiddenChars": "~,#,%,&,*,{,},\\,:,<,>,?,/,|,",
    "replacementChar": "_"
}


Clear-Host
. .\common.ps1
#get copy configuration
$config = GetConnectionConfiguration -filePath ".\config\configuration.json"
$logFileName = "$($config.lofFileName))_$(Get-Date -Format "yyyyMMddhhmmss").log"
$loadedFolders = @();
$startDate = Get-Date
function CheckAlreadyConnected {
    Param([Parameter (Mandatory = $true)][string]$url)
    $ctx = Get-PnPContext
    if ($null -eq $ctx) {
        Connect-PnPOnline -Url $url -Interactive -ErrorAction Stop
        WriteInfo -Message "connected to $($url)"
    }
    else {
        $web = Get-PnPWeb
        if ($web.Url.Trim().ToLower().trim("/") -ne $url.Trim().ToLower().trim("/")) {
            Disconnect-PnPOnline
            WriteInfo -Message "Disconnect-PnPOnline  $($web.Url)"
            Connect-PnPOnline -Url $url -Interactive -ErrorAction Stop
            WriteInfo -Message "connected to $($url)"
        }
    }
}
CheckAlreadyConnected -url $config.siteUrl
$web = Get-PnPWeb

function copyFileToSp {
    Param([Parameter (Mandatory = $true)][string]$sourceFilePath,
        [Parameter (Mandatory = $true)][string]$destinationPath
        , $created, $modified
    )
    #check target folder exists
    if (-not $loadedFolders.Contains($destinationPath)) {
        $folder = Get-PnPFolder -Url $destinationPath -ErrorAction SilentlyContinue
        if ($null -eq $folder) {
            $datas = $destinationPath.Split("/", [System.StringSplitOptions]::RemoveEmptyEntries)
            $folderName = $datas[$datas.Length - 1];
            $target = $destinationPath.Substring(0, $destinationPath.Length - $folderName.Length - 1)

            Add-PnPFolder -Name $folderName -Folder $target
        }
        $loadedFolders += $destinationPath
    }
    $destination
    $destinationPath
    $Asset = @{}
    $valuesOk = $false
    # modified
    if ($null -ne $modified) {
        $date = Get-Date -Date $modified 
        $Asset.add("Modified", $date.ToString("yyyy-MM-dd HH:mm"))
        $valuesOk = $true;
    }
    # created
    if ($null -ne $created) {
        $date = Get-Date -Date $created 
        $Asset.add("Created", $date.ToString("yyyy-MM-dd HH:mm"))
        $valuesOk = $true;
    }

    if ($valuesOk -eq $true) {    
        $spFile = Add-PnPFile -Path $sourceFilePath -Folder $destinationPath -Values $Asset
    }
    else {        
        $spFile = Add-PnPFile -Path $sourceFilePath -Folder $destinationPath
    }
    $pp = 0;
    $pp ++;
}

if ($config.includeChildFolders) {
    $filesToCopy = Get-ChildItem -LiteralPath $config.sourceFolder -Recurse
}
else {
    $filesToCopy = Get-ChildItem -LiteralPath $config.sourceFolder 
}
$filesToCopy.Length


foreach ($file in $filesToCopy) {
    $file.FullName;
    $file.LastAccessTime
    if ($file.GetType().Name -eq "FileInfo") {
        $filePath = $file.DirectoryName
        $destinationFileName = $filePath.Substring($config.sourceFolder.Length).trim("\").Replace("\", "/")
        $destinationFileName = "$($web.ServerRelativeUrl)/$($config.targetFolder.Trim().Trim("/"))/$($destinationFileName)".Replace("%20", " ").TrimEnd("/")
        Write-Host "dest : '$($destinationFileName)'"
        copyFileToSp -sourceFilePath $file.FullName -destinationPath $destinationFileName -modified $file.LastWriteTime -created $file.CreationTime
    }
}
WriteInfo -Message "nb files / folders added : $($filesToCopy.Length)"
AddNiceTimeSpan -start $startDate

Power Automate Update ListItem ContentType

On 06/05/2024

Power Automate Update ListItem ContentType

With "Send an HTTP request to SharePoint"

Headers : 


{
  "Accept": "application/json; odata=verbose",
  "content-type": "application/json; odata=verbose",
  "X-HTTP-Method": "MERGE",
  "If-Match": "*"
}
Updateitemct

Sharepoint REST

On 19/02/2024

PnP references

msdn PNP

Get field in list

/_api/Web/Lists/getbytitle('Pages')/Fields?$select=InternalName,Id,Hidden,TypeAsString,Required&$filter=InternalName eq 'Title'

Get list content types

Get contentType fields

/_api/Web/Lists/getbytitle('Pages')/ContentTypes('0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF390028D78BF8D3054E38BEF0590B43C4BA0E00572E4F21E9A2274ABC0C3A6B4238D562')/fields?$filter=(Hidden eq false)and (Group ne '_Hidden')&$select= InternalName,Hidden,Required,Title,TypeAsString,Required,Id,Group

Query search

/_api/search/query?querytext='Path:https://mcn365.sharepoint.com/sites/a_IntranetPays_42/sitePages/*'&selectproperties='Title,Path'

/_api/search/query?querytext='owstaxIdJiveTags:GP0*+OR+owstaxIdJiveCategories:GP0*'&rowsperpage=0&rowlimit=5&selectproperties='Title%2cowstaxIdJiveCategories%2cowstaxIdJiveTags'&sortlist='Rank:descending%2cmodifiedby:ascending'&clienttype='ContentSearchRegular'">https://mcncs36sddcsdcsint.com/sites/a_IntranetPays_42/_api/search/query?querytext='owstaxIdJiveTags:GP0*+OR+owstaxIdJiveCategories:GP0*'&rowsperpage=0&rowlimit=5&selectproperties='Title%2cowstaxIdJiveCategories%2cowstaxIdJiveTags'&sortlist='Rank:descending%2cmodifiedby:ascending'&clienttype='ContentSearchRegular'

test code

/_api/Web/Lists/getbytitle('Pages')/ContentTypes('0x010100C568DB52D9D0A1d37AF390028D78BF8D3054E38BEF0590B43C4BA0E00572E4F21E9A2274ABC0C3A6B4238D562')/fields?$filter=(Hidden eq false)and (Group ne '_Hidden')&$select= InternalName,Hidden,Required,Title,TypeAsString,Required,Id,Group

query user multi

/_api/web/lists/getbytitle('ResponsableRegions')/items(1)?$select=ResponsablesRegions/EMail,Id&$expand=ResponsablesRegions

get fields

/_api/web/lists/GetByTitle('Liste des chantiers')/fields?$filter=Hidden eq false&$orderby=Title&$select=Title,InternalName,TypeAsString,Hidden