Advertise with Googlier.com  U2U Blog General U2U Blog https://blogs.u2u.be/u2u/ http://www.rssboard.org/rss-specification BlogEngine.NET 3.3.5.0 en-US https://blogs.u2u.be/u2u/opml.axd http://www.dotnetblogengine.net/syndication.axd My name U2U Blog 0.000000 0.000000 U2U Online V2.1 is here! <p>This blog post explains the latest features present in the most recent version of our online platform, online.u2u.be, which we, as trainers, fondly refer to as 'U2U Online'. In case you are unfamiliar with U2U Online, it is likely that you have not attended any of our training courses, as that is where the true magic happens.</p> <p>U2U Online serves as our in-house online learning platform, granting our students access to all the necessary materials linked to their ongoing trainings. This includes theoretical resources, practical exercises, videos, references, general information, and even a group chat to communicate with each other. By exhibiting all these resources in a centralized location, we strive to ensure an optimal user-friendly experience for our students.</p> <p>To provide context, it is crucial to understand the rationale behind the online learning platform. Have you ever encountered the Learning Pyramid Model? This model explains various modes of learning and assesses their efficacy in retaining knowledge. Notably, the model differentiates between passive and active learning methods, with the latter exhibiting a higher rate of knowledge retention compared to the former.</p> <p><img src="https://blogs.u2u.be/u2u/image.axd?picture=/Ruben/u2uonline/Picture1.png" alt="/Ruben/u2uonline/Picture1.png" /></p> <p>Figure 1: Learning Pyramid : https://www.arlo.co/blog/overview-of-the-learning-pyramid-for-training-providers</p> <p>While it is imperative to incorporate active learning methods, it is equally important not to disregard the passive learning techniques. A well-rounded training program entails a balance between the two. At U2U, we have carefully combined passive and active learning methods to optimize the learning retention for our students.</p> <p>Merely providing demonstrations may facilitate a 30 percent recall of the theory, but this alone does not suffice. Therefore, we complement our approach with active learning methods. For instance, we initiate classroom discussions by encouraging participants to share their existing knowledge on the subject matter or by posing thought-provoking questions such as 'What do you think will happen and can you explain why?'.</p> <p>Furthermore, students are provided ample time to engage in detailed exercises that allow them to put the theory into practice, resulting in a remarkable 75 percent retention rate. As such, it is crucial for us to prioritize a seamless user experience including both theoretical and practical aspects. To this end, we have strived to optimize the user experience by developing and improving our online learning platform, U2U Online.</p> <p>It has been a few weeks since the implementation of U2U Online V2.1. As is customary with any software program or product, we conducted thorough testing to rectify any remaining minor bugs before unrolling it to the wider audience. Those initial challenges are now behind us, and on behalf of the entire U2U team, Now that those initial challenges are behind us, the entire U2U team is delighted and proud of the result. That's why I'm writing this blog post to walk you through the major differences between V2.0 and V2.1, along with the exciting new features.</p> <h3>Separation of Theory and Labs</h3> <p>Each of our courses consist of modules including both theoretical and practical components. The theory portion involves PowerPoint slides, while the lab segment involves hands-on exercises corresponding to the recently acquired knowledge. In the previous version of U2U Online, theory and labs were combined into a single Word document with numerous pages, requiring considerable scrolling to locate the exercises.</p> <p>In the newest iteration of U2U Online, we have divided each module into distinct theory and lab sections. Bid farewell to the arduous task of searching for exercise starting points within lengthy documents! Within each module, theory and lab materials are elegantly displayed side by side, facilitating seamless transition between theory and practical applications. Thus, we have successfully resolved Issue 1.</p> <p><img src="https://blogs.u2u.be/u2u/image.axd?picture=/Ruben/u2uonline/Picture2.png" alt="/Ruben/u2uonline/Picture2.png" /></p> <p>Figure 2: Side by Side rendered Theory and Labs</p> <h3>Enhanced Rendering of Theory Presentations</h3> <p>In U2U Online V2.0, the theory portion of each module comprised screenshots of PowerPoint slides, arranged in a web layout with two slides per page. This approach presented several challenges, such as the inability to click on hyperlinks, copy-paste text, or navigate through subsections smoothly.</p> <p><img src="https://blogs.u2u.be/u2u/image.axd?picture=/Ruben/u2uonline/Picture3.png" alt="/Ruben/u2uonline/Picture3.png" /></p> <p>Figure 3: Theory rendering in V2.0 (old)</p> <p>To rectify these limitations, we now render each slide individually in the new version of U2U Online. Moreover, we have incorporated a subsection navigator on the left-hand side for effortless navigation to specific subsections as needed. With this direct slide rendering approach, interactive features such as hyperlink functionality and text copying are fully enabled.</p> <p><img src="https://blogs.u2u.be/u2u/image.axd?picture=/Ruben/u2uonline/Picture4.png" alt="/Ruben/u2uonline/Picture4.png" /></p> <p>Figure 4: Theory rendering in V2.1 (new) Enhanced Rendering of Labs</p> <h3>Enhanced Rendering of Labs</h3> <p>Similar to the theory portion, labs were previously rendered within a single Word document comprising multiple pages. Each lab typically encompassed multiple exercises belonging to different subsections, without the ability to navigate between them easily. While it was possible to interact with the text, such as copying specific code snippets, the discrepancy between Word and the rendering engine introduced frustrations in selecting specific text regions.</p> <p><img src="https://blogs.u2u.be/u2u/image.axd?picture=/Ruben/u2uonline/Picture5.png" alt="/Ruben/u2uonline/Picture5.png" /></p> <p>Figure 5: Lab rendering in V2.0 (old)</p> <p>To address these issues, we now directly render labs within the U2U Online platform, eliminating the need for Word as an intermediate source. We employ Markdown to visualize text, images, and code snippets in a clear format. This approach empowers end-users to interact effortlessly with the exercise materials. Text automatically scales in relation to the available screen space, and each lab subsection is presented as a separate scrollable page, ensuring a coherent visualization of all associated steps. Furthermore, the section navigator allows swift navigation between sections when necessary.</p> <p>Additionally, lab files associated with a given exercise, such as starter files, resources, and solutions, previously required users to navigate back to the menu, download the files, and then manually find their place in the exercise again. To mitigate this inconvenience, we have incorporated a solution in the new version. At the top of the Section Navigator, users can now directly download lab files associated with the current exercise, eliminating the need to leave the page or lose track of progress. All necessary resources are conveniently accessible from within the current page.</p> <p><img src="https://blogs.u2u.be/u2u/image.axd?picture=/Ruben/u2uonline/Picture6.png" alt="/Ruben/u2uonline/Picture6.png" /></p> <p>Figure 6: Rendering Labs in V2.1 (new)</p> <h3>Streamlined Evaluations</h3> <p>At the conclusion of each training course, we kindly request that students complete an evaluation form. This valuable feedback allows us to continually enhance our course materials and ensure alignment with student expectations. As a token of appreciation for completing the evaluation, students are granted access to a digital certificate for their course participation.</p> <p>In U2U Online V2.0, students had to click a link within the course to be redirected to an external webpage for evaluation submission. After completing the evaluation, they needed to manually return to U2U Online to download their certificate, potentially sharing it on social media or saving it as a PDF. However, in U2U Online V2.1, we have integrated the entire evaluation process within the platform itself. There is no longer a need for redirection or returning to U2U Online after submitting the evaluation. The entire process is now centralized.</p> <p><img src="https://blogs.u2u.be/u2u/image.axd?picture=/Ruben/u2uonline/Picture7.png" alt="/Ruben/u2uonline/Picture7.png" /></p> <p>Figure 7: Evaluations included in V2.1 (new)</p> <p><img src="https://blogs.u2u.be/u2u/image.axd?picture=/Ruben/u2uonline/Picture8.png" alt="/Ruben/u2uonline/Picture8.png" /></p> <p>Figure 8: Integrated sharing of the obtained certificate in V2.1 (new)</p> <p>These four improvements constitute the most significant enhancements in U2U Online V2.1. If you haven't explored these features yet, I highly recommend taking a look and experiencing them firsthand. Of course, there is always room for further improvements, and we are actively working on designing V2.2 with additional features that will prove advantageous. Stay tuned for updates on these new features and their anticipated release date.</p> https://blogs.u2u.be/u2u/post/u2u-online-v2-1-is-here https://blogs.u2u.be/u2u/post/u2u-online-v2-1-is-here#comment https://blogs.u2u.be/u2u/post.aspx?id=194cf174-f138-4124-9bed-4b2bbdf7f8a5 Tue, 13 Jun 2023 10:29:00 +0000 ruben@u2u.be https://blogs.u2u.be/u2u/pingback.axd https://blogs.u2u.be/u2u/post.aspx?id=194cf174-f138-4124-9bed-4b2bbdf7f8a5 0 https://blogs.u2u.be/u2u/trackback.axd?id=194cf174-f138-4124-9bed-4b2bbdf7f8a5 https://blogs.u2u.be/u2u/post/u2u-online-v2-1-is-here#comment https://blogs.u2u.be/u2u/syndication.axd?post=194cf174-f138-4124-9bed-4b2bbdf7f8a5 Using the Power Platform CLI with Power Pages <p>In this blog post you will see how to use the Power Platform CLI with Power Pages. This feature enables you to download the content of a Power Pages site and edit it in Visual Studio Code. Once finished, you can upload all your changes back to the Power Pages site. Another use case for this feature is to move a Power Pages site between production, acceptance and production environments.</p> <p>As a first step you will need to install and download <a href="https://code.visualstudio.com">Visual Studio Code</a>. Once installed you can follow the steps below to install the Power Platform CLI.</p> <ol> <li>Start Visual Studio Code.</li> <li>Select the <strong>Extensions</strong> icon from the left-side window.</li> </ol> <p><img src="https://blogs.u2u.be/u2u/u2u/image.axd?picture=/Scherm%C2%ADafbeelding%202023-03-30%20om%2009.39.23.png" alt="enter image description here" title="enter image title here" /></p> <ol start="3"> <li>In the search text box, type <strong>Power Platform Tools</strong>.</li> </ol> <p><img src="https://blogs.u2u.be/u2u/u2u/image.axd?picture=/Scherm%C2%ADafbeelding%202023-03-30%20om%2009.47.00.png" alt="enter image description here" title="enter image title here" /></p> <ol start="4"> <li>Click the on the blue <strong>Install</strong> button.</li> <li>Restart Visual Studio Code.</li> </ol> <p>Once Visual Studio Code is restarted, go to <strong>View</strong> -&gt; <strong>Terminal</strong> to open a new Terminal window. In this new terminal window type <strong>pac</strong> and press enter.</p> <p><img src="https://blogs.u2u.be/u2u/u2u/image.axd?picture=/Scherm%C2%ADafbeelding%202023-03-30%20om%2010.27.37.png" alt="enter image description here" title="enter image title here" /></p> <p>This will list all the commands that can be executed using the Power Platform CLI.</p> <p>As a first step, we must authenticate with the Dataverse environment that hosts the Power Pages site for which we want to download the content. For this we can run the <em>pac auth create --environment [Dataverse Environment URL]</em> command.</p> <pre><code>pac auth create --environment https://u2udev.crm4.dynamics.com </code></pre> <p>Once you successfully login, the following output will be displayed.</p> <pre><code>'trainer@u2ucourse.com' authenticated successfully. Validating connection... Default organization: U2UDev Authentication profile created * UNIVERSAL https://u2udev.crm4.dynamics.com/ : trainer@u2ucourse.com Public </code></pre> <p>If you now want to list all Power Pages sites in the selected environment, you can use the <em>pac paportal list</em> command. As you can see in the output below, there are 2 Power Pages sites present in the selected environment.</p> <pre><code>PS /Users/jurgenpostelmans&gt; pac paportal list Connected to... U2UDev Connected as trainer@u2ucourse.com Completed listing Power Pages websites Index Website Id Friendly Name [1] fd99279d-cace-ed11-b597-0022489ee5d6 U2U Blog Post - u2ublogpost [2] ad7c0439-f381-ed11-81ad-0022489fd57f U2U Portal - u2uportal </code></pre> <p>To download the content of a Power Pages site, you can use the <em>pac paportal download --path [Local Path] --webSiteId [WebSiteId-GUID]</em> command. The <em>path</em> parameter indicates where the Power Pages website content will be downloaded. The <em>WebSiteId-GUID</em> is the ID of the Power Pages site and can be found in the output of the <em>pac paportal list</em> command above.</p> <pre><code>PS /Users/jurgenpostelmans/blogpost&gt; pac paportal download --path ./ --webSiteId fd99279d-cace-ed11-b597-0022489ee5d6 Information: Be careful when you're updating public sites. The changes you make are visible to anyone immediately. To check site visibility go to https://make.powerpages.microsoft.com/. To get additional information, please visit https://go.microsoft.com/fwlink/?linkid=2204350 Started downloading website fd99279d-cace-ed11-b597-0022489ee5d6 Connected to... U2UDev Connected as trainer@u2ucourse.com Downloading: Website [adx_website]... Downloaded: Website [adx_website] Downloading: Website Language [adx_websitelanguage]... ... Updating manifest... Manifest updated successfully. Power Pages website download succeeded in 13.465261 secs. </code></pre> <p>Once the download is finished, a new folder is created that is made up of the display name and name of your Power Pages site.</p> <pre><code>PS /Users/jurgenpostelmans/blogpost&gt; ls -l total 0 drwxr-xr-x@ 25 jurgenpostelmans staff 800 Mar 30 10:58 u2u-blog-post---u2ublogpost PS /Users/jurgenpostelmans/blogpost&gt; </code></pre> <p>Open this folder with Visual Studio Code.</p> <p><img src="https://blogs.u2u.be/u2u/u2u/image.axd?picture=/Scherm%C2%ADafbeelding%202023-03-30%20om%2011.17.32.png" alt="enter image description here" title="enter image title here" /></p> <p>If we now want to edit the content of the Home page, open the file that is named <em>Home.en-US.webpage.copy.html</em>.</p> <p><img src="https://blogs.u2u.be/u2u/u2u/image.axd?picture=/Scherm%C2%ADafbeelding%202023-03-30%20om%2011.21.02.png" alt="enter image description here" title="enter image title here" /></p> <p>Let's add some custom text and a small piece of Liquid code inside the innermost div tag.</p> <pre><code>&lt;div class='row sectionBlockLayout text-left' style='display: flex; flex-wrap: wrap; margin: 0px; min-height: auto; padding: 8px;'&gt; &lt;div class='container' style='padding: 0px; display: flex; flex-wrap: wrap;'&gt; &lt;div class='col-md-12 columnBlockLayout' style='flex-grow: 1; display: flex; flex-direction: column; min-width: 300px;'&gt; This page was modified in Visual Studio Code&lt;br&gt; {{ now }} &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; </code></pre> <p>To upload the changes, the <em>pac paportal upload --path [Local Path]</em> command can be used.</p> <pre><code>PS /Users/jurgenpostelmans/blogpost/u2u-blog-post---u2ublogpost&gt; pac paportal upload --path ./ Information: Be careful when you're updating public sites. The changes you make are visible to anyone immediately. To check site visibility go to https://make.powerpages.microsoft.com/. To get additional information, please visit https://go.microsoft.com/fwlink/?linkid=2204350 Started uploading website data Connected to... U2UDev Connected as trainer@u2ucourse.com Loading Power Pages website manifest... Manifest loaded successfully. Updated record(s) for table: adx_webpage Power Pages website upload succeeded in 5.77 secs. </code></pre> <p>Notice how it only uploads data for the adx_webpage entity because the command only uploads new/modified/deleted content. When the content of a Power Pages site is download, a manifest.yaml file is created inside the .portalconfig folder.</p> <p><img src="https://blogs.u2u.be/u2u/u2u/image.axd?picture=/Scherm%C2%ADafbeelding%202023-03-30%20om%2011.33.19.png" alt="enter image description here" title="enter image title here" /></p> <p>As you can see in the screenshot, a checksum is calculated and stored for each record that was downloaded. If the content of a record gets modified, the current checksum will not match with the checksum in the manifest. This way the pac paportal upload command can upload only the changes.</p> https://blogs.u2u.be/u2u/post/using-the-power-platform-cli-with-power-pages https://blogs.u2u.be/u2u/post/using-the-power-platform-cli-with-power-pages#comment https://blogs.u2u.be/u2u/post.aspx?id=82341edd-d70f-4e23-8787-78b0347281b3 Thu, 30 Mar 2023 11:17:00 +0000 Power Platform Power Pages jurgen@u2u.be https://blogs.u2u.be/u2u/pingback.axd https://blogs.u2u.be/u2u/post.aspx?id=82341edd-d70f-4e23-8787-78b0347281b3 0 https://blogs.u2u.be/u2u/trackback.axd?id=82341edd-d70f-4e23-8787-78b0347281b3 https://blogs.u2u.be/u2u/post/using-the-power-platform-cli-with-power-pages#comment https://blogs.u2u.be/u2u/syndication.axd?post=82341edd-d70f-4e23-8787-78b0347281b3 SharePoint Column Formatting - Rounded images <p>Is recently had the need to show a picture column in a SharePoint list as a Persona element as provided in the <a href="https://developer.microsoft.com/en-us/fabric-js/components/persona/persona">Office UI Fabric</a>. Now if it came down to it, it was just enough to have an image with a border radius, and not all the other mumbo jumbo from Persona.</p> <p>The result looks as follows: <img src="https://u2ublogimages.blob.core.windows.net/robrecht/column-formatting-persona.PNG" alt="Column formatting image to persona" title="Column formatting image to persona" /></p> <p>The json to put in the column formatting pane:</p> <pre><code>{ &quot;$schema&quot;: &quot;https://developer.microsoft.com/json-schemas/sp/column-formatting.schema.json&quot;, &quot;elmType&quot;: &quot;div&quot;, &quot;children&quot;: [ { &quot;elmType&quot;: &quot;img&quot;, &quot;attributes&quot;: { &quot;src&quot;: &quot;@currentField&quot; }, &quot;style&quot;: { &quot;width&quot;: &quot;100px&quot;, &quot;border-radius&quot;: &quot;50%&quot; } } ] } </code></pre> https://blogs.u2u.be/u2u/post/sharepoint-column-formatting-rounded-images https://blogs.u2u.be/u2u/post/sharepoint-column-formatting-rounded-images#comment https://blogs.u2u.be/u2u/post.aspx?id=4c7071fb-4453-49d2-af2a-b5a10e91e553 Wed, 20 Feb 2019 17:52:00 +0000 SharePoint Online robrecht@u2u.be https://blogs.u2u.be/u2u/pingback.axd https://blogs.u2u.be/u2u/post.aspx?id=4c7071fb-4453-49d2-af2a-b5a10e91e553 0 https://blogs.u2u.be/u2u/trackback.axd?id=4c7071fb-4453-49d2-af2a-b5a10e91e553 https://blogs.u2u.be/u2u/post/sharepoint-column-formatting-rounded-images#comment https://blogs.u2u.be/u2u/syndication.axd?post=4c7071fb-4453-49d2-af2a-b5a10e91e553 Using out of the box web parts as Tabs in Teams <p>This blog post is a follow-up on the <a href="https://www.sharepointeurope.com/events/building-microsoft-teams-sharepoint-framework/">session</a> we gave at the European SharePoint Conference. In this session I demonstrated how you can take your custom SPFx web parts and make them available in Microsoft Teams as a configurable tab.</p> <p>This functionality was introduced in <a href="https://github.com/SharePoint/sp-dev-docs/wiki/SharePoint-Framework-v1.7-release-notes">SPFx 1.7</a> in Preview. Now, how SPFx makes this possible, is by adding a Teams manifest to your project that contains the configurable tabs. It looks as follows:</p> <pre><code class="language-json">... &quot;configurableTabs&quot;: [ { &quot;configurationUrl&quot;: &quot;https://{teamSiteDomain}{teamSitePath}/_layouts/15/TeamsLogon.aspx?SPFX=true&amp;dest={teamSitePath}/_layouts/15/teamshostedapp.aspx%3FopenPropertyPane=true%26teams%26componentId=be78544a-3fbf-466e-aadb-41bac5a984f0&quot;, &quot;canUpdateConfiguration&quot;: false, &quot;scopes&quot;: [ &quot;team&quot; ] } ], ... </code></pre> <p>In the segment above, observe that the magic is handled by 2 new application pages in SharePoint:</p> <ul> <li><strong>TeamsLogon.aspx</strong>: this page will make sure the user is logged into SharePoint, which is needed since Teams and SharePoint run in different domains</li> <li><strong>TeamsHostedApp.aspx</strong>: this page will do all the magic of configuring and loading your web part into a page</li> </ul> <p>The <strong>TeamsHostedApp.aspx</strong> contains a reference to the <strong>componentId</strong>, this is the ID of your web part which you can find in the web part manifest. This got me thinking, if SharePoint uses SPFx to build their own web parts, then it should also be possible to use this approach on the out of the box client side web parts?</p> <p>So just find the web part you are interested in using the REST api</p> <pre><code>GET /_api/web/GetClientSideWebParts </code></pre> <p>I'm actually very interested in the Highlighted Content web part:</p> <pre><code class="language-json">{ &quot;value&quot;: [ { &quot;ComponentType&quot;: 1, &quot;Id&quot;: &quot;**daf0b71c-6de8-4ef7-b511-faae7c388708**&quot;, &quot;Manifest&quot;: &quot;{\&quot;preconfiguredEntries\&quot;:[{\&quot;title\&quot;:{\&quot;default\&quot;:\&quot;Highlighted content\&quot;,\&quot;ar-SA\&quot;:\&quot;المحتوى المميز\&quot;,\&quot;az-Latn-AZ\&quot;:\&quot;Vurğulanmış məzmun\&quot;,\&quot;bg-BG\&quot;:\&quot;Акцентирано съдържание\&quot; ... </code></pre> <p>Using the same Teams manifest, we can now alter it to our liking, making sure we provide a unique Id and replace the <strong>componentId</strong> to the one from the Highlighted Content web part. Below is the full manifest.</p> <pre><code class="language-json">{ &quot;$schema&quot;: &quot;https://developer.microsoft.com/en-us/json-schemas/teams/v1.2/MicrosoftTeams.schema.json&quot;, &quot;manifestVersion&quot;: &quot;1.2&quot;, &quot;packageName&quot;: &quot;HighlightedContent&quot;, &quot;id&quot;: &quot;be78544a-3fbf-466e-aadb-41bac5a984f3&quot;, &quot;version&quot;: &quot;0.1&quot;, &quot;developer&quot;: { &quot;name&quot;: &quot;SPFx + Teams Dev&quot;, &quot;websiteUrl&quot;: &quot;https://products.office.com/en-us/sharepoint/collaboration&quot;, &quot;privacyUrl&quot;: &quot;https://privacy.microsoft.com/en-us/privacystatement&quot;, &quot;termsOfUseUrl&quot;: &quot;https://www.microsoft.com/en-us/servicesagreement&quot; }, &quot;name&quot;: { &quot;short&quot;: &quot;Highlighted Content&quot; }, &quot;description&quot;: { &quot;short&quot;: &quot;Highlighted Content&quot;, &quot;full&quot;: &quot;Highlighted Content&quot; }, &quot;icons&quot;: { &quot;outline&quot;: &quot;tab20x20.png&quot;, &quot;color&quot;: &quot;tab96x96.png&quot; }, &quot;accentColor&quot;: &quot;#004578&quot;, &quot;configurableTabs&quot;: [ { &quot;configurationUrl&quot;: &quot;https://{teamSiteDomain}{teamSitePath}/_layouts/15/TeamsLogon.aspx?SPFX=true&amp;dest={teamSitePath}/_layouts/15/teamshostedapp.aspx%3FopenPropertyPane=true%26teams%26componentId=daf0b71c-6de8-4ef7-b511-faae7c388708&quot;, &quot;canUpdateConfiguration&quot;: false, &quot;scopes&quot;: [ &quot;team&quot; ] } ], &quot;validDomains&quot;: [ &quot;*.login.microsoftonline.com&quot;, &quot;*.sharepoint.com&quot;, &quot;*.sharepoint-df.com&quot;, &quot;spoppe-a.akamaihd.net&quot;, &quot;spoprod-a.akamaihd.net&quot;, &quot;resourceseng.blob.core.windows.net&quot;, &quot;msft.spoppe.com&quot; ], &quot;webApplicationInfo&quot;: { &quot;resource&quot;: &quot;https://{teamSiteDomain}&quot;, &quot;id&quot;: &quot;00000003-0000-0ff1-ce00-000000000000&quot; } } </code></pre> <p>Now zip the manifest together with the icons and upload to your Teams App Store. Once it is uploaded, you will be able to add it to a channel within a team. You will see the Property Pane asking you for input and finally the web part being rendered inside the newly created tab.</p> <p><img src="https://u2ublogimages.blob.core.windows.net/robrecht/Highlighted_Content_in_Teams_1.png" alt="Configuring the Property Pane" title="Property Pane configuration" /></p> <p><img src="https://u2ublogimages.blob.core.windows.net/robrecht/Highlighted_Content_in_Teams_2.png" alt="Highlighted Content Web Part as Teams Tab" title="Highlighted Content Web Part as Teams Tab" /></p> https://blogs.u2u.be/u2u/post/using-out-of-the-box-webparts-as-tabs-in-teams https://blogs.u2u.be/u2u/post/using-out-of-the-box-webparts-as-tabs-in-teams#comment https://blogs.u2u.be/u2u/post.aspx?id=74e4be50-8e42-4881-aee4-9759f155564f Fri, 30 Nov 2018 11:43:00 +0000 Teams SharePoint Development SPFx robrecht@u2u.be https://blogs.u2u.be/u2u/pingback.axd https://blogs.u2u.be/u2u/post.aspx?id=74e4be50-8e42-4881-aee4-9759f155564f 0 https://blogs.u2u.be/u2u/trackback.axd?id=74e4be50-8e42-4881-aee4-9759f155564f https://blogs.u2u.be/u2u/post/using-out-of-the-box-webparts-as-tabs-in-teams#comment https://blogs.u2u.be/u2u/syndication.axd?post=74e4be50-8e42-4881-aee4-9759f155564f Only load those things you need in CSOM <p>This is a small post to emphasize why you should only load the things you need with Client Side Object Model for SharePoint. One of the most straightforward reasons to load only what you need is &quot;<em><strong>The amount of data that goes over the wire</strong></em>&quot;. Yes, but what about security? If you are loading things you do not need, you might get an unwanted <strong><em>ServerUnauthorizedAccessException</em></strong>.</p> <p>Consider the following code snippet that is taken from <strong>OfficeDevPnP.Core</strong>.</p> <pre><code class="language-csharp">public static Folder EnsureFolderPath(this Web web, string webRelativeUrl, params Expression&lt;Func&lt;Folder, object&gt;&gt;[] expressions) { if (webRelativeUrl == null) { throw new ArgumentNullException(nameof(webRelativeUrl)); } //Web root folder should be returned if webRelativeUrl is empty if (webRelativeUrl.Length != 0 &amp;&amp; string.IsNullOrWhiteSpace(webRelativeUrl)) { throw new ArgumentException(CoreResources.FileFolderExtensions_EnsureFolderPath_Folder_URL_is_required_, nameof(webRelativeUrl)); } // Check if folder exists if (!web.IsPropertyAvailable(&quot;ServerRelativeUrl&quot;)) { web.Context.Load(web, w =&gt; w.ServerRelativeUrl); web.Context.ExecuteQueryRetry(); } var folderServerRelativeUrl = UrlUtility.Combine(web.ServerRelativeUrl, webRelativeUrl, &quot;/&quot;); // Check if folder is inside a list var listCollection = web.Lists; web.Context.Load(listCollection, lc =&gt; lc.Include(l =&gt; l.RootFolder)); web.Context.ExecuteQueryRetry(); List containingList = null; foreach (var list in listCollection) { if (folderServerRelativeUrl.StartsWith(UrlUtility.Combine(list.RootFolder.ServerRelativeUrl, &quot;/&quot;), StringComparison.InvariantCultureIgnoreCase)) { // Load fields from the list containingList = list; break; } } ... } </code></pre> <p>Observe that the code is loading the folder for every list, just to use the <strong>ServerRelativeUrl</strong>. When you execute this method with e.g. App-Only authentication for an app that has Manage on Site Collection. You might get a <strong><em>ServerUnauthorizedAccessException</em></strong> on the <strong>ExecuteQueryRetry</strong>, which seems kinda strange ... given the fact that the add-in has Manage permissions on Site Collection. Updating the code to only include the <strong>ServerRelativeUrl</strong> per <strong>Folder</strong> fixes this nasty Exception.</p> <pre><code class="language-csharp">// Check if folder is inside a list var listCollection = web.Lists; web.Context.Load(listCollection, lc =&gt; lc.Include(l =&gt; l.RootFolder.ServerRelativeUrl)); web.Context.ExecuteQueryRetry(); </code></pre> https://blogs.u2u.be/u2u/post/only-load-those-things-you-need-in-csom https://blogs.u2u.be/u2u/post/only-load-those-things-you-need-in-csom#comment https://blogs.u2u.be/u2u/post.aspx?id=b97021d7-397d-4913-8289-09281e22142c Mon, 27 Aug 2018 14:49:00 +0000 robrecht@u2u.be https://blogs.u2u.be/u2u/pingback.axd https://blogs.u2u.be/u2u/post.aspx?id=b97021d7-397d-4913-8289-09281e22142c 0 https://blogs.u2u.be/u2u/trackback.axd?id=b97021d7-397d-4913-8289-09281e22142c https://blogs.u2u.be/u2u/post/only-load-those-things-you-need-in-csom#comment https://blogs.u2u.be/u2u/syndication.axd?post=b97021d7-397d-4913-8289-09281e22142c Using the MSGraphClient in SPFx to return blob images <p>In SharePoint Framework <a href="https://github.com/SharePoint/sp-dev-docs/wiki/Release-Notes-for-SPFx-Package-Version-1.4.1">version 1.4</a>, they introduced the new <strong>MSGraphClient</strong>, which makes your live a lot easier when you need to call the Microsoft Graph. For reference sake, getting access to the MSGraphClient can be done as follows.</p> <pre><code class="language-javascript">import { WebPartContext } from '@microsoft/sp-webpart-base'; import { MSGraphClient } from '@microsoft/sp-client-preview'; export class DataService implements IDataService { private msGraphClient: MSGraphClient; constructor(private context: WebPartContext) { this.msGraphClient = context.serviceScope.consume(MSGraphClient.serviceKey); } } </code></pre> <p>When you need to call the Microsoft Graph, but the result is not a json object, but an image. Then you probably want the result as a blob. This can be achieved by using the <strong>responseType</strong> method as follows.</p> <pre><code class="language-javascript">public GetImage(id: string): Promise&lt;string&gt; { return this.msGraphClient .api(`/users/${id}/photos/48x48/$value`) .responseType('blob') .get() .then((blob: Blob) =&gt; { return blob ? URL.createObjectURL(blob) : null; }); } </code></pre> https://blogs.u2u.be/u2u/post/using-the-msgraphclient-in-spfx-to-return-blob-images https://blogs.u2u.be/u2u/post/using-the-msgraphclient-in-spfx-to-return-blob-images#comment https://blogs.u2u.be/u2u/post.aspx?id=7a194587-d49b-4f55-95d7-b9617d710dc7 Mon, 2 Jul 2018 18:43:00 +0000 robrecht@u2u.be https://blogs.u2u.be/u2u/pingback.axd https://blogs.u2u.be/u2u/post.aspx?id=7a194587-d49b-4f55-95d7-b9617d710dc7 0 https://blogs.u2u.be/u2u/trackback.axd?id=7a194587-d49b-4f55-95d7-b9617d710dc7 https://blogs.u2u.be/u2u/post/using-the-msgraphclient-in-spfx-to-return-blob-images#comment https://blogs.u2u.be/u2u/syndication.axd?post=7a194587-d49b-4f55-95d7-b9617d710dc7 Calling the SharePoint Online API from within Microsoft Flow <p>In this blog post I will explain how you can call the SharePoint API from within Microsoft Flow, without having to fall back to Azure Functions, custom connector, premium connectors, ... just pure Microsoft Flow.</p> <p>I still remember Microsoft Flow in the beginning, when the SharePoint connector only had 4 actions, while now it already has more than 25 actions. But this is still just a fraction of what you want to do inside your flows. All those other things like: creating a sub site, accessing the SharePoint groups, ... is not possible (yet?). But there is a powerful REST API in SharePoint available, that allows you to do everything you've ever dreamed of (in SharePoint). And Microsoft Flow has an HTTP connector which allows you to call such APIs. The only problem with that API is ... it requires <strong>authentication</strong>, like an <strong>Access Token</strong>.</p> <h3>Using add-in-only policy to get an access token</h3> <p>Now, this got me thinking ... would it be possible to use the add-in authentication inside flow to get that access token. Obviously, I'm interested in the scenario of add-in-only policy, since I have no user to authenticate. <em>I will consider Azure AD in a later blog post.</em> The Microsoft <a href="https://docs.microsoft.com/en-us/sharepoint/dev/sp-add-ins/add-in-authorization-policy-types-in-sharepoint">documentation </a> explains how an add-in can make add-in only calls. In its essence, retrieving the access token falls back to a <strong>POST</strong> call against <strong>https://accounts.accesscontrol.windows.net/</strong></p> <p>The POST will look something like this:</p> <pre><code>POST https://accounts.accesscontrol.windows.net/REALM/tokens/OAuth/2 Content-Type: application/x-www-form-urlencoded grant_type=client_credentials&amp;client_id=CLIENTID@REALM&amp;client_secret=CLIENTSECRET=&amp;scope=00000003-0000-0ff1-ce00-000000000000/TENANT.sharepoint.com@REALM&amp;resource=00000003-0000-0ff1-ce00-000000000000/TENANT.sharepoint.com@REALM </code></pre> <p>We just need retrieve and fill in the missing parameters:</p> <ul> <li>REALM: Realm is a unique identifier assigned to your SharePoint tenant.</li> <li>CLIENTID: Identifier for your add-in, obtained during registration.</li> <li>CLIENTSECRET: Secret for your add-in, obtained during registration.</li> <li>TENANT: The name of your tenant.</li> </ul> <h3>Obtaining the Client ID and Secret</h3> <p>First you will have to register a client id and secret, you can do this at https://tenant.sharepoint.com/_layouts/AppRegNew.aspx, replacing tenant with your tenant name. Generate a new client id and secret, give it a name and fill in an app domain and redirect URI.</p> <p><img src="https://u2ublogimages.blob.core.windows.net/robrecht/spflow01.PNG" alt="Add-In Registration" title="Add-In Registration" /></p> <p>Store the client id and secret somewhere, you will need them later.</p> <h3>Granting permissions to your client id</h3> <p>Next, we need to grant permissions to our client id, you can do this on https://tenant-admin.sharepoint.com/_layouts/AppInv.aspx. Fill in your client id and do a lookup.</p> <p><img src="https://u2ublogimages.blob.core.windows.net/robrecht/spflow02.PNG" alt="Granting permissions" title="Granting permissions" /></p> <p>Provide the permission request xml, I'm asking for full control on the tenant, you could ask for less permissions if needed. Don't forget to click create and consent.</p> <pre><code>&lt;AppPermissionRequests AllowAppOnlyPolicy=&quot;true&quot; &gt; &lt;AppPermissionRequest Scope=&quot;http://sharepoint/content/tenant&quot; Right=&quot;FullControl&quot; /&gt; &lt;/AppPermissionRequests&gt; </code></pre> <h3>Getting the Realm</h3> <p>The realm is a unique identifier assigned to your SharePoint tenant. You can find this for your tenant at https://tenant.sharepoint.com/_layouts/AppPrincipals.aspx</p> <p><img src="https://u2ublogimages.blob.core.windows.net/robrecht/spflow03.PNG" alt="Getting the Realm" title="Getting the Realm" /></p> <h3>Creating the flow</h3> <p>We now have all the blanks we needed and can start creating the flow. To be able to reuse the settings, I've put them inside a <strong>Parse JSON</strong> block. Fill in your own realm and target host.</p> <p><img src="https://u2ublogimages.blob.core.windows.net/robrecht/spflow04.PNG" alt="" /></p> <p>Next we will construct the client id and scope that are used in the request, I'm putting these in a separate step since they include the <strong>@</strong> symbol which is a special character for Microsoft Flow.</p> <p>The client id is created with a <strong>Initialize variable</strong> and formula</p> <pre><code>concat(body('Parse_JSON')?['clientId'], '@', body('Parse_JSON')?['realm']) </code></pre> <p>The scope is created with a <strong>Initialize variable</strong> and formula</p> <pre><code>concat(body('Parse_JSON')?['principal'],'/',body('Parse_JSON')?['targetHost'],'@', body('Parse_JSON')?['realm']) </code></pre> <p>Now we can get the access token with an <strong>HTTP</strong> action</p> <p><img src="https://u2ublogimages.blob.core.windows.net/robrecht/spflow05.PNG" alt="" /></p> <p>This will return us a response from which you can extract the access token using the <strong>Parse JSON</strong> and finally you can use your access token to call the SharePoint API!</p> <p><img src="https://u2ublogimages.blob.core.windows.net/robrecht/spflow06.PNG" alt="" /></p> <p>You can now start using the SharePoint API for all your GETs, for CREATE, PUT, PATCH, DELETE you will need to fetch a digest token as well.</p> https://blogs.u2u.be/u2u/post/calling-the-sharepoint-online-api-from-within-microsoft-flow https://blogs.u2u.be/u2u/post/calling-the-sharepoint-online-api-from-within-microsoft-flow#comment https://blogs.u2u.be/u2u/post.aspx?id=f60727ff-7998-4960-94c7-6f75189d5d06 Fri, 9 Feb 2018 10:21:00 +0000 Flow SharePoint SharePoint Online robrecht@u2u.be https://blogs.u2u.be/u2u/pingback.axd https://blogs.u2u.be/u2u/post.aspx?id=f60727ff-7998-4960-94c7-6f75189d5d06 0 https://blogs.u2u.be/u2u/trackback.axd?id=f60727ff-7998-4960-94c7-6f75189d5d06 https://blogs.u2u.be/u2u/post/calling-the-sharepoint-online-api-from-within-microsoft-flow#comment https://blogs.u2u.be/u2u/syndication.axd?post=f60727ff-7998-4960-94c7-6f75189d5d06 Showing a panel from your CommandSet in SPFx solution <p>In this blog post I will illustrate how you can build an SPFx solution that will open up a panel at the side when clicking a button in the commandbar in SharePoint Online. I'll illustrate this using React components, but it will be similar using other frameworks. With the introduction of CommandSet in SPFx, it is possible to add your custom buttons to the command bar in SharePoint Online. If you already played with the sample generated by Yeoman or had a look at the samples on the internet, you will observe that most samples stick to using the <strong>Dialog</strong>. Now, a lot of existing actions in a modern SharePoint Online site pop up that pane at the right. What if you want this as well?</p> <p>Use yeoman to generate a default commandset SPFx project. I'll use the Panel from Office UI Fabric and create my own React component around it, not that it is mandatory, but it will give me more control later on ... The code for my component:</p> <pre><code class="language-javascript">import * as React from 'react'; import { Panel, PanelType } from 'office-ui-fabric-react/lib/Panel'; export default class CustomPanel extends React.Component&lt;{}, {}&gt; { public render(): React.ReactElement&lt;{}&gt; { return ( &lt;div&gt; &lt;Panel isOpen={true} type={ PanelType.smallFixedFar } headerText='U2U Panel' closeButtonAriaLabel='Close' &gt; &lt;p&gt;Hello there!&lt;/p&gt; &lt;/Panel&gt; &lt;/div&gt; ); } } </code></pre> <p>Now the trick is to make sure that when a button is clicked, that you create a div element inside your page in which you can then render your panel. You can do this inside the <strong>onExecute</strong> of the default generated code for your CommandSet. The code that does the trick looks as follows:</p> <pre><code class="language-javascript">public onExecute(event: IListViewCommandSetExecuteEventParameters): void { switch (event.itemId) { ... case 'COMMAND_2': const div = document.createElement('div'); const element: React.ReactElement&lt;{} &gt; = React.createElement( CustomPanel, { } ); ReactDom.render(element, div); break; default: throw new Error('Unknown command'); } } </code></pre> <p>The result will look as follows: <img src="https://u2ublogimages.blob.core.windows.net/robrecht/spf-commandset-pane.PNG" alt="enter image description here" title="enter image title here" /></p> https://blogs.u2u.be/u2u/post/showing-a-panel-from-your-commandset-in-spfx-solution https://blogs.u2u.be/u2u/post/showing-a-panel-from-your-commandset-in-spfx-solution#comment https://blogs.u2u.be/u2u/post.aspx?id=f5a31d97-5379-4016-a8eb-b81f65ead5aa Thu, 25 Jan 2018 10:50:00 +0000 SharePoint Online SPFx robrecht@u2u.be https://blogs.u2u.be/u2u/pingback.axd https://blogs.u2u.be/u2u/post.aspx?id=f5a31d97-5379-4016-a8eb-b81f65ead5aa 0 https://blogs.u2u.be/u2u/trackback.axd?id=f5a31d97-5379-4016-a8eb-b81f65ead5aa https://blogs.u2u.be/u2u/post/showing-a-panel-from-your-commandset-in-spfx-solution#comment https://blogs.u2u.be/u2u/syndication.axd?post=f5a31d97-5379-4016-a8eb-b81f65ead5aa C# value type boxing by interfaces - Enter generics <p>This blog post continues on a previously written blog post, which you can find <a href="https://blogs.u2u.be/u2u/post/c-value-type-boxing-by-interfaces">here</a>. In that blog post, I illustrated that interfaces are reference types, and if you use interfaces together with structs, you have to be aware that boxing and unboxing might take place.</p> <p>In this article it is time to combine this with generics. The nice thing about generics is that the compiler will actually generate the corresponding code behind the scenes in order to be able to handle the templated type, a.k.a. <strong>T</strong>.</p> <p>For the sake of reference, here are the interface and struct used in the previous post.</p> <pre><code class="language-csharp">interface IBankAccount { void Add(int amount); } struct BankAccount : IBankAccount { int value; public BankAccount(int value) =&gt; this.value = value; void IBankAccount.Add(int amount) =&gt; value += amount; public void Add(int amount) =&gt; value += amount; public void Print() =&gt; Console.WriteLine($&quot;Value: {value}&quot;); } </code></pre> <p>Inside my console application, I defined the following generic method:</p> <pre><code class="language-csharp">static void AddGeneric&lt;T&gt;(ref T ba, int amount) =&gt; ((IBankAccount)ba).Add(amount); </code></pre> <p>This method looks straightforward: it casts, whatever <strong>ba</strong> might be into the interface IBankAccount in order to invoke the Add method on it. I even pass the argument by reference, since we wouldn't want the Add to happen on a copy of the BankAccount, now would we? When we execute the following code, the result is just like before:</p> <pre><code class="language-csharp">BankAccount ba = new BankAccount(100); AddGeneric(ref ba, 50); ba.Print(); // Prints 100 and not 150 </code></pre> <p>The problem is still not solved, because the <strong>Add</strong> still takes place on a boxed version of the BankAccount. The compiler has to few information to deduct that you want to execute the <strong>Add</strong> method on the value type. So let's rewrite this method with constraints:</p> <pre><code class="language-csharp">static void AddGenericContrained&lt;T&gt;(ref T ba, int amount) where T : IBankAccount =&gt; ba.Add(amount); </code></pre> <p>And when we execute:</p> <pre><code class="language-csharp">BankAccount ba = new BankAccount( AddGenericContrained(ref ba, 50); ba.Print(); // Prints 150!!! </code></pre> <p>This seems to work, all thanks to the compiler (and the code of course). In this case, the compiler can clearly deduct that you want to execute the implementation of <strong>IBankAccount.Add</strong> on the struct (value type) BankAccount and therefore generates the appropriate code that skips the formality of boxing, and immediately applies the function.</p> <h3>Conclusion</h3> <p>If, and only if, you work with interfaces on value types, be aware of the consequences related to boxing. Remember that using generic constraint to constrain the interface method call will assist the compiler in generating the right code that directs the method call directly to the right function.</p> https://blogs.u2u.be/u2u/post/c-value-type-boxing-by-interfaces-enter-generics https://blogs.u2u.be/u2u/post/c-value-type-boxing-by-interfaces-enter-generics#comment https://blogs.u2u.be/u2u/post.aspx?id=b1e72725-4eb0-43dd-ae08-4197be5d4fe4 Wed, 24 Jan 2018 09:31:00 +0000 .NET robrecht@u2u.be https://blogs.u2u.be/u2u/pingback.axd https://blogs.u2u.be/u2u/post.aspx?id=b1e72725-4eb0-43dd-ae08-4197be5d4fe4 0 https://blogs.u2u.be/u2u/trackback.axd?id=b1e72725-4eb0-43dd-ae08-4197be5d4fe4 https://blogs.u2u.be/u2u/post/c-value-type-boxing-by-interfaces-enter-generics#comment https://blogs.u2u.be/u2u/syndication.axd?post=b1e72725-4eb0-43dd-ae08-4197be5d4fe4 C# value type boxing by interfaces <p>I recently had to explain the difference between value types and reference types in .NET to people that are new to .NET and object oriented programming. This explanation then gradually roles into the concept of <strong>boxing</strong> and <strong>unboxing</strong>, where boxing is the process of converting a value type to the type object or to <strong>any interface type</strong> implemented by this value type. When boxing a value type, the CLR wraps the value inside a <strong>System.Object</strong> and stores it on the managed heap. Unboxing extracts the value type from the object and puts it on the stack.</p> <p>So, when explaining boxing versus unboxing, what do you do? You put a value type inside an object and cast it back...</p> <pre><code class="language-csharp">int i = 42; object o = i; // boxing i = (int)o; //unboxing </code></pre> <p>However, <strong>interfaces</strong> are reference types as well and value types can implement an interface. So boxing will also happen when you treat a value type as an interface it implements. That got me thinking ... If you consider the following code:</p> <pre><code class="language-csharp">interface IBankAccount { void Add(int amount); } struct BankAccount : IBankAccount { int value; public BankAccount(int value) =&gt; this.value = value; void IBankAccount.Add(int amount) =&gt; value += amount; public void Add(int amount) =&gt; value += amount; public void Print() =&gt; Console.WriteLine($&quot;Value: {value}&quot;); } </code></pre> <p>Nothing really strange here, an interface <code>IBankAccount</code> that supports a method <code>Add(int)</code>. A struct <code>BankAccount</code> that implements <code>IBankAccount</code>. The <code>IBankAccount.Add(int)</code> is implemented privately, and there is also a method <code>Add(int)</code> defined on it.</p> <p>Now consider the following code</p> <pre><code class="language-csharp">BankAccount ba = new BankAccount(100); ba.Add(50); ba.Print(); IBankAccount iba = ba; iba.Add(50); ba.Print(); </code></pre> <p>This piece of code will probably not print out what you might expect... The output is as follows:</p> <pre><code class="language-csharp">Value: 150 Value: 150 </code></pre> <p>What is happening here? When creating the variable <code>ba</code>, the CLR will put this on the stack with an initial value of <strong>100</strong>. Next, when executing the <code>ba.Add(50)</code>, we add 50 to the value that is stored inside <code>ba</code> (on the stack), resulting in <strong>150</strong>. Now we treat <code>ba</code> as its interface <code>IBankAccount</code> and put it inside a variable <code>iba</code>. This is where the <strong>boxing</strong> is happening, so the CLR takes a copy of how <code>ba</code> now looks like and stores this on the heap (value <strong>150</strong>). We now call the <code>IBankAccount.Add(int)</code>, this will increment the value on the boxed version on the heap. So this did not update the original value on the stack.</p> https://blogs.u2u.be/u2u/post/c-value-type-boxing-by-interfaces https://blogs.u2u.be/u2u/post/c-value-type-boxing-by-interfaces#comment https://blogs.u2u.be/u2u/post.aspx?id=5139e932-c147-4433-bbd2-e3e725454355 Wed, 1 Nov 2017 09:05:00 +0000 .NET robrecht@u2u.be https://blogs.u2u.be/u2u/pingback.axd https://blogs.u2u.be/u2u/post.aspx?id=5139e932-c147-4433-bbd2-e3e725454355 0 https://blogs.u2u.be/u2u/trackback.axd?id=5139e932-c147-4433-bbd2-e3e725454355 https://blogs.u2u.be/u2u/post/c-value-type-boxing-by-interfaces#comment https://blogs.u2u.be/u2u/syndication.axd?post=5139e932-c147-4433-bbd2-e3e725454355