tag:blogger.com,1999:blog-67800364890357293972024-03-28T09:36:15.978+02:00Bresleveloper Digital - the best at SharePoint, PowerApps and Office365, Contact us now!Developing with the help of God and rabi Nachman - Contact now at https://www.bresleveloper.co.ilBresleveloperhttp://www.blogger.com/profile/12654150644649676499noreply@blogger.comBlogger287125tag:blogger.com,1999:blog-6780036489035729397.post-24331285417783371022024-03-08T16:13:00.001+02:002024-03-08T16:14:13.126+02:00Host Angular app INSIDE C# Web Api 2 (as single app) (not .Net Core) and install in server (and browse from outside)<h4 style="text-align: left;">Host Angular inside ANY C# / ASP.NET solution.</h4><p><br />Table of Content:</p><p></p><ol style="text-align: left;"><li>Web Dev Tools List</li><li>Create DB (ms-sql)</li><li>Create API (c# webapi 2) + connect to DB</li><li>Create Angular project + connect to API</li><li>Host the NgApp inside the API</li><li>Install in Server + connect from internet</li></ol><div><br /></div><div></div><p></p><h1>1. Web Dev Tools List</h1><div><span style="font-size: medium;"><span style="font-weight: 400;">The list of tools :</span></span></div><div><span style="font-size: medium;"><span style="font-weight: 400;">they are all "Next, </span>Next, Next...", and they are all on google search,</span></div><div><p></p><ol style="text-align: left;"><li><span style="font-size: medium;">SSMS - Sql Server Management Tool - for SQL (also installs server)</span></li><li><span style="font-size: medium;">VS22 - Visual Studio 2022 Community - dont forget to check Asp.Net dev tools with .Net framewroks 4.6.1-4.8</span></li><li><span style="font-size: medium;">VSC - Visual Studio Code</span></li><li><span style="font-size: medium;">NodeJS (version compatible to the angular version)</span></li><li><span style="font-size: medium;">Angular (your version)</span></li><li><span style="font-size: medium;">For server install - windows server (2022) with IIS</span></li></ol><div><span style="font-size: medium;"><br /></span></div><div><span style="font-size: medium;"><br /></span></div><div><span style="font-size: medium;"><h1>2. Create DB</h1><div>Open SSMS (type "SQL" in start)</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRHejNyv7G_u6ifBL0zbvAPR8jRi1o__SrWPK1cuHXoHDvI9JtBeyAu4dnBJnsc7UoSkoxgst3oU1UNU4i9uQHHA2xWVBCl5u5yQvvhGJ8qYPKAItJYIa1xWBQ6jHfgK0A7K2TqTopY1W4qQM1WmRPakQn3OOYmmKTKyB_PS2ANap5DbeeydeGGAK263Ex/s842/1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="691" data-original-width="842" height="329" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRHejNyv7G_u6ifBL0zbvAPR8jRi1o__SrWPK1cuHXoHDvI9JtBeyAu4dnBJnsc7UoSkoxgst3oU1UNU4i9uQHHA2xWVBCl5u5yQvvhGJ8qYPKAItJYIa1xWBQ6jHfgK0A7K2TqTopY1W4qQM1WmRPakQn3OOYmmKTKyB_PS2ANap5DbeeydeGGAK263Ex/w400-h329/1.png" width="400" /></a></div><br /><div>There, copy your server name (before click "Connect"):</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSyanYoKLexkInCctgelZ_4M_Hn1Art3bbqsgGcltGr7HOLhFCZEhhf7571Anqzl1mLBAjWy65hphbiStJFRkWOgt_Nb6PZh3mUii6Bb_m8qgtcnsaae1pTe4B_1rPyCLFJmSxlfmQwSk1PmXqtCenV16WJ_16wz9MSJmIPPJrQm709306c6orSxh6Kpn6/s507/2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="337" data-original-width="507" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSyanYoKLexkInCctgelZ_4M_Hn1Art3bbqsgGcltGr7HOLhFCZEhhf7571Anqzl1mLBAjWy65hphbiStJFRkWOgt_Nb6PZh3mUii6Bb_m8qgtcnsaae1pTe4B_1rPyCLFJmSxlfmQwSk1PmXqtCenV16WJ_16wz9MSJmIPPJrQm709306c6orSxh6Kpn6/s16000/2.png" /></a></div><br /><div>Create a DB and a Table (just right-click -> "New..." and give names)</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-GwRbs53NAeNYiPxLoyhK0qsXZFwC4Fw8Iui0bAxqYL1U1_z3C1WVlxTr-XGqA2zzUm9Zw4C5JN1GT-MplukMvGcSnya0nGCn4J82849WeBk_TfhdBw11wCV2abcND6QmeIwUEohFnaLD0TR1A2Dt_GGv_AuQSiUz5U-BYO_e-gZTTFVRk5UnMxoHa0PW/s485/3.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="254" data-original-width="485" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-GwRbs53NAeNYiPxLoyhK0qsXZFwC4Fw8Iui0bAxqYL1U1_z3C1WVlxTr-XGqA2zzUm9Zw4C5JN1GT-MplukMvGcSnya0nGCn4J82849WeBk_TfhdBw11wCV2abcND6QmeIwUEohFnaLD0TR1A2Dt_GGv_AuQSiUz5U-BYO_e-gZTTFVRk5UnMxoHa0PW/s16000/3.png" /></a></div><br /><div>Make sure you have an "Id" columns that is key identity, by double-clicking the "is identity" from the props pane like this:</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8hX6X6GF-jqFG_BGjH0RKuM3DkJIKEqBz_xEQyJrOEeYNSN0mdde-2cP1stG3-y4HF1aTpi-xFPBA2xt1KJ0Fd6v6eatu3yllT_765jELLWO_NzR3tVANi6K2kPE15KDMEuCS-zG9x_VwBv4bPNcd3eCRzzcrateJrFe4h8yhZPfa1s3tuhOjQ0pxCeHg/s429/4.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="375" data-original-width="429" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8hX6X6GF-jqFG_BGjH0RKuM3DkJIKEqBz_xEQyJrOEeYNSN0mdde-2cP1stG3-y4HF1aTpi-xFPBA2xt1KJ0Fd6v6eatu3yllT_765jELLWO_NzR3tVANi6K2kPE15KDMEuCS-zG9x_VwBv4bPNcd3eCRzzcrateJrFe4h8yhZPfa1s3tuhOjQ0pxCeHg/s16000/4.png" /></a></div><div><span style="font-size: medium;"><br /></span></div><div>CTRL+S to save and give it a name.</div><div><br /></div><div>And right click on your new table (click refresh icon if you dont see it) and you get a simple ui for new items, and CTRL+S to save</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhsBpekr1H0oM9-hI_y-NpUApqLEYniSKFW8XNIF0Anhg-xedpI-160J0nGuUunqqEXIhusZuhQw0zL7uz1LLP5rewxX2iassfp3H09PlHV6uzfD2o36fFDlOAr3XeXXt-7S0mcyeOkS6_lWjB3YfW-UFXcVZ7RylR-kPrIj9IpeXf24HV9Dtz5Jm6eoX6/s309/5.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="125" data-original-width="309" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhsBpekr1H0oM9-hI_y-NpUApqLEYniSKFW8XNIF0Anhg-xedpI-160J0nGuUunqqEXIhusZuhQw0zL7uz1LLP5rewxX2iassfp3H09PlHV6uzfD2o36fFDlOAr3XeXXt-7S0mcyeOkS6_lWjB3YfW-UFXcVZ7RylR-kPrIj9IpeXf24HV9Dtz5Jm6eoX6/s16000/5.png" /></a></div><div><span style="font-size: medium;"><br /></span></div>Thats it! you have a fully functional SqlDB!</span></div><div><span style="font-size: medium;"><br /></span></div><div><span style="font-size: medium;"><br /><div><br /></div></span></div><div><br /></div><div><h1>3. Create API</h1></div><div><br /></div><div>Open VS22 and create new project of type "ASP.NET web Application (.NET Framework)", make sure its NOT with "Core" and it "C#" type and not VB: </div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsuH0GfQIXqfu_IraZIGyn4QypKo5c-8cVUD_HnggAa-Jc1oMnKXn05CgQUS3yPQjx_O0dFQUUKAY3N-0QfXA7AV3r4ZxqiEfgc7doU3e7jwitQZXv4G5-5nHGtS_DW-V8lA_2SC8JAWZEPiy0bHI1o0KRVacsgQupgb2pJDh-1iTBVubUC9xVdjdR7yJ_/s1027/6.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="688" data-original-width="1027" height="428" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsuH0GfQIXqfu_IraZIGyn4QypKo5c-8cVUD_HnggAa-Jc1oMnKXn05CgQUS3yPQjx_O0dFQUUKAY3N-0QfXA7AV3r4ZxqiEfgc7doU3e7jwitQZXv4G5-5nHGtS_DW-V8lA_2SC8JAWZEPiy0bHI1o0KRVacsgQupgb2pJDh-1iTBVubUC9xVdjdR7yJ_/w640-h428/6.png" width="640" /></a></div><br /><div>Then choose "Empty" and "Web API".</div><div>You can do this with ANY ASP.NET solution as long as you will add all files, MVC or whatever.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCSLSX-qXW5xHuJ6W09_MIfx9Oe-khl-sGsZBBv2TO8i21ZwH7iJHlTjTZTDjOXNAlD4aaFHS76n1iNfVH2xYXXflhGzE6WNQt95fWz76obnvQ7WlxN-KudkiNFTozo82RghJ4oL2L_iInE15RSHULg3CcVz-5nG5RGLn-O-b0fEYdLgmdTK-oR7KobGC3/s1041/7.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="377" data-original-width="1041" height="232" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCSLSX-qXW5xHuJ6W09_MIfx9Oe-khl-sGsZBBv2TO8i21ZwH7iJHlTjTZTDjOXNAlD4aaFHS76n1iNfVH2xYXXflhGzE6WNQt95fWz76obnvQ7WlxN-KudkiNFTozo82RghJ4oL2L_iInE15RSHULg3CcVz-5nG5RGLn-O-b0fEYdLgmdTK-oR7KobGC3/w640-h232/7.png" width="640" /></a></div><br /><div>We then need to add 2 Nugets packages:</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgRTih4BdpLNm9A4i_HR_rTOKZKXWBi57e3ZB7EUtSGb6QsTZcKGU0tiaFMEtpiZazfUgqZos2SVoyZaVXA2L_jjw7mrQ1ihDE-x_LGTfR54tm0HEd_wPXA4_U1wTw5BOe1kR2iqjzlzI5KLhkD4AKQPPsrMzLhKz_yze8V4gcombee0aiyK-aMAqr0uXpf/s392/8.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="215" data-original-width="392" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgRTih4BdpLNm9A4i_HR_rTOKZKXWBi57e3ZB7EUtSGb6QsTZcKGU0tiaFMEtpiZazfUgqZos2SVoyZaVXA2L_jjw7mrQ1ihDE-x_LGTfR54tm0HEd_wPXA4_U1wTw5BOe1kR2iqjzlzI5KLhkD4AKQPPsrMzLhKz_yze8V4gcombee0aiyK-aMAqr0uXpf/s16000/8.png" /></a></div><br /><div><br /></div><div>And we need These 2:</div><div>The "Cors" is for... enabling cors :)</div><div>The "MVC" is to enable advanced routing to enable files ect. (Angular is Files, remember?)</div><div class="separator" style="clear: both; text-align: center;"><br /></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIXQTENWl4C_XPBa4O2jLXkuvOwX-lCysWXxLkXkrb5elCJlFwLlP21vf4gMLymdS_E07FH1E81snnrW4FM4Fv2QIOPOQ2HE9TFy9XXFXdo1AO4eBoWCgXwZcfravYakhsrTCCilRuHhq655AwzflE8m-OZvnh6_ZHisuNF6fqoxbNHpLF4CJXsP7QhS_j/s704/9.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="403" data-original-width="704" height="366" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIXQTENWl4C_XPBa4O2jLXkuvOwX-lCysWXxLkXkrb5elCJlFwLlP21vf4gMLymdS_E07FH1E81snnrW4FM4Fv2QIOPOQ2HE9TFy9XXFXdo1AO4eBoWCgXwZcfravYakhsrTCCilRuHhq655AwzflE8m-OZvnh6_ZHisuNF6fqoxbNHpLF4CJXsP7QhS_j/w640-h366/9.png" width="640" /></a></div><br /><div><br /></div><div>Now we have an empty API, with 1 file containing basic routing ("WebApiConfig.cs") and its 1 Method is called in the "Global.asax.cs".</div><div><br /></div><div>Lets make a test controller so we can run and see this api in action!</div><div><ol style="text-align: left;"><li>Right click "controllers" (or any other place)</li><li>-> Add -></li><li>"Web Api Controller Class (v2.1)"</li><li>name it "ValuesController" (only must end with "Controller" i.e. remove the "1" in the end)</li></ol></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiB7s97y6Hrjt7c_hx0dPil265M3xkf6LGto7H8zsNcbuV0ToND8Y9wxzapJqscqz-Xy7R7cYa202yFUQKexZLsH0ixgzo7DhVFzJG6O6xiOytUnZFVqNiBKh5Nw0fFfdj1mBMYFFf_NybKg5hlJ8OdzGCHXWv-2-91Le0KkiQ1yFuE_tzYyxSifGmofr-O/s691/10.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="622" data-original-width="691" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiB7s97y6Hrjt7c_hx0dPil265M3xkf6LGto7H8zsNcbuV0ToND8Y9wxzapJqscqz-Xy7R7cYa202yFUQKexZLsH0ixgzo7DhVFzJG6O6xiOytUnZFVqNiBKh5Nw0fFfdj1mBMYFFf_NybKg5hlJ8OdzGCHXWv-2-91Le0KkiQ1yFuE_tzYyxSifGmofr-O/s16000/10.png" /></a></div><br /><div><br /></div><div>Run the solution, it will open a browser with "403", add to url "/api/values" and results!</div><div><br /></div><div>Lets connect to our SQL</div><div>This is a connection string for an app to connect locally to a SQL DB </div><div><br /></div><span style="font-family: courier;"><span style="color: #93c47d;">"Data Source=<<i>server address</i>>;Initial Catalog=<db name>;Integrated Security=True"</span></span><div><br /></div><div>So in our case, remember i asked to copy the value when you connect to your DB? (if not - back to top).</div><div>In my case, with the value above of server address, and DB name of "Ng2", and putting it in the "Web.Config" this is the result:</div><div><br /></div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7FV_7VmOmw43H0qntFGVEcleurNgJuwTyd76mqFH2rgZM_knxgmyVzOPVO3omu2f7KoPIXbDj7X3vpOsTwOdNqT4vp_SlIbcXvRnsUimkXuNU94clxC4GcnBAY6ZHxeprg9Ib0XzpKG8mJEff57vbY83QI5O79jV1iIzTafD4wCktLXgQc44-GzXCmpG5/s1220/11.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="145" data-original-width="1220" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7FV_7VmOmw43H0qntFGVEcleurNgJuwTyd76mqFH2rgZM_knxgmyVzOPVO3omu2f7KoPIXbDj7X3vpOsTwOdNqT4vp_SlIbcXvRnsUimkXuNU94clxC4GcnBAY6ZHxeprg9Ib0XzpKG8mJEff57vbY83QI5O79jV1iIzTafD4wCktLXgQc44-GzXCmpG5/s16000/11.png" /></a></div><br /><div><br /></div><div>You should add a DB.cs file or DAL.cs with some generic or smart code like i did in this blogpost</div><div><a href="https://bresleveloper.blogspot.com/2024/03/3-simple-ways-to-retrieve-generic-sql.html" target="_blank">https://bresleveloper.blogspot.com/2024/03/3-simple-ways-to-retrieve-generic-sql.html</a></div><div><br /></div><div>So lets assume you did it.</div><div><br /></div><div>But in this post example lets say we create another Controller named "NGController" and change "public IEnumerable<string> Get()" to this:</div><div><br /></div><div><div><span style="color: #93c47d; font-family: courier;">public string Get() { return DB.GetTableAsJsonStr("TNG"); }</span></div></div><div><br /></div><div>Test by browsing to "/api/ng"</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7j3017Tn7t_AxTKxFFBkl_hG3YzIhhsG_78h764zilqCAqJ91AREjWMeLJPIogoVx6GiQ63zzt4m2qC0wF75F7a6tROiqsYigm13_JJTdbrz4MR6A2XzOICMhbrL_8PGHouDi1cbIT4aarJzypg7v6ULmg_dPloDghc2xhl-k_7Rv_NrHtVGx1D8tMM1W/s727/12.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="238" data-original-width="727" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7j3017Tn7t_AxTKxFFBkl_hG3YzIhhsG_78h764zilqCAqJ91AREjWMeLJPIogoVx6GiQ63zzt4m2qC0wF75F7a6tROiqsYigm13_JJTdbrz4MR6A2XzOICMhbrL_8PGHouDi1cbIT4aarJzypg7v6ULmg_dPloDghc2xhl-k_7Rv_NrHtVGx1D8tMM1W/s16000/12.png" /></a></div><br /><div><br /></div><h1 style="text-align: left;">4. Create Angular project + connect to API</h1><div>We 1st need to enable cors (please google this if you dont know "cors" meaning) in our API</div><div>Open WebApiConfig.cs and add this to the top of the default Register method.</div><div><div><br /></div><div><span style="color: #93c47d; font-family: courier;">var cors = new EnableCorsAttribute("http://localhost:4200", "*", "*");</span></div><div><span style="color: #93c47d; font-family: courier;">cors.SupportsCredentials = true;</span></div><div><span style="color: #93c47d; font-family: courier;">config.EnableCors(cors);</span></div></div><div><br /></div><div>Make sure you installed "NodeJS" .</div><div>Then we need the command line "start- > cmd"</div><div>Install Angular </div><div><br /></div><div>If you are using Angular 17 and above and you want a "normal" project with "app.module.ts" than add this to your ng new command, and choose 'n' for the SSR question</div><div><br /></div><div><span style="color: #93c47d; font-family: courier;">ng new AngularHostNg --no-standalone</span></div><div><br /></div><div>Create a service and a component</div><div><br /></div><div><span style="color: #93c47d; font-family: courier;">ng generate service services/ngt-svc</span></div><div><span style="color: #93c47d; font-family: courier;">ng generate component components/ngt-table</span></div><div><br /></div><div>Service is simple as ever</div><div><br /></div><div><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div> <span style="color: #dcdcaa;">getNg</span>() <span style="color: #d4d4d4;">:</span> <span style="color: #4ec9b0;">Observable</span><<span style="color: #4ec9b0;">any</span>>{</div><div> <span style="color: #c586c0;">return</span> <span style="color: #569cd6;">this</span>.<span style="color: #9cdcfe;">http</span>.<span style="color: #dcdcaa;">get</span><<span style="color: #4ec9b0;">any</span>>(<span style="color: #ce9178;">"https://localhost:44356/api/ng"</span>).<span style="color: #dcdcaa;">pipe</span>(<span style="color: #dcdcaa;">map</span>(<span style="color: #9cdcfe;">x</span> <span style="color: #569cd6;">=></span> <span style="color: #9cdcfe;">JSON</span>.<span style="color: #dcdcaa;">parse</span>(<span style="color: #9cdcfe;">x</span>)))</div><div> }</div></div></div><div><br /></div><div>In the component ts lets define our array from service. We define data as empty array otherwise if it starts as null angular wont detect changes correctly, and might throw errors.</div><div><br /></div><div><br /></div><div><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div><span style="color: #c586c0;">export</span> <span style="color: #569cd6;">class</span> <span style="color: #4ec9b0;">NGTTableComponent</span> {</div><div> <span style="color: #9cdcfe;">data</span><span style="color: #d4d4d4;">:</span><span style="color: #4ec9b0;">any</span>[]<span style="color: #d4d4d4;">=</span>[];</div><div> <span style="color: #569cd6;">constructor</span>(<span style="color: #569cd6;">public</span> <span style="color: #9cdcfe;">svc</span><span style="color: #d4d4d4;">:</span><span style="color: #4ec9b0;">NgtSvcService</span>) {</div><div> <span style="color: #9cdcfe;">svc</span>.<span style="color: #dcdcaa;">getNg</span>().<span style="color: #dcdcaa;">subscribe</span>(<span style="color: #9cdcfe;">x</span><span style="color: #569cd6;">=></span> <span style="color: #569cd6;">this</span>.<span style="color: #9cdcfe;">data</span><span style="color: #d4d4d4;">=</span><span style="color: #9cdcfe;">x</span>)</div><div> }</div><div>}</div></div></div><div><br /></div><div>For the HTML</div><div><br /></div><div><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div><span style="color: grey;"><</span><span style="color: #569cd6;">table</span> <span style="color: #f44747;">border</span>=<span style="color: #ce9178;">"1"</span><span style="color: grey;">><</span><span style="color: #569cd6;">tbody</span><span style="color: grey;">></span></div><div> <span style="color: grey;"><</span><span style="color: #569cd6;">tr</span><span style="color: grey;">></span>data<span style="color: grey;"></</span><span style="color: #569cd6;">tr</span><span style="color: grey;">></span></div><div> <span style="color: grey;"><</span><span style="color: #569cd6;">tr</span><span style="color: grey;">><</span><span style="color: #569cd6;">th</span><span style="color: grey;">></span>Id<span style="color: grey;"></</span><span style="color: #569cd6;">th</span><span style="color: grey;">><</span><span style="color: #569cd6;">th</span><span style="color: grey;">></span>Name<span style="color: grey;"></</span><span style="color: #569cd6;">th</span><span style="color: grey;">></</span><span style="color: #569cd6;">tr</span><span style="color: grey;">></span></div><div> <span style="color: grey;"><</span><span style="color: #569cd6;">tr</span> <span style="color: #9cdcfe;">*ngFor</span>=<span style="color: #ce9178;">"</span><span style="color: #569cd6;">let</span> <span style="color: #9cdcfe;">x</span> <span style="color: #569cd6;">of</span> <span style="color: #9cdcfe;">data</span><span style="color: #ce9178;">"</span><span style="color: grey;">></span></div><div> <span style="color: grey;"><</span><span style="color: #569cd6;">td</span><span style="color: grey;">></span>{{<span style="color: #9cdcfe;">x</span>.<span style="color: #9cdcfe;">Id</span>}}<span style="color: grey;"></</span><span style="color: #569cd6;">td</span><span style="color: grey;">></span></div><div> <span style="color: grey;"><</span><span style="color: #569cd6;">td</span><span style="color: grey;">></span>{{<span style="color: #9cdcfe;">x</span>.<span style="color: #9cdcfe;">Name</span>}}<span style="color: grey;"></</span><span style="color: #569cd6;">td</span><span style="color: grey;">></span></div><div> <span style="color: grey;"></</span><span style="color: #569cd6;">tr</span><span style="color: grey;">></span> </div><div><span style="color: grey;"></</span><span style="color: #569cd6;">tbody</span><span style="color: grey;">></</span><span style="color: #569cd6;">table</span><span style="color: grey;">></span></div></div></div><div><br /></div><div>Enjoy your Angular with</div><div><br /></div><div><span style="color: #93c47d; font-family: courier;">ng serve --open</span></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><h1 style="text-align: left;"><br /></h1><h1 style="text-align: left;">5. Host the NgApp inside the API</h1><div>Lets start by packaging Angular to the C# WebAPI folder</div><div><br /></div><div><span style="color: #93c47d; font-family: courier;">ng build</span></div><div><br /></div><div>Angular 17 will create this</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgyeH849XjUVUCeYoTT70pveqqdFKKHEY0UijLVLPSyC7Aj7E23w4IZRTgjBjE7LuQc3mAYfT3qJIop0kpAdYCALlPyFN7ZE8liOZi2-2XVHa4jLHVVczhdPr7AOJq3Dtw0K2gTQqGd-i8JnOU52RYC96hs5pcHGQwd5wJvAK4ioPxRU2pvk0zS-CxSL70/s473/13.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="193" data-original-width="473" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgyeH849XjUVUCeYoTT70pveqqdFKKHEY0UijLVLPSyC7Aj7E23w4IZRTgjBjE7LuQc3mAYfT3qJIop0kpAdYCALlPyFN7ZE8liOZi2-2XVHa4jLHVVczhdPr7AOJq3Dtw0K2gTQqGd-i8JnOU52RYC96hs5pcHGQwd5wJvAK4ioPxRU2pvk0zS-CxSL70/s16000/13.png" /></a></div><br /><div>So lets copy the entire "/dist" folder to the .Net solution folder</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiVenOHx1gd8tZexPfukTBpLho91whEreFrt0WrC0k326nw4GakRAgoOet45Nl4fHLvHjIlC0EyKtqA7NGd9CsyafJv_CRz9t9wc5SQiYBfU7zwSPBWHBdPAe-lLBZ5Ip6J_tby1Sy4fWUc9OYUwPI0pTFgWlfUFsmMfEMoAuMnRfMXsrv4PkjyP6FfsfLf/s519/14.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="411" data-original-width="519" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiVenOHx1gd8tZexPfukTBpLho91whEreFrt0WrC0k326nw4GakRAgoOet45Nl4fHLvHjIlC0EyKtqA7NGd9CsyafJv_CRz9t9wc5SQiYBfU7zwSPBWHBdPAe-lLBZ5Ip6J_tby1Sy4fWUc9OYUwPI0pTFgWlfUFsmMfEMoAuMnRfMXsrv4PkjyP6FfsfLf/s16000/14.png" /></a></div><br /><div><br /></div><div><br /></div><div>Now we need to modify the C# WebAPI solution to generally serve files, and html files, and also to default select the angular index.html</div><div><br /></div><div>Enable serving html filed:</div><div>Open Web.Config and add buildProviders like this</div><div><br /></div><div><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, "Courier New", monospace; line-height: 19px; white-space: pre;"><div style="font-size: 14px;"><span style="color: grey;"><</span><span style="color: #569cd6;">configuration</span><span style="color: grey;">></span></div><div style="font-size: 14px;"> <span style="color: grey;"><</span><span style="color: #569cd6;">appSettings</span><span style="color: grey;">></span></div><div style="font-size: 14px;"> <span style="color: grey;"><</span><span style="color: #569cd6;">add</span> <span style="color: #9cdcfe;">key</span>=<span style="color: #ce9178;">"myConnStrApp"</span> </div><div style="font-size: 14px;"><span style="color: #9cdcfe;">value</span>=<span style="color: #ce9178;">"Data Source=localhost\SQLEXPRESS;Initial Catalog=Ng2;Integrated Security=True"</span> <span style="color: grey;">/></span></div><div style="font-size: 14px;"> <span style="color: grey;"></</span><span style="color: #569cd6;">appSettings</span><span style="color: grey;">></span></div><div style="font-size: 14px;"> <span style="color: grey;"><</span><span style="color: #569cd6;">system.web</span><span style="color: grey;">></span></div><div style="font-size: 14px;"> <span style="color: grey;"><</span><span style="color: #569cd6;">compilation</span> <span style="color: #9cdcfe;">debug</span>=<span style="color: #ce9178;">"true"</span> <span style="color: #9cdcfe;">targetFramework</span>=<span style="color: #ce9178;">"4.8"</span> <span style="color: grey;">></span></div><div style="font-size: 14px;"><span style="color: grey;"><br /></span></div><div> <span style="color: grey;"><</span><span style="color: #569cd6;">buildProviders</span><span style="color: grey;">></span></div><div> <span style="color: grey;"><</span><span style="color: #569cd6;">add</span> <span style="color: #9cdcfe;">extension</span>=<span style="color: #ce9178;">".html"</span> <span style="color: #9cdcfe;">type</span>=<span style="color: #ce9178;">"System.Web.Compilation.PageBuildProvider"</span> <span style="color: grey;">/></span></div><div> <span style="color: #6a9955;"><!-- Allows for routing everything to ~/index.html --></span></div><div> <span style="color: grey;"></</span><span style="color: #569cd6;">buildProviders</span><span style="color: grey;">></span></div><div><span style="color: grey;"><br /></span></div><div style="font-size: 14px;"> <span style="color: grey;"></</span><span style="color: #569cd6;">compilation</span><span style="color: grey;">></span></div><div style="font-size: 14px;"> <span style="color: grey;"><</span><span style="color: #569cd6;">httpRuntime</span> <span style="color: #9cdcfe;">targetFramework</span>=<span style="color: #ce9178;">"4.8"</span> <span style="color: grey;">/></span></div><div style="font-size: 14px;"> <span style="color: grey;"></</span><span style="color: #569cd6;">system.web</span><span style="color: grey;">></span></div><div style="font-size: 14px;"> <span style="color: grey;"><</span><span style="color: #569cd6;">runtime</span><span style="color: grey;">></span></div><div style="font-size: 14px;"><span style="color: grey;">.</span></div><div style="font-size: 14px;"><span style="color: grey;">.</span></div><div style="font-size: 14px;"><span style="color: grey;">.</span></div></div></div><div><br /></div><div>Next go to WebApiConfig.cs and add a new method under Register, to define new routing options.</div><div>Notice that the default value for "indexHtml" variable can be changed to your design.</div><div><br /></div><div><div style="background-color: #1f1f1f; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div style="color: #cccccc;"><span style="color: #569cd6;">public</span> <span style="color: #569cd6;">static</span> <span style="color: #569cd6;">void</span> <span style="color: #dcdcaa;">RegisterRoutes</span>(<span style="color: #4ec9b0;">RouteCollection</span> <span style="color: #9cdcfe;">routes</span>) {</div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">routes</span><span style="color: #cccccc;">.</span><span style="color: #9cdcfe;">RouteExistingFiles</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #569cd6;">true</span><span style="color: #cccccc;">;</span></div><div style="color: #cccccc;"> <span style="color: #9cdcfe;">routes</span>.<span style="color: #dcdcaa;">IgnoreRoute</span>(<span style="color: #ce9178;">"{file}.js"</span>);</div><div style="color: #cccccc;"> <span style="color: #9cdcfe;">routes</span>.<span style="color: #dcdcaa;">IgnoreRoute</span>(<span style="color: #ce9178;">"{file}.html"</span>);</div><div style="color: #cccccc;"> <span style="color: #9cdcfe;">routes</span>.<span style="color: #dcdcaa;">IgnoreRoute</span>(<span style="color: #ce9178;">"{file}.css"</span>);</div><div style="color: #cccccc;"> <span style="color: #9cdcfe;">routes</span>.<span style="color: #dcdcaa;">IgnoreRoute</span>(<span style="color: #ce9178;">"{file}.ts"</span>);</div><span style="color: #cccccc;"><br /></span><div style="color: #cccccc;"> <span style="color: #569cd6;">string</span> <span style="color: #9cdcfe;">indexHtml</span> <span style="color: #d4d4d4;">=</span> <span style="color: #ce9178;">"~/dist/angular-host-ng/browser/index.html"</span>;</div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">routes</span><span style="color: #cccccc;">.</span><span style="color: #dcdcaa;">MapPageRoute</span><span style="color: #cccccc;">(</span><span style="color: #ce9178;">"spa"</span><span style="color: #cccccc;">, </span><span style="color: #ce9178;">"{*anything}"</span><span style="color: #cccccc;">, </span><span style="color: #9cdcfe;">indexHtml</span><span style="color: #cccccc;">);</span></div><div style="color: #cccccc;">}</div></div></div><div><br /></div><div>Next is Global.asax.cs, add the new routing config</div><div><br /></div><div><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div> <span style="color: #569cd6;">public</span> <span style="color: #569cd6;">class</span> <span style="color: #4ec9b0;">WebApiApplication</span> : <span style="color: #4ec9b0;">System</span>.<span style="color: #4ec9b0;">Web</span>.<span style="color: #4ec9b0;">HttpApplication</span></div><div> {</div><div> <span style="color: #569cd6;">protected</span> <span style="color: #569cd6;">void</span> <span style="color: #dcdcaa;">Application_Start</span>()</div><div> {</div><div> <span style="color: #9cdcfe;">GlobalConfiguration</span>.<span style="color: #dcdcaa;">Configure</span>(<span style="color: #9cdcfe;">WebApiConfig</span>.<span style="color: #9cdcfe;">Register</span>);</div><div> <span style="color: #9cdcfe;">WebApiConfig</span>.<span style="color: #dcdcaa;">RegisterRoutes</span>(<span style="color: #9cdcfe;">RouteTable</span>.<span style="color: #9cdcfe;">Routes</span>);</div><div> }</div><div> }</div></div></div><div><br /></div><div>And then edit the index.html itself inside the dist inside the API C#, we need to update the "base href" meta, so that the file will try to get his other files (js, css) from the right location, in our case</div><div><br /></div><div><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><span style="color: grey;"><</span><span style="color: #569cd6;">base</span> <span style="color: #9cdcfe;">href</span>=<span style="color: #ce9178;">"/dist/angular-host-ng/browser/index.html"</span><span style="color: grey;">></span></div></div><div><br /></div><div>Now you can close the angular, and run your API, and get server a fully functional Angular+API app!</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi961ZncMFM_tzLNDotbgc55Aqq20mXPn_k0ahkzbvivrEzuakOldvQP9XxQ66GUn40_MYUhuBk34GyWWsUwx__WGBvE43UaZnDCBNXKkd5kr8DkOTJ8I3G2-LLccCiDUTuhGRtpOal2B5nRA8VLI69K6B3buv0hyphenhyphen6j2WYEHwsXEVnsU5rKXl9sUNLAKV9D/s543/15.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="154" data-original-width="543" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi961ZncMFM_tzLNDotbgc55Aqq20mXPn_k0ahkzbvivrEzuakOldvQP9XxQ66GUn40_MYUhuBk34GyWWsUwx__WGBvE43UaZnDCBNXKkd5kr8DkOTJ8I3G2-LLccCiDUTuhGRtpOal2B5nRA8VLI69K6B3buv0hyphenhyphen6j2WYEHwsXEVnsU5rKXl9sUNLAKV9D/s16000/15.png" /></a></div><br /><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><h1 style="text-align: left;">6. Install in Server + connect from internet</h1><div>That's really a bonus if you have a server. You can try get ~200$ credits from google could or azure, or pay few coins to your local provider just for the fin of it.</div><div>I assume its Windows Server 2022.</div><div><br /></div><div>Steps are</div><div><ol style="text-align: left;"><li>copy-paste you entire C# folder to the server</li><li>give it Everyone permissions (research and change later)</li><ol><li>right click folder -> Properties -> Security</li><li>"Edit" button</li><li>"Add" button</li><li>write everyone and click "Check Names" button</li><li>"OK" -> "OK" -> "OK"</li></ol><li>Open IIS and create new website and connect to the folder. give any name, say "angular-host"</li><ol><li>Instructions here <a href="https://www.ssl.com/how-to/create-new-website-iis-10/" target="_blank">https://www.ssl.com/how-to/create-new-website-iis-10/</a></li></ol><li>Open hosts file</li><ol><li>WIN key + r</li><li>drivers</li><li>open "etc" folder</li><li>edit "hosts" file</li><li>add in the end "127.0.0.1 angular-host"</li><li><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizHLBZ5dqaiQIyBf3xSONFQDGXaL5qiF4z9w4bzozW8S2KT2qJqIN6Htn5M0YLp1N2-2G2rdzyKfY97o_ULr12I7G1Y8EmTu_erRi0l591EDHurBZbLm7iWwTLxQk1MRqff_bI34uKo9IYakP0RKckEPIohUQf8GBnvniW_CNj7MwYD-XeyN-iA8IRla6z/s736/16.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="189" data-original-width="736" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizHLBZ5dqaiQIyBf3xSONFQDGXaL5qiF4z9w4bzozW8S2KT2qJqIN6Htn5M0YLp1N2-2G2rdzyKfY97o_ULr12I7G1Y8EmTu_erRi0l591EDHurBZbLm7iWwTLxQk1MRqff_bI34uKo9IYakP0RKckEPIohUQf8GBnvniW_CNj7MwYD-XeyN-iA8IRla6z/s16000/16.png" /></a></div><br /></li></ol><li>Browse in server "http://angular-host/"</li></ol><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgu3yU-nKCXjodAnlxm0znl_ZH2byKXH4-svm0-6pJ_oMFxfHgsP0Tbs1GSVshAfF-SJvkzrQelgAl1zZBOnpdI7muo-OTmpqzfSk4AdqOiogK6vD-tkJ2QcQdX8zUlAToClo2THjpjJ194scGGNvGVYESKagSl4xVugh3XQNcNLRpH5wzIfSR0VdA9Z_8z" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="178" data-original-width="581" src="https://blogger.googleusercontent.com/img/a/AVvXsEgu3yU-nKCXjodAnlxm0znl_ZH2byKXH4-svm0-6pJ_oMFxfHgsP0Tbs1GSVshAfF-SJvkzrQelgAl1zZBOnpdI7muo-OTmpqzfSk4AdqOiogK6vD-tkJ2QcQdX8zUlAToClo2THjpjJ194scGGNvGVYESKagSl4xVugh3XQNcNLRpH5wzIfSR0VdA9Z_8z=s16000" /></a></div><br /><br /></div></div><div>NOTE that if you dont duplicate and set your SQL in the server you will not serve data.</div><div>Just do Part 1 of this blog in the server (and update web.config in server if needed).</div><div><br /></div><div>Browse outside your server?</div><div><br /></div><div>Fast local way is to edit YOUR hosts file and add "<server ip> angular-host", you now can browse "http://angular-host/" from your computer to the server.</div><div><br /></div><div>Open to the World via IP:</div><div><br /></div><div>Add new binding with some other port, and browse to it</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPg3dwuYt9wvMivoswl3oHMLMpnfejg9JPgeqWBIL-m1uluMwVCPXrZzUyVt8sTJbhuvwXLvQU8o79NjijWmje9A9xJgpE7-FJmT-XVXwxRE7U9ZprMZq4c8Pbuhg1fUWbXGqdi9gvo0O6rg1xc8cXQy1lxjQ5QXLKXUBaxzs1ocMTAD5YnsplETe4Wr13/s1044/17.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="476" data-original-width="1044" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPg3dwuYt9wvMivoswl3oHMLMpnfejg9JPgeqWBIL-m1uluMwVCPXrZzUyVt8sTJbhuvwXLvQU8o79NjijWmje9A9xJgpE7-FJmT-XVXwxRE7U9ZprMZq4c8Pbuhg1fUWbXGqdi9gvo0O6rg1xc8cXQy1lxjQ5QXLKXUBaxzs1ocMTAD5YnsplETe4Wr13/s16000/17.png" /></a></div><br /><div>Configure Firewall to allow access to this port by followinf instuctions from here:</div><div><a href="https://kb.diadem.in/how-to-configure-iis-to-access-website-using-ip-address_1080.html" target="_blank">https://kb.diadem.in/how-to-configure-iis-to-access-website-using-ip-address_1080.html</a></div><div><br /></div><div>You now can browse directly via IP to the correct file (must use full path)</div><div><span style="color: #93c47d; font-family: courier;">http://<IP x.x.x.x>:29546/dist/angular-host-ng/browser/index.html/</span></div><div><br /></div><div>SEND YOUR APP TO FRIENDS~~</div><div><br /></div><div><br /></div></div>Bresleveloperhttp://www.blogger.com/profile/12654150644649676499noreply@blogger.com0tag:blogger.com,1999:blog-6780036489035729397.post-50131417266251785402024-03-08T14:27:00.002+02:002024-03-10T00:30:48.836+02:003 Simple ways to retrieve generic SQL data with C# / WebAPI controller (using DataRow)<p>Full Code in the end! :)</p><p>Assuming you have some connection string:</p><p><span style="color: #93c47d; font-family: courier;">private static string ConnectionString = ConfigurationManager.AppSettings["myConnStrApp"];</span></p><div>And assuming we are using the simple "SqlDataAdapter", our Method will start with:</div><div><br /></div><div><div><span style="color: #93c47d; font-family: courier;">using (SqlConnection conn = new SqlConnection(ConnectionString)) {</span></div><div><span style="color: #93c47d; font-family: courier;"> using (SqlDataAdapter adp = new SqlDataAdapter("SELECT * FROM " + tblname, conn)) {</span></div><div><span style="color: #93c47d; font-family: courier;"> DataSet ds = new DataSet("ds");</span></div><div><span style="color: #93c47d; font-family: courier;"> adp.Fill(ds);</span></div></div><div><br /></div><div><br /></div><div>We have 2 generic options</div><p></p><ol style="text-align: left;"><li>return (convert to) Json String</li><li>build json object</li></ol><div>Lets 1st mention the "standard" way - by class model.</div><div>That means we need to create a new C# class per sql table, and then create matching parsing mechanism, meaning that the rest of our method contains this (see more in the end):</div><div><br /></div><div><div><span style="color: #93c47d; font-family: courier;">var rows = ds.Tables[0].Rows;</span></div><div><span style="color: #93c47d; font-family: courier;">for (int i = 0; i < rows.Count; i++) {</span></div><div><span style="color: #93c47d; font-family: courier;"> var r = rows[i];</span></div><div><span style="color: #93c47d; font-family: courier;"> t.Add(new TNG() {</span></div><div><span style="color: #93c47d; font-family: courier;"> Id = int.Parse(r["Id"].ToString()),</span></div><div><span style="color: #93c47d; font-family: courier;"> </span><span style="color: #93c47d; font-family: courier;"> </span><span style="color: #93c47d; font-family: courier;">Name = r["Name"].ToString(),</span></div><div><span style="color: #93c47d; font-family: courier;"> });</span></div><div><span style="color: #93c47d; font-family: courier;">}</span></div></div><p></p><p>But what if we have multiple tables? What if we just want to test things? What if we expect Changes? Or maybe I just dont wanna cuz its of no use?</p><p>GENERICS OPTIONS (after code above)</p><p><br /></p><h1 style="text-align: left;">Return (convert to) JSON String</h1><p>pro: most simple, least amount of code, can be tested with xml view on browser</p><p>con: its a string and not an object, needs to be parsed at client (JSON.parse)</p><p>Change the method to return string and add this line:</p><p><span style="color: #93c47d; font-family: courier;">return JsonConvert.SerializeObject(ds.Tables[0]);</span></p><div>That's it.</div><div>xml testing by browse:</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_f_vxk_lxel6ORr8KQc8ORlgVKCs3PICW_iZuFkEf1t63jl4eFS6kK2JnnuaxNard-ehBY3HVYJ2AnWEycOFKwsW9tbYuvq76K7gGQGQUsgxges0tAVw-oMmD6VJZV-C9l_lyIl9YhH_ehXB_kpJaHx6kOr_f-t5G647P7V4YXI_vLJE78TA3aA255PoE/s727/12.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="238" data-original-width="727" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_f_vxk_lxel6ORr8KQc8ORlgVKCs3PICW_iZuFkEf1t63jl4eFS6kK2JnnuaxNard-ehBY3HVYJ2AnWEycOFKwsW9tbYuvq76K7gGQGQUsgxges0tAVw-oMmD6VJZV-C9l_lyIl9YhH_ehXB_kpJaHx6kOr_f-t5G647P7V4YXI_vLJE78TA3aA255PoE/s16000/12.png" /></a></div><br /><div><br /></div><h1 style="text-align: left;">Build JSON Object</h1><p>pro: simple generic json object in c#</p><p>con: 6 rows instead of 1 lol, no xml testing</p><p>Change the method to return objectand add this line:</p><p><span style="color: #93c47d; font-family: courier;">string[] cols = ds.Tables[0].Columns.Cast<DataColumn>().Select(d=> d.ColumnName).ToArray();</span></p><p><span style="color: #93c47d; font-family: courier;">var rows = ds.Tables[0].Rows;</span></p><p><span style="color: #93c47d; font-family: courier;">return ds.Tables[0].Rows.Cast<DataRow>().Select(r => {</span></p><p><span style="color: #93c47d; font-family: courier;"> var jo = new JObject();</span></p><p><span style="color: #93c47d; font-family: courier;"> foreach (string c in cols) { jo.Add(c, JToken.FromObject(r[c])); }</span></p><p><span style="color: #93c47d; font-family: courier;"> return jo;</span></p><p><span style="color: #93c47d; font-family: courier;">});</span></p><p>The "Select" are just fancy way to do "foreach" with LINQ.</p><p>xml testing by browse:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgesC4a-aXd7RghP9Xd_1yA713kEInuk4Aw_2xFLWWQS8swxRxzCbSVBjwvLYq99sLwsLeWJeDVigYk6igqnEkVSeoZgM0YLbJ9zjLqS4sI5pUTwheN2SbcFhP2JHWp0oq1ShIe8v8pRxyd2_JEtf7kwq_MbSK2Kkm7-BxAA_HS1BY71jwDGUlsXIPdrPkK/s1013/12.2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="311" data-original-width="1013" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgesC4a-aXd7RghP9Xd_1yA713kEInuk4Aw_2xFLWWQS8swxRxzCbSVBjwvLYq99sLwsLeWJeDVigYk6igqnEkVSeoZgM0YLbJ9zjLqS4sI5pUTwheN2SbcFhP2JHWp0oq1ShIe8v8pRxyd2_JEtf7kwq_MbSK2Kkm7-BxAA_HS1BY71jwDGUlsXIPdrPkK/s16000/12.2.png" /></a></div><br /><p><br /></p><p><br /></p><p>here are the source codes:</p><p></p><ol style="text-align: left;"><li>the code in the example (<a href="https://drive.google.com/file/d/1zYJChHIusLingYueGzRiRlvjc9BGxpoC/view?usp=sharing" rel="nofollow" target="_blank">DB_example.cs</a>)</li><li>full power generic DAL with alot of stuff, Reflection and CRUD and more (<a href="http://DAL.cs" rel="nofollow" target="_blank">DAL.cs</a>).</li></ol><p></p><p><br /></p><p><br /></p><p><br /></p>Bresleveloperhttp://www.blogger.com/profile/12654150644649676499noreply@blogger.com0tag:blogger.com,1999:blog-6780036489035729397.post-34792236726330097472024-02-08T23:25:00.000+02:002024-02-08T23:25:00.252+02:00PowerApps Patch new SharePoint list item with conditional / dynamically adding fields<p><b>Important</b> </p><p>if you get an error that says:</p><p><span style="font-family: courier;">powerapps patch sharepoint list error the specified column "" does not exist the column with the most similar name is ""</span></p><p>that would be because you use the column DisplayName instead of the InternalName</p><p><br /></p><p><b>Solution:</b></p><p><br /></p><p> Set(oItem, {</p><p> Title:me.displayName,</p><p> EmpITref:{Id:meSP.ID, Value:meSP.Title},</p><p> EducationLevel:EducationLevel.SelectedText.Value,</p><p> CertCount:vis,</p><p> WorkYearsExp:WorkYearsExp.SelectedText.Value,</p><p> anythingElse:anythingElse.Text,</p><p> DigitalSignature:DigSign.Text</p><p>});</p><p><br /></p><p>If(vis<>0, Set( oItem, Patch (oItem, {</p><p> Cert1_x002d_OrgAd:cert1_orgAd.Text, </p><p> Cert1_x002d_Title: cert1_title.Text,</p><p> Cert1_x002d__x0023_:cert1_No.Text, </p><p> Cert1_x002d_GrantDate:cert1_grant.SelectedDate,</p><p> Cert1_x002d_exp_x002e_Date:cert1_exp.SelectedDate</p><p>})));</p><p><br /></p><p><br /></p><p>If(vis=2, Set( oItem, Patch (oItem, {</p><p> Cert2_x002d_OrgAd:cert2_orgAd.Text, </p><p> Cert2_x002d_Title: cert2_title.Text,</p><p> Cert2_x002d__x0023_:cert2_No.Text, </p><p> Cert2_x002d_GrantDate:cert2_grant.SelectedDate,</p><p> Cert2_x002d_exp_x002e_Date:cert2_exp.SelectedDate</p><p>})));</p><p><br /></p><p><br /></p><p>Patch('Some List', Defaults('Some List'), oItem)</p><p><br /></p><p><br /></p><p><br /></p>Bresleveloperhttp://www.blogger.com/profile/12654150644649676499noreply@blogger.com0tag:blogger.com,1999:blog-6780036489035729397.post-6212187657990781482023-12-14T01:08:00.003+02:002023-12-14T01:12:27.483+02:00Share Folder to Multiple External Users one by onne - Power Automate (item created)<p> Scenario:</p><p>there is a new SP sub-folder every day with some files.</p><p>this needs to be shared to about 40 different emails daily.</p><p>BUT with 2 exclusions:</p><p>1. sometimes we want these 35 emails, sometimes some other 38 emails. so every day we exclude a few emails, every day different ones.</p><p>2. we do not want the "clients" to know about one another, so no bulk sharing or just external link, only share 1 by 1.</p><p><br /></p><p>SOLUTION:</p><p>1. SPList for emails ("clients")</p><p>2. SP Library containing "main" folder, inside we create daily SP sub-folder with a name (usualy just the date like "25.8.23")</p><p>3. SPList for trigger the flow (power automate) on item created, getting from+archive for daily broadcast</p><p>4. flow for filtering out excluded emails, and sending 1 by 1 a share email.</p><p><br /></p><p>lets go</p><p><br /></p><h3 style="text-align: left;">1. SPList for emails ("clients")</h3><p>really nothing other than new list, Title for "client name" and another text field for email</p><p><br /></p><h3 style="text-align: left;">2. SP Library containing "main" folder, inside we create daily SP sub-folder with a name (usualy just the date like "25.8.23")</h3><p>as stated</p><h3 style="text-align: left;">3. SPList for trigger the flow (power automate) on item created, getting from+archive for daily broadcast</h3><p>new list, fields:</p><p>1. Title (is meaningless)</p><p>2. "exclude_emails" is multi-lookup for SPList of emails above, with added the email itself to show beyond the title (technical name is "Projected Field"), THIS IS A MUST (will be used in the flow)</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh00I7JTfYLgKV4pmeTBxgNZaoGvx9ZIUfZg8puk78-rTu8fNzTtnBzEagt16W0Q4a2MTtxmZZD4tF1ApBjk2hICfaWBC_zyHrq4DFTKxU2LVrxFSXAl3G7mMCjYMAdQ6WK-AZOa-ypvtVn-6XSv8bmsgbVvdlBRMtP7Gz4WnYVrR4XUycvZwRK1_6sZ8u5/s599/0.0.5.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="368" data-original-width="599" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh00I7JTfYLgKV4pmeTBxgNZaoGvx9ZIUfZg8puk78-rTu8fNzTtnBzEagt16W0Q4a2MTtxmZZD4tF1ApBjk2hICfaWBC_zyHrq4DFTKxU2LVrxFSXAl3G7mMCjYMAdQ6WK-AZOa-ypvtVn-6XSv8bmsgbVvdlBRMtP7Gz4WnYVrR4XUycvZwRK1_6sZ8u5/s16000/0.0.5.png" /></a></div><p>3. "folder_name" text field for the textual value of the name of the subfolder (like "25.8.23")</p><p>its mainly used as initiate form</p><p><br /></p><h3 style="text-align: left;">4. flow for filtering out excluded emails, and sending 1 by 1 a share email.</h3><p>here is the "meat"</p><p><br /></p><p>in general, the main goals are:</p><p>1. get request digest for the final HTTP requests for sharing</p><p>2. get all emails</p><p>3. filter out emails to exclude</p><p>4. send sharing emails.</p><p><br /></p><p>I will show all the steps, with code, and comments when needed</p><p><br /></p><p><br /></p><p>1. "When an item is created"</p><p>[in hebrew the word "Shibolet" is the upper part of the wheat when its ready]</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiamcV4Vq4eCPAk59Wiz9dYJza0zH155dXlGPkMiUq3ykO6q-sR-vNb73Vn9vsU7g0-NmMH4BRyRc6IT-3PfUn4_QyqXKEUrulO2_Fao9luJUdIIo0UCq8kfrVjd7795_4-DmbiBA2_7JgBYHXz-UGAfaY_k4PQ-QZVJr3P1mdvtHXxyaODtvn6U_nJAZOf/s620/1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="196" data-original-width="620" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiamcV4Vq4eCPAk59Wiz9dYJza0zH155dXlGPkMiUq3ykO6q-sR-vNb73Vn9vsU7g0-NmMH4BRyRc6IT-3PfUn4_QyqXKEUrulO2_Fao9luJUdIIo0UCq8kfrVjd7795_4-DmbiBA2_7JgBYHXz-UGAfaY_k4PQ-QZVJr3P1mdvtHXxyaODtvn6U_nJAZOf/s16000/1.png" /></a></div><div><br /></div>2. "Send an HTTP request to SharePoint" for getting the form request digest value<div><br /></div><div>params:</div><div>Site Address - your site</div><div>Method - POST</div><div>Uri - <span style="color: #93c47d; font-family: courier;">_api/contextinfo</span></div><div>Headers:</div><div> <span style="color: #93c47d;"><span style="font-family: courier;">Accept : </span><span style="font-family: courier;">application/json;odata=nometadata</span></span></div><div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimjkWGx5lCZfuitRquOSCLgc4uoF21gIKIXHPVDgj10qZR0zP4hlojOtLtIlrStP6yTSY28J58ARFX_wLZ4eCfEZ3Tk7XQyZ2iHVtuPZfDwFdFIHDTaY1hL9qGjf8Ryn8rD7adRMvh4IgNE1V8eqLc3_6JrUgBXh-EVqezHPp1lmjK3jxsWDjLz6ftm2pJ/s622/2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="311" data-original-width="622" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimjkWGx5lCZfuitRquOSCLgc4uoF21gIKIXHPVDgj10qZR0zP4hlojOtLtIlrStP6yTSY28J58ARFX_wLZ4eCfEZ3Tk7XQyZ2iHVtuPZfDwFdFIHDTaY1hL9qGjf8Ryn8rD7adRMvh4IgNE1V8eqLc3_6JrUgBXh-EVqezHPp1lmjK3jxsWDjLz6ftm2pJ/s16000/2.png" /></a></div><br /><p>3. "Compose" it for future use</p><p>insert into the "Add dynamic content" pane this value</p><p><span style="color: #93c47d; font-family: courier;">body('HTTP_GET_DIGEST')?['FormDigestValue']</span></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3ht1DjR_b2XVNTFY1QzpLtWWtc40_reb0Aa8bEcblfIRQV8jS6W7We3sAEI9hISM0BffkjOnJtp8gaZi4FxesX46kx72fOB7JcCIiwV-pSBX04CeiZ9QKPUWDhQC8KITMSWUceb3mITrqLcARaZv1m1LjgideaNyQNilZlnr-HACe__H9496Y3CCpHtXc/s1026/3.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="287" data-original-width="1026" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3ht1DjR_b2XVNTFY1QzpLtWWtc40_reb0Aa8bEcblfIRQV8jS6W7We3sAEI9hISM0BffkjOnJtp8gaZi4FxesX46kx72fOB7JcCIiwV-pSBX04CeiZ9QKPUWDhQC8KITMSWUceb3mITrqLcARaZv1m1LjgideaNyQNilZlnr-HACe__H9496Y3CCpHtXc/s16000/3.png" /></a></div><br /><p>4. "Initialize variable" for the folder name value from "item created" for easy access</p><p>my column/SPField name is "folderNameToShare"</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRCCLSgymzVv0nFTNvl0rGUIhOVKztyrQE9SGs_Xw7AcAqcFKf7NAG6TBp4ksuo5LsSXHK_tdH3UgmyNrfhjEOt2CbItYgZBf0rTbOvJL9p04krdUagphQtAaq9VlbGkeTG8q9E2myviSsIqtbn2nBbDGp6AM08Kmd0SS5ukamHyQiXECAeG4OCNVTC6a7/s619/4.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="203" data-original-width="619" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRCCLSgymzVv0nFTNvl0rGUIhOVKztyrQE9SGs_Xw7AcAqcFKf7NAG6TBp4ksuo5LsSXHK_tdH3UgmyNrfhjEOt2CbItYgZBf0rTbOvJL9p04krdUagphQtAaq9VlbGkeTG8q9E2myviSsIqtbn2nBbDGp6AM08Kmd0SS5ukamHyQiXECAeG4OCNVTC6a7/s16000/4.png" /></a></div><br /><p>5. "Get folder metadata using path" </p><p>because we need the itemID of this subfolder (any folder in SPList or Library is an item behind the scenes)</p><p>use <span style="font-family: courier;"><span style="color: #93c47d;">/LibraryTitle/FolderName/</span><span style="color: #c27ba0;">varFolderName</span></span><span style="color: #c27ba0;"> </span>from last step</p><p>[NOTE this can be annoying with adding the last slash "/", for me it didnt show, so I added to the variable, and I got an error, and then I saw IT DID added it, and then removed from the variable and it worked, so a bit annoying]</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjh-mgv-csbs7pBlHCRoRi4BKXmKfpNm9EvBHzWgZTtbiaz2wjwQPkcQIu9D8yLD4wfKSgEvS3W6ArpPUV0uhGTzWwE4lyZV_x8RaUREEYUzz_-naAxnfOHYXzMsDnGe1_HowpWnZaS_Py2pjTFrqIQzVgs5LMtJAsHkKtRHlw0wxmgCyajT2nJmInUCMbL/s624/5.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="161" data-original-width="624" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjh-mgv-csbs7pBlHCRoRi4BKXmKfpNm9EvBHzWgZTtbiaz2wjwQPkcQIu9D8yLD4wfKSgEvS3W6ArpPUV0uhGTzWwE4lyZV_x8RaUREEYUzz_-naAxnfOHYXzMsDnGe1_HowpWnZaS_Py2pjTFrqIQzVgs5LMtJAsHkKtRHlw0wxmgCyajT2nJmInUCMbL/s16000/5.png" /></a></div><br /><p>6. "Initialize variable" for the folder "itemId"</p><p>take notice this is an integer</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQ2LQQYpSlEKuIS-TCEEV9Yib-hYHcz152VGFUszO1_y_9HaIYvErWprfeC8EQ94J-IEBkDqxpxehVcwTM5a2eWorM9cJJSQ74T1wOIZBXaxneVf5ZWqs-tIWSmN5T4gB27ZZhyqzQQnB-Xf4UiTcZGPiQUN7jiOssMPO-6vdOeF4EfKvUNCYfyabfbNYh/s638/6.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="208" data-original-width="638" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQ2LQQYpSlEKuIS-TCEEV9Yib-hYHcz152VGFUszO1_y_9HaIYvErWprfeC8EQ94J-IEBkDqxpxehVcwTM5a2eWorM9cJJSQ74T1wOIZBXaxneVf5ZWqs-tIWSmN5T4gB27ZZhyqzQQnB-Xf4UiTcZGPiQUN7jiOssMPO-6vdOeF4EfKvUNCYfyabfbNYh/s16000/6.png" /></a></div><br /><p>7. "Initialize variable" for the Library Guid</p><p>I will eventually use this api "_api/web/lists('GUID')/..." just beacuse its easier for me, and its a static Library, but you can get this manually, or use ".../lists/getbytitle('SPList_Title')/..."</p><p>just open library settings and copy it from the URL</p><p>cut out the start ("%7B") and the end ("%7D") which are just url encoding for "{" and "}"</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrAAIxkZzJGmaGnRTaEPWUE9o8eYCCgduFbzqlT83_rsxt8YSvot_K6Y0PsafxrfzumKDkFEUWfdnCKGo4YdYn_qgUAN4z9M1skJl-eyu0QqTXQC13BdrxNJOUH9dnqkne3STFc6UT620mdu2JMeKW9udr4JmCoIq36UcS_v8osJIqPc19zntJ-WXqp_Gs/s582/7.5.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="51" data-original-width="582" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrAAIxkZzJGmaGnRTaEPWUE9o8eYCCgduFbzqlT83_rsxt8YSvot_K6Y0PsafxrfzumKDkFEUWfdnCKGo4YdYn_qgUAN4z9M1skJl-eyu0QqTXQC13BdrxNJOUH9dnqkne3STFc6UT620mdu2JMeKW9udr4JmCoIq36UcS_v8osJIqPc19zntJ-WXqp_Gs/s16000/7.5.png" /></a></div></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguTjBYQiInYWACZ-_5V0PajpL2q2QLtV5T2QlxASff1ODxqj3ZoG7UyTFcZbmBHIFRuTHReC149ZVBdogaND5GP0t3jzUIkQG-2dDQEl7umzlDqat_DTQ774H-4UV2bMsqq972eCVbD5EuqjL7dajUV5oqpKg06eCrgz9ebg6lkxXT0_VMTwSNCkJeHGUW/s626/7.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="193" data-original-width="626" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguTjBYQiInYWACZ-_5V0PajpL2q2QLtV5T2QlxASff1ODxqj3ZoG7UyTFcZbmBHIFRuTHReC149ZVBdogaND5GP0t3jzUIkQG-2dDQEl7umzlDqat_DTQ774H-4UV2bMsqq972eCVbD5EuqjL7dajUV5oqpKg06eCrgz9ebg6lkxXT0_VMTwSNCkJeHGUW/s16000/7.png" /></a></div><div><br /></div>8. "Get items" for getting all emails<div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYkOT-qh0A9gnKwWTyXcLPuN_T2AuEwv8ZDcr-XEILP87WVni-rZpXhPejjpDbIHCniTQlEUXjFAaM5Ydz7WvtAJoh4wOuLhHrmY3eZhUR_cWgcuwyCBH5PXLbRBXZxnCfcQfBRoLNq4j_JBIEoXe_w_V_KybxrnvC8hOYTxr-d6JSjLLld1V0C7LRar7L/s621/8.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="274" data-original-width="621" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYkOT-qh0A9gnKwWTyXcLPuN_T2AuEwv8ZDcr-XEILP87WVni-rZpXhPejjpDbIHCniTQlEUXjFAaM5Ydz7WvtAJoh4wOuLhHrmY3eZhUR_cWgcuwyCBH5PXLbRBXZxnCfcQfBRoLNq4j_JBIEoXe_w_V_KybxrnvC8hOYTxr-d6JSjLLld1V0C7LRar7L/s16000/8.png" /></a></div><br /><div>9. "Initialize variable" for the final emails array</div><div>no population currently, Initialize variable must be a top level (or "global", unlike "nested") step</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiiXmfi2PEu_Fz6bZ3HauIwX1hKLjun-C42-lXrRx7IwBxX4F5vrLyM9o3rEPop-eqBNNhS69K8_tWUvKG6e3EqWwa0U4_UhRXgLoj2FeP8yNWwv63l0xiQBLQ72cpqOSTOYpKFaBVafL14T4TfblzXrrJ6LfXutyHQli1KYKLo-K6s9QxXdgi-2F990zQV/s629/9.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="191" data-original-width="629" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiiXmfi2PEu_Fz6bZ3HauIwX1hKLjun-C42-lXrRx7IwBxX4F5vrLyM9o3rEPop-eqBNNhS69K8_tWUvKG6e3EqWwa0U4_UhRXgLoj2FeP8yNWwv63l0xiQBLQ72cpqOSTOYpKFaBVafL14T4TfblzXrrJ6LfXutyHQli1KYKLo-K6s9QxXdgi-2F990zQV/s16000/9.png" /></a></div><br /><div>10. "Parse JSON", to get the list of emails to exclude from the multi lookup field values</div><div><br /></div><div>The "JSON parse" step include Content and Schema, so you can later select specific values from the content. In order to get the value for Schema MS provided a button "Generate from sample"</div><div><br /></div><div>the Content is the Projected field, the one with "FieldName:EMAIL"</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSdbjd-2zh74pZV3blXVPDCC48psW-BBW0B9DF27Yg7F8-q4YyPH0xRMt2gRrwNXGCDWv_dCNOmiNqKpxPE5yJX-rep6p58biO76goqh3K0JmMdZEM_J01R-nUB7_XSN_obYhvT30nEl-E1KVthWw-ASP4MtH3jwVSqBMCMJP6iAuPAKfi5kjS7T_yxpHZ/s1011/10.2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="331" data-original-width="1011" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSdbjd-2zh74pZV3blXVPDCC48psW-BBW0B9DF27Yg7F8-q4YyPH0xRMt2gRrwNXGCDWv_dCNOmiNqKpxPE5yJX-rep6p58biO76goqh3K0JmMdZEM_J01R-nUB7_XSN_obYhvT30nEl-E1KVthWw-ASP4MtH3jwVSqBMCMJP6iAuPAKfi5kjS7T_yxpHZ/s16000/10.2.png" /></a></div><br /><div><br /></div><div>so what you need to do is:</div><div>a. use value without schema</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhudLBtk67GA189FJOwcuwjZ8cepgzGPRlBALiXvxT2EBjZ8MdH9qIGBmC26uNzdccWqg8JX5pAqL_idcxdmHZ6WpfwVW7_1ZCiFi9ILcvBo6rzO8OPc64-AxSbXVApf829OLhR3OziO4noAjOg1KWt0R6BB4ovPPlmT4vlYkny2Zckey3PGWqhvtlXkHu6/s640/10.1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="101" data-original-width="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhudLBtk67GA189FJOwcuwjZ8cepgzGPRlBALiXvxT2EBjZ8MdH9qIGBmC26uNzdccWqg8JX5pAqL_idcxdmHZ6WpfwVW7_1ZCiFi9ILcvBo6rzO8OPc64-AxSbXVApf829OLhR3OziO4noAjOg1KWt0R6BB4ovPPlmT4vlYkny2Zckey3PGWqhvtlXkHu6/s16000/10.1.png" /></a></div>b. run your flow (Test -> create some item)</div><div>c. after flow run, open step and copy Content value<br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8RrkSEjf_TRzB9ji6a0jDPqfnylx-mYn-CS6ERwW-_pK7Ygxtu4ZCnIsIV4H5elUkUyA9irE62yWHsg1pes_MUr_hNXMNmgOGrMsVFi5FmXQ_F5rpAsz-PPDSyT9-XL4KU5LkDpfpsTAfW9OUQ7XucxIyAA77PCv1c1PY6goNBI5Fs4Pa_j0Ap0gMYqRp/s700/10.3.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="392" data-original-width="700" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8RrkSEjf_TRzB9ji6a0jDPqfnylx-mYn-CS6ERwW-_pK7Ygxtu4ZCnIsIV4H5elUkUyA9irE62yWHsg1pes_MUr_hNXMNmgOGrMsVFi5FmXQ_F5rpAsz-PPDSyT9-XL4KU5LkDpfpsTAfW9OUQ7XucxIyAA77PCv1c1PY6goNBI5Fs4Pa_j0Ap0gMYqRp/s16000/10.3.png" /></a></div><br /><div>d. back to edit mode, click "Generate from sample", paste the content, click "Done", and the schema will magically appear</div><div>FINAL:</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEoO9zdBgnLvQFePdG3j1qYoDpA_tuJtjWmqjtM7zP1Ccf3oZSlxjJKK6pZddxVZB21mwyQ2AkQ7Hi3ivFa5UOurtFtfEoWpujG5RdHbb_aR5fMQAnsse5tG9lKKLF2EoGjRtqlsNcrfuoiHLz0NP65KJ6Py5X1IMdoxvLdJB16bSz9EhxPoiawkP9UIEK/s627/10.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="348" data-original-width="627" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEoO9zdBgnLvQFePdG3j1qYoDpA_tuJtjWmqjtM7zP1Ccf3oZSlxjJKK6pZddxVZB21mwyQ2AkQ7Hi3ivFa5UOurtFtfEoWpujG5RdHbb_aR5fMQAnsse5tG9lKKLF2EoGjRtqlsNcrfuoiHLz0NP65KJ6Py5X1IMdoxvLdJB16bSz9EhxPoiawkP9UIEK/s16000/10.png" /></a></div><br /><div><br /></div><div>11. "Initialize variable" for the emails to exclude array</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiBIBVlhjGTkA62FPBOsBjCFsUD20hatlRJkvfZW-1wQW6L6QCoStIzARpzTMY6oQVitPZV8VZ0Adifq2whag2e_3Qcjh-DIgAiHEPSgvFAYlbW8wTHNh-0xY8mH7L-UKbimDTJjmN_fzxJE1M8mj8dYLO3sUKkeNYueME99IYKRQUbjysxLFdoVlHofSFP" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="191" data-original-width="620" src="https://blogger.googleusercontent.com/img/a/AVvXsEiBIBVlhjGTkA62FPBOsBjCFsUD20hatlRJkvfZW-1wQW6L6QCoStIzARpzTMY6oQVitPZV8VZ0Adifq2whag2e_3Qcjh-DIgAiHEPSgvFAYlbW8wTHNh-0xY8mH7L-UKbimDTJjmN_fzxJE1M8mj8dYLO3sUKkeNYueME99IYKRQUbjysxLFdoVlHofSFP=s16000" /></a></div><div><br /></div>12. "Apply to each" with "Append to array variable" </div><div>populating the emails to exclude array</div><div><br /></div><div>you DONT need to create "Apply to each" step.</div><div>Instead create "Append to array variable" step and choose "Value" from the side pane, from the "Parse JSON" step (step 10), and he will wrap your step with a loop.</div><div><br /></div><div>because we worked to generate a schema we can use the "Value" parameter for the SPField.</div><div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbmza49__OXM7Yr2oM3rzT4U6rFk9qErHW-SOR7D75oCce4zE4C60EXpUaGWLiYPkNqd4dMa2AxiO7nHP-3iw7ujB6p3gJagfUKllzemdkG5qZonvW2VnmX1pOuOofgMKWutejoX55MxY3WcNbJP2WK-mGHwPZ7yHLRRzxA1LlY955YmrYa-u-avUOX80u/s1050/12.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="472" data-original-width="1050" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbmza49__OXM7Yr2oM3rzT4U6rFk9qErHW-SOR7D75oCce4zE4C60EXpUaGWLiYPkNqd4dMa2AxiO7nHP-3iw7ujB6p3gJagfUKllzemdkG5qZonvW2VnmX1pOuOofgMKWutejoX55MxY3WcNbJP2WK-mGHwPZ7yHLRRzxA1LlY955YmrYa-u-avUOX80u/s16000/12.png" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><br /></div><div>13. "Initialize variable" for the flag specific email</div><div>should we add it or exclude it from final array</div><div><br /></div><div>[a FLAG in programming is a boolean parameter that is meant to indicate if we should start or stop doing something in the code]</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiHBZ1yeN5M_5j0c4IRExcGq_0r0GkhbpR4JTb7N2AJmnCAxjX0fRImsG5lz8Ai8rNaoDYnmtJ4jrGpnuW-RWBy8JSbwnDWKRVLSoCACNgVIrc-eY0-89gB7Yg0YqcLP-rpVoBEoN0cdsUA3gv0KWn5QzRsJ-OEpzZlcFUa6oXnJoiH6IYbshz9MPHApYIy" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="194" data-original-width="622" src="https://blogger.googleusercontent.com/img/a/AVvXsEiHBZ1yeN5M_5j0c4IRExcGq_0r0GkhbpR4JTb7N2AJmnCAxjX0fRImsG5lz8Ai8rNaoDYnmtJ4jrGpnuW-RWBy8JSbwnDWKRVLSoCACNgVIrc-eY0-89gB7Yg0YqcLP-rpVoBEoN0cdsUA3gv0KWn5QzRsJ-OEpzZlcFUa6oXnJoiH6IYbshz9MPHApYIy=s16000" /></a></div><br /><br /></div><div><br /></div>14. EXCLUDING EMAILS</div><div><br /></div><div>so when we create an item, we select some emails to exclude from the full list of emails.</div><div>THEREFOR we need a big doube-loop (nested loop) step that works like this:</div><div><br /></div><div>[might be room for improve]</div><div><br /></div><div>I will write/specify</div><div>- all the substeps in writing <span> </span><span> </span>(marked as letters)</div><div>- all the substeps in big picture<span> </span>(marked as same letters)</div><div>- all the substeps individually<span> </span><span> </span>(marked as same letters)</div><div><br /></div><h4 style="text-align: left;">- all the substeps in writing</h4><div>A. - for each item in "all emails" list (loop)</div><div>B. - get specific email</div><div>C. - set flag to true </div><div>D. - test if exclude array has items (not empty)</div><div><div>E. - if yes</div></div><div><span>F. </span><span> - for each item in excluded items (step 10 above) </span>(nested loop)</div><div><span>G. </span> - get specific email to exclude</div><div><span>H. </span><span> - test if email equals to </span>email to exclude</div><div><div><span>I. </span><span> </span> - if yes</div></div><div><span>J. </span><span> </span><span> </span><span> - set flag to false</span><br /></div><div>K. - if flag is true</div><div>L. <span> </span>- add email to final list</div><div><br /></div><div><h4>- all the substeps in picture</h4></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpQYt6W57-dnmVvWRYxHXBAFCPViDkaYWfD8kEL3tEcJwVVOunR7JDGzev8ArTafP0e6MLaEE6OknDIJGr5HmNW9afZaWMdb7jxjEJO_GFiBRhN-aF9uNhfwEnzsh3uvfqUMhc52qLHNoApRUWn2BP-gtjwiOQXgjZQJsWTv5666R1Zuo0vKTl1rjIydvO/s852/14%20main.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="852" data-original-width="691" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpQYt6W57-dnmVvWRYxHXBAFCPViDkaYWfD8kEL3tEcJwVVOunR7JDGzev8ArTafP0e6MLaEE6OknDIJGr5HmNW9afZaWMdb7jxjEJO_GFiBRhN-aF9uNhfwEnzsh3uvfqUMhc52qLHNoApRUWn2BP-gtjwiOQXgjZQJsWTv5666R1Zuo0vKTl1rjIydvO/s16000/14%20main.png" /></a></div><br /><div><br /></div><div><h4>- all the substeps in individually </h4></div><div>A+B. "Apply to each" + "Compose"</div><div><div>you DONT need to create "Apply to each" step.</div><div>Instead create "Compose" step and choose "EMAIL" from the side pane from step 8 (get all items from all emails list) , and he will wrap your step with a loop.</div></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgMODzNeydsxKVeK1Aitz3eT-ax80Qfw-U5NAuY37PVudwYz_iMV9deQt88KEbOO8JxaeGvE9DJ84tgNsOV3I2tziIQMzSci5yX4pWGMGN1IbVIMe6t9PP2ZHnlHgxsFHZyMeltDov4BmUo-RLoLNbzD6Y7oFvufA1gGsL8lKALDXY2BX6b65ZJgayG6OQd" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="301" data-original-width="1013" src="https://blogger.googleusercontent.com/img/a/AVvXsEgMODzNeydsxKVeK1Aitz3eT-ax80Qfw-U5NAuY37PVudwYz_iMV9deQt88KEbOO8JxaeGvE9DJ84tgNsOV3I2tziIQMzSci5yX4pWGMGN1IbVIMe6t9PP2ZHnlHgxsFHZyMeltDov4BmUo-RLoLNbzD6Y7oFvufA1gGsL8lKALDXY2BX6b65ZJgayG6OQd=s16000" /></a></div><br /><br /></div><div>C. "Set variable" flag to true</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhE0M53QOJD69Z16amViGnBvNHRa9G7IXYoYkuwItwieXicqd1DS44Eikg5Pdyj6MDsjq04uBlDYgBjM50szy5KEN_wyUgo7bIL22UN-SH6qmRjxoSpH41HyG-Q5Ve02rSwMlf61amBR73KNrIPAQmD__vFcNNabEDBbyeKT6guVNu39jko8kE7x4XJFPCY" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="148" data-original-width="621" src="https://blogger.googleusercontent.com/img/a/AVvXsEhE0M53QOJD69Z16amViGnBvNHRa9G7IXYoYkuwItwieXicqd1DS44Eikg5Pdyj6MDsjq04uBlDYgBjM50szy5KEN_wyUgo7bIL22UN-SH6qmRjxoSpH41HyG-Q5Ve02rSwMlf61amBR73KNrIPAQmD__vFcNNabEDBbyeKT6guVNu39jko8kE7x4XJFPCY=s16000" /></a></div><br />D. "Condition" is emails to exclude array NOT empty</div><div><i>meaning we need to test for emails to exclude</i></div><div><br /></div><div>on the left hand side, in side pane click "Expression" and insert <span style="color: #93c47d; font-family: courier;">empty(variables('varArrExcludeEmails'))</span></div><div>make sure you set the name to the correct variable</div><div><br /></div><div>on the right hand side, in side pane click "Expression" choose the "false" variable</div><div><br /></div><div>and for comparison choose "is equal to" </div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi3Rfb5tP7H7fKjwzThYwttOcxzGSIoabEtVb34WOoBv0hPj3Nyw0rX2eXNXRD3ZX7IOPzkUrKE4T0U-nu_8uSYZbsvzN-ziafvZWMyWqc2GLu_XZ99wv73aKZkbIM46CxAeP1btrb9QTvtnj9JADcqVn854JhKc1upCPgyiClwM_z04mdHj3JneeUNgOU9" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="178" data-original-width="618" src="https://blogger.googleusercontent.com/img/a/AVvXsEi3Rfb5tP7H7fKjwzThYwttOcxzGSIoabEtVb34WOoBv0hPj3Nyw0rX2eXNXRD3ZX7IOPzkUrKE4T0U-nu_8uSYZbsvzN-ziafvZWMyWqc2GLu_XZ99wv73aKZkbIM46CxAeP1btrb9QTvtnj9JADcqVn854JhKc1upCPgyiClwM_z04mdHj3JneeUNgOU9=s16000" /></a></div><br /><br /></div><div>E+F+G. "If yes" + "Apply to each" + "Compose"</div><div>you DONT need to create "Apply to each" step.</div><div><div><br /></div><div>Instead, inside the "If yes" sub-step, create "Compose" step and choose "Value" from the side pane, from the "Parse JSON" step (step 10), and he will wrap your step with a loop. (just like we did in step 12)</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiTTOfFKpUJGxgPQeZO0tNDvQ6qVEjY0oeIImuGSmutcfceLGVm8JckFkfoBo1zaCx8kWQ9z5ki-EWyeeFy2XdYDUSclSVKT5cktgsDRLPSuc7gQxclfX8r3uqpCU51ElNEfLTJ1R6m-DX7tEpVxUVgqDo7lxvrt8QS5wsQf6lYtkuZP1V0a35j61ElfEjm" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="442" data-original-width="1114" src="https://blogger.googleusercontent.com/img/a/AVvXsEiTTOfFKpUJGxgPQeZO0tNDvQ6qVEjY0oeIImuGSmutcfceLGVm8JckFkfoBo1zaCx8kWQ9z5ki-EWyeeFy2XdYDUSclSVKT5cktgsDRLPSuc7gQxclfX8r3uqpCU51ElNEfLTJ1R6m-DX7tEpVxUVgqDo7lxvrt8QS5wsQf6lYtkuZP1V0a35j61ElfEjm=s16000" /></a></div><div><br /></div><br />H. "Condition" is email equal to exclude email</div><div><i>meaning we need to update the flag</i></div><div><br /></div><div>on the left hand side, in side pane, find the Compose step where we set the current email we are testing</div><div><br /></div><div>on the right hand side, in side pane, find the Compose step where we set the current EXCLUDE email we are testing</div><div><br /></div><div>and for comparison choose "is equal to" </div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjY4NXBhKZyiLC_ZukgrlduFAb8JY4pvDcAJhlEAl2B9R8b5mtZkGHjGZnNdbBSyy_7c80M4The2K4coVxJBodzgAPCUmodzFvaPCeQiKojW-CVZndvKgjqWa4IBSJ-DB6ipwOQAYxrUZz_AJhhohebZEVaVqrxn_vz0wpifn-EifHxKZSSQlvG4A9Ke1Q8" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="372" data-original-width="839" src="https://blogger.googleusercontent.com/img/a/AVvXsEjY4NXBhKZyiLC_ZukgrlduFAb8JY4pvDcAJhlEAl2B9R8b5mtZkGHjGZnNdbBSyy_7c80M4The2K4coVxJBodzgAPCUmodzFvaPCeQiKojW-CVZndvKgjqWa4IBSJ-DB6ipwOQAYxrUZz_AJhhohebZEVaVqrxn_vz0wpifn-EifHxKZSSQlvG4A9Ke1Q8=s16000" /></a></div><br /><br /></div><div>I+J. "Set variable" and set flag to false</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEh_j18uK6yDJfiS6XtWS1wkVYlTRZxKVCIiKBFwzGjWMyifP7EOgonYd9HXcm3esS2edEZCJfR5d_mHerrHZDol231P-qAoVd_rIvmsSlnv8kJwO4RpeE3XUNEPU9ModsA3ElnpiqwQtkXTgmlmt82qt5OghOsRM9LUte3OMWHshtu1IbyoGdonmKVBuvDK" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="227" data-original-width="647" src="https://blogger.googleusercontent.com/img/a/AVvXsEh_j18uK6yDJfiS6XtWS1wkVYlTRZxKVCIiKBFwzGjWMyifP7EOgonYd9HXcm3esS2edEZCJfR5d_mHerrHZDol231P-qAoVd_rIvmsSlnv8kJwO4RpeE3XUNEPU9ModsA3ElnpiqwQtkXTgmlmt82qt5OghOsRM9LUte3OMWHshtu1IbyoGdonmKVBuvDK=s16000" /></a></div><br /><br /></div><div>we now "end" the 2 conditions, and the next steps (K, L) are OUTSITE these conditions steps above, see the big picture for reference.</div><div><br /></div><div><br /></div><div>K. "Condition" is our flag variable equals true</div><div><br /></div><div>after all that testing, if the flag is still true, we need to add this email to the final list. </div><div>if the flag is false it means we need to exclude it (meaning SKIP adding to final list)</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiUl6OyM-p8AbGK_OXUuUESR46FR94fPcbPj2k2_tj0ZnBTj0qRWRZqmCjOJKbYzAMmCRMq6AIMKUNnWfVtC7BhL7aTsrRVvy683Wu862LvJo0zlbdXxS7Ikp2uvwU58j6RrQ6stGopy2_ChOxD1z9n0ss3yED4KCZ1wqbC6z4dMSaf121BM9ad0Fs8oU5a" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="204" data-original-width="622" src="https://blogger.googleusercontent.com/img/a/AVvXsEiUl6OyM-p8AbGK_OXUuUESR46FR94fPcbPj2k2_tj0ZnBTj0qRWRZqmCjOJKbYzAMmCRMq6AIMKUNnWfVtC7BhL7aTsrRVvy683Wu862LvJo0zlbdXxS7Ikp2uvwU58j6RrQ6stGopy2_ChOxD1z9n0ss3yED4KCZ1wqbC6z4dMSaf121BM9ad0Fs8oU5a=s16000" /></a></div><br /><br /></div><div>L. "Append to array variable" the current email to the final array</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiXdj8hfMjcSjU_mJquxKUmnPjhOgM0qgcgjlB5qSg1Ygn3vNvU6uPs-34iqVU91gqK38XmZZaXpUAeydRB111f9RcCo5f4gPtAq7aJLBP0pGdCi0CYE2mV9oWbHCthAMPkp69Jw_A8HCis7SE1Tlk9uyyD3qbnuW0jN-fRRhgV_ZgqPAyemTp2HcdvBvck" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="248" data-original-width="647" src="https://blogger.googleusercontent.com/img/a/AVvXsEiXdj8hfMjcSjU_mJquxKUmnPjhOgM0qgcgjlB5qSg1Ygn3vNvU6uPs-34iqVU91gqK38XmZZaXpUAeydRB111f9RcCo5f4gPtAq7aJLBP0pGdCi0CYE2mV9oWbHCthAMPkp69Jw_A8HCis7SE1Tlk9uyyD3qbnuW0jN-fRRhgV_ZgqPAyemTp2HcdvBvck=s16000" /></a></div><br /><br /></div><div><br /></div><div><br /></div><div><br /></div><div>15. "Apply to each" + "Compose" + "Send an HTTP request to SharePoint"</div><div><br /></div><div><div>this time, you DO need to create "Apply to each" step, and put inside the final emails array. give it a good clear name, so you can easily find it for the next step of Compose </div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiQJ7lTU60FRe2JRUxXUn-TyBMvBApU2Tvfo2lcLDA8WtVeDYqMfoffz2AFL5Hp8tOMj7N3oTzuUhtO9ATknb2Eh3AbrIilek4XxIqXna8iP8Of8HFnCyxwGNEa0eeGVuVgAneb26RuhGZgkDf15a5JauTNypTp3q6SHC2_7gAr0c8uNZn0da1kI6ORlgi1" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="131" data-original-width="655" src="https://blogger.googleusercontent.com/img/a/AVvXsEiQJ7lTU60FRe2JRUxXUn-TyBMvBApU2Tvfo2lcLDA8WtVeDYqMfoffz2AFL5Hp8tOMj7N3oTzuUhtO9ATknb2Eh3AbrIilek4XxIqXna8iP8Of8HFnCyxwGNEa0eeGVuVgAneb26RuhGZgkDf15a5JauTNypTp3q6SHC2_7gAr0c8uNZn0da1kI6ORlgi1=s16000" /></a></div><br /><br /></div><div>Then create "Compose" step and choose "Current Item" from the side pane for this specific loop</div></div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgJxYdRAz_oWkEqfLcD9o-MggYGz0hMdl6tCyfoA9SUdbBlQFTBWcUfKKXpz2gvheDFbsHHZGl765pharJ8bEDSsNHp2CVFHerEgQl6aLaaSwV6xo38RDWNmsm1DaSjB1NrGnecX5K70RVnpSBJ-V4ul9DWxHuBQt9qSrl19pw5kI71r5DdhD1Ggzu5mfPh" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="284" data-original-width="660" src="https://blogger.googleusercontent.com/img/a/AVvXsEgJxYdRAz_oWkEqfLcD9o-MggYGz0hMdl6tCyfoA9SUdbBlQFTBWcUfKKXpz2gvheDFbsHHZGl765pharJ8bEDSsNHp2CVFHerEgQl6aLaaSwV6xo38RDWNmsm1DaSjB1NrGnecX5K70RVnpSBJ-V4ul9DWxHuBQt9qSrl19pw5kI71r5DdhD1Ggzu5mfPh=s16000" /></a></div><br /><br /></div></div><div>and now FINALLY we can start sharing!</div><div><br /></div><div>the next step is "Send an HTTP request to SharePoint" which is still INSIDE this last loop</div><div><br /></div><div><br /></div><div>"Send an HTTP request to SharePoint" contains 5 parameters, and we need to set them very carefully or we get various errors, so follow (at least until it works) letter by letter.</div><div><br /></div><div>example errors caused just by not copying letter by letter:</div><div><span style="color: #e06666; font-family: courier;">- json pattern json flow is not valid</span></div><div><span style="color: #e06666; font-family: courier;">- Cannot convert a primitive value to the expected type 'Edm.Boolean'. See the inner exception for more details (can also be 'Edm.String')</span></div><div><br /></div><div>the 5 parameters are:</div><div><br /></div><div>A. Site Address (simple - your site)</div><div>B. Method (simple, POST)</div><div>C. Uri </div><div>D. Headers</div><div>E. Body</div><div><br /></div><div>I will detail C-E</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgmlM2_dKC-ZKOBvl10T1Kn0qgeBojKa9cQQirzVn14rMaChkFULdvb3vGKddKhFEArrVm5ggEXVkfVoQcfDehM4-1wz00n6wHefgBsRU8_j6H_Kq_oj8huc_FUEDcidcklys-8p4-X0oFuFPa5BZKWNJ4FknM4UVR_LysvDjBKgCbV9hMBDClexVT_3VOM" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="373" data-original-width="631" src="https://blogger.googleusercontent.com/img/a/AVvXsEgmlM2_dKC-ZKOBvl10T1Kn0qgeBojKa9cQQirzVn14rMaChkFULdvb3vGKddKhFEArrVm5ggEXVkfVoQcfDehM4-1wz00n6wHefgBsRU8_j6H_Kq_oj8huc_FUEDcidcklys-8p4-X0oFuFPa5BZKWNJ4FknM4UVR_LysvDjBKgCbV9hMBDClexVT_3VOM=s16000" /></a></div><br /><br /></div><div><br /></div><div>C. Uri </div><div><br /></div><div>In steps 6+7 above we defined variables for the Library GUID and also the folder itemID.</div><div>We will now use then in our Uri value. </div><div>NOTICE the single quotes around the variables, they are MUST</div><div><br /></div><div>the value should be like this (make sure you set your actual variables)</div><div><br /></div><div><span style="color: #93c47d; font-family: courier;">_api/web/lists('</span><span style="color: #c27ba0; font-family: courier;">varLibraryGuid</span><span style="color: #93c47d; font-family: courier;">')/GetItemByID(</span><span style="color: #93c47d; font-family: courier;">'</span><span style="font-family: courier;"><span style="color: #c27ba0;">varFolderId</span></span><span style="color: #93c47d; font-family: courier;">')/sharelink</span></div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjVWMtLD8Wxn4-sr3fnSJjWT6TTSZL-u5SQmeJMnYgV77XjzKU2724vw58rBisFE-LNL3zeLqngp9Wn880Eite1uofKA5WB3tOUhV2KOQp2kEKnQNhQYtNsJfpPvjv1r_cveH1FYPSkUcbkqb35CbtZGKTj-fuWGjJ7FCELbnkLKajZ811VGUHv9v4_AHVp" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="66" data-original-width="587" src="https://blogger.googleusercontent.com/img/a/AVvXsEjVWMtLD8Wxn4-sr3fnSJjWT6TTSZL-u5SQmeJMnYgV77XjzKU2724vw58rBisFE-LNL3zeLqngp9Wn880Eite1uofKA5WB3tOUhV2KOQp2kEKnQNhQYtNsJfpPvjv1r_cveH1FYPSkUcbkqb35CbtZGKTj-fuWGjJ7FCELbnkLKajZ811VGUHv9v4_AHVp=s16000" /></a></div><br /><br /></div><div>D. Headers</div><div>In step 3 we stored our Digest value, here we use it</div><div><br /></div><div>there are 3 headers:</div><div>Accept : application/json;odata=nometadata</div><div>Content-Type : application/json;odata=nometadata</div><div>X-RequestDigest : [Outputs of step 3]</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjaviSS3CC5dlMXnswPUAYCHxX_vX_Ek2faHqk8IXjq0iXOzi7x4j-HW1B1beocIlfL02ZQFPLVVtvukkF0cFc5MUsGjQ4LHLA0Mhr2Otvz-07-X9qa649PvmN7TyKLIXAhhYne3-R6Rpzl0vftukjEu6oW3J3EThZfrYFXgI6gP9yDe_oTCLkaECRg8nvN" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="264" data-original-width="984" src="https://blogger.googleusercontent.com/img/a/AVvXsEjaviSS3CC5dlMXnswPUAYCHxX_vX_Ek2faHqk8IXjq0iXOzi7x4j-HW1B1beocIlfL02ZQFPLVVtvukkF0cFc5MUsGjQ4LHLA0Mhr2Otvz-07-X9qa649PvmN7TyKLIXAhhYne3-R6Rpzl0vftukjEu6oW3J3EThZfrYFXgI6gP9yDe_oTCLkaECRg8nvN=s16000" /></a></div><div><br /></div><div><br /></div><br />E. Body</div><div><br /></div><div>THIS one was REALLY tricky and annoying. I initially tried to copy some code from some similar sources but kept getting the errors above and more.</div><div><br /></div><div>Eventually I copied the body value from MS when sharing manually to Specific Person, View Only.</div><div><br /></div><div>To do that, click the sharing button on your folder you want to share</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgdVHxJ9SYCH4o88AJEc7E7Sepk4UJssdo9gnyToVQJYWfGeZL4wtfrdLaGYy-_aALZB4dUS47vPcw2Q8jyxFdRwNtrEiQH6dutyTAOOu2p_OZ0FlS6avSyrGlGwRbx9OqUJpRXPqaaeTDvWlN3YpZw1yl7eml6hCjKpknhuLoMBxaZVuf083JcECN_z_Qh" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="49" data-original-width="318" src="https://blogger.googleusercontent.com/img/a/AVvXsEgdVHxJ9SYCH4o88AJEc7E7Sepk4UJssdo9gnyToVQJYWfGeZL4wtfrdLaGYy-_aALZB4dUS47vPcw2Q8jyxFdRwNtrEiQH6dutyTAOOu2p_OZ0FlS6avSyrGlGwRbx9OqUJpRXPqaaeTDvWlN3YpZw1yl7eml6hCjKpknhuLoMBxaZVuf083JcECN_z_Qh=s16000" /></a></div>and set your preferences. </div><div>Then just before hitting "Send" click F12 to open Developers Tools, and go to "Network" tab.</div><div>Then click "Send".</div><div><br /></div><div>You will be able to see the request itself as MS makes it:</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiqwjsBoQbghpivHuk70XR8leMGd72zyZSJ4Xm4veVR5jDDEFRsrrtE5WmTzsBnUV-I-HAgC5tqHR-47YgrwzTNKnfzzhVrLPgdxExyiV4gGf5tzwnKwRFFj2hZInYeDgYlgP-z93QasRKNuXfg0BEM2SHzrlhccorpKIYXug4TSznBAun66BC2_azAAS7m" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="264" data-original-width="1197" src="https://blogger.googleusercontent.com/img/a/AVvXsEiqwjsBoQbghpivHuk70XR8leMGd72zyZSJ4Xm4veVR5jDDEFRsrrtE5WmTzsBnUV-I-HAgC5tqHR-47YgrwzTNKnfzzhVrLPgdxExyiV4gGf5tzwnKwRFFj2hZInYeDgYlgP-z93QasRKNuXfg0BEM2SHzrlhccorpKIYXug4TSznBAun66BC2_azAAS7m=s16000" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgMTOJFhMUxpnfIHmPqXc4KEbB8FwH7u2n5Da6ah-T_pECB1B26aP2m8m1rdklFGIOfF04xEoKb4175MhmttD7Lo4p8QFuf8-wc4iKbDjT0sGtSKiXIEGZ6MvLuM4E75sswdZpAAsX0q11-2N45TVk9nCG6P9_FEX_EGGOOnLL-PSEi4a4Wf4uYawLBh4wa" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="276" data-original-width="1229" src="https://blogger.googleusercontent.com/img/a/AVvXsEgMTOJFhMUxpnfIHmPqXc4KEbB8FwH7u2n5Da6ah-T_pECB1B26aP2m8m1rdklFGIOfF04xEoKb4175MhmttD7Lo4p8QFuf8-wc4iKbDjT0sGtSKiXIEGZ6MvLuM4E75sswdZpAAsX0q11-2N45TVk9nCG6P9_FEX_EGGOOnLL-PSEi4a4Wf4uYawLBh4wa=s16000" /></a></div><div><br /></div><div><br /></div><div><br /></div>Now click on that, a new pane will open, there click on "Payload" tab, and there click on "view source" near "Request Payload" item </div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjpbS6LJe6tdGYnzCYMk9fFbkKOn3o8exZoiNi_I81D2QamhLxDdApsLO8R0KJ2T_MXoAOa0RUkZtXZIfAFWF7vYmVHIZ1Z4O6nVOPpYvS1kvIJ4TwXbVT9BVwHC79LodKqdz_giI_oJ1MQI9DGkX9miNXkKY063p2wmHE7EA_eY3VLOKHPeDUF8GV39JQb" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="319" data-original-width="1113" src="https://blogger.googleusercontent.com/img/a/AVvXsEjpbS6LJe6tdGYnzCYMk9fFbkKOn3o8exZoiNi_I81D2QamhLxDdApsLO8R0KJ2T_MXoAOa0RUkZtXZIfAFWF7vYmVHIZ1Z4O6nVOPpYvS1kvIJ4TwXbVT9BVwHC79LodKqdz_giI_oJ1MQI9DGkX9miNXkKY063p2wmHE7EA_eY3VLOKHPeDUF8GV39JQb=s16000" /></a></div><br /><br /></div><div><br /><br />that will result in a long peace of raw json that we need to copy (well, I copied it for you ♥)</div><div><br /></div><div>now will come:</div><div>1. original copied raw json text</div><div>2. picture</div><div>3. formatted text + few details</div><div>4. what you need to adjust for our flow</div><div><br /></div><div>1. original copied raw json text</div><div><br /></div><div><span style="font-family: courier; font-size: x-small;line-height: normal;">{"request":{"createLink":true,"settings":{"linkKind":6,"expiration":null,"role":1,"restrictShareMembership":true,"updatePassword":false,"password":"","scope":2,"nav":""},"peoplePickerInput":"[{\"Key\":\"arielspamtest@gmail.com\",\"DisplayText\":\"arielspamtest@gmail.com\",\"IsResolved\":true,\"Description\":\"arielspamtest@gmail.com\",\"EntityType\":\"\",\"EntityData\":{\"SPUserID\":\"arielspamtest@gmail.com\",\"Email\":\"arielspamtest@gmail.com\",\"IsBlocked\":\"False\",\"PrincipalType\":\"UNVALIDATED_EMAIL_ADDRESS\",\"AccountName\":\"arielspamtest@gmail.com\",\"SIPAddress\":\"arielspamtest@gmail.com\",\"IsBlockedOnODB\":\"False\"},\"MultipleMatches\":[],\"ProviderName\":\"\",\"ProviderDisplayName\":\"\"}]","emailData":{"body":"ef ef","subject":""}}}</span><br /><br /></div><div><br /></div><div>2. picture</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEieXtuDqB0Dzn8xo-xTXQxOCASdGN2gPSlwfwaUzYzVSajgUVsR0cJvujWfo6i-JKVzjgq9M5MRqLlFx33vlYgzOH3ytFSKDnpNXnDFWqyl3R5kTrfYghLeuFieFtdL_HJjpl7p0v3uVWzOVyuRjA96SISsohGgV3XeVXj8TCr_m_dF8hfkatsxSkmbn8D7" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="262" data-original-width="1617" src="https://blogger.googleusercontent.com/img/a/AVvXsEieXtuDqB0Dzn8xo-xTXQxOCASdGN2gPSlwfwaUzYzVSajgUVsR0cJvujWfo6i-JKVzjgq9M5MRqLlFx33vlYgzOH3ytFSKDnpNXnDFWqyl3R5kTrfYghLeuFieFtdL_HJjpl7p0v3uVWzOVyuRjA96SISsohGgV3XeVXj8TCr_m_dF8hfkatsxSkmbn8D7=s16000" /></a></div><br /><br /></div><div><br /></div><div>3. formatted text</div><div><br /></div><div><div><span style="color: #93c47d; font-family: courier;">{</span></div><div><span style="color: #93c47d; font-family: courier;"> "request":</span></div><div><span style="color: #93c47d; font-family: courier;"> { </span></div><div><span style="color: #93c47d; font-family: courier;"> "createLink":true,</span></div><div><span style="color: #93c47d; font-family: courier;"> "settings":{</span></div><div><span style="color: #93c47d; font-family: courier;"> "linkKind":6,</span></div><div><span style="color: #93c47d; font-family: courier;"> "expiration":null,</span></div><div><span style="color: #93c47d; font-family: courier;"> "role":1,</span></div><div><span style="color: #93c47d; font-family: courier;"> "restrictShareMembership":true,</span></div><div><span style="color: #93c47d; font-family: courier;"> "updatePassword":false,</span></div><div><span style="color: #93c47d; font-family: courier;"> "password":"",</span></div><div><span style="color: #93c47d; font-family: courier;"> "scope":2,</span></div><div><span style="color: #93c47d; font-family: courier;"> "nav":""</span></div><div><span style="color: #93c47d; font-family: courier;"> },</span></div><div><span style="color: #93c47d; font-family: courier;"> </span></div><div><span style="color: #93c47d; font-family: courier;"> "peoplePickerInput":"[</span></div><div><span style="color: #93c47d; font-family: courier;"> {</span></div><div><span style="color: #93c47d; font-family: courier;"> \"Key\":\"arielspamtest@gmail.com\",</span></div><div><span style="color: #93c47d; font-family: courier;"> \"DisplayText\":\"arielspamtest@gmail.com\",</span></div><div><span style="color: #93c47d; font-family: courier;"> \"IsResolved\":true,</span></div><div><span style="color: #93c47d; font-family: courier;"> \"Description\":\"arielspamtest@gmail.com\",</span></div><div><span style="color: #93c47d; font-family: courier;"> \"EntityType\":\"\",</span></div><div><span style="color: #93c47d; font-family: courier;"> \"EntityData\":{</span></div><div><span style="color: #93c47d; font-family: courier;"> \"SPUserID\":\"arielspamtest@gmail.com\",</span></div><div><span style="color: #93c47d; font-family: courier;"> \"Email\":\"arielspamtest@gmail.com\",</span></div><div><span style="color: #93c47d; font-family: courier;"> \"IsBlocked\":\"False\",</span></div><div><span style="color: #93c47d; font-family: courier;"> \"PrincipalType\":\"UNVALIDATED_EMAIL_ADDRESS\",</span></div><div><span style="color: #93c47d; font-family: courier;"> \"AccountName\":\"arielspamtest@gmail.com\",</span></div><div><span style="color: #93c47d; font-family: courier;"> \"SIPAddress\":\"arielspamtest@gmail.com\",</span></div><div><span style="color: #93c47d; font-family: courier;"> \"IsBlockedOnODB\":\"False\"</span></div><div><span style="color: #93c47d; font-family: courier;"> },</span></div><div><span style="color: #93c47d; font-family: courier;"> \"MultipleMatches\":[],</span></div><div><span style="color: #93c47d; font-family: courier;"> \"ProviderName\":\"\",</span></div><div><span style="color: #93c47d; font-family: courier;"> \"ProviderDisplayName\":\"\"</span></div><div><span style="color: #93c47d; font-family: courier;"> }</span></div><div><span style="color: #93c47d; font-family: courier;"> ]",</span></div><div><span style="color: #93c47d; font-family: courier;"> "emailData":{</span></div><div><span style="color: #93c47d; font-family: courier;"> "body":"here add nice message",</span></div><div><span style="color: #93c47d; font-family: courier;"> "subject":""</span></div><div><span style="color: #93c47d; font-family: courier;"> }</span></div><div><span style="color: #93c47d; font-family: courier;"> }</span></div><div><span style="color: #93c47d; font-family: courier;">}</span></div></div><div><br /></div><div>I tried removing some parts, always got errors.</div><div>some nice sources for code, the C# item really helped understand the request body:</div><div><span style="font-family: arial;"><a href="https://sharepoint.stackexchange.com/questions/303243/sponline-how-to-generate-a-sharing-link-via-rest" target="_blank">stack exchange post</a></span></div><div><span style="font-family: arial;"><a href="https://github.com/kavaghela/sharedocumentspapi/tree/main/SP.ShareDocumentUsingRESTAPI/SP.ShareDocumentUsingRESTAPI" target="_blank">github with C# project for sharing API for SharePoint</a></span></div><div><span style="font-family: arial;"><a href="https://learn.microsoft.com/en-us/answers/questions/1282962/what-is-the-rest-api-endpoint-that-i-can-use-share" target="_blank">some post to create shareable link</a></span></div><div><br /></div><div>some definitions:</div><div><a href="https://learn.microsoft.com/en-us/dotnet/api/microsoft.sharepoint.client.sharinglinkkind?view=sharepoint-csom" target="_blank"><span style="font-family: arial;">MS enum linkKind</span></a></div><div><a href="https://learn.microsoft.com/en-us/dotnet/api/microsoft.sharepoint.client.sharing.role?view=sharepoint-csom" target="_blank"><span style="font-family: arial;">MS enum role</span></a></div><div><span style="font-family: arial;"><a href="https://sharepoint.stackexchange.com/questions/283733/what-is-sharinglinkkind-flexible-and-how-to-use-it" target="_blank">what is linkKind flexible</a></span></div><div><br /></div><div><br /></div><h3 style="text-align: left;">4. what you need to adjust for our flow</h3><div>just copy the formatted value and replace all instances of the email value to the value of the output inside this loop, representing an email to share with privately</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhMo0uNtgGM3omYUshnp5wrhMObZPTqKB-BSXgVy2ASTd8kTiUWblwLeSzUskd_iz2TAR9goIB7aKM2Dnn4KBsom5pSp15z1gV53QHwPAU4oMCuz5fbQ_M0wJ13Iu_-_8GKQA5x3YekHckDsq2cXoElvEy6mNvOuELCYrvD3_9Qp4Gj1RRo9YBTPZE4mybo" style="margin-left: 1em; margin-right: 1em;"><br /></a><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhMo0uNtgGM3omYUshnp5wrhMObZPTqKB-BSXgVy2ASTd8kTiUWblwLeSzUskd_iz2TAR9goIB7aKM2Dnn4KBsom5pSp15z1gV53QHwPAU4oMCuz5fbQ_M0wJ13Iu_-_8GKQA5x3YekHckDsq2cXoElvEy6mNvOuELCYrvD3_9Qp4Gj1RRo9YBTPZE4mybo" style="margin-left: 1em; margin-right: 1em;"></a><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhMo0uNtgGM3omYUshnp5wrhMObZPTqKB-BSXgVy2ASTd8kTiUWblwLeSzUskd_iz2TAR9goIB7aKM2Dnn4KBsom5pSp15z1gV53QHwPAU4oMCuz5fbQ_M0wJ13Iu_-_8GKQA5x3YekHckDsq2cXoElvEy6mNvOuELCYrvD3_9Qp4Gj1RRo9YBTPZE4mybo" style="margin-left: 1em; margin-right: 1em;"></a><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjlAzIL5f6KPGw7CArlgSMhRh6o5k625TuETmvNaCPL86zKwSoEXPwuRAgOS4CBiozyDw9w-iCI06NfGiNCBnAvR2-LZq3CQ0S2M6ddECGRqrk40zw_37jDpgt4FPsCoUfZyhvocx7WGDhuTGvto6l18peOhrLCwmqt0tGECyDJUNDAWowg0SeT5ctWbPGo" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="771" data-original-width="931" src="https://blogger.googleusercontent.com/img/a/AVvXsEjlAzIL5f6KPGw7CArlgSMhRh6o5k625TuETmvNaCPL86zKwSoEXPwuRAgOS4CBiozyDw9w-iCI06NfGiNCBnAvR2-LZq3CQ0S2M6ddECGRqrk40zw_37jDpgt4FPsCoUfZyhvocx7WGDhuTGvto6l18peOhrLCwmqt0tGECyDJUNDAWowg0SeT5ctWbPGo=s16000" /></a></div><br /><br /></div><br /></div>Save and Test and Adjust to your needs :)</div><div><br /></div><div><br /></div></div>Bresleveloperhttp://www.blogger.com/profile/12654150644649676499noreply@blogger.com0tag:blogger.com,1999:blog-6780036489035729397.post-90716188093665019872022-12-26T17:52:00.005+02:002023-06-18T20:02:51.863+03:00Power Automate Copy SharePoint Page Content to another page in another site<p>This step is part for the large post</p><p><a href="https://bresleveloper.blogspot.com/2022/12/duplicate-sharepoint-online-subsite.html" target="_blank">Duplicate Sharepoint Online Subsite with all Site Contents and Page[s] - Power Automate</a></p><p>But can be used individually.</p><p><br /></p><p>The process is simple, in modern pages they did it so when you save/publish your page they send an ajax request with all the content, so you can just copy all that content and make your own HTTP with that content to update the desired page.</p><p><br /></p><p>After this you can browse this additional content</p><p><a href="https://www.devfacto.com/blog/one-sharepoint-modern-page" target="_blank">1 - copy page and add it to navigation</a></p><p><a href="https://collab365.com/how-to-add-text-to-any-part-of-a-sharepoint-page-using-power-automate/" target="_blank">2 - edit the content that you copy</a></p><p><br /></p><p><br /></p><p>This is a 3 part tutorial</p><p>1 - get the data for page content</p><p>2 - prepare some data on item created (trigger)</p><p>3 - checkout / save / publish page</p><p><br /></p><p><br /></p><h1 style="text-align: left;">Part 1 - get the data for page content</h1><p>1. Open browser and go to your template page (page you want to copy)</p><p>2. Edit page </p><p>3. Open develpopers tools (F12) and go to Network tab</p><p>4. Check the "Preserve log button"</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfP0EYgFI4sQj3HYD2PPpaf21-GwIDVSoW8xQ4Qq9eV_BHTqwUK2tHVTsPC2taJ-6jflVUZy0t0Xoj2pXn1UchcomBJIAEEHWoLklt8nUHnsToO8JEHwycsVZ7AzoiJkd6OSSEL_0NdxGDebRWB7xhzJqxW1DLn4kU16O9FBQoFcribkyBW97p2G6pIA/s591/d13.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="388" data-original-width="591" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfP0EYgFI4sQj3HYD2PPpaf21-GwIDVSoW8xQ4Qq9eV_BHTqwUK2tHVTsPC2taJ-6jflVUZy0t0Xoj2pXn1UchcomBJIAEEHWoLklt8nUHnsToO8JEHwycsVZ7AzoiJkd6OSSEL_0NdxGDebRWB7xhzJqxW1DLn4kU16O9FBQoFcribkyBW97p2G6pIA/s16000/d13.png" /></a></div><br /><p>5. Save/Publish the page</p><p>6. In the filter write "save"</p><p>7. Click View Source to change the date from parsed view to raw view</p><p>8. Ohh yes, copy it all! Save it somewhere like in a list Multitext column or Notepad++</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBOKBj3YVPCNyBR843r-BIytSptuwbfSUm2zErV-wYxWaXn6_z0hkV0BAGRgtUZzVN0s6GEZQJaHXrJvnxpIlelctZMNCwAGmOYhwbiCqkSsbIIGkvORl_SIkbgisRJD4Qig1gWrSZJ4pv9W-c-KLnX3rz_LKlxx42n-dHLn6wq7p6YZ8qMjp_-GhnQg/s1285/d14.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="358" data-original-width="1285" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBOKBj3YVPCNyBR843r-BIytSptuwbfSUm2zErV-wYxWaXn6_z0hkV0BAGRgtUZzVN0s6GEZQJaHXrJvnxpIlelctZMNCwAGmOYhwbiCqkSsbIIGkvORl_SIkbgisRJD4Qig1gWrSZJ4pv9W-c-KLnX3rz_LKlxx42n-dHLn6wq7p6YZ8qMjp_-GhnQg/s16000/d14.png" /></a></div><br /><p><br /></p><p><br /></p><h1 style="text-align: left;">Part 2 - prepare some data </h1><p>In my case, following the <a href="https://bresleveloper.blogspot.com/2022/12/duplicate-sharepoint-online-subsite.html" target="_blank">main post</a>, I created a new subsite and saved it in a helper list, which then triggers the flow and contains the target site url in its Title. Therefor the page rel url is fixed ("Home.aspx)</p><p>We need to know [purpose - step in following flow]:</p><p>a. When we want to copy (trigger the flow) -> When item is created</p><p>b. Source content (above step) <span> </span><span> </span><span> </span><span> </span><span> -> saved somewhere</span></p><p>c. Target site url and page<span> </span><span> </span><span> </span><span> </span><span> </span><span> </span><span> -> </span>init var SiteFullUrl HomePageUrl</p><p>d. Target page ID<span> </span><span> </span><span> </span><span> </span><span> </span><span> </span><span> </span><span> </span><span> </span><span> </span><span> -> get file metadata</span></p><p><br /></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZqQjZJgbnq5WlyYn7OnIDYp6xRn-9uH84dcyQQEE55bmOiGTAOpHUSwLK-9390I1f1YJX-x4RVSLfrBBrKMsq01gsMHqJQn_6Dxt9dRady9jfJWQGXptxL9ggSLvgHCs4qEZHLaRrAyCL8C8hSbA_XI2pkwOyyzFwb9I363dr3EqKLBB4boyPS3vg9w/s800/c1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="800" data-original-width="624" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZqQjZJgbnq5WlyYn7OnIDYp6xRn-9uH84dcyQQEE55bmOiGTAOpHUSwLK-9390I1f1YJX-x4RVSLfrBBrKMsq01gsMHqJQn_6Dxt9dRady9jfJWQGXptxL9ggSLvgHCs4qEZHLaRrAyCL8C8hSbA_XI2pkwOyyzFwb9I363dr3EqKLBB4boyPS3vg9w/s16000/c1.png" /></a></div><br /><p>* EDIT: we had some problems with step "Get file metadata" one time duplicating the process and the solution was to wrap the "SiteFullIUrl" and the "/SitePages/Home.aspx" with encodeURIComponent()</p><p>On a side note to copy an existing page from site to site is most simple, and you can further automate that, and make it a Pre-Step for content copy instead my subsite creation</p><p>* IMPORTANT:</p><p>The values here, "/play-michal" or "/CollabHome.aspx" are just the specific files and sites we used in our dev enviorment. please change to your values as following:</p><p>"Current Site Address" - your source site from where you want to copy the page</p><p>"File to Copy" - the page itself in the source site</p><p>"Destination Site Address" & "Destination Folder" - as stated</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPcDVpfe0rn2WkJvVFseYF0wunF_uGoa6enGiQhdhZzd1HbM8aSpplJazwJnRP0MBV9AceeXfnHA3nWRugK75KPJJcY-At0qsV-_0vzu1cjZEv9-TBPgQDKGvBRrPLoHbzrlk_9Fe85q6kr5vKSzqL-erlcy8IYS-2zRQ1T_-lCh3CxpbeyB7W7KWeNA/s618/c2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="272" data-original-width="618" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPcDVpfe0rn2WkJvVFseYF0wunF_uGoa6enGiQhdhZzd1HbM8aSpplJazwJnRP0MBV9AceeXfnHA3nWRugK75KPJJcY-At0qsV-_0vzu1cjZEv9-TBPgQDKGvBRrPLoHbzrlk_9Fe85q6kr5vKSzqL-erlcy8IYS-2zRQ1T_-lCh3CxpbeyB7W7KWeNA/s16000/c2.png" /></a></div><br /><p><br /></p><h1 style="text-align: left;"><br />Part 3 - checkout / save / publish page</h1><p>These 3 action steps are identical except the last Uri fragment, and the body for the middle step</p><p><br /></p><p>These are "Send an HTTP request to SharePoint" action step, and lets detail it:</p><p><br /></p><p>a. The full site url</p><p>b. POST</p><p>c. The 3 Uri are [ its always /pages(<itemId>)/ ]. I took the ID from the metadata step above.</p><p><span style="color: #6aa84f; font-family: courier;"> _api/sitepages/pages(@{outputs('Get_file_metadata')?['body/ItemId']})/CheckOutPage</span></p><p><span style="color: #6aa84f; font-family: courier;"> _api/sitepages/pages(@{outputs('Get_file_metadata')?['body/ItemId']})/savepageasdraft</span></p><p><span style="color: #6aa84f; font-family: courier;"> _api/sitepages/pages(@{outputs('Get_file_metadata')?['body/ItemId']})/Publish</span></p><p><br /></p><p>d. Headers</p><p><span style="color: #6aa84f; font-family: courier;"> Accept application/json;odata=verbose</span></p><p><span style="color: #6aa84f; font-family: courier;"> Content-Type application/json;odata=verbose</span></p><p><br /></p><p>e. No body but the save part (details and image just ahead)</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrOVZRXWvWID-rdIuygCEbat3lsCm_lM8dJkF5PZ0Yghi715bEx9XCP3M2HUukLyv8KeN7MSdXCvWUHpJVd2CFftxnl8ZosJdisGZc0uiqHQioURAwZmH89j_gbkvO9HABPoTBoDnr8x2hAQ4yJ4K-CtGP_EKO0EY5hd3M8-UyBCwuR09yfNPE6NPbcw/s762/c3.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="762" data-original-width="633" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrOVZRXWvWID-rdIuygCEbat3lsCm_lM8dJkF5PZ0Yghi715bEx9XCP3M2HUukLyv8KeN7MSdXCvWUHpJVd2CFftxnl8ZosJdisGZc0uiqHQioURAwZmH89j_gbkvO9HABPoTBoDnr8x2hAQ4yJ4K-CtGP_EKO0EY5hd3M8-UyBCwuR09yfNPE6NPbcw/s16000/c3.png" /></a></div><br /><p><br /></p><p>In the Save HTTP action step you need to put all the content you copied with your browser</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfRNBFUTb74FSgmnxEuwRcZaBI8wsuM9XjT299_vC7m7IChUgcltnhyC3PSuEiMZVP7GxiN3g1Z2t7aWGpBcD_I_KKfMrnSqXDGAWrNRCxvTY7qaAHQe1z-zi1NDGaQUWi9RbQGljjZtPtusreIOMTtSq-_KqyYeI_-ChT-mRKXZ0zMmp-3Nbsd2PrEg/s657/c4.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="657" data-original-width="638" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfRNBFUTb74FSgmnxEuwRcZaBI8wsuM9XjT299_vC7m7IChUgcltnhyC3PSuEiMZVP7GxiN3g1Z2t7aWGpBcD_I_KKfMrnSqXDGAWrNRCxvTY7qaAHQe1z-zi1NDGaQUWi9RbQGljjZtPtusreIOMTtSq-_KqyYeI_-ChT-mRKXZ0zMmp-3Nbsd2PrEg/s16000/c4.png" /></a></div><br /><p><br /></p><p>The End</p><p><br /></p><p>In my case I also needed to change some data and used <a href="https://collab365.com/how-to-add-text-to-any-part-of-a-sharepoint-page-using-power-automate/" target="_blank">2 - edit the content that you copy</a>.</p><p><br /></p><p><br /></p><p><br /></p><p><br /></p>Bresleveloperhttp://www.blogger.com/profile/12654150644649676499noreply@blogger.com1tag:blogger.com,1999:blog-6780036489035729397.post-85888570579422181122022-12-26T16:05:00.006+02:002023-02-12T10:01:59.303+02:00Power Automate Create sites scripts, designs, and archive for later use<p> This step is part of the longer post </p><p><a href="https://bresleveloper.blogspot.com/2022/12/duplicate-sharepoint-online-subsite.html" target="_blank">Duplicate Sharepoint Online Subsite with all Site Contents and Page[s] - Power Automate</a></p><p>And therefor was moved to a seperated post.</p><p><br /></p><p><br /></p><h1>3. Create sites scripts, designs, and archive</h1><div>This is a 1-time process (for each design), so it should live as a manually triggered flow. Whenever you make a change, you need to run this again (and change what is relevant, if needed).</div><div>REMEMBER - that will only affect newly created subsites, not old ones!</div><div><br /></div><div>I will show 1 Flow for 1 template, and you should create multiple, 1 for each template</div><div><br /></div><div>This is the full flow, and I will explain each piece</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2y-WpnW8xMjaPQwn3BFdW-s2OyNfL8QR8hptgw32RSoGEvzBY5WnTVoCmGX3pHgSy2aF2671O4vlAtD2hzDx_ZEPvalyi5u9QBbQfbBt7Hzzl8ZD5-ApVxE0jSZ0M1PsUUOs7-uRE1BLOCXDn_vOktAQ-P51Ld20U4ywfU_xq8kOWoNPjATU_TSkbEg/s837/d1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="837" data-original-width="623" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2y-WpnW8xMjaPQwn3BFdW-s2OyNfL8QR8hptgw32RSoGEvzBY5WnTVoCmGX3pHgSy2aF2671O4vlAtD2hzDx_ZEPvalyi5u9QBbQfbBt7Hzzl8ZD5-ApVxE0jSZ0M1PsUUOs7-uRE1BLOCXDn_vOktAQ-P51Ld20U4ywfU_xq8kOWoNPjATU_TSkbEg/s16000/d1.png" /></a></div><p><br /></p><div><br /></div><h2>Steps 1-3: </h2><div>1 - Trigger, nothing to it,</div><div>2 - Initialize Variable type of Array, to store the array of our lists and libraries to be copied</div><div>3 - Get all lists, retrieve all Custom Lists and Document Libraries Display Names, from the Template Site (in my example - "ITX2-root")</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7VYfFYjBf_9KcpFy-I9Xf4LBcJqfPg7y5StxLPFMIFgjZxT303GMHcFr6-mAphtwVPvNWcH2qqe9bsB9Du7zyWt_vSO8Vg-rK_qe_WVR79pTiI_nZRhOZixdHmHHXIlpL0asF0NRHLDwwVEmOG2vJwy2FwRQn34XZZYmD1xZS-GPBTEdO9BTgWLAUPw/s622/d2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="490" data-original-width="622" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7VYfFYjBf_9KcpFy-I9Xf4LBcJqfPg7y5StxLPFMIFgjZxT303GMHcFr6-mAphtwVPvNWcH2qqe9bsB9Du7zyWt_vSO8Vg-rK_qe_WVR79pTiI_nZRhOZixdHmHHXIlpL0asF0NRHLDwwVEmOG2vJwy2FwRQn34XZZYmD1xZS-GPBTEdO9BTgWLAUPw/s16000/d2.png" /></a></div><p><br /></p><div><br /></div><div>NOTICE - this is problematic in 2 ways:</div><div>First it retrieves only the Display Names, meaning that in the following scenarios you will get bad results as we need the Internal Names:</div><div>A. you changes the name</div><div>B. you created the lists/libs not in english letters (Hebrew ect.)</div><div><br /></div><div>Second you only get 2 types - Custom Lists (100) and Document Libraries (101). If you need more lists like Calendars and other defaults you cant get them like this.</div><div><br /></div><div>The solution for both of these problems is to use HTTP step for <span style="color: #6aa84f; font-family: courier;">/_api/web/lists</span> and then you need to smartly filter that ect., its more messy and require more work and wont be covered here.</div><div>Read more - <a href="https://sharepoint.stackexchange.com/questions/156826/api-web-lists-returns-all-the-lists-including-system-generated">https://sharepoint.stackexchange.com/questions/156826/api-web-lists-returns-all-the-lists-including-system-generated</a></div><div><br /></div><div><div><br /></div><div><br /></div><h2>Step 4 - Apply to Each</h2><div><br /></div><div>We need to 1st decide who is a list (type 100) and who is a library (type 101) since we need to add the "/Lists" prefix to the lists</div><div><br /></div><div>A simple condition but we must write the expressions ourselves since the Dynamic Content doesnt show the "Type" property</div><div><br /></div><div>So add a Condition, click "Name" for example from the get all lists step and fix it</div><div>Final value (for me) is <span style="color: #6aa84f; font-family: courier;">items('Apply_to_each')?['Type']</span></div><div>For the right-hand side just <span style="color: #6aa84f; font-family: courier;">string(100)</span></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQf8-CgFsW7wvtMaXxuUKIWdG2yes9D9ZHFkf_x2P-kYTIf204JTub-IXy6QZZg9wcSXFz4Y25CO38UzNnGwDp_IyEZD5csXG8HvBDxP4sBh8S7LDqfdaukgWRfkzFNmHKsP_shDDHE1P-qgEvircDsKk4ttGyGuBHwdbdmZJTcezLesIkbPIJELfC1w/s616/d3.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="186" data-original-width="616" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQf8-CgFsW7wvtMaXxuUKIWdG2yes9D9ZHFkf_x2P-kYTIf204JTub-IXy6QZZg9wcSXFz4Y25CO38UzNnGwDp_IyEZD5csXG8HvBDxP4sBh8S7LDqfdaukgWRfkzFNmHKsP_shDDHE1P-qgEvircDsKk4ttGyGuBHwdbdmZJTcezLesIkbPIJELfC1w/s16000/d3.png" /></a></div><div><br /></div><br /><div>If Yes, its a list, lets add it to our variable with the prefix</div></div><div><span style="color: #6aa84f; font-family: courier;">concat('Lists/', items('Apply_to_each')?['DisplayName'])</span></div><div>Notice - no starting slash (/)</div><div>If you are using another api, you need to put the list actual url (i.e. internal name)</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi11UQ3enjiRjudkPCaJxfGq8Fz_aUHXhfQGdtxzHaquHqatNNKtNwfwMf21vhQEIK1-wuw7pmNHd583VVApNuGXDHx7JB9auruz-w9s-ukPeQP62hDVYTST68IybGoJroAaHscggjbta_mXR5LLAcNj3f0LsZqlgTcrmKYzDYTy4WzAUWxVu6p1A5BqQ/s645/d4.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="255" data-original-width="645" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi11UQ3enjiRjudkPCaJxfGq8Fz_aUHXhfQGdtxzHaquHqatNNKtNwfwMf21vhQEIK1-wuw7pmNHd583VVApNuGXDHx7JB9auruz-w9s-ukPeQP62hDVYTST68IybGoJroAaHscggjbta_mXR5LLAcNj3f0LsZqlgTcrmKYzDYTy4WzAUWxVu6p1A5BqQ/s16000/d4.png" /></a></div><p><br /></p><div><br /></div><div><div><br /></div><div>If No, its a Document Library, and we need another condition since the Display Name of the default DocLib is always changed, "Documents" for English, and whatever for your language, so you need to handle those cases and set the value to "Shared%20Documents"</div><div><br /></div><div>So another Condition, test for the above 2 values, for our default we add </div><div><span style="color: #6aa84f; font-family: courier;">concat('', 'Shared%20Documents')</span> </div><div>and for others </div><div><span style="color: #6aa84f; font-family: courier;">concat('', items('Apply_to_each')?['DisplayName'])</span></div><div>(and yes you can remove the concat, but I just copy-pasted)</div><div>MAKE SURE THE CONDITION IS <b>OR</b></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6olJ2NyfnZlFId9sr2PFl7CAHMwCdLgpis8zO_7kI52pp1NngJEz3dxXvlMSOaDzPw-S2U9XggT_ex4xoZzJEPEago2d-xvoAc8ufxq40f7LXC-af6jFYgnL1pGKxud9-hEoMdJzM-4en4EEswfBspvURJ7YKLPv3psG6PLzh2EHSA6L_Yklbv9pDCA/s1066/2023-02-12%2010_00_37-Window.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="483" data-original-width="1066" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6olJ2NyfnZlFId9sr2PFl7CAHMwCdLgpis8zO_7kI52pp1NngJEz3dxXvlMSOaDzPw-S2U9XggT_ex4xoZzJEPEago2d-xvoAc8ufxq40f7LXC-af6jFYgnL1pGKxud9-hEoMdJzM-4en4EEswfBspvURJ7YKLPv3psG6PLzh2EHSA6L_Yklbv9pDCA/s16000/2023-02-12%2010_00_37-Window.png" /></a></div><br /><b><br /></b></div><div><b><br /></b></div><div><br /></div><div><br /></div><h2>Steps 5-7:</h2><div>5 - Compose the Variable</div><div><br /></div><div>6 - Get Site Script - its a "Send an HTTP request to SharePoint" action step, and lets detail it:</div><div><br /></div><div>a. The site address is not important as it is saves globally</div><div>b. POST method</div><div>c. <span style="color: #6aa84f; font-family: courier;">/_api/Microsoft.SharePoint.Utilities.WebTemplateExtensions.SiteScriptUtility.GetSiteScriptFromWeb</span></div><div>d. <span style="color: #6aa84f; font-family: courier;">Accept </span> <b>+ </b> <span style="color: #6aa84f; font-family: courier;">application/json;odata=verbose</span></div><div>e. The body of GetSiteScriptFromWeb endpoint can be adjusted a bit if needed as <a href="https://learn.microsoft.com/en-us/sharepoint/dev/declarative-customization/site-design-rest-api#getsitescriptfromweb" target="_blank">documented here</a>. <b>Make sure </b>you change the webUrl to your webUrl</div><div><div><span style="color: #6aa84f; font-family: courier;"><br /></span></div><div><span style="color: #6aa84f; font-family: courier;">{</span><span style="color: #6aa84f; font-family: courier;">"webUrl":"https://bresleveloper.sharepoint.com/sites/ITX2-root",</span></div><div><span style="color: #6aa84f; font-family: courier;">"info":{</span></div><div><span style="color: #6aa84f; font-family: courier;">"IncludeBranding":true,</span></div><div><span style="color: #6aa84f; font-family: courier;">"IncludedLists":@{outputs('Compose')},</span></div><div><span style="color: #6aa84f; font-family: courier;">"IncludeRegionalSettings":true,</span></div><div><span style="color: #6aa84f; font-family: courier;">"IncludeSiteExternalSharingCapability":true,</span></div><div><span style="color: #6aa84f; font-family: courier;">"IncludeTheme":true,</span></div><div><span style="color: #6aa84f; font-family: courier;">"IncludeLinksToExportedItems":true</span><span style="color: #6aa84f; font-family: courier;">}</span><span style="color: #6aa84f; font-family: courier;">}</span></div></div><div><br /></div><div><br /></div><div>7 - Parse the JSON property from the results. </div><div><span style="color: #6aa84f; font-family: courier;">outputs('Get_Site_Script')?['body']?['d']?['GetSiteScriptFromWeb']?['JSON']</span></div><div>Use <span style="color: #6aa84f; font-family: courier;">{}</span> for the schema since we dont need it detailed</div><div><br /></div></div><div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSJT4VpxBP-6S25CRCAmHJ3bvqi0FZi8RhXW8uBFqwmgQ65Utl5btELey6c1csB9DeCfASqgeZE0qGlsdqSSBpIvJiIMd8Jj6qqJrCcQmN6daYc7ZCrleQy2PpYA2anMmBVv6a95-Ie-NWBk3FSA4B7pqOg-7-p-dpbc6uoeCM2NF2JtgQxzY3lCqhlA/s854/d6.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="854" data-original-width="624" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSJT4VpxBP-6S25CRCAmHJ3bvqi0FZi8RhXW8uBFqwmgQ65Utl5btELey6c1csB9DeCfASqgeZE0qGlsdqSSBpIvJiIMd8Jj6qqJrCcQmN6daYc7ZCrleQy2PpYA2anMmBVv6a95-Ie-NWBk3FSA4B7pqOg-7-p-dpbc6uoeCM2NF2JtgQxzY3lCqhlA/s16000/d6.png" /></a></div><br /><div><br /></div><div><br /></div><div><br /></div><div><br /></div><h2>Step 8 - Create New Site Script</h2><div>This will create a SiteScript that includes all the lists and libs needed to be added and with all their columns (and formatting). You should take a look the the "Raw Output" of the "Parse JSON" action step. We will put it the the Site Design step that can include more than 1 script.</div><div><br /></div><div>Its a "Send an HTTP request to SharePoint" action step, and lets detail it:</div><div>The site address is not important as it is saves globally</div><div>Uri is (Make Sure to change the @title value to your end)</div><div><br /></div><div><span style="color: #6aa84f; font-family: courier;">/_api/Microsoft.SharePoint.Utilities.WebTemplateExtensions.SiteScriptUtility.CreateSiteScript(Title=@title)?@title='ITX2-Script'</span></div><div><br /></div><div>Headers : </div><div><br /></div><div><span style="color: #6aa84f; font-family: courier;">Accept application/json; odata.metadata=minimal</span></div><div><span style="color: #6aa84f; font-family: courier;">Content-Type application/json;charset=utf-8</span></div><div><span style="color: #6aa84f; font-family: courier;">x-requestdigest _spPageContextInfo.formDigestValue</span></div><div><span style="color: #6aa84f; font-family: courier;">ODATA-VERSION 4.0</span></div><div><br /></div><div>And the body is the value parsed for the "Parse JSON" action step</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjU_186dm1KQbmjNygQfmA67jo3gVH9b8Kp3WlWISaxFru3YozF7h55Op_qZBzAvA_hvAKFny0BJKKhWlJX1Qw70QPMZCk42p5rR5daYj8jx5d3MZWBmTWfRZAUqEF1pgqazHYMnlLUpmOmXPpR_er9rrEKSqjvNRtM6sLUJeLPMAcGUg7_BhsYiBW6dg/s629/d7.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="448" data-original-width="629" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjU_186dm1KQbmjNygQfmA67jo3gVH9b8Kp3WlWISaxFru3YozF7h55Op_qZBzAvA_hvAKFny0BJKKhWlJX1Qw70QPMZCk42p5rR5daYj8jx5d3MZWBmTWfRZAUqEF1pgqazHYMnlLUpmOmXPpR_er9rrEKSqjvNRtM6sLUJeLPMAcGUg7_BhsYiBW6dg/s16000/d7.png" /></a></div></div><div><div><br /></div><div><br /></div></div><div><br /></div><div><br /></div><div><h2>Step 8 - Create New Site Design</h2></div><div><div>Its a "Send an HTTP request to SharePoint" action step, and lets detail it:</div><div>The site address is not important as it is saves globally</div><div>Uri is (Make Sure to change the @title value to your end)</div><div><br /></div><div><span style="color: #6aa84f; font-family: courier;">/_api/Microsoft.SharePoint.Utilities.WebTemplateExtensions.SiteScriptUtility.CreateSiteDesign</span></div><div><br /></div><div>Headers : </div><div><br /></div><div><span style="color: #6aa84f; font-family: courier;">Accept application/json; odata=verbose</span></div><div><br /></div><div>And the body includes the ID of the SiteScript from the above step (Make Sute to change the Title and Description to you end)</div></div><div><br /></div><div><div><span style="color: #6aa84f; font-family: courier;">{</span></div><div><span style="color: #6aa84f; font-family: courier;"> "info":{</span></div><div><span style="color: #6aa84f; font-family: courier;"> "Title":"ITX2-Design",</span></div><div><span style="color: #6aa84f; font-family: courier;"> "Description":"SubSite Example Test with lists libs files items",</span></div><div><span style="color: #6aa84f; font-family: courier;"> "SiteScriptIds":["@{body('Create_New_Site_Script')?['Id']}"],</span></div><div><span style="color: #6aa84f; font-family: courier;"> "WebTemplate":"1",</span></div><div><span style="color: #6aa84f; font-family: courier;"> "PreviewImageUrl": "",</span></div><div><span style="color: #6aa84f; font-family: courier;"> "PreviewImageAltText": "Customer tracking site design theme"</span></div><div><span style="color: #6aa84f; font-family: courier;"> }</span></div><div><span style="color: #6aa84f; font-family: courier;">}</span></div></div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzz35q-lFu6YaMJaDcQP9oI5NDKjfdBCVknwlgF6_kIFZce7Wn4GlfCu4UlNTOTe6Pjaje5R2nGKV4fBQd0KDuo8jgdHXrV6iNzDHax9Rtg0qohdJ0m11D1IbVWqpqxFBQ9c7L6lFt_va6kBCBQFCAcNyhH_cYIGLqRGpCMB8wWvCHepNSLV4J7GH9kQ/s637/d8.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="552" data-original-width="637" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzz35q-lFu6YaMJaDcQP9oI5NDKjfdBCVknwlgF6_kIFZce7Wn4GlfCu4UlNTOTe6Pjaje5R2nGKV4fBQd0KDuo8jgdHXrV6iNzDHax9Rtg0qohdJ0m11D1IbVWqpqxFBQ9c7L6lFt_va6kBCBQFCAcNyhH_cYIGLqRGpCMB8wWvCHepNSLV4J7GH9kQ/s16000/d8.png" /></a></div><br /><div><br /></div><div><br /></div><div><br /></div><div><div><br /></div><div><br /></div><h2 style="text-align: left;">Step 10 - Archive in helper list</h2><div>This action is actually Create Item. To further inspect the Archive Helper list I created visit the main post</div><div><a href="https://bresleveloper.blogspot.com/2022/12/duplicate-sharepoint-online-subsite.html" target="_blank">Duplicate Sharepoint Online Subsite with all Site Contents and Page[s] - Power Automate</a></div><div><br /></div><div>Here the Site Address IS important and needs to match the helper site.</div></div><div>Just copy-paste your titles and the script ID.</div><div>For the template id use:</div><div><span style="color: #6aa84f; font-family: courier;">body('Create_New_Site_Design')?['d']?['CreateSiteDesign']?['Id']</span></div><div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDRGUS9YSL475tmPbbZMqtuf-4GHpl_oUEnTMBRCsIM8g9w8CaoAiyDu8eMUrtmfaM7Jf5tPWP-sbYrvVkQV2rQ1P4XGGvPDFh-cN4HLZHQ079POpyCAjBGlRGSIZfUdEE9mkqEiQE_Xrx9RY1OVG-c7Os1soYVXsUy-ltENI9Aceuo_mJh5BEQNnXDA/s663/d9.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="499" data-original-width="663" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDRGUS9YSL475tmPbbZMqtuf-4GHpl_oUEnTMBRCsIM8g9w8CaoAiyDu8eMUrtmfaM7Jf5tPWP-sbYrvVkQV2rQ1P4XGGvPDFh-cN4HLZHQ079POpyCAjBGlRGSIZfUdEE9mkqEiQE_Xrx9RY1OVG-c7Os1soYVXsUy-ltENI9Aceuo_mJh5BEQNnXDA/s16000/d9.png" /></a></div><br /><div><br /></div><div><br /></div><div><br /></div></div><div><div><br /></div><div>Best advise is to manually test it and use the correct data from the archive helper list whenver needed.</div><div>If you prefer there are examples out-there of how to make all this via O365-PowerShell</div><div><br /></div><div><br /></div></div><div><div>Best of Luck</div><div><br /></div><div><br /></div><div><br /></div></div><div><div><br /></div><div><br /></div></div><div><br /></div><div><br /></div><div><br /></div>Bresleveloperhttp://www.blogger.com/profile/12654150644649676499noreply@blogger.com0tag:blogger.com,1999:blog-6780036489035729397.post-51538734542859495852022-12-26T15:25:00.007+02:002022-12-26T17:58:27.673+02:00Duplicate Sharepoint Online Subsite with all Site Contents and Page[s] - Power Automate<p> Too many was written, too scattered. So i'll try to make something complete.</p><p>Our client wans a simple flow, when SP Items is created, duplicate 2 Template Sites, BOTH ARE SUBSITES, with the Document Libraries (can be also lists), and squeeze in (inject) some content (to the pages).</p><p>Well 1st let me add that if the request would have included some design, or being a Root Site (like when you click "SharePoint" and "Create Site") than you would definitely use the "Create Site Design" ect. to some Library and apply it.</p><p>But for me its different, so lets describe the actual steps:</p><p><br /></p><p>Its a bit of a process but I believe that you can take it in less than 1 hour with this full tutorial</p><p><br /></p><h2 style="text-align: left;">Table of Content</h2><p>1. Build 2 ROOT sites and design them, theme, lists, libraries, columns (no content! - for content only PNP xml Engine)</p><p>2. Create helper lists in helper site (just another side site for some management)</p><p>3. Create sites scripts, designs, and archive for later use (remember, I have 2 templates for 2 sites per record)</p><p>4. Duplicate Subsites and apply designs (design include script)</p><p>5. Copying HomePage Content</p><p><br /></p><h3 style="text-align: left;">General Tip -</h3><div>In PowerAutomate the "output('<name>')" or "body('<name>')" the <name> is the name of the step oyu renamed it, so when copying my code fragments please notice to set the corrent name. You can do this by adding any property from that step and hovering it with the mouse.</div><div><br /></div><h4 style="text-align: left;">NOTICE - </h4><div>many of the HTTP steps have similar headers, but many are not the same and you will get some error like "Invalid files or arguments for site template '7" so dont copy-paste blindly, instead copy the right ones from each step.</div><div><br /></div><div>Also careful from those extra spaces before and after that you dont see.</div><div><br /></div><div><br /></div><div><br /></div><h1>1. Build 2 ROOT sites</h1><p>This is a technical step. For tutorial purpose please build 2 new root websites (click SharePoint and click "+ Create Site"), you MUST make them TEAM SITES since we can only create teams-sites as subsites. </p><p>Also they must be root since you cant create a site scripts and design from subsite (steps in Power Automate not working)</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJZrtPLtHIHsQopw0gEFpG5ozwcKDss8GT87-Vublom1mCkMfkY2e_y2RwwJUAYm4t8Z7jlIsnSNfA7T4_0YqlJfMLXJB0d4DviHDpBS3iql77ASa5pZr63QMXPfxpfnFzxUCyaJGs6adJOOTVgngeNMXQ8pkQ4wJ3CIdQOjyf2n7GJSxi9GnvHLRMvA/s184/dup3.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="95" data-original-width="184" height="95" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJZrtPLtHIHsQopw0gEFpG5ozwcKDss8GT87-Vublom1mCkMfkY2e_y2RwwJUAYm4t8Z7jlIsnSNfA7T4_0YqlJfMLXJB0d4DviHDpBS3iql77ASa5pZr63QMXPfxpfnFzxUCyaJGs6adJOOTVgngeNMXQ8pkQ4wJ3CIdQOjyf2n7GJSxi9GnvHLRMvA/s1600/dup3.png" width="184" /></a></div><br /><p><br /></p><p><br /></p><p><br /></p><p><br /></p><h1>2. Create helper lists</h1><div>Some steps will need extra help by saving their states and archiving data.</div><div>I would advise to create helper site and make 3 lists:</div><div>1. Design Archive list</div><div>2. NewSubsiteAtoCopyHPcontent</div><div>3. NewSubsiteBtoCopyHPcontent</div><div><br /></div><div>You could manage that otherwise, for me it was the simplest solution</div><div>2+3 has only Title</div><div>1 has the following, all simple text except the page content</div><div>- Title</div><div><div>- ScriptID</div><div>- DesignTemplateID</div><div><div>- ScriptName</div><div>- DesignTemplateName</div><div>- originalSiteUrl</div><div>- HomePageSaveValue</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKf4T9saINlPJH5qG1JIBdVUaJ7ATuv9dZq85ymIBGmMGdIn3jl9T939vJ3OzOphWJ7DCa6k2Tabp2AzCS-394kEnZTnG-34R91_7K954EltZ3qFlz94P3CWOf-Nmw-DSa2vUhmh0jocwzDD8yqvfkw0wPJXnd2FBWIMfuCX6Y-cB30QsssmBvV4hJAA/s1413/duo4.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="226" data-original-width="1413" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKf4T9saINlPJH5qG1JIBdVUaJ7ATuv9dZq85ymIBGmMGdIn3jl9T939vJ3OzOphWJ7DCa6k2Tabp2AzCS-394kEnZTnG-34R91_7K954EltZ3qFlz94P3CWOf-Nmw-DSa2vUhmh0jocwzDD8yqvfkw0wPJXnd2FBWIMfuCX6Y-cB30QsssmBvV4hJAA/s16000/duo4.png" /></a></div><br /><div><br /></div></div></div><div><br /></div><h1 style="text-align: left;">3. Create sites scripts, designs, and archive</h1><div>This is a 1-time stand-alone process (for each design), and a bit long one with all the images so I made is a separate post</div><div><a href="https://bresleveloper.blogspot.com/2022/12/power-automate-create-sites-scripts.html" target="_blank">Power Automate Create sites scripts, designs, and archive for later use</a></div><div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div></div><h1 style="text-align: left;">4. Duplicate Subsites and apply designs </h1><div>This is a simple step, where we Create Subsite and Apply Design. around it we do some waiting for the processes to complete and create a new item for the next flow.</div><div><br /></div><div>The reason is that when I tried to make all the Page Content step here it just failed, no idea why.</div><div><br /></div><div>In this example I made it with a manual trigger instead of "When Item is Created", and therefor created a variable instead of manually creating the SubSiteUrl according to my end needs.</div><div><br /></div><div>In my real flow I have some rules to generate the Url/name.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYJQPhlHbtvQIEJlwutW85QvOytnrrscS0yu4-sxaKIig72Qmoc9t7JKjsoBQDwTtUTYQc1tyhX6TR8tx7OrE76wGuFoCTi-pYCO7tmK2lrJOCSLF0LhiQAeRfXUKpC0UkFw9oRgmBvCwvZ0JbCGm-EjVriP-pMDWkCxpUk0H-vdyWnKKHaC0ciUVfbg/s711/d10.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="711" data-original-width="633" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYJQPhlHbtvQIEJlwutW85QvOytnrrscS0yu4-sxaKIig72Qmoc9t7JKjsoBQDwTtUTYQc1tyhX6TR8tx7OrE76wGuFoCTi-pYCO7tmK2lrJOCSLF0LhiQAeRfXUKpC0UkFw9oRgmBvCwvZ0JbCGm-EjVriP-pMDWkCxpUk0H-vdyWnKKHaC0ciUVfbg/s16000/d10.png" /></a></div><br /><div>The other steps are Manual Trigger, as is, 1st delay is 5 seconds, 2nd delay is 2 minutes. </div><div>The 1st delay is recommended, the 2nd is not really important.</div><div>The variable is the full url for the new SubSite, and can be in any other place.</div><div><br /></div><div>The Create Item is adding the new full url to a helper list A with Title only, containing the Url, for use in step for Page Content Copy</div><div><br /></div><div><br /></div><h2 style="text-align: left;">Step Create SubSite</h2><div>Make sure that in Url you put the right value, which is only the last fragment for the Url for that subsite.</div><div><br /></div><div>its a "Send an HTTP request to SharePoint" action step, and lets detail it:</div><div><br /></div><div>a. The right Parent Site</div><div>b. POST</div><div>c. <span style="color: #6aa84f; font-family: courier;">/_api/web/webinfos/add</span></div><div>d. Headers</div><div><span style="color: #6aa84f; font-family: courier;"> Accept <span> </span><span> </span>application/json;odata=verbose</span></div><div><span style="color: #6aa84f; font-family: courier;"> Content-Type <span> </span>application/json;odata=verbose</span></div><div><br /></div><div>e. You should set the values in body all values according to your needs except WebTemplate, where "STS#3" is team site without outlook group.</div><div>Old <a href="https://learn.microsoft.com/en-us/previous-versions/office/sharepoint-csom/ee538092(v=office.15)" target="_blank">Documentation here</a> (couldn't find new)</div><div><br /></div><div><div><span style="color: #6aa84f; font-family: courier;">{ 'parameters': </span></div><div><span style="color: #6aa84f; font-family: courier;"><span style="white-space: pre;"> </span>{ '__metadata': </span></div><div><span style="color: #6aa84f; font-family: courier;"><span style="white-space: pre;"> </span>{ 'type': 'SP.WebInfoCreationInformation' }, </span></div><div><span style="color: #6aa84f; font-family: courier;"><span style="white-space: pre;"> </span>'Url':'ITXR-TEST-3', </span></div><div><span style="color: #6aa84f; font-family: courier;"><span style="white-space: pre;"> </span>'Title':'ITXR-TEST-3', </span></div><div><span style="color: #6aa84f; font-family: courier;"><span style="white-space: pre;"> </span>'Description':'ITXR-TEST', </span></div><div><span style="color: #6aa84f; font-family: courier;"><span style="white-space: pre;"> </span>'Language':'1033', </span></div><div><span style="color: #6aa84f; font-family: courier;"><span style="white-space: pre;"> </span>'WebTemplate':'STS#3', </span></div><div><span style="color: #6aa84f; font-family: courier;"><span style="white-space: pre;"> </span>'UseUniquePermissions':false</span></div><div><span style="color: #6aa84f; font-family: courier;"><span style="white-space: pre;"> </span>} </span></div><div><span style="color: #6aa84f; font-family: courier;">} </span></div></div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRE2l8zLKd1aHtOSMtwNw8LYsQxa4Zz6ENez09t2EV4gSmYzrPb-ri-p29SzqM3q1hSkhVpYL1iHF3tn5coHVYMh_a45MG3fKHHfgSZANR8BodJIrucfneoy7sEVWOSWd6MG6A9rCbDe9jA4Lrm_rb9LHotFM3zNvKVuh_hcXidFMBqC2sVvAk3jsGWQ/s641/d11.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="598" data-original-width="641" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRE2l8zLKd1aHtOSMtwNw8LYsQxa4Zz6ENez09t2EV4gSmYzrPb-ri-p29SzqM3q1hSkhVpYL1iHF3tn5coHVYMh_a45MG3fKHHfgSZANR8BodJIrucfneoy7sEVWOSWd6MG6A9rCbDe9jA4Lrm_rb9LHotFM3zNvKVuh_hcXidFMBqC2sVvAk3jsGWQ/s16000/d11.png" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><br /></div><br /><div><br /></div><div><br /></div><div><h2>Step Apply site Design</h2></div><div>Since there is no way to create a Subsite with design, we must do it in 2 steps, create and apply.</div><div><br /></div><div>Copy the Design ID from the archive list that you want for this subsite manually as it should not change usually. If you expect it to change make some ID column in your archive list and pull the right ID every time.</div><div><br /></div><div>Its a "Send an HTTP request to SharePoint" action step, and lets detail it (no headers this time):</div><div><br /></div><div>a. Site address is the full url of the newly created subsite</div><div>b. POST</div><div>c. Uri is</div><div><span style="color: #6aa84f; font-family: courier;"> /_api/Microsoft.SharePoint.Utilities.WebTemplateExtensions.SiteScriptUtility.AddSiteDesignTaskToCurrentWeb</span></div><div><br /></div><div>d. Body is <span style="color: #6aa84f; font-family: courier;">{ "siteDesignId": "<your design id>" }</span></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiIZqK6BIz02g49uF43vCHGLkfvdLepk-JwvQLLrs8vZ3I8mEDulg9-egNwO0qsNY92qCe2iuyxDu_RP3175yULnms0HhhsQUtdr73RMAOnufKFttZTSIJDOG8qqUZtt8TjDGhpIXFJLkmFBIof6WaY--s_961yd8JieYQl22AvxrOOzdIKKvBMIkCYWg/s641/d12.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="338" data-original-width="641" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiIZqK6BIz02g49uF43vCHGLkfvdLepk-JwvQLLrs8vZ3I8mEDulg9-egNwO0qsNY92qCe2iuyxDu_RP3175yULnms0HhhsQUtdr73RMAOnufKFttZTSIJDOG8qqUZtt8TjDGhpIXFJLkmFBIof6WaY--s_961yd8JieYQl22AvxrOOzdIKKvBMIkCYWg/s16000/d12.png" /></a></div><br /><div><br /></div><div><br /></div><h1 style="text-align: left;"><br /></h1><h1 style="text-align: left;">5. Copying HomePage Content</h1><div>Since it can be used generic I again make it a differen post</div><div><br /></div><div><br /></div><div><a href="https://bresleveloper.blogspot.com/2022/12/power-automate-copy-sharepoint-page.html" target="_blank">Power Automate Copy SharePoint Page Content to another page in another site</a></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div>This has been fun and opens a lot of Ideas and automation templating options</div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div>Bonus Content</div><div><br /></div><div><a href="https://tomriha.com/identify-which-sharepoint-item-columns-were-updated-in-power-automate/" target="_blank">Identify which SharePoint item columns were updated in Power Automate</a></div><div><br /></div><div><a href="https://tomriha.com/get-previous-values-of-modified-sharepoint-columns-in-power-automate/" target="_blank">Get previous value(s) of modified SharePoint column(s) in Power Automate</a></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div>Bresleveloperhttp://www.blogger.com/profile/12654150644649676499noreply@blogger.com0tag:blogger.com,1999:blog-6780036489035729397.post-46583548785718255492022-11-29T16:18:00.002+02:002022-11-29T16:18:12.736+02:00BadGateway (502) CreateSiteScript Power Automate<p> using endpoint </p><p><span style="color: #6aa84f; font-family: courier;">/_api/Microsoft.SharePoint.Utilities.WebTemplateExtensions.SiteScriptUtility.CreateSiteScript(Title=@title)?@title='DemoSiteScript'</span></p><p>we got error <span style="color: #6aa84f; font-family: courier;">BadGateway</span></p><p><br /></p><p>solution - user must be <b>SharePoint Admin</b></p>Bresleveloperhttp://www.blogger.com/profile/12654150644649676499noreply@blogger.com0tag:blogger.com,1999:blog-6780036489035729397.post-41961999061833470452022-03-09T00:21:00.011+02:002022-04-12T00:26:49.938+03:00SPFx REAL compatibily materix<p>Links for sources at the bottom, including Version Notes</p><h2 style="text-align: left;">General Recommendations:</h2><p>1. Use <a href="https://github.com/coreybutler/nvm-windows/releases/download/1.1.7/nvm-setup.zip" target="_blank">nvm</a> (nvm install <nodeVersion>, nvm use <nodeVersion>) (<a href="https://content.breatheco.de/en/how-to/nvm-install-windows#:~:text=Steps%20to%20install%20with%20nvm%3A&text=Install%20nvm%20Go%20to%20your,that%20you%20will%20hit%20too." target="_blank">official site</a>)</p><p>2. The rest do not install with -g</p><p>3. Sometimes the best solution is to uninstall node and clean npm from AppData</p><p>4. Testing CMD's at the bottom end</p><p><br /></p><p>[click the table to enlarge]</p><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjrl9v2jbPoFWhGL_bfUD75MsBSf0xNc8aw16oYZrAkcA7aEhDAl-ieBLvgCbVhsF2uotoOjF4JyC-vGwKzhzE7VofazpcbgUVbZcrrRhSA9fhgBXlOBR73VcIbcnSlo40NuLtIpyuqnIgIJpmy7PYlCp0sabT2ppuVS2QEiAPnLNK7saJrMZ2p2D8igw" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="615" data-original-width="1496" src="https://blogger.googleusercontent.com/img/a/AVvXsEjrl9v2jbPoFWhGL_bfUD75MsBSf0xNc8aw16oYZrAkcA7aEhDAl-ieBLvgCbVhsF2uotoOjF4JyC-vGwKzhzE7VofazpcbgUVbZcrrRhSA9fhgBXlOBR73VcIbcnSlo40NuLtIpyuqnIgIJpmy7PYlCp0sabT2ppuVS2QEiAPnLNK7saJrMZ2p2D8igw=s16000" /></a></div><br /><br /></div><div style="text-align: left;">According to<a href="https://stackoverflow.com/questions/67252829/yo-microsoft-sharepoint-fails-on-typeerror-lookups-flatmap-is-not-a-function" target="_blank"> this stackoverflow answers</a> they all prefer, at least up to spfx 1.11.0, and with node 10.x, to use gulp 3.9.1 and yo 3.1.0</div><br /></div><h1 style="text-align: left;">SOURCES for versioning items</h1><p></p><p><a href="https://docs.microsoft.com/en-us/sharepoint/dev/spfx/compatibility" target="_blank">SPFx - https://docs.microsoft.com/en-us/sharepoint/dev/spfx/compatibility</a></p><p><a href="https://nodejs.org/en/download/releases/" target="_blank">Node.js support for NPM - https://nodejs.org/en/download/releases/</a></p><p><a href="https://docs.microsoft.com/en-us/sharepoint/dev/spfx/sharepoint-2019-support#nodejs-gulp--yeoman-versions" target="_blank">Gulp, Yeoman, and OnPrem - https://docs.microsoft.com/en-us/sharepoint/dev/spfx/sharepoint-2019-support#nodejs-gulp--yeoman-versions</a></p><p><a href="https://gist.github.com/LayZeeDK/c822cc812f75bb07b7c55d07ba2719b3" target="_blank">Angular according - https://gist.github.com/LayZeeDK/c822cc812f75bb07b7c55d07ba2719b3</a></p><p><a href="https://bresleveloper.blogspot.com/2022/03/spfx-make-application-customizer-for.html" target="_blank">node-sass version lock - https://bresleveloper.blogspot.com/2022/03/spfx-make-application-customizer-for.html</a> (version found according to errors/prev spfx working build)</p><p><br /></p><h3 style="text-align: left;">HOW TO TEST YOUR MACHINE VERSIONS</h3><p>npm -v</p><p>node -v</p><p>npm ls -g --depth=0</p><p>Last one will return gulp, yo, @ms (sometimes it doesnt print until you press ENTER)</p><p><br /></p><p>"npm install <package>" for local project</p><p>"npm install -g <package>" for machine global use</p><p>relevant for gulp, yo and @ms.</p><p><br /></p><p>for any other dependency before "npm i" you must <a href="https://bresleveloper.blogspot.com/2022/03/spfx-make-application-customizer-for.html" target="_blank">use version lock with npm-shrinkwrap.json</a></p><p><br /></p><p>soft cleaning npm</p><p>npm uninstall <package> //for as many as needed</p><p>npm cache clean --force </p><p>npm cache verify </p><p><br /></p><p><br /></p><h3 style="text-align: left;">Version Notes (<a href="https://docs.microsoft.com/en-us/sharepoint/dev/spfx/compatibility" target="_blank">All Versions Docs</a>)</h3><p><a href="https://docs.microsoft.com/en-us/sharepoint/dev/spfx/use-aad-tutorial" target="_blank">Consume Microsoft Graph [1.4.1]</a></p><p><a href="https://docs.microsoft.com/en-us/sharepoint/dev/spfx/building-search-extensions" target="_blank">Search Extensions for 2019 [1.10.0]</a></p><p><a href="https://docs.microsoft.com/en-us/sharepoint/dev/spfx/subscribe-to-list-notifications" target="_blank">Subscribe to list notifications [1.10.0] - Libraries only until 1.12.1</a></p><p><a href="https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/basics/determine-web-part-width" target="_blank">rendered web part size & changes [1.12.1]</a></p><p><br /></p><p><br /></p><p><a href="https://www.bresleveloper.co.il/#section-62549b124af9b" target="_blank">Contact us now!</a></p><p><br /></p>Bresleveloperhttp://www.blogger.com/profile/12654150644649676499noreply@blogger.com1tag:blogger.com,1999:blog-6780036489035729397.post-81403992838693109812022-03-08T19:44:00.007+02:002022-03-08T19:44:56.318+02:00Flow How to test if MultiChoice is empty array?<p>Usually this should be empty, null or empty string. But after my migration</p><p><br /></p><p> 1. find the value, with loops it can be something like "items('Apply_to_each_2')?['Value']"</p><p>2. init variable and set it as Expression "string(items('Apply_to_each_2')?['Value'])"</p><p>3. test is vs "string('[]')"</p>Bresleveloperhttp://www.blogger.com/profile/12654150644649676499noreply@blogger.com0tag:blogger.com,1999:blog-6780036489035729397.post-10004012402981838092022-03-08T18:31:00.001+02:002022-03-08T19:55:56.746+02:00SPFX Make Application Customizer for version 1.8.x<p><a href="https://github.com/bresleveloper/SPFx-AppCustomizer-v1.8.1" target="_blank">github solution made just for you ♥</a></p><p><br /></p><p> 1. create 2 projects, 1 as "2019 onwards" to select "Application Customizer" and another simple for SPO, I am assuming that the yo version in your machine is 1.8.x</p><p><span style="color: #6aa84f; font-family: courier;">npm ls -g --depth=0 @microsoft/generator-sharepoint</span></p><p><br /></p><p>2. change accordingly values in </p><p><span> </span>"/config/config.json"</p><p><span> </span>"/config/package-solution.json"</p><p>make sure you change words "webparts" to "extensions", also adding "ApplicationCustomizer" everywhere, including "...Strings" in last line of config.json</p><p>create the "..ApplicationCustomizer.ts" file and all the files in its folder</p><p>and create the 2 XML files under "sharepoint/assets"</p><p>copy the GUID from "..ApplicationCustomizer.manifest.json" to the XML's (and serve.json)</p><p><br /></p><p>3. create npm-shrinkwrap.json with the following</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div>{</div><div> <span style="color: #9cdcfe;">"dependencies"</span>: {</div><div> <span style="color: #9cdcfe;">"graceful-fs"</span>: {</div><div> <span style="color: #9cdcfe;">"version"</span>: <span style="color: #ce9178;">"4.2.2"</span></div><div> },</div><div> <span style="color: #9cdcfe;">"node-sass"</span>: {</div><div> <span style="color: #9cdcfe;">"version"</span>: <span style="color: #ce9178;">"4.14.1"</span></div><div> }</div><div> }</div><div>}</div></div><p>and do npm install</p><p>4. make sure you have node 1.10.13 or less</p><p>gulp build; gulp bundle --ship; gulp package-solution --ship;</p><p><br /></p><p>[might need "npm rebuild node-sass"]</p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p>Bresleveloperhttp://www.blogger.com/profile/12654150644649676499noreply@blogger.com1tag:blogger.com,1999:blog-6780036489035729397.post-24730259923443962392021-07-22T15:08:00.004+03:002022-04-12T00:27:56.549+03:00Power Apps Sort Alphabetically <p> Collect(AllTags, 'list1'.'Tag Number', 'list2'.'Tag Number', list3.'Tag Number');</p><p><br /></p><p>ClearCollect(disticntSortTags,</p><p> Distinct(</p><p> Sort( AllTags, Tag_x0020_Number, Ascending ) , Tag_x0020_Number))</p><p><br /></p><p><br /></p><p><br /></p><p><a href="https://www.bresleveloper.co.il/#section-62549b124af9b" target="_blank">Contact us now </a>for more help developing great Power Apps!</p>Bresleveloperhttp://www.blogger.com/profile/12654150644649676499noreply@blogger.com2tag:blogger.com,1999:blog-6780036489035729397.post-66198095955029628362021-07-22T12:52:00.001+03:002021-07-22T12:52:07.951+03:00microsoft flow (power automation) test if lookup is empty<p> You need to use "empty" expression on the Lookup Value and Condition equals to expression "true"</p><p>(of false or not equal ect.)</p>Bresleveloperhttp://www.blogger.com/profile/12654150644649676499noreply@blogger.com1tag:blogger.com,1999:blog-6780036489035729397.post-41308206868846620292021-07-14T13:14:00.004+03:002022-04-12T00:30:12.411+03:00SFPx Node-Saas Error<p> If you get this kind of error</p><p>Node Sass does not yet support your current environment: Windows 64-bit with Unsupported runtime (83)</p><p><br /></p><p>or any other kind of gulp build errors, and if its after you upgraded to Node 14.X, and upgraded your YO generator, then its because all the dependencies changed, and you need to migrage.</p><p><br /></p><p>so here is a simple solution:</p><p><br /></p><p>1. Create a side clean SPFx project (Webpart or Extension (Extension is only with 2019+))</p><p>2. Save somewhere (just in case) the following files <span style="background-color: #1e1e1e; color: #ce9178; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">package.json, </span><span style="background-color: #1e1e1e; color: #ce9178; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">tsconfig.json, </span><span style="background-color: #1e1e1e; color: #ce9178; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">package-lock.json</span></p><p>3. Copy from your new project to your old project only 2 files <span style="background-color: #1e1e1e; color: #ce9178; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">package.json, </span><span style="background-color: #1e1e1e; color: #ce9178; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">tsconfig.json</span></p><p>* change the name and version of your project in the package.json file</p><p>4. Run gulp clean</p><p>5. Run npm install</p><p>6. Run gulp build ect.</p><p><br /></p><p>Do you need an SPFx Developers? <a href="https://www.bresleveloper.co.il/#section-62549b124af9b" target="_blank">Contact us now!</a><br /></p>Bresleveloperhttp://www.blogger.com/profile/12654150644649676499noreply@blogger.com1tag:blogger.com,1999:blog-6780036489035729397.post-79704727376519747652021-06-21T16:31:00.005+03:002021-06-21T16:31:33.041+03:00Sharepoint Online List Item Edit Display Forms link<p> /sites/Portal/Lists/List10/DispForm.aspx?ID=4</p><p><br /></p><p>/sites/Portal/Lists/List10/EditForm.aspx?ID=4</p>Bresleveloperhttp://www.blogger.com/profile/12654150644649676499noreply@blogger.com1tag:blogger.com,1999:blog-6780036489035729397.post-76389338222939800112021-06-16T03:25:00.006+03:002021-06-16T03:38:45.376+03:00SharePoint Online/2019 javascript/spfx click/popstate not working<p>Are you working with SharePoint 2019?</p><p>Maybe making some SPFx Extensions? </p><p>popstate not working? </p><p>click not working? </p><p>MutationObserver not working? </p><p><br /></p><p>well, take a look at this</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_lYk5eZqLVN6TEHazG3htspT7Bt5NaXRyq2K5WBaZoQTFEd3rR-7ci-mQ3yTOADZuFcnSO7jyhtebdu44UQl85q3aB2JFV6y4O7n7h5g1dCJFDEkIPo_RXyBLCB_8hZhO3Bqe5PKPUdaM/s1915/2019.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="509" data-original-width="1915" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_lYk5eZqLVN6TEHazG3htspT7Bt5NaXRyq2K5WBaZoQTFEd3rR-7ci-mQ3yTOADZuFcnSO7jyhtebdu44UQl85q3aB2JFV6y4O7n7h5g1dCJFDEkIPo_RXyBLCB_8hZhO3Bqe5PKPUdaM/s16000/2019.png" /></a></div><br /><p>As you can see, Microsoft are intercepting every click in the page, while stopping any bubbling.</p><p>Therefor, any window/document/body onclick event will not work, in case you are trying to globally catch it.</p><p>Same for popstate or MutationObserver.</p><p>*Note - on SPO it seems that popstate event works.</p><p><br /></p><p>Solution - make an interval to keep watching href change:</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div><span style="color: #569cd6;">let</span> <span style="color: #9cdcfe;">xPrevLocation</span> = <span style="color: #9cdcfe;">location</span>.<span style="color: #9cdcfe;">href</span></div><div><span style="color: #569cd6;">let</span> <span style="color: #9cdcfe;">xOnLocationChangeFunctions</span> = []</div><div><span style="color: #dcdcaa;">setInterval</span>(()<span style="color: #569cd6;">=></span>{</div><div> <span style="color: #c586c0;">if</span> (<span style="color: #9cdcfe;">location</span>.<span style="color: #9cdcfe;">href</span> != <span style="color: #9cdcfe;">xPrevLocation</span>){</div><div> <span style="color: #9cdcfe;">console</span>.<span style="color: #dcdcaa;">log</span>(<span style="color: #ce9178;">"interval location change"</span>, <span style="color: #9cdcfe;">xPrevLocation</span>, <span style="color: #9cdcfe;">location</span>.<span style="color: #9cdcfe;">href</span>)</div><div> <span style="color: #9cdcfe;">xPrevLocation</span> = <span style="color: #9cdcfe;">location</span>.<span style="color: #9cdcfe;">href</span></div><div> <span style="color: #c586c0;">for</span> (<span style="color: #569cd6;">let</span> <span style="color: #9cdcfe;">i</span>=<span style="color: #b5cea8;">0</span>; <span style="color: #9cdcfe;">i</span><<span style="color: #9cdcfe;">xOnLocationChangeFunctions</span>.<span style="color: #9cdcfe;">length</span>; <span style="color: #9cdcfe;">i</span>++){</div><div> <span style="color: #9cdcfe;">xOnLocationChangeFunctions</span>[<span style="color: #9cdcfe;">i</span>]();</div><div> }</div><div> }</div><div>}, <span style="color: #b5cea8;">350</span>)</div></div><p>Usage:</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div><span style="color: #9cdcfe;">window</span>[<span style="color: #ce9178;">'xOnLocationChangeFunctions'</span>].<span style="color: #dcdcaa;">push</span>(()<span style="color: #569cd6;">=></span>{</div><div> <span style="color: #6a9955;">//code...</span></div><div><div style="line-height: 19px;">})</div></div></div><p><br /></p><p>My Scenario:</p><p>I've added some scripts via Extension, but when user changed a page, I needed to re-run those scripts, like page load/ready. </p><p>*Note - this is not really page load/ready, just a notification that the page has changed location</p><p><br /></p><p>Upgrade - run it aslo 1st time, so that <span style="background-color: #1e1e1e; color: #ce9178; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">xOnLocationChangeFunctions</span> can be used for 1st time also</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><br /><div><span style="color: #569cd6;">let</span> <span style="color: #9cdcfe;">xPrevLocation</span> = <span style="color: #9cdcfe;">location</span>.<span style="color: #9cdcfe;">href</span></div><div><span style="color: #569cd6;">let</span> <span style="color: #9cdcfe;">xOnLocationChangeFunctions</span> = []</div><div><span style="color: #569cd6;">let</span> <span style="color: #9cdcfe;">xIsFirstLoad</span> = <span style="color: #569cd6;">true</span>;</div><br /><div><span style="color: #dcdcaa;">setInterval</span>(()<span style="color: #569cd6;">=></span>{</div><div> <span style="color: #c586c0;">if</span> (<span style="color: #9cdcfe;">location</span>.<span style="color: #9cdcfe;">href</span> != <span style="color: #9cdcfe;">xPrevLocation</span>){</div><div> <span style="color: #9cdcfe;">console</span>.<span style="color: #dcdcaa;">log</span>(<span style="color: #ce9178;">"interval location change"</span>, <span style="color: #9cdcfe;">xPrevLocation</span>, <span style="color: #9cdcfe;">location</span>.<span style="color: #9cdcfe;">href</span>)</div><div> <span style="color: #9cdcfe;">xPrevLocation</span> = <span style="color: #9cdcfe;">location</span>.<span style="color: #9cdcfe;">href</span></div><div> <span style="color: #c586c0;">for</span> (<span style="color: #569cd6;">let</span> <span style="color: #9cdcfe;">i</span>=<span style="color: #b5cea8;">0</span>; <span style="color: #9cdcfe;">i</span><<span style="color: #9cdcfe;">xOnLocationChangeFunctions</span>.<span style="color: #9cdcfe;">length</span>; <span style="color: #9cdcfe;">i</span>++){</div><div> <span style="color: #9cdcfe;">xOnLocationChangeFunctions</span>[<span style="color: #9cdcfe;">i</span>]();</div><div> }</div><div> }</div><br /><div> <span style="color: #c586c0;">if</span>(<span style="color: #9cdcfe;">xIsFirstLoad</span> == <span style="color: #569cd6;">true</span>){</div><div> <span style="color: #9cdcfe;">xIsFirstLoad</span> == <span style="color: #569cd6;">false</span></div><div> <span style="color: #c586c0;">for</span> (<span style="color: #569cd6;">let</span> <span style="color: #9cdcfe;">i</span>=<span style="color: #b5cea8;">0</span>; <span style="color: #9cdcfe;">i</span><<span style="color: #9cdcfe;">xOnLocationChangeFunctions</span>.<span style="color: #9cdcfe;">length</span>; <span style="color: #9cdcfe;">i</span>++){</div><div> <span style="color: #9cdcfe;">xOnLocationChangeFunctions</span>[<span style="color: #9cdcfe;">i</span>]();</div><div> }</div><div> }</div><div>}, <span style="color: #b5cea8;">350</span>)</div><div><br /></div></div><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p>Bresleveloperhttp://www.blogger.com/profile/12654150644649676499noreply@blogger.com1tag:blogger.com,1999:blog-6780036489035729397.post-66459421138438532422021-06-03T21:39:00.006+03:002021-06-03T21:39:57.076+03:00debugManifestsFile Error<p> <span style="background-color: white; color: #24292e; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 14px;">Error loading debug script. Ensure the server is running and the "debugManifestsFile" parameter URL is correct.</span></p><span style="background-color: white; color: #24292e; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 14px;">Error: Script error for "</span><a href="https://localhost:4321/temp/manifests.js" rel="nofollow" style="background-color: white; box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 14px; text-decoration-line: none;">https://localhost:4321/temp/manifests.js</a><span style="background-color: white; color: #24292e; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 14px;">"</span><div><span style="background-color: white; color: #24292e; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 14px;"><br /></span></div><div><span style="background-color: white; color: #24292e; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 14px;"><br /></span></div><div><span style="background-color: white; color: #24292e; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 14px;">SOLUTION</span></div><div><span style="background-color: white; color: #24292e; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 14px;">browse to </span><a href="https://localhost:4321/temp/manifests.js" rel="nofollow" style="background-color: white; box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 14px; text-decoration-line: none;">https://localhost:4321/temp/manifests.js</a> and allow to proceed to unsafe, and refresh the original page</div>Bresleveloperhttp://www.blogger.com/profile/12654150644649676499noreply@blogger.com2tag:blogger.com,1999:blog-6780036489035729397.post-1112805408401940882021-06-02T21:10:00.005+03:002022-04-12T00:31:19.471+03:00SPFx with Angular, Full tutorial<p> White you can use the <a href="https://pnp.github.io/generator-spfx/howtos/angularelements/" target="_blank">pnp-generator</a>, I really wanted to created this kind of solution all by myself, since I also wanted to implement it with SP-2019, with SFPx 1.4.x</p><p><br /></p><p><span style="font-size: x-large;">Part 1: Setup</span></p><p>I started with a clean machine, installed <a href="https://nodejs.org/en/download/" target="_blank">LTS node 14.17.0</a>, and installed latest gulp, yo, sp-generator for yo, and angular-cli</p><p>command:</p><p><span style="color: #6aa84f; font-family: courier;">npm install gulp yo @microsoft/generator-sharepoint --global</span></p><p>results:</p><p><span style="color: #6aa84f; font-family: courier;">+ gulp@4.0.2</span></p><p><span style="color: #6aa84f; font-family: courier;">+ yo@4.2.0</span></p><p><span style="color: #6aa84f; font-family: courier;">+ @microsoft/generator-sharepoint@1.12.1</span></p><p>command:</p><p><span style="color: #6aa84f; font-family: courier;">npm install -g @angular/cli</span></p><p>results:</p><p><span style="color: #6aa84f; font-family: courier;">+ @angular/cli@12.0.2</span></p><p>*NOTE - To the time of writing, I had an error</p><p><span style="font-family: courier;"><span style="color: #e06666;">generating browser application bundles</span></span></p><p>so I downgraded to angular 10.x, and re-create the project</p><p><span style="color: #6aa84f; font-family: courier;">npm uninstall -g @angular/cli</span></p><p><span style="color: #6aa84f; font-family: courier;">npm cache clean --force</span></p><p><span style="color: #6aa84f; font-family: courier;">npm install -g @angular/cli@10.2.0</span></p><p><br /></p><p>And so our journey begins!</p><p>Create new Ng project</p><p><span style="color: #6aa84f; font-family: courier;">ng new ng-for-spfx-ex</span></p><p>Create new SPFx WebPart Project - just <span style="color: #6aa84f; font-family: courier;">yo</span></p><p><br /></p><p><br /></p><p><span style="font-size: x-large;">Part 2: Test NG with SPFx</span></p><p>Within the <span style="color: #6aa84f; font-family: courier;">angular.json </span> file lets change the output path, I've made 1 folder, within I created both projects, and named the SPFx one as "SPFx-with-NG", as well as the WebPart. Config outputPath to a new "ng files" folder will make it so <span style="color: #6aa84f; font-family: courier;">ng build</span> will create all new files there.</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><span style="color: #9cdcfe;">"outputPath"</span>: <span style="color: #ce9178;">"../SPFx-with-NG/src/webparts/spFxWithNg/ng files"</span>,</div><p>(Also to make is faster I changed .browserslistrc to support only last 1 Chrome version)</p><p><br /></p><p><span style="font-size: medium;"><b>ANGULAR</b></span></p><p>Angular need to be changed from being an app, to being a Custom Element.</p><p>THEORY Explained (shortly):</p><p>When you create an angular app, its a JS code that goes and finds a tag named, usually, <span style="color: grey; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;"><</span><span style="color: #569cd6; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">app-root</span><span style="color: grey; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">></</span><span style="color: #569cd6; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">app-root</span><span style="color: grey; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">></span>, and from there its implementing all kind of things.</p><p>Therefor your <span style="color: grey; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;"><</span><span style="color: #569cd6; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">script</span><span style="color: grey; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">></span> callings are <b>after</b> the tag calling in your HTML.</p><p>But with SPFx we use <span style="background-color: #1e1e1e; color: #dcdcaa; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">require</span> ect. so its bootstrapping before the tag is rendered. </p><p><span style="color: #666666;">[*NOTE - You could theoretically change the <span style="background-color: #1e1e1e; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">ngDoBootstrap</span> in a way that will wait for it, but then using Angular Elements is much less work.</span></p><p><span style="color: #666666;">Another viable way (sometimes) is to add the scripts to a Document Library, and in the <span style="background-color: #1e1e1e; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">render</span> call both the tag and all the scripts from their right place. I used to do that with AngularJS + SP13.]</span></p><p>Custom Elements were introduced in ES5/6, its an API to create your own elements tags, instead a DIV tag, make your own MY-CLOCK tag. </p><p>Important for our case is that you "teach" the browser about your new tag, and he will render in accordingly, and he will watch the DOM for you, so you put the tag whenever you want, the browser compiles it according to instructions.</p><p><br /></p><p>PRACTICE</p><p>In order to change the whole app from an app to an element we need to do a few changes:</p><p>Install @angular/elements - <span style="color: #6aa84f; font-family: courier;">ng add @angular/elements</span></p><p>Then change the end or <span style="color: #6aa84f; font-family: courier;">app.module.ts</span> to:</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div> <span style="color: #9cdcfe;">providers</span><span style="color: #9cdcfe;">:</span> [{<span style="color: #9cdcfe;">provide</span><span style="color: #9cdcfe;">:</span> <span style="color: #4fc1ff;">APP_BASE_HREF</span>, <span style="color: #9cdcfe;">useValue</span><span style="color: #9cdcfe;">:</span> <span style="color: #ce9178;">'/'</span>}],</div><div> <span style="color: #6a9955;">//bootstrap: [AppComponent]</span></div><div> <span style="color: #9cdcfe;">entryComponents</span><span style="color: #9cdcfe;">:</span> [<span style="color: #4ec9b0;">AppComponent</span>]</div><div>})</div><div><span style="color: #c586c0;">export</span> <span style="color: #569cd6;">class</span> <span style="color: #4ec9b0;">AppModule</span> { </div><div> <span style="color: #569cd6;">constructor</span>(<span style="color: #569cd6;">private</span> <span style="color: #9cdcfe;">injector</span>: <span style="color: #4ec9b0;">Injector</span>) {}</div><br /><div> <span style="color: #569cd6;">public</span> <span style="color: #dcdcaa;">ngDoBootstrap</span>() {</div><div> <span style="color: #c586c0;">if</span> (!<span style="color: #9cdcfe;">customElements</span>.<span style="color: #dcdcaa;">get</span>(<span style="color: #ce9178;">'app-any-name'</span>)) {</div><div> <span style="color: #569cd6;">const</span> <span style="color: #4ec9b0;">AppElement</span> = <span style="color: #dcdcaa;">createCustomElement</span>(<span style="color: #4ec9b0;">AppComponent</span>, { <span style="color: #9cdcfe;">injector</span><span style="color: #9cdcfe;">:</span> <span style="color: #569cd6;">this</span>.<span style="color: #9cdcfe;">injector</span> });</div><div> <span style="color: #9cdcfe;">customElements</span>.<span style="color: #dcdcaa;">define</span>(<span style="color: #ce9178;">'app-any-name'</span>, <span style="color: #4ec9b0;">AppElement</span>);</div><div> }</div><div> }</div><div>}</div></div><p>Explained:</p><p></p><ul style="text-align: left;"><li>Adding provider to fill the missing <span style="color: grey; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;"><</span><span style="color: #569cd6; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">base</span><span style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;"> </span><span style="color: #9cdcfe; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">href</span><span style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">=</span><span style="color: #ce9178; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">"/"</span><span style="color: grey; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">></span> meta-tag</li><li>Tell angular no to try and <span style="background-color: #1e1e1e; color: #dcdcaa; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">Bootstrap</span> i.e. running all its JS immediately</li><li>Tell angular that <span style="background-color: #1e1e1e; color: #4ec9b0; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">AppComponent</span> is a root-component via <span style="background-color: #1e1e1e; color: #9cdcfe; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">entryComponents</span> </li><li>Ask angular to transform <span style="background-color: #1e1e1e; color: #4ec9b0; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">AppComponent</span> to a Custom Element (<span style="background-color: #1e1e1e; color: #dcdcaa; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">createCustomElement</span>)</li></ul><div><br /></div><p></p><p><b style="font-size: large;">SPFx</b></p><p>Calling our scripts</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div><span style="color: #6a9955;">/**** ANGULAR *****/</span></div><div><span style="color: #dcdcaa;">require</span>(<span style="color: #ce9178;">'./ng files/runtime.js'</span>)</div><div><span style="color: #dcdcaa;">require</span>(<span style="color: #ce9178;">'./ng files/polyfills.js'</span>)</div><div><span style="color: #dcdcaa;">require</span>(<span style="color: #ce9178;">'./ng files/styles.js'</span>)</div><div><span style="color: #dcdcaa;">require</span>(<span style="color: #ce9178;">'./ng files/vendor.js'</span>)</div><div><span style="color: #dcdcaa;">require</span>(<span style="color: #ce9178;">'./ng files/main.js'</span>)</div></div><p>And rendering</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><span style="color: #569cd6;">this</span>.<span style="color: #9cdcfe;">domElement</span>.<span style="color: #9cdcfe;">innerHTML</span> = <span style="color: #ce9178;">`<app-any-name></app-any-name>`</span></div><p>DONE! you can try <span style="color: #6aa84f; font-family: courier;">gulp serve</span></p><p><br /></p><p><b style="font-size: large;">AUTOMATION</b></p><p>If you want to use the "save-compile-show in browser" functionality use <span style="color: #6aa84f; font-family: courier;">ng build --watch</span> on your Ng project, and <span style="color: #6aa84f; font-family: courier;">gulp serve</span><span style="color: #6aa84f; font-family: courier;"> </span>on the SPFx project</p><p>To use it on your SharePoint site use <span style="color: #6aa84f; font-family: courier;">/_layouts/15/workbench.aspx</span>. You will need to refresh the page.</p><p>To test it in your SharePoint page, instead the workbench.aspx, just add </p><p><span style="color: #6aa84f; font-family: courier;">?loadSPFX=true&debugManifestsFile=https%3A%2F%2Flocalhost%3A4321%2Ftemp%2Fmanifests.js</span></p><p>Finally you can make it that gulp will serve inside your SharePoint page by changing inside <span style="color: #6aa84f; font-family: courier;">/config/</span><span style="color: #6aa84f; font-family: courier;">serve.json</span> this line to this</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div> <span style="color: #9cdcfe;">"initialPage"</span>: <span style="color: #ce9178; white-space: normal;">"https://bresleveloper.sharepoint.com/sites/teamsiteexample/spfx-angular?loadSPFX=true&debugManifestsFile=https%3A%2F%2Flocalhost%3A4321%2Ftemp%2Fmanifests.js"</span>,</div><div></div></div><p><br /></p><p><b style="font-size: large;">PageContext</b></p><p>I really like to have the <span style="background-color: #1e1e1e; color: #4ec9b0; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">PageContext</span> whenever I develop side-thing to SPFx.</p><p>The trick I usually do is <span style="color: #9cdcfe; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">window</span><span style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">[</span><span style="color: #ce9178; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">'ctx'</span><span style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">] = </span><span style="color: #569cd6; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">this</span><span style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">.</span><span style="color: #4fc1ff; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">context</span><span style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">.</span><span style="color: #9cdcfe; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">pageContext</span> within the <span style="background-color: #1e1e1e; color: #dcdcaa; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">render</span> function</p><p>But I want angular to know my ctx, so I clicked F12 on <span style="color: #9cdcfe; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">pageContext</span> and then again on <span style="background-color: #1e1e1e; color: #4ec9b0; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">PageContext</span> and that got me to a <span style="color: #6aa84f; font-family: courier;">index-internal.d.ts</span> file. I copied it to my angular models folder, deleted the <span style="background-color: #1e1e1e; color: #c586c0; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">import</span> on top and deleted error refs or changed them to <span style="background-color: #1e1e1e; color: #4ec9b0; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">any</span> </p><p>Result:</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div><span style="color: #c586c0;">export</span> <span style="color: #569cd6;">class</span> <span style="color: #4ec9b0;">AppComponent</span> {</div><div> <span style="color: #9cdcfe;">title</span> = <span style="color: #ce9178;">'Ng-For-SPFx'</span>;</div><div> <span style="color: #9cdcfe;">ctx</span>:<span style="color: #4ec9b0;">PageContext</span> = <span style="color: #9cdcfe;">window</span>[<span style="color: #ce9178;">'ctx'</span>]</div><div> <span style="color: #9cdcfe;">absUrl</span> = <span style="color: #569cd6;">this</span>.<span style="color: #9cdcfe;">ctx</span>.<span style="color: #9cdcfe;">site</span>.<span style="color: #4fc1ff;">absoluteUrl</span></div><div>}</div></div><p><br /></p><p>Thanks to Andrew Connell for Inspiration and guidance in his <a href="https://www.voitanos.io/series/spfx-angular-elements/" target="_blank">Angular SPFx Series</a></p><p>You can check in on <a href="https://github.com/bresleveloper/SPFx-with-Angular" target="_blank">my github</a></p><p><br /></p><p><br /></p><p><br /></p><p>Would you like my help to create SPFx webparts with or without Angular? <a href="https://www.bresleveloper.co.il/#section-62549b124af9b" target="_blank">Contact us now!</a></p>Bresleveloperhttp://www.blogger.com/profile/12654150644649676499noreply@blogger.com5tag:blogger.com,1999:blog-6780036489035729397.post-73165243829052417262021-05-25T16:35:00.007+03:002021-05-25T16:35:46.908+03:00Windows run as a different user from CMD<p> On my journey to fix my dev env I learned some cool trick on how to run stuff in windows as another user, from CMD</p><p>* For running the .exe files you need to navigate to their folders or run full path</p><p><br /></p><p>The basic operation to run as user is</p><p><span style="color: #6aa84f; font-family: courier;">runas /netonly /user:<DOMAIN>\<USER> <COMMAND></span></p><div>So, for example domain is MyDom, and user is OthrUsr</div><div><br /></div><div><br /></div><div>- Just open CMD</div><div><span style="color: #6aa84f; font-family: courier;"><br /></span></div><div><span style="color: #6aa84f; font-family: courier;">runas /netonly /user:MyDom\OthrUsr cmd</span></div><div><br /></div><div><br /></div><div>- Open PowerShell</div><div><br /></div><div><span style="color: #6aa84f; font-family: courier;">cd "C:\Windows\System32\WindowsPowerShell\v1.0\"</span></div><div><span style="color: #6aa84f; font-family: courier;">runas /netonly /user:</span><span style="color: #6aa84f; font-family: courier;">MyDom\OthrUsr</span><span style="color: #6aa84f; font-family: courier;"> PowerShell.exe</span></div><div><br /></div><div><br /></div><div>- Open SharePoint PowerShell</div><div><div><span style="color: #6aa84f; font-family: courier;"><br /></span></div><div><span style="color: #6aa84f; font-family: courier;">cd "C:\Windows\System32\WindowsPowerShell\v1.0\"</span></div><div><span style="color: #6aa84f; font-family: courier;">runas /netonly /user:</span><span style="color: #6aa84f; font-family: courier;">MyDom\OthrUsr</span><span style="color: #6aa84f; font-family: courier;"> PowerShell.exe</span></div><div><span style="color: #6aa84f; font-family: courier;"><br /></span></div><div><span style="color: #6aa84f; font-family: courier;">. "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\CONFIG\POWERSHELL\Registration\SharePoint.ps1"</span></div><div><br /></div></div><div>*According to your SP version the "16" might be 15 (for sp13) or 14 (for sp10)</div><div><br /></div><div><br /></div><div><br /></div><div>- Open SQL Server Management Studio (SSMS) (path can chage according to version)</div><div><span style="color: #6aa84f; font-family: courier;"><br /></span></div><div><span style="color: #6aa84f; font-family: courier;">cd "C:\Program Files (x86)\Microsoft SQL Server Management Studio 18\Common7\IDE\"</span></div><div><span style="color: #6aa84f; font-family: courier;"><br /></span></div><div><span style="color: #6aa84f; font-family: courier;">runas /netonly /user:</span><span style="color: #6aa84f; font-family: courier;">MyDom\OthrUsr</span><span style="color: #6aa84f; font-family: courier;"> ssms.exe</span></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div>
Bresleveloperhttp://www.blogger.com/profile/12654150644649676499noreply@blogger.com0tag:blogger.com,1999:blog-6780036489035729397.post-82770158794102720832021-05-25T15:39:00.003+03:002021-05-25T15:57:53.127+03:00new SharePoint 2019 machine - cant login into SQL Server<p> We have created a newly win2k16 server with sharepoint 2019, and it worked great...</p><p>Until I tried to enable apps (SPFx), and I needed to create a Subscription Service, and therefor use the SQLSERVER.</p><p>Whatever I tried I got these error depending on the test, with PowerShell commands (PS), SQL logs, or Sql Server Management Studio (SSMS).</p><p><span style="color: #6aa84f; font-family: courier;">Error: 18456, Severity: 14, State: 5.</span></p><p><span style="color: #6aa84f; font-family: courier;">Login failed for user '<DOMAIN>\<USER>'. Reason: Could not find a login matching the name provided. [CLIENT: <local machine>]</span></p><p><span style="color: #6aa84f; font-family: courier;">Error: 18461, Severity: 14, State: 1.</span></p><p><span style="color: #6aa84f; font-family: courier;">Login failed for user '<DOMAIN>\<USER>'. Reason: Server is in single user mode. Only one administrator can connect at this time. [CLIENT: <local machine>]</span></p><p><span style="color: #6aa84f; font-family: courier;">Error: 18456, Severity: 14, State: 58.</span></p><p><span style="color: #6aa84f; font-family: courier;">Login failed for user '<DOMAIN>\<USER>'. Reason: An attempt to login using SQL authentication failed. Server is configured for Windows authentication only. [CLIENT: <local machine>]</span></p><p>In the end I found <a href="https://www.mssqltips.com/sqlservertip/6531/sql-server-single-user-mode-connection-with-sql-server-management-studio/" target="_blank">this article</a> that suggests to put SQLSERVER in single user mode, and connect after clearing other connections, mainly SQL Server Agent service.</p><p>Follow the article steps</p><p></p><ul style="text-align: left;"><li>The "Configuration Manager" comes with its version in its name (2016)</li><li>Add the "-m" startup parameter</li><li>Stop the rest of the services</li><li>Login via SSMS</li></ul><div>My following actions were:</div><div><ul style="text-align: left;"><li>Added my "Administrator" account via Object Explorer -> Security -> right click Logins -> "New Login..."</li><li>Added myself all server roles, and "db_owner" on all Databeses (User Mapping)</li><li>Changed "sa" user Status -> Login to Enabled</li><li>Added farm account all server roles</li></ul></div><div>Going back to Configuration Manager, I removed the "-m", and restarted all services</div><div><br /></div><div>Everything works now!</div><div><br /></div><p><br /></p>Bresleveloperhttp://www.blogger.com/profile/12654150644649676499noreply@blogger.com0tag:blogger.com,1999:blog-6780036489035729397.post-40765903579577285562021-05-25T01:23:00.010+03:002022-04-12T00:33:01.914+03:00Setting new SP2019 dev machine for SPFx<p>I've made this collection for any other time me or you will get a new SP machine for local dev.</p><p><br /></p><p>Create your Web-App and change the <span style="color: #6aa84f; font-family: courier;">hosts </span>file with new value <span style="color: #6aa84f; font-family: courier;">127.0.0.1 my-webapp</span></p><p><br /></p><p>Make browser stop asking for credentials endlessly</p><p><a href="https://sharepoint.stackexchange.com/questions/11792/external-access-continually-prompted-for-credentials#answer-11846">https://sharepoint.stackexchange.com/questions/11792/external-access-continually-prompted-for-credentials#answer-11846</a></p><p><br /></p><p><br /></p><p>Sometimes you want to enable multi rdp (not multi user, just multi sessions for same user)</p><p><a href="https://social.technet.microsoft.com/Forums/en-US/c52b6da7-cdf2-4e1f-95d1-6471c6f2f6b0/easiest-way-to-enable-more-than-2-concurrent-rdp-sessions-on-windows-server-2016?forum=winserverTS#1701fa1f-c7fb-4093-a525-8b188e17c440">https://social.technet.microsoft.com/Forums/en-US/c52b6da7-cdf2-4e1f-95d1-6471c6f2f6b0/easiest-way-to-enable-more-than-2-concurrent-rdp-sessions-on-windows-server-2016?forum=winserverTS#1701fa1f-c7fb-4093-a525-8b188e17c440</a></p><p>OR</p><p><a href="https://help.matrix42.com/020_ESM/MyWorkspace/10MyWorkspace_Guide/HowTo%3A_Enable_multiple_sessions_for_Remote_Desktop_Services#:~:text=Enable%20Multiple%20RDP%20Sessions,-Log%20into%20the&text=Go%20to%20Computer%20Configuration%20%3E%20Administrative,Desktop%20Services%20session%20to%20Disabled.">https://help.matrix42.com/020_ESM/MyWorkspace/10MyWorkspace_Guide/HowTo%3A_Enable_multiple_sessions_for_Remote_Desktop_Services#:~:text=Enable%20Multiple%20RDP%20Sessions,-Log%20into%20the&text=Go%20to%20Computer%20Configuration%20%3E%20Administrative,Desktop%20Services%20session%20to%20Disabled.</a></p><p>And in that case, enable open chrome for other sessions</p><p><a href="http://www.jochenhebbrecht.be/site/2015-09-13/windows/second-rdp-session-cant-open-a-chrome-window-if-first-session-runs-chrome-already">http://www.jochenhebbrecht.be/site/2015-09-13/windows/second-rdp-session-cant-open-a-chrome-window-if-first-session-runs-chrome-already</a></p><p><br /></p><p><br /></p><p>Setup dev environment for SPFx for 2019 (Also see Problems in the end)</p><p><a href="https://social.technet.microsoft.com/wiki/contents/articles/53207.get-your-sharepoint-2019-environment-ready-for-spfx-development.aspx">https://social.technet.microsoft.com/wiki/contents/articles/53207.get-your-sharepoint-2019-environment-ready-for-spfx-development.aspx</a></p><p>Link to 8.17 <a href="https://nodejs.org/dist/v8.17.0/">https://nodejs.org/dist/v8.17.0/</a></p><p>Link to msi <a href="https://nodejs.org/dist/v8.17.0/node-v8.17.0-x64.msi">https://nodejs.org/dist/v8.17.0/node-v8.17.0-x64.msi</a></p><p>Unlike the article, installing node 8.x did not work, and I found someone stating that installing 14.x works great, so I uninstalled everything, deleted "npm" folders from AppData and reinstalled node 14.x and installed the rest with npm</p><p><span style="color: #6aa84f; font-family: courier;">npm install gulp yo @microsoft/generator-sharepoint --global</span></p><p>Then for every project you create you must do </p><p></p><ul style="text-align: left;"><li>Select SharePoint 2019 and onward</li><li>Create file npm-shrinkwrap.json as below</li><li>npm install again</li><li>gulp build will now work</li></ul><p></p><p>Otherwise you will get gulp build error <span style="color: #6aa84f; font-family: courier;">ReferenceError: primordials is not defined</span></p><div>solution</div><div><a href="https://timonweb.com/javascript/how-to-fix-referenceerror-primordials-is-not-defined-error/#heres-what-you-need-to-do">https://timonweb.com/javascript/how-to-fix-referenceerror-primordials-is-not-defined-error/#heres-what-you-need-to-do</a></div><div>each project again create file <span style="color: #6aa84f; font-family: courier;">npm-shrinkwrap.json</span> and write inside</div><div><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div>{</div><div> <span style="color: #9cdcfe;">"dependencies"</span>: {</div><div> <span style="color: #9cdcfe;">"graceful-fs"</span>: {</div><div> <span style="color: #9cdcfe;">"version"</span>: <span style="color: #ce9178;">"4.2.2"</span></div><div> }</div><div> }</div><div>}</div></div></div><p>and run <span style="color: #6aa84f; font-family: courier;">npm i</span> again, then gulp build will work</p><p><br /></p><p><br /></p><p>Enable App Catalog for SP2019 (Also see Problems in the end)</p><p><a href="https://support.shortpoint.com/support/solutions/articles/1000273552-you-get-error-saying-sorry-apps-are-turned-off-if-you-know-who-runs-the-server-tell-them-to-enabl">https://support.shortpoint.com/support/solutions/articles/1000273552-you-get-error-saying-sorry-apps-are-turned-off-if-you-know-who-runs-the-server-tell-them-to-enabl</a></p><p><br /></p><p>Save list as template for modern 2019 (SP PS)</p><p><a href="https://support.bindtuning.com/hc/en-us/articles/360030872511-Activate-Custom-Script-for-SharePoint-2019">https://support.bindtuning.com/hc/en-us/articles/360030872511-Activate-Custom-Script-for-SharePoint-2019</a></p><p><span style="color: #6aa84f; font-family: courier;">(Get-SPSite -Identity "https://Server/sites/SiteURL").DenyPermissionsMask= [Microsoft.SharePoint.SPBasePermissions]::EmptyMask</span></p><p><br /></p><p>OTHER PROBLEMS</p><p>Something stupid (MS-like), if your deployment fails with</p><p><span style="color: #6aa84f; font-family: courier;">Invalid SharePoint App Package. Error: Part URI is not valid per rules defined in the Open Packaging Conventions specification.</span></p><p>That is because you created folders and solution names with dashes or spaces.</p><p><a href="http://www.intelogist.eu/2020/02/how-to-resolve-part-uri-is-not-valid-spfx/">http://www.intelogist.eu/2020/02/how-to-resolve-part-uri-is-not-valid-spfx/</a></p><p><br /></p><p>Another thing, if you get errors in the step of configuring Subscription Service, see how I solved it</p><p><a href="https://bresleveloper.blogspot.com/2021/05/new-sharepoint-2019-machine-cant-login.html">https://bresleveloper.blogspot.com/2021/05/new-sharepoint-2019-machine-cant-login.html</a></p><p><br /></p><p><br /></p><p><br /></p><p>Do you need help to stabilize your SPFx DEV machines?</p><p>Do you need Cloud Dev Machines for SPFx?</p><p>Can I help you develop SPFx for 365, SPO, or SP2019?</p><p><a href="https://www.bresleveloper.co.il/#section-62549b124af9b" target="_blank">Contact us now!</a></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p>Bresleveloperhttp://www.blogger.com/profile/12654150644649676499noreply@blogger.com0tag:blogger.com,1999:blog-6780036489035729397.post-74702372752771347962021-05-23T20:28:00.006+03:002021-05-25T15:56:46.356+03:00SPFx migrating solution from Online to 2019<p> Let me start that is would be much easier to just create a new solution, specify it as an 2019 and copy the code.</p><p>But I'm an explorer type of man...</p><p>So I did created another solution for 2019 and copied some files</p><p><span style="color: #93c47d; font-family: courier;">tsconfig.json</span></p><p><span style="color: #93c47d; font-family: courier;">package.json</span></p><p><span style="color: #93c47d; font-family: courier;">package-lock</span></p><p><span style="color: #93c47d; font-family: courier;">.yo-rc.json</span></p><p>under <span style="color: #93c47d; font-family: courier;">/config</span>, I matched the content of those files (dont copy-paste)</p><p><span style="color: #93c47d; font-family: courier;">copy-assets.json</span></p><p><span style="color: #93c47d; font-family: courier;">package-solution.json</span></p><p><br /></p><p>Also in all /src/webparts/<someWP>/<someWP>.manifest.json mark this line</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><span style="color: #9cdcfe;">"supportedHosts"</span>: [<span style="color: #ce9178;">"SharePointWebPart"</span>],</div><p>And finally in your code dont use stuff like <span style="color: #569cd6; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">public</span><span style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;"> </span><span style="color: #9cdcfe; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">listDetails</span><span style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">:[]=[]</span> instead just <span style="color: #569cd6; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">public</span><span style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;"> </span><span style="color: #9cdcfe; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">listDetails</span><span style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">=[]</span></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p>Bresleveloperhttp://www.blogger.com/profile/12654150644649676499noreply@blogger.com1tag:blogger.com,1999:blog-6780036489035729397.post-40995325317295472592021-05-20T14:53:00.004+03:002021-05-25T15:56:53.757+03:00SPFx: Can't view Install Errors<p>Simple solution is to disable "DenyAddAndCustomizePages" to just enable those pages for the site collection</p><p><br /></p><p>NOTICE - its site collection level</p><p><span style="color: #b6d7a8; font-family: courier;"><br /></span></p><p><span style="color: #93c47d; font-family: courier;">$tenantUrl = "https://<tenant>-admin.sharepoint.com"</span></p><p><span style="color: #93c47d; font-family: courier;">Connect-SPOService -Url $tenantUrl</span></p><p><span style="color: #93c47d;"><span style="font-family: courier;">$siteUrl = "https://</span><span style="font-family: courier;"><tenant></span><span style="font-family: courier;">.sharepoint.com/sites/teamsiteexample"</span></span></p><p><span style="color: #93c47d; font-family: courier;">Set-SPOSite -Identity $siteUrl -DenyAddAndCustomizePages $false</span></p><p><span style="color: #93c47d; font-family: courier;">Disconnect-SPOService </span></p><p><br /></p><p>You can always change it back to $true</p>Bresleveloperhttp://www.blogger.com/profile/12654150644649676499noreply@blogger.com0tag:blogger.com,1999:blog-6780036489035729397.post-64204204052111931822021-04-25T11:39:00.001+03:002021-05-25T16:36:14.036+03:00Narnia 1 - OverTheWire - Updated WalktroughNarnia 1 is about teaching you to write, inject and use ShellCode.<br />
<br />
*NOTE - this tutorial is for programmers like me, that just learn some web programming, maybe having fun with some hacking ect., no idea about c or assembler. If you're a computer science deg. this might be wwaayy over simplifying, and therefor its not for you.<br />
<br />
The main purpose of this tutorial is not HOW to solve narnia1, but to UNDERSTAND how to solve it a-z.<br />
<br />
What's ShellCode? Its a crafted piece of code, usually meant for a shell, usually binary code (in hex representation, called Object Code)<br />
<br />
So as stated there are 3 stages here, -write, -inject, -run. I'll start from the last.<br />
<br />
<span style="font-size: large;">RUN YOUR SHELLCODE</span><br />
<br />
Well, in order to run your shellcode, you need to know where is the exploit, and adapt the code to that, so lets open narnia and lets see the exlpoit in "<span style="font-family: Courier New, Courier, monospace;">narnia1.c</span>":<br />
<span style="font-family: Courier New, Courier, monospace;"> ret = getenv("EGG");</span><br />
<span style="font-family: Courier New, Courier, monospace;"> ret();</span><br />
<div>
<br /></div>
Well, whats "<span style="font-family: Courier New, Courier, monospace;">ret</span>"? It is defined "<span style="font-family: Courier New, Courier, monospace;">int (*ret)();</span>", and when googling that it means a function pointer, expected to return and int.<br />
That goes will when googling what "<span style="font-family: "Courier New", Courier, monospace;">getenv</span>", which is returning the pointer to an<br />
Environment Variable.<br />
<br />
*If you don't know what a pointer is, please learn about that, its important, since if this couple breaks and one of them is not a pointer it won't work.<br />
<br />
Therefor we now understand that the code in "<span style="font-family: Courier New, Courier, monospace;">narnia1</span>", it's a program that's running a function that has its pointer in the Environment Variable of "<span style="font-family: "Courier New", Courier, monospace;">EGG</span>".<br />
<br />
So if we want to exploit that, we can try running our own code in that function.<br />
<br />
<span style="font-size: large;">INJECT YOUR SHELLCODE</span><br />
<br />
As said, we can just inject our own code to "<span style="font-family: "Courier New", Courier, monospace;">EGG</span>". For that we need to understand a very simple thing, that code is a "c" compiled code running, so we cant send c there... that won't work.<br />
<br />
So to what C is being compiled to? C (and many other "elite" or "high-level" languages) compiles to Assembler (a "low level" language), and that gets "assembled" or "relocate" or "mapped" to Object Code or Binary Code.<br />
<br />
So in the end what you will want to inject some Binary Code or Object Code (which can be written in hex instead of binary, since the computer works in bytes). Write some hex Object Code to "<span style="font-family: "Courier New", Courier, monospace;">EGG</span>" and you have your exploit.<br />
<br />
<br />
<span style="font-size: large;">WRITE YOUR SHELLCODE</span><br />
<br />
This is the part where everything gets interesting. 1st of all you don't really have to write any code, when you get that you can just google "shellcode x" where "x" is the code you need, already prepared and tested for you like from <a href="http://shell-storm.org/shellcode/">http://shell-storm.org/shellcode/</a> which is referenced as the Shellcode DB.<br />
<br />
But that's all boring and we didn't learn anything. Its like the closest thing to a spoiler.<br />
BTW you can do all kind of ShellCode with a tool called "<span style="font-family: Courier New, Courier, monospace;">msfvenom</span>" but i couldn't find a way causing it to create a ShellCode for "<span style="font-family: Courier New, Courier, monospace;">seteuid(geteuid(),geteuid())</span>", i guess since there isn't a way doing it from shell.<br />
<br />
Therefor, we MUST turn to... Assembly.<br />
<br />
Why not c? Well, <a href="https://stackoverflow.com/questions/30877635/how-is-shellcode-generated-from-c-with-code-example#answer-30893456">here is your stack answer</a>. To make is super short and simple, when c compiles to assembler it generates call address, and in the target machine you don't have that address, so you need to use system call directly.<br />
<br />
<br />
<span style="font-size: large;">ASSEMBLER INTRODUCTION</span><br />
<br />
You should go and quickly do a little assembly tutorial.<br />
<br />
Now lets get to the point, we want to call c functions via assembler. All those functions lie in a file that is a table for them,<br />
<br />
Next, talking in x32, lets go. Since we want to do system calls lets see them, but we all now have x64 linux its in a different file than in the x32:<br />
"<span style="font-family: Courier New, Courier, monospace;">head -20 /usr/include/x86_64-linux-gnu/asm/unistd_32.h</span>"<br />
<br />
we can look for specific command, for example we now know that even when there is the "<span style="font-family: Courier New, Courier, monospace;">s</span>" flag on a file we still need to call "<span style="font-family: Courier New, Courier, monospace;">setreuid</span>" to make it effective, so<br />
"<span style="font-family: Courier New, Courier, monospace;">cat /usr/include/x86_64-linux-gnu/asm/unistd_32.h | grep setreuid</span>"<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />Bresleveloperhttp://www.blogger.com/profile/12654150644649676499noreply@blogger.com1tag:blogger.com,1999:blog-6780036489035729397.post-9773643840676167662021-04-25T11:39:00.000+03:002021-04-25T11:39:16.717+03:00CTF365 Security Shepherd Walkthrough - No Spoilers Just Hintshopefully, lets try to do all of the <a href="https://security-shepherd.ctf365.com/index.jsp">CTF365 Security Shepherd</a> lessons and challenges, and help everybody with all they need but no spoilers.<br />
<br />
that doens't meant you don't need to do your learning, if i didn't write something, that means (a) learn the topic (b) read instructions.<br />
<br />
simple way to work through is installing burp-suite CE and make it work with ssl, but i'll try to also teach, where possible, you can use other tools.<br />
<br />
<span style="font-size: x-large;"><b>LESSONS</b></span><br />
*any lession with a asterisk [*] in its name means that it would have been easier with burp, just altering the request. [need to install cert. etc.]<br />
<br />
<br />
<span style="font-size: large;"><b>Broken Session Management*</b></span><br />
the session is managed via cookies, can be changed via<br />
Chrome Dev Tools (F12) -> Application tab -> Storage -> Cookies.<br />
(maybe you must click the button 1st)<br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;"><b>Cross-Site Request Forgery</b></span><br />
after putting a value in the box and submitting, right-click and Inspect-Element the image.<br />
you must put a full url i.e. https://.....<br />
try put a funny image from google search.<br />
<br />
<span style="font-size: large;"><b>Cross Site Scripting (XSS)</b></span><br />
if you miss the answer itself written in-front of your eyes, time to learn some html and javascript.<br />
<br />
<span style="font-size: large;"><b>Failure to Restrict URL Access</b></span><br />
here you need to learn to research the html itself of the page(s) to see more content(s).<br />
click F12, or right click and either Inspect Element or View Source.<br />
*tip - learn the "display" css property and its values.<br />
<br />
<span style="font-size: large;"><b>Insecure Cryptographic Storage</b></span><br />
any online tool<br />
<br />
<span style="font-size: large;"><b>Insecure Direct Object References*</b></span><br />
looking at the html source we can see the ajax request and manually send it altered.<br />
<br />
<span style="font-size: large;"><b>Poor Data Validation*</b></span><br />
same as last one.<br />
<br />
<span style="font-size: large;"><b>Security Misconfiguration</b></span><br />
admin:password. make sure you're not on CAPS LOCK.<br />
<br />
<a class="lesson" href="https://www.blogger.com/null" id="408610f220b4f71f7261207a17055acbffb8a747"><span style="font-size: large;"><b>SQL Injection</b></span></a><br />
the target db is mysql, so comment is "#". after 3 tries you get a hint, which is really the answer.<br />
<br />
<a class="lesson" href="https://www.blogger.com/null" id="6f5db377c28da4179bca1a43ede8d6bcf7bd322e"><span style="font-size: large;"><b>Untrusted Input</b></span></a><br />
maybe one day i'll install the Mobile Machine<br />
<br />
<b><span style="font-size: large;">Unvalidated Redirects and Forwards</span></b><br />
you must put a full url i.e. https://security-shepherd.ctf365.com/.... <b>both</b> times....<br />
and user can start with a dash [-] like "<i>-13245678"</i><br />
<i><br /></i>
<i><br /></i>
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />Bresleveloperhttp://www.blogger.com/profile/12654150644649676499noreply@blogger.com0