Top
Photo by engin akyurt on Unsplash

Tworzenie listy SharePoint Online przy użyciu innej jako szablonu


W tym wpisie chciałbym podzielić się z Wami fajnym rozwiązaniem, które niedawno zbudowałem dla mojego klienta. Pozwala ono na dynamiczne tworzenie list w oparciu o istniejące listy (szablony) wraz z ustawieniem wszystkich potrzebnych metadanych i uprawnień.

Krótkie wprowadzenie do wymagań

Klient ma proces, który przetwarza przychodzące pliki XML, a następnie zapisuje z nich dane na powiązanych listach SharePoint. Jednak pomysł wstępnego stworzenia dziesiątek list jest kiepski. Co więcej, zawsze może zaistnieć sytuacja, w której lista dla danych metadanych jeszcze nie istnieje. Z tego powodu zdecydowałem się raczej przyjrzeć dynamicznemu tworzeniu list.

Jak dynamicznie tworzyć listę?

Głównym wyzwaniem było znalezienie sprytnego sposobu na dynamiczne tworzenie nowej listy. Potrzebowałem rozwiązania, które pozwoli użytkownikom biznesowym na samodzielne zarządzanie szablonami w elastyczny i łatwy sposób.

Najpierw próbowałem używać site scripts w site designs – ale nie było to idealne rozwiązanie, ponieważ każda zmiana szablonu listy wymagałaby od administratora utworzenia nowego site script, a następnie aktualizacji tego już zarejestrowanego.

Następnie myślałem o jakiejś liście konfiguracyjnej, w której wszystkie kolumny i widoki byłyby w jakiś sposób zdefiniowane, oraz o przepływie, który utworzyłby listę, a następnie kolumny jedna po drugiej, ale… serio? Przecież musi być łatwiejszy sposób 😉

Na koniec postanowiłem zbadać, co się dzieje, gdy nowa lista zostanie utworzona przy użyciu istniejącej, za pomocą akcji dostępnych w interfejsie SPO.

Create a list using existing one as source.

Pomyślałem, że musi istnieć endpoint do tego, jednak nie udało mi się go znaleźć w żadnych dokumentach Microsoftu. W Google znalazłem ten wspaniały post (Create new SharePoint list from existing SP list with Power Automate (tomriha.com)) autorstwa Tomáša Říha, w którym wyjaśnił, w jaki sposób udało mu się uzyskać adres URL endpointa i schemat treści żądania przy użyciu developer tools w przeglądarce. Postępowałem zgodnie z opisanymi krokami, podejrzałem wysyłane żądania podczas tworzenia nowej listy z istniejącej i voilla!

ExecuteSiteScript endpoint to create a new list via SharePoint REST API.

To zadziałało doskonale. I to był przełom. Cała reszta była już naprawdę łatwa!

Kroki procesu

Kroki dla utworzenia nowej listy są następujące:

  1. Znajdź istniejącą listę-szablon (istniejącą listę SPO ze wszystkimi kolumnami, widokami, formatowaniem itp.) i pobierz jej site script,
  2. Zmodyfikuj site script, aby zmienić nazwę nowej listy i jej widoczność w nawigacji,
  3. Utwórz nową listę,
  4. Ustaw jej unikalne uprawnienia.

Krok 1 – znajdź listę i pobierz jej site script

Proces najpierw używa endpointa opisanego w dokumentach Microsoft (SharePoint site design REST API | Microsoft Learn) by pobrać site script listy. Endpoint do tego jest następujący (źródło: https://learn.microsoft.com/en-us/sharepoint/dev/declarative-customization/site-design-rest-api#getsitescriptfromlist).

Calling endpoint to get site script form the list
Adres enpointa: _api/Microsoft.Sharepoint.Utilities.WebTemplateExtensions.SiteScriptUtility.GetSiteScriptFromList
Request body: {"listUrl":"ABSOLUTE_URL_TO_SOURCE_LIST"}

Korzystając z akcji “Send HTTP request to SharePoint”, proces wysyła żądanie GET i w zamian otrzymuje kod JSON, który następnie parsuje w celu pobrania z niego właściwego site script:

Site script posiada poniższą strukturę:

{
    "$schema": "https://developer.microsoft.com/json-schemas/sp/site-design-script-actions.schema.json",
    "actions": [
        {
            "verb": "createSPList",
            "listName": "[[LInvoices_approvals_T0001_listName]]",
            "templateType": 100,
            "color": "[[LInvoices_approvals_T0001_color]]",
            "icon": "[[LInvoices_approvals_T0001_icon]]",
            "addNavLink": "[[LInvoices_approvals_T0001_addNavLink]]",
            "description": "[[LInvoices_approvals_T0001_description]]",
            "identity": "LInvoices_approvals_T0001",
            "subactions": [
                {
                    HERE YOU CAN FIND ALL ACTIONS TO CREATE COLUMNS, VIEWS, ADD FORMATTING ETC...
                }
            ]
        }
    ],
    "bindings": {
        "LInvoices_approvals_T0001_listName": {
            "source": "Input",
            "defaultValue": "Invoices approvals TEST"
        },
        "LInvoices_approvals_T0001_icon": {
            "source": "Input",
            "defaultValue": "8"
        },
        "LInvoices_approvals_T0001_description": {
            "source": "Input",
            "defaultValue": ""
        },
        "LInvoices_approvals_T0001_color": {
            "source": "Input",
            "defaultValue": "5"
        },
        "LInvoices_approvals_T0001_addNavLink": {
            "source": "Input",
            "defaultValue": "false"
        }
    }
}IMAGE

Krok 2 – Zmodyfikuj site script

Bardzo ważnym krokiem jest wyodrębnienie identyfikatora site scriptu (site script identity) – jest to przyrostek dołączany do każdego atrybutu w kodzie JSON, który jest unikalny i wymagany, aby móc dotrzeć do dowolnego atrybutu za pomocą ścieżki. W powyższym przykładzie jest to: LInvoices_approvals_T0001. Następnie za pomocą wyrażenia setProperty proces zmienia tytuł nowej listy, która jest przechowywana we właściwości „bindings”. Odbywa się to za pomocą poniższego wyrażenia.

Najpierw: first(body('Parse_script_as_JSON')?['actions'])?['identity'] to get script identity.
A potem: setProperty(body('Parse_script_as_JSON'), 'bindings', setProperty(body('Parse_script_as_JSON')?['bindings'], concat(first(body('Parse_script_as_JSON')?['actions'])?['identity'], '_listName'), setProperty(body('Parse_script_as_JSON')?['bindings']?[concat(first(body('Parse_script_as_JSON')?['actions'])?['identity'], '_listName')], 'defaultValue', 'New name'))) aby ustawić rzeczywistą, nową nazwę. Wyrażenie jest dość długie, ponieważ wymaga zagnieżdżonej właściwości setProperty w celu zaktualizowania nazwy listy w powiązaniach.

Istotne! Bardzo ważne jest, aby pamiętać, że setProperty jest w stanie odczytać tylko jeden poziom poniżej danego atrybutu. Zatem jeśli chcesz manipulować atrybutem, który jest naprawdę głęboko w strukturze JSON, musisz wywołać setProperty na każdym poziomie (zagnieżdżony setProperty), jak w moim przykładzie powyżej (Reference guide for expression functions – Azure Logic Apps | Microsoft Learn).

Następnie, używając podobnego wyrażenia setProperty, proces ustawia flagę dodawania listy do nawigacji witryny.

Krok 3 – utwórz nową listę

Korzystając z developer tools, zgodnie z radą Tomáša Říha, pobrałem adres URL endpointa i schemat treści żądania. Jednakże przed opublikowaniem zmanipulowanego skryptu witryny należy w nim wyescape’ować cudzysłowy. Robię to za pomocą następującego wyrażenia: replace(replace(string(outputs('Set_true_to_add_list_to_navigation')), '"', '\"'), '\', '\\').

Endpoint _api/Microsoft.Sharepoint.Utilities.WebTemplateExtensions.SiteScriptUtility.ExecuteTemplateScript
Ciało żądania: {"script": "SITE SCRIPT CONVERTED TO ESCAPED STRING"}

I to wszystko. Lista zostaje stworzona w ciągu kilku sekund. Zwróć uwagę, jak szybko proces jest wykonywany bez żadnej naprawdę wyrafinowanej logiki. No może poza wyrażeniami setProperty 😉.

Krok 4 – ustaw uprawnienia listy

Endpointy, w których można uzyskać/ustawić uprawnienia, są bardzo dobrze udokumentowane w dokumentach Microsoft (Set custom permissions on a list by using the REST interface | Microsoft Learn). Manipulowanie uprawnieniami jest dość proste, ale ponieważ w moim przypadku listy są tworzone dynamicznie, grupy uprawnień SPO dla list również powinny być generowane, ponieważ mogą one nie istnieć w momencie tworzenia listy. Oto kroki:

  1. Sprawdź, czy istnieje grupa uprawnień SPO,
  2. Utwórz ją jeśli nie, nadaj jej uprawnienia do odczytu witryny, ustaw właściciela,
  3. Przerwij dziedziczenie uprawnień na liście,
  4. Uzyskaj i usuń wszystkie istniejące uprawnienia,
  5. Przyznaj uprawnienia nowo utworzonej grupie (i ewentualnie innym).

Kroki a i b – sprawdź, czy grupa istnieje i utwórz jeśli nie

Najpierw proces wywołuje endpoint: _api/Web/SiteGroups/?$filter=Title eq 'NAZWA GRUPY', aby sprawdzić, czy grupa istnieje. Jeśli nie, wysyła POST do endpointa: _api/Web/SiteGroups z poniższą treścią żądania:

{
    "__metadata": {
        "type": "SP.Group"
    },
    "Title": "GROUP NAME",
    "Description": ""
}

Aby grupę utworzyć.

Po utworzeniu pobiera jej identyfikator, a następnie ustawia uprawnienia do witryny wysyłającej żądanie POST do endpointa: _api/Web/RoleAssignments/addroleassignment(principalid={GROUP ID}?,roledefid=1073741826).

Role identifier valueRole definition
1073741825Guest
1073741826Reader (Read)
1073741827Contributor (Contribute)
1073741828Web Designer
1073741829Administrator (Full control)
Źródło: https://docs.microsoft.com/en-us/openspecs/sharepoint_protocols/ms-wssfo3/72879a3a-6dd5-4cc8-bdc2-4acfe078ac5

Istotne! Dopóki grupa nie otrzyma żadnych uprawnień do witryny, nie pojawi się ona na liście grup uprawnień witryny.

Na koniec proces zmienia właściciela grupy na konkretnego pracownika. Odbywa się to poprzez wysłanie żądania POST do: _api/web/sitegroups({ID GROUP})/SetUserAsOwner({ID UŻYTKOWNIKA Z LISTY USERINFO Z WITRYNY}).

Ważne jest tutaj uznanie i zaakceptowanie faktu, że dosłownie nie istnieje endpoint REST, który można użyć w celu ustawienia innej grupy jako właściciela nowo utworzonej grupy. I nie będzie (źródło: https://github.com/SharePoint/sp-dev-docs/issues/5031#issuecomment-594710013)! Można to zrobić tylko za pomocą żądania SOAP (źródło: https://github.com/pnp/pnpjs/issues/957#issuecomment-565484501).

Kroki c i d – złam dziedziczenie uprawnień i usuń istniejące uprawnienia

Następnie używając endpointa: /_api/web/lists/getbytitle('NAZWA WYŚWIETLANA LISTY')/breakroleinheritance(true), proces przerywa dziedziczenie uprawnień na liście. Następnie za pomocą endpointa: /_api/web/lists/getbytitle('NAZWA WYŚWIETLANA LISTY')/roleassignments pobiera wszystkie istniejące uprawnienia, a następnie w pętli (dla każdego istniejącego roleassignment) wywołuje ten endpoint: /_api/ web/lists/getbytitle('NAZWA WYŚWIETLANA LISTY')/roleassignments/getbyprincipalid('ID ROLI').

W celu usunięcia każdego uprawnienia.

Ważne! Upewnij się, że cały proces wykorzystuje konto administratora witryny do usuwania uprawnień, w przeciwnym razie, jeśli usunie uprawnienia dla siebie, dalsze kroki nie będą możliwe.

Krok e – nadaj nowe uprawnienia

Na koniec proces wykonuje żądanie POST do: /_api/web/lists/getbytitle('NAZWA WYŚWIETLANA LISTY')/roleassignments/addroleassignment(principalid='ID GRUPY',roledefid='1073741827').

W celu nadania uprawnień dla nowo utworzonej grupy na tej liście.

Podsumowanie

I to wszystko! W ten sposób możesz całkowicie dynamicznie tworzyć nową listę, używając istniejącej listy jako szablonu, a następnie ustawić jej uprawnienia. Takie podejście jest najlepsze, ponieważ pozwala użytkownikom biznesowym łatwo tworzyć nowe szablony i zarządzać istniejącymi, bez konieczności angażowania działu IT. Sam proces jest bardzo generyczny, więc nawet jeśli potrzebny będzie nowy szablon, firma może go stworzyć samodzielnie. Muszą jedynie być przestrzegane konwencje nazewnictwa i nic więcej.

Mam nadzieję, że uznasz ten post za przydatny. Osobiście jestem bardzo dumny z tego rozwiązania, mimo że złożenie go i uruchomienie zajęło mi kilka dni!


Tomasz Poszytek

Cześć! Nazywam się Tomasz. Jestem ekspertem w dziedzinie automatyzacji procesów i budowaniu rozwiązań dla biznesu z użyciem Power Platform. Jestem Microsoft MVP i Nintex vTE.

Brak komentarzy

Wyślij komentarz

Witryna wykorzystuje Akismet, aby ograniczyć spam. Dowiedz się więcej jak przetwarzane są dane komentarzy.