tag:blogger.com,1999:blog-81386942024-03-22T02:46:38.249+01:00Damjan's CornerDamjanhttp://www.blogger.com/profile/02201297910253584960noreply@blogger.comBlogger21125tag:blogger.com,1999:blog-8138694.post-84623272534276424142023-01-24T20:38:00.027+01:002023-01-24T20:57:05.412+01:00Joca ubica<span style="font-family: arial;">Bio je avgust 1999. Doselio sam se sa porodicom u Beograd. Završio sam prethodno treći razred gimnazije prirodno-matematičkog smera, i prva bitna stvar u Beogradu je bila da nađem novu školu.<br />Odlazim sa ocem u Treću gimnaziju, tamo nam se direktor izvinjava, kaže da imaju previše učenika, pokazuje male učionice.<br />Odlazimo u Jedanaestu gimnaziju, tamo direktorka priča istu priču i onda kaže “Idi ti u šestu, tamo sigurno ima mesta”.<br /><br />Raspitujemo se kod rodbine za lokaciju Šeste gimnazije, stižemo ispred, i na zgradi vidimo ogromni grafit: “<i>Supersonična je šesta</i>”. Tata kaže, e ova.. ova će ti biti dobra.<br /><br />Ulazimo unutra, direktor nas prima, naravno mesta ima, ostavljam podatke, daje mi broj učionice, odeljenje i poželi dobrodošlicu.<br /><br />Prvi septembar. Ja stigao malo ranije. Nije mi prvi put da sam novi u školi. Znam već fazone kako se upoznati i uklopiti. Đaci polako pristižu, upoznajemo se.. standardna priča.. odakle dolaziš, kakav si đak, kakvu muziku slušaš, kako igraš fudbal..<br /><br />Kreće prvi čas, razredna, srpski jezik... vidim fina neka žena, opuštena atmosfera, đaci dosta pričaju.. ljudi oko mene pričaju sa mnom.. u jednom trenutku razredna se okreće i kaže... ej ti, novi, mnogo pričaš, ajd izađi napolje.. đaci kreću da negoduju... nemojte razredna.. ja ustajem i izlazim.. čekam ispred vrata da se čas završi. Nije mi prvi put ni da me izbace napolje.. nema ljutnje, pričao sam.. žena je stvarno dobra, vidim da joj malo izmiče disciplina, pa trenira strogoću na meni, a novi sam, pa mi je to skroz ok.<br /><br />Posle časa se vraćam unutra i vidim da se nešto dešava... svi napeti... nešto su saznali na času.. čujem samo Joca... Joca... peticija..<br /><br />Neki papir kreću da pronose po razredu... svi se potpisuju... pitam šta se dešava? Kažu... promenili su nam profesora za matu... sad nam predaje <b>Joca ubica!</b> <br /><br />OK.. razmišljam... bio sam u dosta različitih škola.. čuo desetine nadimaka za profesore... nadimak ubica još nikad nisam čuo, ali... kakav li je to sad lik kad ga se tako boje svi, a nije im nikad predavao... Papir dolazi do mene... razmišljam... u prethodnoj školi kad je bilo glasanje da se smeni profesor fizike, sa drugom sam jedini bio protiv, iako sam imao 3 iz fizike.. tako da nisam nešto za te varijante, ali nov sam... da ne talasam sad... potpišem se. Neki od najglasnijih đaka odnose papir kod direktora.. ostatak dana prolazi fino... svi srećni što su smenili ubicu.. Ja se upoznajem sa celim razredom.. <br /><br />Dan drugi. Čas matematike. Ulazi kratko ošišan lik u polo majici. U jednoj ruci nosi dnevnik, a u drugoj neki papir. Kaže nešto u fazonu... ooo, dobar dan deco.. nismo se još ni upoznali, a vidim već hoćete da me se rešite.. Joca!<br /><br />Kreće da čita imena sa onog papira... za svakog od nas pita koju smo ocenu imali iz matematike i zašto hoćemo da ga smenimo... dolazi do mene.. Ja kažem da sam nov, da sam imao četvorku u prethodnoj školi... on nešto šaljivo prokomentarisa na temu što sam tek došao a već hoću da menjam nastavnike.. ja se nasmejah..<br /><br />Dani prolaze, meni se škola sviđa.. ekipa u razredu je dobra... igramo dobar fudbal na fizičkom.. imamo jednog vrhunskog igrača, par nas solidnih.. sve u svemu dobra ekipa.. možda možemo i da uradimo nešto na školskom turniru.. Profesori kao u bilo kojoj školi... ima zanimljivih, ima smotanih, ima bizarnih, ima predanih poslu.. bukvalno kao u bilo kojoj drugoj školi.<br /><br />To je inače bila prva školska godina posle bombardovanja, tako da je u prvih mesec dana svaki nastavnik imao obavezu da prođe gradivo iz dela drugog polugodišta prethodnog razreda, jer tada nismo išli u školu.. sve u svemu, bilo je dosta žurbe i kod njih da se pređe staro gradivo i počne sa novim..<br /><br />Dolazi prvi kontrolni iz matematike... Stižu rezultati... Par trojki, nešto dvojki i mnogo jedinica. Tad smo skapirali zašto čoveka zovu ubica. Od mogućih 100 poena, potrebno je <b>38</b> za dvojku. Ja sam dobio trojku.<br /><br />Kasnije sam skapirao šta je suštinski problem... ja sam imao sreće da sam u Gimnaziji u Vršcu imao prof. Lekić Petrovič, koja je ozbiljno znala matematiku i što je bitnije, umela da prenese to znanje i održi disciplinu na času. Razred u Šestoj, u koji sam došao nije bio te sreće... imali su više različitih profesora mate u ranijim godinama, a u prethodnoj godini neku, koliko se sećam da su rekli, ne baš zainteresovanu osobu, kod koje su mogli da se provlače bez mnogo učenja... a matematika ne prašta... jednom propušteno se nadoknađuje samo uz mnogo rada.<br /><br />Potom smo imali još mnogo, mnogo kontrolnih i pismenih zadataka kod njega.<br /><br />Sećam se situacije, Joca diktira zadatke... neko od ekipe koja je sedela oko mene me nešto pita.. ja odgovorim.. Joca čuje i kaže mi... Tomić.. piši minus pet! Ja kažem.. ali profesore, nisam.. Joca... minus deset... Ja... ali profesore, nis.. minus petnaest! Ja dižem ruku, razumeo! Na papir za kontrolni upisujem -15. Dolaze rezultati, ja skupio valjda nekih 50ak poena... on sve lepo izračunao, zaokružio na papiru onih -15, oduzeo i dao mi keca. Zasluženo :)<br /><br />U drugom polugodištu se desila slična situacija, ali sam bio malo pametniji i upisao “<b>- mnogo</b>” na papir, tako da sam prošao bez oduzimanja poena.<br /><br />Bilo je, naravno, i mnogo primera ljudi koji su stvarno dosta radili.. vežbali matematiku danima i mesecima, ali su rupe u znanju bile tolike da nikako nisu mogli da prebace 38 poena. A imali smo stvarno dosta kontrolnih i pismenih zadataka. Na samom kraju je, čini mi se, manje od 10 ljudi išlo na popravni kod njega. Praktično svako, ko je barem jednom dobio dvojku, je imao 2 na kraju. Svi su kasnije položili popravni ispit i niko nije ponavljao.<br /><br />Iz učeničke perspektive neke stvari možda izgledaju strašno u trenutku kada se dešavaju. Ali sve je to deo procesa školovanja. U školi se ne uči samo gradivo. To je priprema za život. I moje mišljenje je da nas je Joca dobro pripremao. Koliko je bilo u njegovoj moći.<br /><br />Ja sam u nekom trenutku tokom te godine konačno odlučio šta ću da studiram. Znao sam da ću polagati matematiku na prijemnom ispitu, i trudio se da istu što više vežbam. Na kraju sam uzeo i maturski rad kod Joce, iako su mi mnogi govorili da sam blesav i da treba da uzmem nešto prosto, da ne trošim puno vremena na to. Rezultat je bio da sam imao 4 iz matematike, dobio 4 na maturskom radu, i uz Jocine besplatne dodatne časove u školi, spremio prijemni i upao na budžet bez bilo kakvih dodatnih privatnih časova.<br /><br />Posle toga ga nikada više nisam video. Povremeno je bilo vesti po novinama i kasnije društvenim mrežama, o nekim suspenzijama, procesima i sl. protiv njega. Bio je tu i tamo u nekim emisijama na TV-u, youtube-u.. Pratio sam tu i tamo sa strane šta se dešavalo sa njim, kao i ovog poslednjeg puta. Znao sam uvek da se tu radi o nekoj grupi zbunjene dece, kao što smo i mi biil tog 1. septembra 1999, kad smo pisali peticiju protiv njega. Dece koja u tom trenutku ne mogu da znaju šta je dobro za njih na dugi rok. Dece koja, ponekad uz pomoć roditelja, pokušavaju da idu linijom manjeg otpora. I znao sam da nasuprot njih stoji jedan principijelan i čvrst čovek, koji se trudi da tu decu usmeri na pravi put. Put koji sigurno vodi ka više znanja, boljem obrazovanju, i kasnije lepšem životu.<br /><br />Danas je Joca sahranjen. <br /><br />Ja trenutno živim daleko, i nisam bio u prilici da odem na sahranu, a znam da se dosta bivših đaka pojavilo na groblju. Ovaj tekst je bio moj način da se oprostim od dobrog profesora.<br /><br />P.S.<br />Po društvenim mrežama je poslednjih dana bilo dosta komentara u stilu.. “ubili su ga”.. “oterali su ga u grob”.. na račun roditelja dece koja su podigla poslednju tužbu protiv njega.. ali ja se nikako ne bih složio sa tim.. Prevelika je Joca bio faca da bi ga jedna suspenzija ubila.<br /></span>Damjanhttp://www.blogger.com/profile/02201297910253584960noreply@blogger.com0tag:blogger.com,1999:blog-8138694.post-35073332698132520472020-07-16T18:05:00.000+02:002020-07-16T18:05:41.935+02:00Modern SharePoint story<i>John, a complete beginner</i>: Wow, this is great... I can create a responsive, fancy list with shiny form and save the items there! Woohoo!<br /><br /><i>John, after two hours of experience</i>: This is nice... I can add more different columns! We should recreate our old Excel application here!<br /><br /><i>John, after five hours of experience</i>: Great, I can add another list and connect them both with lookup columns! It will be easy to recreate our old Excel file! Let's do it guys!<br /><br /><i>John, after one day of experience</i>: Hmm... these formulas work, but... it is strange that we cannot reference other rows in formulas... This was easy in Excel...<br /><br />John, after three days of experience: Ok, so this doesn't work, but ok, we don't need it anyway. But hey, we can change the color of our cells in the list! That's something!<br /><br />John, after five days of experience: Ok, so we have removed some features that we had in the old Excel file. That's not a problem. We can live without it. This <b>PowerApps</b> thing is promising... I can easily create forms. Let's recreate our VBA forms from the old Excel file here.<br /><br />John, after 8 days of experience: Ok, we can move the fields around, change colors, change fonts, we can connect to hundreds of external systems, we can even tweet from the form, but it looks like we cannot write this piece of business logic here... hmm.. there must be some way around...<br /><br />John, after 10 days of experience: Ok, I got this. We need an Azure subscription to be able to deploy our Azure Function that will contain the business logic code. We will then call it from the form... I’ll ping Steve to help with this...<br /><br />John, after 14 days of experience: Ok, I need to create an item in the third list after the value in this field changes from <i>A</i> to <i>B</i>. Ok, let's google this... Hmm... Stackoverflow says that there is some <b>Flow</b> thing that can be used here... Hmm... strange... they call it <b>Power Automate</b> on Microsoft's site... let's try it...<br /><br />John, after 20 days of experience: Ok, let's try something else! Oh, it looks like there is something called <b>SharePoint designer</b> and a thing called <b>SharePoint 2010 Workflow</b>... it has exactly what I need. This is easy. I'll just create a workflow, add this action and create an item in the third list. Easy peasy!<br /><br />John, after 20.2 days of experience: It would be good if I could send an email to Pete after an item is created in the third list. Let's try this Flow thing again, <b>they say it's modern, so it must be good</b>!<br /><br />John, after 21 days of experience: Hmm, this looks fine, but it always sends an email from my account... Why can't it send it as some generic user? Hmm, it looks like this can again be done with this <b>SharePoint designer workflow</b> thing. Let's use it. It rocks.<br /><br />John, after 21.5 days of experience: It would be good if we could create a task for Mike after an item is added to the list, so he can check it, set additional fields and approve it, just like in the old Excel file.<br /><br />John, after 21.8 days of experience: Wow, this <b>Planner</b> thing looks promising! It has fancy colors, fancy design... wow, look at these shiny charts! I'm impressed. It's like trello, just shinier! Let's use it. Ok, I'll finally be able to use <b>Flow</b>, after an item is created in the list I'll create a task in Planner for Mike and he will get an email! Great!<br /><br /><div>John, after 23 days of experience: Hmm, it looks like we can't have additional fields in Planner tasks... That's too bad... we were able to implement that in our old Excel file... But ok, we can live without it. Mike will just complete the task and we can later see some statistics in this fancy chart.</div><br /><p style="text-align: left;"><font size="5">Production starts</font></p>Users are adding items, changing items, sharing items...<br /><br />Items are automatically being added to another list...<br /><br />Pete receives emails whenever necessary...<br /><br />Mike gets his tasks...<br /><br /><b>Everyone is happy</b>...<br /><br /><br /><font size="5">Everything runs nicely, with sporadic, minor issues... until...</font><br /><br /><br /><b><i>John, on day x</i></b>: Hmm, it looks like there's some problem with this Flow thing... it doesn't run every time after an item is created... let's google this... oh, it looks like my plan has some limits... we need to buy a new subscription... <br /><br /><b><i>John, on day y</i></b>: Hmm, it looks like there's some problem with this list... some strange limit... let's google this... oh... the list contains more than 5000 items and that's the problem... but this worked fine in our old Excel file!!! Let’s call Steve, he will know how to fix this...<br /><br /><b><i>John, on day z</i></b>: Let's check some SharePoint related news... let's check which new, useful things will be available soon... Oh, what is this? <a href="https://support.microsoft.com/en-us/office/sharepoint-2010-workflow-retirement-1ca3fff8-9985-410a-85aa-8120f626965f" target="_blank">Microsoft will retire SharePoint workflows 2010</a>. Hmm, that must be something old... something that nobody is using anymore... not interesting... let's check some <b>Corona </b>news instead...<br /><br /><div><font size="5">And then... November comes...</font></div><div><b><br /></b></div><div><b>November 3rd 2020</b>: Hmm, it looks like the items are not added to the third list anymore... Why is this happening? This has always worked fine... Let’s call Steve, he will know how to fix this...</div><br /><b>November 9th 2020</b>: Hmm, it looks like Pete is not receiving the emails anymore... Why did this suddenly stop working? Let’s call Steve, he'll know how to fix this...<br /><br /><div><br /></div><div><font size="5">Conclusion</font></div><div><font size="5"></font></div><br /><div>All characters appearing in this story are fictional. Any resemblance to real persons is intentional!</div><div><br /></div><div>If you want to help Mike to be able to create additional fields in his planner tasks, you can <a href="https://planner.uservoice.com/forums/330525-microsoft-planner-feedback-forum/suggestions/14704662-option-to-create-custom-fields-for-tasks" target="_blank">vote here</a>.</div><br />If you want to help John not to be on Steve’s shooting list after November 1st, you can <a href="https://sharepoint.uservoice.com/forums/330318-sharepoint-administration/suggestions/40856713-postope-designer-2010-workflow-engine-retirement" target="_blank">vote here</a>. Fingers crossed!<br /><br />If you are the Steve in your company there’s not much help for you whatsoever... But, in case you need to prepare for November 1st, <a href="https://damjan.blogspot.com/2020/07/sharepoint-online-get-details-of-all-sp-designer-workflows-with-spcoder.html" target="_blank">check out this page</a>. It can help you to identify all the workflows created by Johns. <br /><br /><div>And Pete... he doesn’t need help... he just receives emails... <br /></div><div><br /></div><div></div><div></div><br />Damjanhttp://www.blogger.com/profile/02201297910253584960noreply@blogger.com0tag:blogger.com,1999:blog-8138694.post-70530348445894852842020-07-08T16:42:00.002+02:002020-07-22T17:49:40.977+02:00SharePoint online - Get details of all SP Designer workflows<div>Microsoft has recently <a href="https://support.microsoft.com/en-us/office/sharepoint-2010-workflow-retirement-1ca3fff8-9985-410a-85aa-8120f626965f">announced</a> that SharePoint 2010 Workflows will be retired soon in the SharePoint online.</div><div><br /></div><div>I personally think that the decision to remove a feature that is used a lot in production environments, with less than 4 months notice, during the corona crisis and summer vacations is totally not fair to the clients. Adding the fact that some of the functionalities SharePoint 2010 workflows offer don't have a proper replacement in the rest of MS services makes it unacceptable!</div><div><br /></div><div>There is a <a href="https://sharepoint.uservoice.com/forums/330318-sharepoint-administration/suggestions/40856713-postope-designer-2010-workflow-engine-retirement" target="_blank">UserVoice</a> where you can vote that Microsoft postpones this date, so hopefuly there's somebody there who will listen to the community and give our clients more time for this.<br /></div><div><br /></div><div>Either way, we will need to prepare for the change, whenever it comes, and first thing that we need to do is to identify all the SP Designer Workflows that exist in our environments. In this post I will show how this can be done using SPCoder application.</div><div><br /></div><div></div><div></div><div><font size="5">SPCoder</font><br /></div><div>First thing you need to do is get the latest version of SPCoder, either by downloading the <a href="https://spcoder.com/releases/SPCoder%20latest.zip">zip package</a> or getting the code from <a href="https://github.com/tomdam/spcoder">GitHub repo</a> and building it yourself. You can also check out the SPCoder <a href="https://github.com/tomdam/spcoder/wiki/Installation" target="_blank">installation page</a> for more details.<br /></div><div>Then, run the SPCoder.exe, connect to your SharePoint online site collection using "<a href="https://github.com/tomdam/spcoder/wiki/SharePoint-CSOM-connector" target="_blank">SharePoint CSOM connector</a>" and to the Github repo that contains the <a href="https://github.com/tomdam/spcoderscripts" target="_blank">SPCoder scripts</a> using "<a href="https://github.com/tomdam/spcoder/wiki/Github-connector" target="_blank">Github connector</a>". <br /></div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><img border="0" data-original-height="806" data-original-width="825" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhy6Xvx19Mc9cP7kalsyYEyq20Vvro1lSpdu9R4e5xFhk4-knke5Brd1sDyftefn5YHfIJZdPx3sBD-hsc5ARL7n4CJPLKOL0Fqn85sqkkq-9IrOHyAzSXHTyQNIgvXh7qr9o3R/d/grid3.PNG" /></div><div><br /></div><div>After that, expand the <i>Scripts\SharePoint\SPWorkflowsGetter.csx</i> file,
open its content in SPCoder and execute the code by pressing "Execute (F5)" button. <br /></div><div>With this we have registered the plugin for getting all workflows. If you want to register it automatically the next time SPCoder runs, you can save it in your SPCoder's <i>Scripts\CSharp\Plugins</i> directory.</div><div><br /></div><div>Next thing we need to do is find the site we want to check in SPCoder Explorer view, right click it and choose the "Get Workflows" option. <br /></div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><img border="0" data-original-height="806" data-original-width="826" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh67Rhsk71PoYn9sz-5hR0MNoXpqLoPjCtMzgU8pV2Jl7IgSnS8DEBI4-ZQ8tp8W0MvAgxdF4Lv9eVxXdCRjybsSrdpza-MW5EDbBTxPoBMvWEFy9DxiyyjZ0dRYfxJODUuqB2X/d/grid0.PNG" /></div><div><br /></div><div><br /></div><div>That option will activate the plugin, get all the workflows (both 2010 and 2013) from all the subsites and display the grid with the results afterwards.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><img border="0" data-original-height="808" data-original-width="824" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgP4Phe_qLWMJXggwYlX3sY6-pSG5-meEomP0pF7YvDHAzjucPOkzVL31tXDMPR7CYDozmtncX_jfEpfwRSwyvAsf2gUYiOJ_pFLtdOugSwKr91iuoMkRzL1_l2bPQGMQcTPMG2/d/grid4.PNG" /></div><div><br /></div><div><br /></div><div>The time needed for this operation to finish depends on the number of the subsites, lists, libraries and content types in your site collection, so if you have a lot of those, please wait until it finishes.</div><div><br /></div><div>As you can see from the screenshot above, the report will show you the site url, the list title in case the workflow is related to a list or library, the content type name in case the workflow is related to a content type and both <i>List</i> and <i>ContentType</i> columns will be empty in case of a reusable site workflow. There is also a <i>Type</i> column which tells you if the workflow is 2010 or 2013.</div><div><br /></div><div>This report should be a good starting point for you to plan the restructuring of your environment and finding a way to replace your workflows with some alternatives.<br /></div><div><br /></div>Damjanhttp://www.blogger.com/profile/02201297910253584960noreply@blogger.com1tag:blogger.com,1999:blog-8138694.post-300434203596591032020-07-02T21:50:00.001+02:002020-07-02T21:50:11.735+02:00Generate a maze in C#<div>There are many alghoritms for maze generation out there. The <a href="https://en.wikipedia.org/wiki/Maze_generation_algorithm">wikipedia page</a> gives some nice overview. Some people blogged a lot <a href="http://weblog.jamisbuck.org/2011/2/7/maze-generation-algorithm-recap.html">about mazes</a> and even <a href="https://pragprog.com/titles/jbmaze/">wrote a book</a> about maze generation!</div><div><br /></div><div>While playing with C# scripting in SPCoder, I implemented the recursive backtracking algorithm for maze generation in C#. SPCoder's log window is used to display the generated maze and it was a perfect fit for this task.</div><div><br /></div><div>There are two versions of the algorithm, one that generates a maze and displays it and the other one that stores all the maze versions during the generation and then displays animation where you can see the steps that algorithm took to generate the maze.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><img border="0" data-original-height="882" data-original-width="794" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkhxQzoTVZT6Gi8ElFsLzy3PiZ1hZinIKqj3kAYcMm2uzg7_IrplOL1DjSCX9EBbt95t8UPYrQFXXLzEQ3QDZ62NOzb2oQeS7zz08mxcB6udfk7Ge_N-a-XqigBaXatRfKuO16/d/basic.PNG" style="margin-left: auto; margin-right: auto;" /></td></tr><tr><td class="tr-caption" style="text-align: center;">SPCoder with Basic maze script loaded<br /></td></tr></tbody></table><div class="separator" style="clear: both; text-align: center;"></div><div><br /></div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><img border="0" data-original-height="258" data-original-width="262" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuUXJfgxz9H43kmmhBEn1fHEvNUQsr6lDhy4iDX-DVfl9FVsuuh7PDcVGsv9lEU6lvJRkc4c_uvcwU4Qg9rj6aSeKqIRMXftFGLinKByUxxJHCK5aKRFyjF9ZnGyYaId5ejBHa/d/maze+spcoder.gif" style="margin-left: auto; margin-right: auto;" /></td></tr><tr><td class="tr-caption" style="text-align: center;">SPCoder log window that displays animation <br />generated by Detailed maze example <br /></td></tr></tbody></table><div class="separator" style="clear: both; text-align: center;"></div><div><br /></div><div><br /></div><div>All you need to do to try it out is get the latest version of SPCoder, either by downloading the <a href="https://spcoder.com/releases/SPCoder%20latest.zip">zip package</a> or getting the code from <a href="https://github.com/tomdam/spcoder">GitHub repo</a> and building it yourself, run it, load maze examples and execute them.<br /></div><div><br /></div><div>Maze implementation is stored in separate <a href="https://github.com/tomdam/spcoderscripts">GitHub repo</a>, which will in future contain all new SPCoder scripts. You can use SPCoder <i>GitHub repo connector</i>, connect to <i>https://github.com/tomdam/spcoderscripts</i> and load the <i>BasicMaze.csx</i> and <i>DetailedMaze.csx</i> in SPCoder (as shown on first screenshot). Or you can copy the code directly to SPCoder code window from <a href="https://github.com/tomdam/spcoderscripts/blob/master/Scripts/Maze/BasicMaze.csx">BasicMaze.csx</a> and <a href="https://github.com/tomdam/spcoderscripts/blob/master/Scripts/Maze/DetailedMaze.csx">DetailedMaze.csx</a>.</div><br /><div><br /></div>Damjanhttp://www.blogger.com/profile/02201297910253584960noreply@blogger.com0tag:blogger.com,1999:blog-8138694.post-9071990179425867502020-06-30T22:00:00.001+02:002020-07-19T19:02:39.298+02:00Execute C# DotNetPerls examples in SPCoder<div>For years the website <a href="https://www.dotnetperls.com">www.dotnetperls.com</a> has been a very good resource for learning different programming languages. It is completely based on clearly described code examples and contains pages for different subjects (keywords, types, programming constructs, etc.) interlinked with other related topics.</div><div>The site is useful not only to beginners, but also to more experienced developers, because it contains examples with tips & tricks on many subjects, from basic to advanced ones. Personally, it saved me a lot of time with RegEx and DateTime formatting C# examples a couple of times.<br /></div><div><br /></div><div>Since <a href="https://github.com/tomdam/spcoder/wiki">SPCoder</a> is a tool for writing and executing the C# code, and it supports creation of custom connectors which can connect to any datasource, I've written the DotNetPerls C# connector, which parses the C# examples from the site and makes it easy to open (and execute) them in SPCoder.<br /></div><div><br /></div><div><font size="5">Connecting to DotNetPerls site</font><br /></div><div>All you need to do to try it out is get the latest version of SPCoder, either by downloading the <a href="https://spcoder.com/releases/SPCoder%20latest.zip">zip package</a> or getting the code from <a href="https://github.com/tomdam/spcoder">GitHub repo</a> and building it yourself, run it and make connection using DotNetPerls C# connector. After successful connection, the SPCoder's Explorer window will show the treeview with DotNetPerls C# pages and code examples. <br /></div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><img border="0" data-original-height="806" data-original-width="741" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1_GZjN29fm2fCfhGwiq3JnsfRm5CgEgFSp_0ilYhvXXiRvw-DcXar3-dlituDGvTBRqz1XuIw5OTpZCGpmVK5yi5Q-MCo6huTtzxAWyXyJV2UgEClw5eGsl2gahMXTZRNih34/d/explorerview1.PNG" /></div><div><br /></div><div><br /></div><div>You can then find the code example you want to see, right-click it and use the <i>View Code</i> menu item to open the source code in SPCoder's Code window.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><img border="0" data-original-height="799" data-original-width="868" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbpZRpB_wnNEuMDeaUTmqzPYW0bF4iM-uL4Q-xbxr7JiPjFh9iZbxC10GAgJC-DePdGtg20EOsPn7y69LPVr0ZDva569rUcvn5c6WSuR0k51UUF3NI1wH0OJvNy2gI7B_VUqd_/d/explorerview2.png" /></div><div><br /></div><div>Every code example in the treeview also has the <i>Visit page</i> menu item which will, if you use it, open the browser with exact page where the code is taken from. If you would use the same item as on the screenshot above, that page would be <a href="https://www.dotnetperls.com/enum">https://www.dotnetperls.com/enum</a><br /></div><div><br /></div><div><font size="5">Executing the code</font></div><div>Once you open the code in code window, you can execute it by either pressing <i>F5</i> on keyboard or clicking the <i>Execute (F5)</i> button.</div><div>If we execute the code sample from our previous screenshot, the output window will contain the result of a <i>Console.WriteLine()</i> call.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><img border="0" data-original-height="290" data-original-width="755" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEGRSO5gTR7AJe6dSKTiNWUyfrqBwXnJ1iDwMg09d95Fa57a3WL-rj_ivQH9RcVGs75XsWYhH5WQhLFQdaXYXHMXyoVM3AWE9ws18zSN7W-TR5GbRY5I_9l05cg69yhWL5g2qD/d/execution1.PNG" /></div><div><br /></div><div><font size="5">Using other SPCoder helper windows</font><br /></div><div></div><div>Additional benefit of executing the code examples in SPCoder instead of creating separate Console applications in Visual Studio is that you can execute the code part by part and even use other SPCoder windows to examine the variables and see what is really going on there. For instance, you can open the <i>C# program that uses DataTable </i>(located under <i>datatable</i> topic), and select and execute only the content of the GetTable() method (also selected on the screenshot below).</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><img border="0" data-original-height="806" data-original-width="888" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcxulbNG68lDVIC3v1zqegyHdCY0aA45j6-K6vyzq6ymDaagUQk6RSnf8xoSuvVlVBOkX0m3omtCDblEFoqG49i980e7JiF0REnE-okJXBtSq2xoYN2hR6BXbhYPTOpMQMKYiY/d/datatable1.PNG" /></div><div><br /></div><div>After that, you can use the GridViewer window to check the content of the <i>table</i> variable</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><img border="0" data-original-height="471" data-original-width="882" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXc2nk73P0-uTKkrqjuzZ3tmogTQ0GaK2khVoU1FMf6PK40jIao9rUuIG5LUJ02Evq9FWZCyDvdEIlU42AMooHFzDj9NOPl2xUTVPDl882IF12yR2EAW0mLVkj40T5h0K7VzCo/d/datatable2.PNG" /></div><div><br /></div><div><br /></div><div>You can also use SPCoder <a href="https://github.com/tomdam/spcoder/wiki/Describer">Describer</a> and <a href="https://github.com/tomdam/spcoder/wiki/Properties-viewer">Properties viewer</a> windows to further explore the variables and learn more about their data types.</div><div><br /></div><div><font size="5">Conclusion</font></div><div>In this blog post we saw how SPCoder can be used to connect to a custom datasource (web site) and how content from that datasource can be used with different parts of SPCoder. For the source code of the connector, check out the <a href="https://github.com/tomdam/spcoder/tree/master/src/SPCoder.DotNetPerls">GitHub repo</a>.<br /></div><div><br /></div><div><br /></div>Damjanhttp://www.blogger.com/profile/02201297910253584960noreply@blogger.com0tag:blogger.com,1999:blog-8138694.post-62503742353548576692020-06-24T16:55:00.000+02:002020-07-19T19:03:23.461+02:00Run C# script files in Azure WebJob<div>Azure WebJobs can be used to run background tasks (programs or scripts) in the cloud. You can write your code for WebJob in one of several available languages. <a href="https://docs.microsoft.com/en-us/azure/app-service/webjobs-create">Official documentation</a> states that the following file types are supported: <br /></div><ul style="text-align: left;"><li>.cmd, .bat, .exe (Windows cmd) </li><li>.ps1 (PowerShell) </li><li>.sh (Bash) </li><li>.php (PHP) </li><li>.py (Python) </li><li>.js (Node.js) </li><li>.jar (Java)</li></ul><div><br /></div><div><font size="5">C# support</font><br /></div><div>All .NET languages are supported and <b>C#</b> among them. If you write your code in C#, you can compile it to .exe <b>console application</b> and you will be able to run it as Azure WebJob.</div><div><br /></div><div>What is currently <b>not supported out of the box</b>, is running <b>C# script</b> (.csx) files as Azure WebJobs.</div><div><br /></div><div>While developing the <a href="https://github.com/tomdam/spcoder/wiki">SPCoder</a>, I needed a way to run C# script files as background tasks using Windows Task Scheduler, so I implemented <a href="https://github.com/tomdam/spcoder/wiki/RunnerApp">SPCoder.RunnerApp</a> helper console application, which can be configured to run one or more .csx files. The same application can also be deployed to Azure and run as WebJob.</div><div><br /></div><div>In this post I'll try to explain the steps on how SPCoder.RunnerApp can be deployed to Azure as WebJob and how you can later update just your script files, without the need for redeploying the SPCoder.RunnerApp application package again.</div><br /><div><font size="5">Getting the SPCoder.RunnerApp</font></div><div><font size="5"></font></div><div><br /></div><div>In order to get the SPCoder.RunnerApp you need to either <a href="https://spcoder.com/releases/SPCoder.RunnerApp%202.2.0.0.zip" target="_blank">download</a> the compiled package or clone the <a href="https://github.com/tomdam/spcoder/" target="_blank">github repo</a> and build the package yourself. <br /></div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><img border="0" data-original-height="272" data-original-width="875" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcvH-O167k3V0C4p9DtoX8SBs_bqAbkTimsXFopc3rucg9d5mwQ8s7DiBIl443DeUA-rqYDj52cmMRPuUKPaMX9gklxbaAyj_ON0MerVFGmnm2p3xqlHyNEZmH7IhyphenhyphenOYy1I6fA/d/github1resize.PNG" /></div><div><br /></div><div>After you get the application, you need to write your business logic code. SPCoder.RunnerApp by default runs the <b>Code\init.csx</b> file. If necessary, you can change that setting in SPCoder.RunnerApp.exe.config file, by changing the <b>CodePath</b> app setting:</div><div><pre style="text-align: left;"><add key="CodePath" value="Code\init.csx" /></pre></div><div></div><div></div><div>The recommended way of organizing your code is to use <b>init.csx</b> only for adding necessary assembly references using the <b>#r directive</b> and registering other csx files for execution. <br /></div><div></div><div><pre style="text-align: left;"><span class="pl-c">#r "System.Data"</span>
<span class="pl-c">#r "{{WorkingDirectory}}\mycustomlibrary.dll"<br />//...<br /><br />//....<br />//here you can prepare all the csx files that should be executed<br />string folder = @"Code\";<br />FilesRegisteredForExecution.Add(folder + "code.csx");<br /><br />//FilesRegisteredForExecution.Add(folder + "code1.csx");<br />//FilesRegisteredForExecution.Add(folder + "code2.csx");<br />//....<span> </span><br /></span></pre></div><div></div><div>In the code above you can see the <i>{{WorkingDirectory}}</i> placeholder, which will be replaced with current working directory before execution. <br /></div><div><br /></div><div>If you follow the recommendations, your business logic code should be located in file <b>Code\code.csx. </b>The default implementation has only one line which calls <b>Console.WriteLine();</b> method. There you can write your useful code. You can use any tool for writing the code. </div><div><br /></div><div><font size="5">Creating the WebJob</font></div><div></div><div><div></div><div>First, you need to register for an Azure account and
create an Azure AppService. I won't cover the steps for that here,
since there are a lot of resources available <a href="https://azure.microsoft.com/en-ca/services/app-service" target="_blank">online</a>. <br /></div><div><br /></div></div><div>After you write the code you can test it by simply running SPCoder.RunnerApp.exe application. When you're sure that the code is finished, you can pack the whole folder to zip file and upload it to Azure.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><img border="0" data-original-height="239" data-original-width="875" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcQB9sg3d_sZmCQUlu76dwRKHpH2ck5tnNEQnvkrVw9xXxN0P4fH-X0X83IV62YqAHagosndmovJ3c3r_Q9785ZZee-4RwDvVMS5pBjXmf17NtSfnrgF5LizwppmXNzb0qntem/d/webjob1resize.PNG" /></div><div><br /></div><div></div><div>For all the available options related to WebJobs please check the beforementioned <a href="https://docs.microsoft.com/en-us/azure/app-service/webjobs-create" target="_blank">official documentation</a>.</div><div><br /></div><div><font size="5">Running the WebJob</font></div><div><br /></div><div>After running the WebJob you can check its log for the details, and this is the sample log after running the default implementation:</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><img border="0" data-original-height="308" data-original-width="795" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjaXWeGplSTM1xznfMRauVPGJfABp2GycFRTiMB9ZJbEYfwdstag4BEbNFIr2WbTIMO66EWt1NshdnAf3oFAHtXrcNo_aRYVBY6VdvIvXAzr7sn9G17tKHa_kBcLyBVvOi2b3WY/d/github2.PNG" /></div><div><br /></div><div>You can notice the two INFO entries, which were caused by the Console.WriteLine calls in our .csx files.<br /></div><div><br /></div><div><font size="5">Updating the csx files</font><br /></div><div><br /></div><div>If you need to tweek your csx files after deployment, you can do it directly in the browser using the <a href="https://github.com/projectkudu/kudu" target="_blank">Kudu tools</a>. The page where you can do that is located here: <b>https://{YOURAPPSERVICENAME}.scm.azurewebsites.net/DebugConsole</b> <br /></div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><img border="0" data-original-height="665" data-original-width="875" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoG5695iXE9nyk-ctSISIYiMkuxAV4m7pCIGXAAz48q_Ny9YPAL39aDY6DflG-XxR6EQekUNLL4wKBQ3hUoqtxtiN_HousDm02HjEU3uMswsJdH3ncdpXScL0hJg83tg4zSO5b/d/kudu1resize.PNG" /></div><div><br /></div><div>After you click the Edit icon next to the file name, you will get the following screen, where you can paste your updated code.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><img border="0" data-original-height="266" data-original-width="756" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZShrtu-ofehcTEVHgY1KUROTM82Vqn2mNDtgBoW5qs5zxPmMsXq3hY1t-EsCgmgvgFZ68WwxibKLM-DRr5fv4ISLuUu6UGgbs_DlerRfp_4Aikjb6Ft8syX4zzn0YML8Qtq3Q/d/kudu2.PNG" /></div><div><br /></div><div>Immediately after you save the code it will become available for the next execution of your WebJob.<br /></div><div><br /></div><div><font size="5">Conclusion</font></div><div>This post explains how you can run your C# code writen in csx script files in the Azure cloud, without having to compile it to exe file. The same package can be run with Windows Task Scheduler on a windows machine outside of Azure.</div><div><br /></div><div>For more information on the SPCoder application, SPCoder.RunnerApp and other modules, please check the <a href="https://github.com/tomdam/spcoder/wiki" target="_blank">GitHub wiki documentation</a>.<br /></div><br />Damjanhttp://www.blogger.com/profile/02201297910253584960noreply@blogger.com0tag:blogger.com,1999:blog-8138694.post-89846415877645180312020-06-22T14:16:00.006+02:002020-07-19T19:03:39.893+02:00Corona can even spread to SharePoint - with C#<div style="text-align: left;" trbidi="on">I've used <a href="https://github.com/tomdam/spcoder/wiki/" target="_blank">SPCoder</a>, (the tool for writing and executing C# code, that I had developed) to import Coronavirus data to SharePoint.<br /></div><div style="text-align: left;" trbidi="on"><br /></div><div style="text-align: left;" trbidi="on">The idea is to get the data from the page that tracks the daily <a href="https://www.worldometers.info/coronavirus/#countries" target="_blank">Coronavirus</a> updates in the world:</div><div style="text-align: left;" trbidi="on"><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><img border="0" data-original-height="459" data-original-width="875" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwdItR2ziViRH8Oh1qMTbFWOIhD08nP0Ij7tsdIEOukVMu6xGi5f6VMKq4kjsGUIZa4K8YBOcmcKgqGGsPeD9-6S6H_UQnTjUh990ScF69SIm2kkBpLaqfUIVMHeWIufINL5mg/d/coronablog11.PNG" style="margin-left: auto; margin-right: auto;" title="Worldometer's COVID-19 data" /></td></tr><tr><td class="tr-caption" style="text-align: center;">Worldometer's COVID-19 data<br /></td></tr></tbody></table><div class="separator" style="clear: both; text-align: center;"></div><div style="text-align: left;" trbidi="on"><br /></div><div style="text-align: left;" trbidi="on"><br /></div><div style="text-align: left;" trbidi="on">Then, create a SharePoint list in SharePoint Online site and insert the coronavirus data to it:</div><div style="text-align: left;" trbidi="on"><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><img border="0" data-original-height="523" data-original-width="875" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnEPaJa3iuHK0hDf_k68vN1hhEaHXLW5HtYO0i9zV-5nqoO2W9weL0oDyEeedaLH6xHiso5Kad6_o8dylNq2Mdgvo8DPk4kCZ3bKBjgsky7duPBKrUhXxrkv4nMe7LMtUBHkIO/d/coronablogSP.PNG" style="margin-left: auto; margin-right: auto;" /></td></tr><tr><td class="tr-caption" style="text-align: center;">SharePoint list with data from Worldometer site<br /></td></tr></tbody></table><div class="separator" style="clear: both; text-align: center;"></div><div style="text-align: left;" trbidi="on"><br /></div><div style="text-align: left;" trbidi="on"><br /></div><div style="text-align: left;" trbidi="on">And finally, create a SharePoint modern page, insert a chart webpart to it and show a pie-chart with coronavirus data:</div><div style="text-align: left;" trbidi="on"><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><img border="0" data-original-height="454" data-original-width="875" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipRnmYkGdH3gdjAmYkyusvVuzt8NB-2mzll_stgqtL0tEzDBpu6zHZljD2QhQktE-_PQqCZz9gwulFf5GTPpTnQkiWCNuiEFUXE1iXY6jjygghPkxX4miMeDWm41raYx6jWVyo/d/coronablog3.PNG" style="margin-left: auto; margin-right: auto;" /></td></tr><tr><td class="tr-caption" style="text-align: center;">SharePoint modern page - pie chart with coronavirus data<br /></td></tr></tbody></table><div class="separator" style="clear: both; text-align: center;"></div><div style="text-align: left;" trbidi="on"><br /></div><div style="text-align: left;" trbidi="on"><br /></div><div style="text-align: left;" trbidi="on">The code and explanation are also available on <a href="https://github.com/tomdam/spcoder/wiki/Corona-example" target="_blank">GitHub</a> wiki page. <br /></div><div style="text-align: left;" trbidi="on"></div><div style="text-align: left;" trbidi="on"></div><div style="text-align: left;" trbidi="on"><pre style="text-align: left;">//Get the data using "Web page" connector<br />main.Connect("https://www.worldometers.info/coronavirus/#countries", "Web page"); <br /><br />//Get the table of latest infromation about the corona virus in the different countries<br />//------------<br /><br />//here we use the HtmlAgilityPack project (https://html-agility-pack.net/) <br />//to scrape the page and get the html table element that contains the data<br /><br />var htmlnode = htmldocument.DocumentNode;<br />var htmltable = htmlnode.SelectSingleNode("//table[@id='main_table_countries_today']");<br />var thnodes = htmltable.SelectSingleNode("thead").SelectNodes(".//th");<br /><br />var tbodyNode = htmltable.SelectSingleNode("(tbody)[1]");<br />var nodes = tbodyNode.SelectNodes("tr[not(contains(@class,'row_continent'))]");<br /><br />//create the DataTable object<br />var table = new DataTable("Corona");<br /><br />var headers = thnodes.Select(th => th.InnerText.Trim()<br /><span> </span>.Replace("\n","")<br /><span> </span>.Replace(","," ")<br /><span> </span>.Replace("&nbsp;"," ")<br /><span> </span>.Replace("/"," per "))<br />.ToList();<br /><br />//create the columns in DataTable<br />foreach (var header in headers)<br />{<br /> table.Columns.Add(header);<br />}<br /><br />//get the rows from html table and clean some of the values<br />var rows = nodes.Skip(1).Select(tr => tr<br /> .Elements("td")<br /> .Select(td => td.InnerText.Trim()<br /><span> <span> </span><span> </span><span> </span><span> </span></span>.Replace(",","")<br /><span> <span> </span><span> </span><span> </span><span> </span></span>.Replace("N/A","")<br /><span> <span> </span><span> </span><span> </span><span> </span></span>.Replace("+",""))<br /> .ToArray());<br /><br />//add the rows to the DataTable <br />foreach (var row in rows)<br />{<br /> table.Rows.Add(row);<br />}<br />//------------<br /></pre></div><div style="text-align: left;">After executing the previous part of the code, the coronavirus details are scraped from the web page and stored in DataTable variable called "table". You can open GridViewer in SPCoder, and inspect the variable:</div><div style="text-align: left;"><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><img border="0" data-original-height="600" data-original-width="875" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLF7WGwKJYFwCgu2nWkVm0jBwDh1yUZgKkn3ERv44RqV0BWYxO9GWwvmKKOZGGDuRe-24N7oi4C-qzO2FXWbzjCw7eiJbgy9oQbFzec62JNlpSeyHYq5Pw2kWQZXZvPgyxYpYF/d/coronablog5.PNG" style="margin-left: auto; margin-right: auto;" /></td></tr><tr><td class="tr-caption" style="text-align: center;">SPCoder GridView window showing the scraped data<br /></td></tr></tbody></table><div class="separator" style="clear: both; text-align: center;"></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"></div><div style="text-align: left;">Now we can create the SharePoint list, add the appropriate fields to it, and insert the data from DataTable to the list.</div><div style="text-align: left;"><br /></div><pre style="text-align: left;">//------------<br />//first we need to connect to the SharePoint online site<br />//Here I use the main.Connect method, but you could also use the SPCoder's Explorer window for this.<br />//The third and the fourth parameters of the main.Connect method are username and password. <br />//It is possible to write those values in clear text, but here I use the SPCoder's encryption mechanism.<br />//ENCRYPTEDUSERNAME and ENCRYPTEDPASSWORD have been created using "Crypto helper" window</pre><pre style="text-align: left;">string myUsername = main.Decrypt("ENCRYPTEDUSERNAME");<br />string myPassword = main.Decrypt("ENCRYPTEDPASSWORD");</pre><pre style="text-align: left;">main.Connect("https://MYtenant.sharepoint.com/sites/SPCodertest/", <br />"SharePoint Client O365", myUsername, myPassword);</pre><pre style="text-align: left;">//after this we are connected to the SP Online site and have the context (ClientContext) variable available <br />//you will also notice that the site has appeared in the Explorer window and you can <br />//expand its subsites and see all the lists and libraries</pre><pre style="text-align: left;">//prepare the code for creating lists (you can also open the Utils.csx file in <br />//SPCoder and execute it instead of the following line)<br />execFile("Scripts\\CSharp\\SharePoint\\Utils.csx"); </pre><pre style="text-align: left;">//Here we prepare the fields of the list<br />List<SPCoderField> myFields = new List<SPCoderField> (); </pre><pre style="text-align: left;">for(int i = 0; i < headers.Count; i++)<br />{<br /> var header = headers[i];<br /> string fieldType = "Number";<br /> if (header == "Country Other" || header == "Continent") fieldType = "String";<br /> if (header =="#") continue;<br /> myFields.Add(new SPCoderField {<br /> Name = header.Replace(" ",""), <br /> DisplayName = header.Replace(" ",""), <br /> Type = fieldType, <br /> Group = "Corona", <br /> Values = null}<br /> );<br />} <br />//here we create the SharePoint list called Corona.<br />var list = CreateListWithFields(myFields, web, "Corona", context);<br />//------------</pre><div style="text-align: left;"></div><div style="text-align: left;"><br /></div><div style="text-align: left;">After this step you can open your SharePoint site in browser and check if the list is created. If everything went fine, you should see the empty list called Corona, with the columns that you were also able to see in GridViewer. Names of some of the columns have been changed due to the rules that SharePoint field names must follow, related to special characters.<br /></div><div><br /></div><div>The next step is to insert the data to the list. Here we use standard CSOM code for adding the items to the SharePoint list. We use some batching in order to speed up the process (pushing the data to server after every 50 newly added items):<br /></div><div></div><div style="text-align: left;"><pre>//------------<br />//add the data to the list<br />//System.Data.DataRow row = table.Rows[0];<br />int cnt = 0;<br />foreach(System.Data.DataRow row in table.Rows)<br />{<br /> ListItemCreationInformation itemCreateInfo = new ListItemCreationInformation();<br /> ListItem oListItem = list.AddItem(itemCreateInfo); <br /> oListItem["Title"] = row["Country Other"].ToString();<br /> for(int i = 0; i < headers.Count; i++)<br /> {<br /> var header = headers[i];<br /> if (!String.IsNullOrEmpty(row[header].ToString()))<br /> {<br /> if (header =="#") continue;<br /> string internalName = list.Fields<br /><span> </span><span> </span><span> </span><span> </span><span> </span><span> </span><span> </span><span> </span><span> </span><span> </span>.Where(m => m.Title == header.Replace(" ",""))<br /><span> </span><span> </span><span> </span><span> </span><span> </span> <span> </span><span> </span><span> </span><span> </span>.FirstOrDefault()<br /><span> </span><span> </span><span> </span><span> </span><span> </span><span> </span><span> </span><span> </span><span> </span> .InternalName;<br /> if (header != "Country Other" && header != "Continent")<br /> {<br /> double num = Double.Parse(row[header].ToString());<br /> oListItem[internalName] = num; <br /> }<br /> else<br /> {<br /> oListItem[internalName] = row[header].ToString(); <br /> }<br /> }<br /> }<br /> oListItem.Update();<br />//we are sending the data to the server after every 50 items for performance reasons<br /> if (++cnt % 50 == 0) context.ExecuteQuery();<br />} <br />context.ExecuteQuery();<br />//------------</pre></div><div style="text-align: left;"></div><div style="text-align: left;">After the code above is executed, we should be able to see the data in the SharePoint list.<br /><br />Now, the next step is to create the modern page, add the pie-chart to it and show the Total cases per country on the chart.<br />This part of the code uses <a href="https://github.com/pnp/PnP" target="_blank">OfficeDevPnP CSOM library</a> for easier handling of the modern pages. The library is included in SPCoder, so you don't have to download it yourself.</div><div style="text-align: left;"></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><pre>using OfficeDevPnP.Core.Pages;<br /><br />// get a list of possible client side web parts that can be added<br />ClientSidePage p = new ClientSidePage(context);<br />var components = p.AvailableClientSideComponents();<br /><br />var myWebPart = components.Where(s => s.ComponentType == 1 && s.Manifest.Contains("QuickChartWebPart"))<br /><span> </span><span> </span><span> </span><span> </span><span> </span><span> </span> .FirstOrDefault();<br />CanvasSection cs = new CanvasSection(p, CanvasSectionTemplate.OneColumn, 5);<br />p.AddSection(cs);<br /><br />ClientSideWebPart helloWp = new ClientSideWebPart(myWebPart) { Order = 10 };<br />helloWp.PropertiesJson = <br />@"{'data':[{'id':'7CFFD4B0-436E-430D-94C5-A4F9D22DB3FE','label':'','value':'','valueNumber':0}],'type':1,<br />'isInitialState':false,'dataSourceType':1,'listItemOrderBy':0,<br />'selectedListId':'" + list.Id.ToString() + "','selectedLabelFieldName':'Title',<br />'selectedValueFieldName':'TotalCases','xAxisLabel':'','yAxisLabel':''}";<br /><br />p.AddControl(helloWp, cs.Columns[0]);<br />//This will save the page to SitePages library<br />p.PageTitle = "Corona Stats page 2";<br />p.LayoutType = ClientSidePageLayoutType.Article;<br />p.Save("CoronaStats2.aspx");</pre></div><div style="text-align: left;">After this code runs, you should check the SitePages library on your SharePoint online site and look for the CoronaStats2.aspx page. If everything worked fine the page should contain the pie chart with the total corona cases per country.</div><br />Damjanhttp://www.blogger.com/profile/02201297910253584960noreply@blogger.com0tag:blogger.com,1999:blog-8138694.post-74922229473159553062020-04-19T19:48:00.000+02:002020-04-19T23:50:32.260+02:00SharePoint online - use classic rich text editor in spfx webpart<div dir="ltr" style="text-align: left;" trbidi="on">
<br />
In one of my recent projects for SharePoint online, the client wanted to use the classic rich text editor in the SharePoint Framework (<a href="https://docs.microsoft.com/en-us/sharepoint/dev/spfx/sharepoint-framework-overview" target="_blank">spfx</a>) webpart. The one that is available by default when you edit a Multiple lines of text field in classic SharePoint experience.<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg14CO7uHNuPYiGeIM1buo5bCKl25cgzKKP200md5MF8vnYg-ZxwIeQPtbfrWzZfRPZ02742ne-eRd7CPNIw-AGRiGGuxsmQnkvr1T_XnWPYz450LMpBMJtz7XtUnO-AHJ1czCE/" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="450" data-original-width="936" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg14CO7uHNuPYiGeIM1buo5bCKl25cgzKKP200md5MF8vnYg-ZxwIeQPtbfrWzZfRPZ02742ne-eRd7CPNIw-AGRiGGuxsmQnkvr1T_XnWPYz450LMpBMJtz7XtUnO-AHJ1czCE/" /></a></div>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhf_E-8SmzWD9JWkz-PaMaWWAZU9J_EJaxu8dSL9seeX95pMiXDzQJZQ42CuzhQbR3XndmsK0XXsv6b3-DBLgI-okLovwi1BWSs0ZUg0y0t5XvpuPZhm7O38bDDqoR41GSwjkX3/" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"></a><br />
<div dir="ltr" style="text-align: left;" trbidi="on">
<br /></div>
<div dir="ltr" style="text-align: left;" trbidi="on">
The reason for this was the fact they knew how to use it from previous experience in SharePoint on-premise and the fact that all available rich text editors for modern pages had (and still do have!) many limitations.<br />
<br />
I did some research and found out that Microsoft also uses the classic editor when editing the content of the rich text fields in modern list experience (details pane).</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJbc-saXatAQEtgRnNFOQHz5yTXCMfJVQIqlKowNdlBiyLWvJxrPYb9QIT_Yym-WNVppP-ZCAouH4ao4ntZk6QUZuS1L-Zw34gtdi99j3_MGjlOmAQWNkhrGmcmNMmbiOTPp6G/" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="586" data-original-width="447" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJbc-saXatAQEtgRnNFOQHz5yTXCMfJVQIqlKowNdlBiyLWvJxrPYb9QIT_Yym-WNVppP-ZCAouH4ao4ntZk6QUZuS1L-Zw34gtdi99j3_MGjlOmAQWNkhrGmcmNMmbiOTPp6G/" /></a></div>
<div dir="ltr" style="text-align: left;" trbidi="on">
<br /></div>
<div dir="ltr" style="text-align: left;" trbidi="on">
So I've implemented similar functionality in my webpart, which can be used on any modern or classic page for editing rich text/html. </div>
<div dir="ltr" style="text-align: left;" trbidi="on">
<br />
The implemented DEMO webpart looks like this:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgevFiOKuI71To7ue1pz3OdmTC_tOM9cAJj2xJFlXFVt-XT5GvpAYu3fmaeoXQ2V9Uwgk4r9jyNkWLN9U2b8Kxfn3NY0EF3NK9O5QMw3M1hM1su4mPTGW_-hO7gblNZQg3Lq-EJ/" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="339" data-original-width="711" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgevFiOKuI71To7ue1pz3OdmTC_tOM9cAJj2xJFlXFVt-XT5GvpAYu3fmaeoXQ2V9Uwgk4r9jyNkWLN9U2b8Kxfn3NY0EF3NK9O5QMw3M1hM1su4mPTGW_-hO7gblNZQg3Lq-EJ/" /></a></div>
<div dir="ltr" style="text-align: left;" trbidi="on">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
After clicking on the "Edit html" button, the dialog box with the editor, containing the html code opens:</div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<img border="0" data-original-height="614" data-original-width="1347" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0Nz4Gf3lRLmUzaOiYXtLiBWKl029xVI8-mdE-QbvkPwqcQ74gPayfRXOP_QLeEkidf0UcCR-e1oI4lBJmna-o0_WGvDXsBe9d-lms7laMblJS7FOY6XOmJ-HaiM0AFwideSmV/" /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-_Ec4-N1BPI5RxD7_WFRlALpj_wfRBTfHuhHw7H-I9McMGQQQTluSmTY9Wh0xTdDKN1nYKUAzg1wEDHadpNeRHd6U0ya-bliGfy1VzuI68P9IgXPX8TbEyPXL9JLzSFIGi0Ed/" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div dir="ltr" style="text-align: left;" trbidi="on">
<h3 style="text-align: left;">
</h3>
<div>
You can also edit the html directly if you click the "Edit Source" button:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgc-ay1kRMR8CTtJAEX3p57pvd7VUM9rwiwoPhqv-2ztsOWmFQL8e3k8WNSgb14HQu9XEX7FDH632A7J4z_fb_n4ssvzhE2bnAvPFIF-gB6HWF8js8gO7eE7tZJsEUgJCkksc6V/" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="863" data-original-width="962" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgc-ay1kRMR8CTtJAEX3p57pvd7VUM9rwiwoPhqv-2ztsOWmFQL8e3k8WNSgb14HQu9XEX7FDH632A7J4z_fb_n4ssvzhE2bnAvPFIF-gB6HWF8js8gO7eE7tZJsEUgJCkksc6V/" /></a></div>
<div>
<br /></div>
<h3 style="text-align: left;">
Implementation</h3>
In short, my approach was to:<br />
<ul style="text-align: left;">
<li>use the out of the box NewForm.aspx page in dialog box, with only one rich text field; </li>
<li>fill the field with some initial html using client side code;</li>
<li>allow the user to edit the html and click the Save button;</li>
<li>get the produced html into a javascript variable, prevent default saving and close the dialog box.</li>
</ul>
After that you can do anything with the produced html (save it in a list or library field, send it to some third-party API, save it as a property of a webpart...). My DEMO webpart saves the edited html in it's property variable.</div>
<div dir="ltr" style="text-align: left;" trbidi="on">
<h3 style="text-align: left;">
Setup</h3>
</div>
<div dir="ltr" style="text-align: left;" trbidi="on">
In order for the webpart to work, you need to provide it with the title of a list and a name of the Multiple lines of text column.You can, of course, use any existing list for this, but I created a separate list (called TmpTextEditHelper), added one Multiple lines of text column (called HtmlField) and selected Enhanced rich text (Rich text with pictures, tables, and hyperlinks) as type of text. The important thing to mention is that, webpart will NEVER save anything to this list. </div>
<div dir="ltr" style="text-align: left;" trbidi="on">
<br /></div>
<div dir="ltr" style="text-align: left;" trbidi="on">
</div>
<div dir="ltr" style="text-align: left;" trbidi="on">
The NewForm page can be opened with the following link:<br />
https://YOURTENANT.sharepoint.com/sites/YOURSITE/Lists/TmpTextEditHelper/NewForm.aspx?<b>OnlyIncludeOneField</b>=HtmlField&<b>ClientFormOverwriteSave</b>=1&<b>isdlg</b>=1</div>
<div dir="ltr" style="text-align: left;" trbidi="on">
</div>
<ul style="text-align: left;">
<li>TmpTextEditHelper - The name of the list.</li>
<li>OnlyIncludeOneField=HtmlField - Tells the server side code to render only one field (HtmlField) and exclude other fields like Title.</li>
<li>ClientFormOverwriteSave=1 - Microsoft uses this parameter also when opening the form in modern list edit pane. It probably tells the server side code not to save the form in case it is submitted, but I wasn't able to find any documentation on it.</li>
<li>isdlg=1 - Tells the server side code that the form will be viewed inside the dialog, so it does not render any unnecessary things, like navigation</li>
</ul>
<div dir="ltr" style="text-align: left;" trbidi="on">
<h3 style="text-align: left;">
Installation</h3>
</div>
<div dir="ltr" style="text-align: left;" trbidi="on">
After the webpart is added to AppCatalog, set as trusted and added to the SharePoint site, it can be added to any page. After adding to the page, you need to edit it's properties and set the name of the list and the field to be used, as described above.</div>
<div dir="ltr" style="text-align: left;" trbidi="on">
<br /></div>
<div dir="ltr" style="text-align: left;" trbidi="on">
You can find the source code for the webpart <a href="https://github.com/tomdam/classic-rte-in-spfx" target="_blank">here</a>. </div>
<div dir="ltr" style="text-align: left;" trbidi="on">
<br /></div>
<div dir="ltr" style="text-align: left;" trbidi="on">
<br /></div>
</div>
Damjanhttp://www.blogger.com/profile/02201297910253584960noreply@blogger.com3tag:blogger.com,1999:blog-8138694.post-24998226134383062042014-01-09T23:18:00.002+01:002020-07-17T17:46:38.281+02:00SPCoder<div dir="ltr" style="text-align: left;" trbidi="on">
<h2 style="text-align: left;">
</h2>
Update 2020: This post is related to the old, IronPython version of SPCoder. For the new, C# version of SPCoder, please check <a href="https://github.com/tomdam/spcoder/wiki">this link</a>.<br /></div><div dir="ltr" style="text-align: left;" trbidi="on"> <br />
I've been developing code for MS SharePoint since year 2004. SharePoint was evolving with every new version since then, but all the versions had one thing in common and that is the lack of an "easy" way to manipulate its object model. By "easy" here I mean visual, fast, scriptable. There is no tool where developer/admin could write some code in a visual environment directly on server, using full SharePoint object model, execute it, share it, etc.<br />
<br />
There are a lot of very useful blogs out there where SP developers share a solution to a problem solved by writing simple console application which usually has 5-10 lines of code in which they call a couple of methods from SP object model. I also wrote a number of console apps like that in different situations and it was always taking me a lot of time.. firing up a dev machine with visual studio, writing code/compiling/testing on dev machine, publishing app to server and executing. Of course if you have to change anything you have to go through the procedure of write/compile/test/publish/execute again...<br />
<br />
Ok, you certainly get my point by now :) So, my solution to this problem was to write a tool which you can use to interactively work with SharePoint's object model. It is called SPCoder, it is free to use and you can download it from <a href="https://spcoder.codeplex.com/">https://spcoder.codeplex.com</a>.<br />
<br />
<h3 style="text-align: left;">
About</h3>
SPCoder uses IronPython for accessing SharePoint's object model. It works on server and foundation versions of SharePoint 2007, 2010 and 2013. Here is the screenshot of SPCoder in action:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpC4nIx-mrmfMdqiWez8NgYUL61vibqE-oGFTymfL69EUzEq7DAtkc8aGbM39V4KgfvVOqnpBRAFEpKuE2OpVCfwUCajwakwo3Cc50U1HGyBcNtKohnUQdZnS86KLkekbWhfnC/s1600/spcoder3.png" width="640" /></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
You don't have to have any previous knowledge of IronPython in order to use SPCoder. In fact SPCoder has very useful features which you can use without any coding (Describer and Property viewer). The documentation of all the SPCoder's features can be found here: <a href="https://spcoder.codeplex.com/documentation">https://spcoder.codeplex.com/documentation</a><br />
<br />
Here are a couple examples on what can be done with SPCoder: <br />
<pre><style type="text/css">
.fctbNone{ color:#000000; }
.fctbStyle0{ color:#008000;font-style:oblique; }
.fctbStyle0Style1{ color:#008000;font-style:oblique;color:#0000ff; }
.fctbStyle2{ color:#ff0000; }
.fctbStyle3{ color:#a52a2a; }
.fctbStyle2Style3{ color:#ff0000;color:#a52a2a; }
.fctbStyle1{ color:#0000ff; }
.fctbStyle6{ color:#2b91af; }
</style>
<span style="font-family: "courier new", monospace; font-size: 9.75pt; line-height: 14px;"><span class="fctbStyle0">#download all files from the SP </span><span class="fctbStyle0">library to a local folder</span><span class="fctbNone">
local </span><span class="fctbStyle2">=</span><span class="fctbNone"> </span><span class="fctbStyle3">"C</span><span class="fctbStyle2Style3">:</span><span class="fctbStyle3">\Temp\Imgs"</span><span class="fctbStyle1">
for</span><span class="fctbNone"> i </span><span class="fctbStyle1">in</span><span class="fctbNone"> list</span><span class="fctbStyle6">.</span><span class="fctbNone">Items</span><span class="fctbStyle2">:</span><span class="fctbNone">
f </span><span class="fctbStyle2">=</span><span class="fctbNone"> i</span><span class="fctbStyle6">.</span><span class="fctbNone">File
binary </span><span class="fctbStyle2">=</span><span class="fctbNone"> f</span><span class="fctbStyle6">.</span><span class="fctbNone">OpenBinary</span><span class="fctbStyle2">()</span><span class="fctbNone">
stream </span><span class="fctbStyle2">=</span><span class="fctbNone"> IO</span><span class="fctbStyle6">.</span><span class="fctbNone">FileStream</span><span class="fctbStyle2">(</span><span class="fctbNone">local </span><span class="fctbStyle2">+</span><span class="fctbNone"> </span><span class="fctbStyle3">"</span><span class="fctbStyle2Style3">/</span><span class="fctbStyle3">"</span><span class="fctbNone"> </span><span class="fctbStyle2">+</span><span class="fctbNone"> f</span><span class="fctbStyle6">.</span><span class="fctbNone">Name</span><span class="fctbStyle6">, </span><span class="fctbNone">IO</span><span class="fctbStyle6">.</span><span class="fctbNone">FileMode</span><span class="fctbStyle6">.</span><span class="fctbNone">Create</span><span class="fctbStyle2">)</span><span class="fctbNone">
writer </span><span class="fctbStyle2">=</span><span class="fctbNone"> IO</span><span class="fctbStyle6">.</span><span class="fctbNone">BinaryWriter</span><span class="fctbStyle2">(</span><span class="fctbNone">stream</span><span class="fctbStyle2">)</span><span class="fctbNone">
writer</span><span class="fctbStyle6">.</span><span class="fctbNone">Write</span><span class="fctbStyle2">(</span><span class="fctbNone">binary</span><span class="fctbStyle2">)</span><span class="fctbNone">
writer</span><span class="fctbStyle6">.</span><span class="fctbNone">Close</span><span class="fctbStyle2">()</span></span>
</pre>
--<br />
<pre><style type="text/css">
.fctbNone{ color:#000000; }
.fctbStyle0{ color:#008000;font-style:oblique; }
.fctbStyle0Style6{ color:#008000;font-style:oblique;color:#2b91af; }
.fctbStyle0Style1{ color:#008000;font-style:oblique;color:#0000ff; }
.fctbStyle2{ color:#ff0000; }
.fctbStyle6{ color:#2b91af; }
.fctbStyle3{ color:#a52a2a; }
.fctbStyle1{ color:#0000ff; }
</style>
<span style="font-family: "courier new", monospace; font-size: 9.75pt; line-height: 14px;"><span class="fctbStyle0">#add users to a security group</span><span class="fctbStyle0Style6"> if </span><span class="fctbStyle0">usernames are located </span><span class="fctbStyle0Style1">in</span><span class="fctbStyle0"> the SP list </span><span class="fctbNone">
gr </span><span class="fctbStyle2">=</span><span class="fctbNone"> web</span><span class="fctbStyle6">.</span><span class="fctbNone">SiteGroups[</span><span class="fctbStyle3">"group_name"</span><span class="fctbNone">]</span><span class="fctbStyle1">
for</span><span class="fctbNone"> l </span><span class="fctbStyle1">in</span><span class="fctbNone"> list</span></span><span style="font-family: "courier new", monospace; font-size: 9.75pt; line-height: 14px;"><span class="fctbNone"><span style="font-family: "courier new", monospace; font-size: 9.75pt; line-height: 14px;"><span class="fctbStyle6">1</span></span></span><span class="fctbStyle6">.</span><span class="fctbNone">Items</span><span class="fctbStyle2">:</span><span class="fctbNone">
u </span><span class="fctbStyle2">=</span><span class="fctbNone"> web</span><span class="fctbStyle6">.</span><span class="fctbNone">EnsureUser</span><span class="fctbStyle2">(</span><span class="fctbNone">l[</span><span class="fctbStyle3">"UserId"</span><span class="fctbNone">]</span><span class="fctbStyle2">)</span><span class="fctbNone">
gr</span><span class="fctbStyle6">.</span><span class="fctbNone">AddUser</span><span class="fctbStyle2">(</span><span class="fctbNone">u</span><span class="fctbStyle2">)</span></span>
</pre>
--<br />
<pre><style type="text/css">
.fctbNone{ color:#000000; }
.fctbStyle0{ color:#008000;font-style:oblique; }
.fctbStyle0Style1{ color:#008000;font-style:oblique;color:#0000ff; }
.fctbStyle2{ color:#ff0000; }
.fctbStyle6{ color:#2b91af; }
.fctbStyle3{ color:#a52a2a; }
.fctbStyle1{ color:#0000ff; }
</style>
<span style="font-family: "courier new", monospace; font-size: 9.75pt; line-height: 14px;"><span class="fctbStyle0">#change the content type </span><span class="fctbStyle0Style1">for</span><span class="fctbStyle0"> all items </span><span class="fctbStyle0Style1">in</span><span class="fctbStyle0"> the list</span><span class="fctbNone">
ct </span><span class="fctbStyle2">=</span><span class="fctbNone"> list</span><span class="fctbStyle6">2.</span><span class="fctbNone">ContentTypes[</span><span class="fctbStyle3">"ct_name"</span><span class="fctbNone">]</span><span class="fctbStyle1">
for</span><span class="fctbNone"> i </span><span class="fctbStyle1">in</span><span class="fctbNone"> list</span><span class="fctbStyle6">2.</span><span class="fctbNone">Items</span><span class="fctbStyle2">:</span><span class="fctbNone">
i[</span><span class="fctbStyle3">"Content Type"</span><span class="fctbNone">] </span><span class="fctbStyle2">=</span><span class="fctbNone"> ct</span><span class="fctbStyle6">.</span><span class="fctbNone">Name;
i[</span><span class="fctbStyle3">"Content Type ID"</span><span class="fctbNone">] </span><span class="fctbStyle2">=</span><span class="fctbNone"> ct</span><span class="fctbStyle6">.</span><span class="fctbNone">Id</span><span class="fctbStyle6">.</span><span class="fctbNone">ToString</span><span class="fctbStyle2">()</span><span class="fctbNone">;
i</span><span class="fctbStyle6">.</span><span class="fctbNone">SystemUpdate</span><span class="fctbStyle2">()</span></span>
</pre>
--<br />
<br />
The important thing to note here is that these example scripts will work on any SP server, you just need to drag'n'drop appropriate objects to SPCoder context window and name them like <i>web, </i><i><i>list, </i>list1 and list2</i> (which SPCoder does by default for these types of objects).<br />
<br />
If you want to try SPCoder, please first take a look at this <a href="https://spcoder.codeplex.com/wikipage?title=Quick%20Tutorial" target="_blank">Quick Tutorial</a>. It will take you just a few minutes but it will give you all you need for start.</div>
Damjanhttp://www.blogger.com/profile/02201297910253584960noreply@blogger.com1tag:blogger.com,1999:blog-8138694.post-70039810098449584792007-07-30T03:32:00.000+02:002007-07-30T03:30:32.181+02:00Dynamic generation of javascript codeOne of the biggest problems for every developer/software architect is the level of generalization/specialization of their code.<br />If the code is more coupled to the problem , it solves the problem better, but when the problem changes it is harder to change the code. And the oposite .. when the code is more abstract it is easier to change it, but then (generally speaking) the solution is not so good (as it could be in first case).<br /><br />I am not going to write about the right level of abstraction, because I don’t know it :)<br />Instead, I am going to write about javascript and some of its interesting features.<br /><br />Assume that we have the following problem:<br /><span style="font-style: italic;">We need to write a function that calculates the sum of first n integers.</span><br />The first solution that would probably come to everyone’s mind would be the classic for loop that loops from 1 to n and adds value of the counter to some variable..<br />It could be written as:<br /><pre><br />function getSum1(n)<br />{<br />var s = 0;<br />for(var i = 1; i <= n; i++)<br />s += i;<br />return s;<br />} </pre><br /><br />This is, of course, totally correct solution, but is it the best one?<br />Well, it solves the problem.. and is flexible enough to calculate the sum for every given integer.. so, it probably is the best.<br />But!<br />What if we know the number that will be passed to the function before it is called?<br />If so, it would be better if we wrote the function like this:<br /><pre><br />function getSum2()<br />{<br /> return 1+2+3+4+5+6+7+8+9+10; <br />}<br /></pre><br />It would be much faster than the first one.<br />But, as the beginning of this post says.. it is not flexible. It solves the problem, but it is very coupled to it, so when the problem changes (the number n) it is useless.<br />We have 2 solutions. Which is better? Well, it depends on the fact how many times is function going to be called with the same parameter, and the importance of the execution speed.<br /><br />I wrote some tests that can be found <a href="http://tomdam.googlepages.com/javascript_test.html">here</a>.<br />There are functions that calculate sum of the first 100 integers, and are called 100000 times.<br /><br />First one calls the function with the <b>classic for loop</b> (getSum1):<br /><pre><br />var howManyTimes = 100000;<br />timeStart = new Date();<br />for (var i = 0; i<howManyTimes; i++)<br />{<br /> result = getSum1(100);<br />}<br />timeStop = new Date();<br /></pre><br />It then prints duration and the result.<br /><br />The second test calls the function that generates the code for getSum2 function. After generating , the code is evaluated using the <b>eval function</b> (so the new function is created dynamically):<br /><pre><br />function createFunction1(val,functName)<br />{<br /> var code = "function "+functName+"\n{ \n return 0";<br /> for (var i=1; i<=val;i++)<br /> code+="+"+i; <br /> code += "; \n }"; <br /> return code; <br />}<br /><br />timeStart = new Date(); <br /><b>var code = createFunction1(100,"getSum2()");<br />eval(code);</b><br />for (var i = 0; i<howManyTimes; i++)<br />{<br /> result = getSum2();<br />}<br />timeStop = new Date();<br /></pre><br />After that it also prints duration and the result.<br />And finally the third test generates the function using javascript <b>Function object</b>.<br /><pre><br />function createFunction2(val)<br />{<br /> var code = " return 0";<br /> for (var i=1; i<=val;i++)<br /> code+="+"+i; <br /> code += ";"; <br /> return code; <br />}<br /><br />timeStart = new Date(); <br /><b>var code = createFunction2(100);<br />var getSum3 = new Function(code);</b><br />for (var i = 0; i<howManyTimes; i++)<br />{<br /> result = getSum3();<br />}<br />timeStop = new Date();<br /></pre><br />You can see the results of the tests if you run the test file in your browser.<br />My average results are:<br /><pre><br />In firefox: getSum1 ~ 4900ms<br /> getSum2 ~ 550ms<br /> getSum3 ~ 450ms<br />In IE6: getSum1 ~ 4800ms<br /> getSum2 ~ 840ms<br /> getSum3 ~ 740ms<br /></pre><br /><br />After all this you can say… ok, this is fine, but.. is there any chance that we can use this in real world, in something more complex than the sum of n numbers?<br />Well, there might be.<br />Some of today’s most popular java and .NET frameworks (Spring, Spring.NET) use dynamic code generation in their AOP libraries.<br /><br />I think that in near future some applications could have "smart execution controllers" that would know whether to call an abstract code or to generate concrete code and call it. Especially in applications where performance (speed) is bottle neck.Damjanhttp://www.blogger.com/profile/02201297910253584960noreply@blogger.com6tag:blogger.com,1999:blog-8138694.post-15239462698517336712007-07-27T01:05:00.000+02:002007-07-27T14:57:17.866+02:00Trick Google Calendar and send free SMS!You have probably heard about google calendar's option for sending free SMS notifications about your calendar events. <br />Some time ago, while playing with <a href="http://code.google.com/apis/gdata/calendar.html">google-calendar-data-api</a> , I got an idea to write a program that would allow its users to send sms for free.<br />Sender and recipient both must have their google calendar account.<br />Recipient MUST set up her/his account for receiving SMS messages(This can be done in google calendar in "Settings->Mobile Setup") and then for receiving SMS notifications (in "Settings->Calendars->Notifications" check the "New invitations: SMS" checkbox).<br /><br />So, how it works..<br />When a sender wants to send sms, she/he enters the text of the message and email address(es) of the recipient(s). Then the program, using google-calendar-data-api, tricks google calendar by creating an event (with no body, just the subject (text of the message)) and invites recipient(s) to that event (by sending the event notification to all email addresses user entered).<br /><br />After that, the google calendar account of every recipient informs her/him about the event she/he is invited to by sending the sms notification which contains the event's subject.<br /><br />And that is it. <br />Google Calendar becomes the free sms service :)<br /><br />Of course there are some limitations.<br />Anyone can receive up to 20 messages per day, up to 150 per month. (Number of messages the user can send is unlimited)<br />The format of the message is: "SENDER'S NAME invites you to: MESSAGE TEXT @time and date"<br />The sms is sent by google, so my program can't do anything to format it. <br />The number of the characters for MESSAGE TEXT depends on the length of sender's name.<br />There are about 60 characters available in my case.<br />If your text contains more than that number, the program will split the text and create as much the events as necessary and send several sms messages.<br /><br />The program (written in java) can be downloaded from <a href="http://tomdam.googlepages.com/freesmssender.zip">here</a><br /><br />note:<br />You can't send sms to yourself.Damjanhttp://www.blogger.com/profile/02201297910253584960noreply@blogger.com16tag:blogger.com,1999:blog-8138694.post-29552392609941347282007-02-16T10:32:00.000+01:002007-02-16T10:51:36.422+01:00alat:firebug - žrtva: google calendarKada sam pre nekog vremena napisao programčić za besplatno slanje SMS poruka korišćenjem Google Calendar-ovog servisa za SMS notifikacije, mnogim ljudima se svidela ideja i hteli su da probaju kako radi program, ali kada su se ulogovali na svoj Google Calendar i pokušali da ga podese dočekalo ih je neprijatno iznenađenje. Srbije nije bilo u spisku podržanih zemalja.<br />U razgovoru sa <a target="_blank" href="http://www.insaned.net/">Ivanom</a> i još nekim ljudima zaključio sam da, verovatno zbog skorašnjeg raskida Srbije i Crne Gore ekipa koja održava Google Calendar baš tih dana razdvaja i podatke o zemljama iz bivše zajednice. Međutim, izgleda da im mnogo treba za jednu tako prostu operaciju :)<br /><br />U međuvremenu sam, igravši se sa Firebug extenzijom za Firefox, došao na ideju da probam da prevarim GCalendar, da iako Srbije nema na spisku pokušam da je na silu ubacim i registrujem broj za slanje SMS poruka. Kolega sa posla mi je pomogao, ustupivši mi svoj nalog na kome nije imao registrovane SMS notifikacije.<br />Uspeli smo iz prve :)<br />Pokazalo se da i svemoćni google ponekad nešto zaboravi, tj. da su promenili podatke na serveru, ali da nisu update-ovali korisnički interface.<br /><br />Za one koji žele da urade istu stvar sledi malo uputstvo:<br /><ol><li>Instalirajte Firefox (ukoliko ga imate pređite na sledeći korak)</li><li>Instalirajte <a target="_blank" href="https://addons.mozilla.org/firefox/1843/%20"> Firebug</a> add-on za Firefox</li><li>Ulogujte se na svoj Google calendar i otvorite Settings->Notifications</li><li>Aktivirajte firebug.</li><li>U firebug-u kliknite na dugme Inspect i potom na listu zemalja koja se nalazi na otvorenoj Notifications stranici<br />Time u firebug-u dobijate označen HTML kod tačne lokacije te liste (HTML select tag)</li><li>U firebug-u u select tag-u pronađite bilo koju zemlju (npr. Andorra , ona je među prvima na spisku) i izmenite polje value. Umesto AD (Andorra) stavite RS (Republika Srbija) i pritisnite Enter.<br />Time ste završili sa firebug-om.</li><li>Sada u spisku zemalja na stranici odaberite Andoru, u polje za unos broja telefona unesite svoj broj telefona (+3816...) i kliknite na dugme Send verification code.</li><li>Kada vam na telefon stigne SMS sa kodom ukucajte ga u polje Verification code i kliknite Finish setup.<br /></li></ol>Time je vaše hakovanje uspelo :)<br />Sad možete da koristite Google Calendar-ov servis za slanje SMS poruka.<br /><br />Ukoliko želite da probate gore pomenuti programčić uputstvo možete naći <a href="http://damjan.blogspot.com/2006/12/besplatan-sms.html">ovde</a>Damjanhttp://www.blogger.com/profile/02201297910253584960noreply@blogger.com1tag:blogger.com,1999:blog-8138694.post-48758023896203697982007-02-10T01:21:00.002+01:002022-06-23T08:43:10.991+02:00prevodjenje reči sa dva klika<div dir="ltr" style="text-align: left;" trbidi="on">
Ukoliko često koristite internet i čitate sajtove na engleskom verovatno vam se dosta puta desi da vam zatreba rečnik. Meni se desi.<br />
Kod mene je do sada situacija bila takva da na brzinu kopiram reč za koju mi treba prevod, otvorim u novom tab-u <a href="http://www.metak.com/">www.metak.com</a>, paste-ujem reč i sačekam malo dok dobijem prevod.<br />
U principu, i to mi je bilo prihvatljivo jer imam brze prste...<br />
Pre par dana mi je pala na pamet ideja da napravim nešto, uz pomoć čega biste , dok se nalazite <span style="font-weight: bold;">na bilo kom</span> sajtu mogli sa dva klika da prevedete željenu reč. Bez otvaranja dodatnih strana i kucanja bilo čega.<br />
I posle malo mučenja to mi je pošlo za rukom.<br />
<br />
Napisao sam greasemonkey skriptu koja, kada na bilo kojoj stranici, bilo kog sajta duplo kliknete na neku reč i selektujete je, uzme tu reč, izvrši upit na <a href="http://www.metak.com/">www.metak.com</a> i prikaže vam rezultat (prevod te reči).<br />
Ova skripta će mnoogo ubrzati moje surfovanje, kao i surfovanje svih ljudi koji je budu koristili.<br />
<br />
Uputstvo za instalaciju:<br />
1.Instalirajte <a href="http://www.mozilla.com/en-US/firefox/">Mozilla FireFox</a> (ukoliko ga imate predjite na korak 2)<br />
2.Instalirajte greasemonkey Add-on za Firefox. Možete ga naći na <a href="https://addons.mozilla.org/en-US/firefox/addon/greasemonkey/">ovoj adresi</a><br />
3.Instalirajte skriptu za prevodjenje. Skriptu možete naći <a class="snap_nopreview" href="http://damjantomic.net/projects/translator/metakprevodilac.user.js">ovde</a> <br />
<br />
I to je to. Na prvoj stranici koju otvorite posle instalacije skripte moći ćete, duplim klikom na bilo koju reč istu i da prevedete sa engleskog na srpski ili srpskog na engleski.<br />
<br />
Voleo bih da čujem utiske.<br />
Inače imam još neke idejice u glavi.. pa će uskoro biti još novosti :)</div>
Damjanhttp://www.blogger.com/profile/02201297910253584960noreply@blogger.com7tag:blogger.com,1999:blog-8138694.post-30548160204072615272007-01-24T11:14:00.000+01:002007-01-24T11:15:56.994+01:00cool javascriptOtvorite bilo koji sajt (npr. www.google.com) i u address bar svog browser-a ukucajte<br />javascript:document.body.contentEditable='true'; document.designMode='on'; void 0<br />(ili prosto kliknite na ovaj <a href="javascript:document.body.contentEditable='true'; document.designMode='on'; void 0">link</a>)<br /><br />Pošto to uradite moći ćete u browser-u da menjate sadržaj otvorene stranice :)<br /><br />Na istu foru radi i sledeći script. <br />Otvorite neku stranicu sa više slika (npr. http://www.b92.net/sport/) i u adress bar ukucajte:<br />javascript:R=0; x1=.1; y1=.05; x2=.25; y2=.24; x3=1.6; y3=.24; x4=300; y4=200; x5=300; y5=200; DI=document.images; DIL=DI.length; function A(){for(i=0; i-DIL; i++){DIS=DI[ i ].style; DIS.position='absolute'; DIS.left=Math.sin(R*x1+i*x2+x3)*x4+x5; DIS.top=Math.cos(R*y1+i*y2+y3)*y4+y5}R++}setInterval('A()',5); void(0); <br /><br />(ili <a href="javascript:R=0; x1=.1; y1=.05; x2=.25; y2=.24; x3=1.6; y3=.24; x4=300; y4=200; x5=300; y5=200; DI=document.images; DIL=DI.length; function A(){for(i=0; i-DIL; i++){DIS=DI[ i ].style; DIS.position='absolute'; DIS.left=Math.sin(R*x1+i*x2+x3)*x4+x5; DIS.top=Math.cos(R*y1+i*y2+y3)*y4+y5}R++}setInterval('A()',5); void(0); ">link</a>) i posmatrajte sta se desava :DDamjanhttp://www.blogger.com/profile/02201297910253584960noreply@blogger.com2tag:blogger.com,1999:blog-8138694.post-1375664061260843282006-12-24T03:38:00.000+01:002006-12-24T04:26:28.003+01:00Besplatan SMS!Pre par meseci sam otkrio da google calendar ima opciju za besplatno slanje SMS notifikacija. Može se podesiti da šalje SMS kao podsetnik za događaj iz kalendara ili kao obaveštenje da vas je neko pozvao da prisustvujete nekom događaju.<br />Ova opcija me je baš obradovala, naročito kad sam video da među zemljama koje su podržane za slanje SMS poruka ima i Srbija.<br />Nešto posle ovog otkrića sam naleteo na <a href="http://code.google.com/apis/gdata/calendar.html">google-calendar-data-api</a>. Pomoću tog API-ja možete napisati aplikaciju za pristupanje google calendar-u. Za igranje sa događajima. Za <span style="font-weight:bold;">dodavanje</span>, brisanje, pregled i sl.<br />Čim sam video na ovaj api, sinula mi je ideja da možda mogu napisati aplikaciju koja će, koristeći isti i gore pomenutu opciju google calendara da služi za besplatno slanje SMSova. I posle malo eksperimentisanja uspeo sam (u nekoj meri) u tome.<br />Aplikaciju možete preuzeti <a href="http://tomdam.googlepages.com/FreeSMSSender.zip">ovde</a>.<br />Da biste je mogli koristiti potrebno je da uradite par stvari.<br />Za slanje poruka potrebno je da imate GMail nalog i kreiran google calendar. <br />Da biste mogli da primate poruke, potrebno je u podešavanjima google calendar-a da ostavite vaš broj telefona i otkačite opciju za slanje SMS notifikacije za "New invitations".<br />Što se tiče broja poruka koje mogu da se prime, google ga je ograničio na max 20 dnevno, odnosno 150 mesečno po jednom nalogu. Možete poslati beskonačno mnogo poruka.<br />Format poruke je malo problematičan, jer ne može iz aplikacije da se utiče na njega. Pošto google šalje poruku ona je oblika: "Damjan Tomic invites you to: TEKST VASE PORUKE @vreme i datum" (ako sa mog naloga nekom šaljem), što znači da ne može svih 160 karaktera da se iskoristi za tekst poruke. Veličina zavisi od duzine imena i prezimena onoga ko šalje poruku. Ja sam stavio da default veličina poruke bude 60 karaktera, ali to svako može promeniti u aplikaciji u zavisnosti od dužine imena i prezimena. Ukoliko je poruka koju želite poslati veća od pomenute dužine, program će je izdeliti na više manjih poruka.<br />Aplikacija je trenutno u verziji 1.0 :) tako da nema neke od realno potrebnih opcija, kao što su snimanje podešavanja ili spiska kontakata. To će nadam se uskoro biti ubačeno.<br /><br />Aplikacija je pisana u javi. Potrebno je da imate jre 5.0 na vašem računaru. Ukoliko nemate, možete je skinuti <a href="http://java.sun.com/javase/downloads/index_jdk5.jsp">odavde</a><br /><br />Source će uskoro biti postavljen na http://gnu.fon.bg.ac.yu/subversion/ pa će svako ko želi moći da se igra sa ovim.<br /><br />Očekujem komentare sa utiscima i novim predlozima.Damjanhttp://www.blogger.com/profile/02201297910253584960noreply@blogger.com7tag:blogger.com,1999:blog-8138694.post-1158928365380208152006-09-22T14:19:00.000+02:002006-10-24T15:08:04.940+02:00bez alata nema zanata : aptana - The Web IDEOduvek su me narodne poslovice oduševljavale.<br />Toliko mudrosti u samo par reči! Mudrosti koja traje večno. Mudrosti koja može da se primeni na veliki broj problema sa kojima se ljudi sreću, bez obzira na trenutak u kome se ljudi i problemi nalaze.<br />Iako znam da su te poslovice primenljive na dosta situacija iz prostog razloga što su jako apstraktne, ta rima im nekako daje dušu i čini ih večnim.<br />Elem.<br />Verovatno Vam se desilo da treba da pišete neki JavaScript kood.<br />U principu bilo ko, ko je ikada pravio neku web stranicu imao je potrebu da napiše, ako ništa drugo onda nekakav validator za unos e-maila i sl. U takvim situacijama za pisanje tog JavaScripta se naravno koristi okruženje u kome pravite i ostatak sajta.. dreamweaver, zend, visual studio, notepad... I to je OK.<br />Ali šta sa situacijom kada stranica treba da ima dosta klijentskog kooda. Dosta ~ 1000 linija ili nekoliko hiljada linija..!? Tada dolazi do izražaja činjenica da ni jedno od pomenutih okruženja nema dobru podršku za pisanje JavaScripta.<br />Nekako mi se čini da su svi ti alati , pošto su pravljeni za html + php,asp,jsp...(ili šta već) , JavaScript shvatili kao nužno zlo, i pravili samo neke osnovne stvari vezane za to nužno zlo...<br /><br />Sa razvojem Web 2.0 priče naglo je porasla potreba za okruženjem koje je <span style="font-weight: bold;">napravljeno za JavaScript</span>. Moram priznati da sam se oduševio kada sam naleteo na <a href="http://www.aptana.com/">Aptana</a>-u.<br />To je IDE zasnovan na Eclipse platformi. Postoji standalone varijanta, kao i plugin za Eclipse, ukoliko ga već imate na računaru.<br />Ne bih sada da nabrajam opcije koje poseduje Aptana, jer postoji dokumentacija na zvaničnom sajtu, samo bih napomenuo da ima jako dobar <span style="font-weight: bold;">Code Assist</span> ili <span style="font-weight: bold;">Intelli Sense </span>(ako tako neko više voli ;) )<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.aptana.com/images/code_assist.png"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://www.aptana.com/images/code_assist.png" alt="" border="0" /></a><br />Neke od stvari koje uskoro planiraju da dodaju okruženju su:<br /><ul><li>Internationalization</li><li>PHP Colorizing </li><li>PHP Code Assist </li><li>JavaScript Debugging </li><li>Refactoring</li><li>....</li></ul>Sve u svemu, ozbiljno preporučujem da probate Aptana-u, jer će Vam sigurno uštedeti dosta vremena u radu.Damjanhttp://www.blogger.com/profile/02201297910253584960noreply@blogger.com1tag:blogger.com,1999:blog-8138694.post-1151393489310463652006-06-27T09:26:00.000+02:002006-12-24T02:36:09.031+01:00sudoku<p><br />Verovatno ste svi čuli za igru Sudoku.<br />Pravila su vrlo jednostavna:<br />Dobijete matricu dimenzija 9x9, koja je dodatno izdeljena na 9 manjih matrica (3x3), u neka polja su upisani brojevi od 1 do 9 i vi treba da popunite celu matricu tako da se u svakom redu i koloni, ali i u unutrašnjoj matrici od 3 x 3 polja, nađu brojevi od 1 do 9, pri čemu se oni ne smeju ponavljati.<br />Jedan nemački matematičar izračunao je da je ukupan broj kombinacija u ovoj igri 6.670.903.752.021.072.936.960, što, kako negde pročitah, odgovara broju mikrona do najbliže zvezde.<br />Više o igri naravno možete pročitati na: <a href="http://en.wikipedia.org/wiki/Sudoku">http://en.wikipedia.org/wiki/Sudoku</a><br /></p><p>Imao sam priliku da se igram malo sa ovim, i ono što sam napravio možete videti <a href="http://tomdam.googlepages.com/sudoku">ovde</a>.<br />U pitanju je JAVA aplet koji rešava bilo koji sudoku zadatak (koji ima rešenje, naravno). Ukoliko ima više rešenja, prikazaće prvo na koje naiđe.<br />Što se algoritma tiče, program prvo pokušava da na "pametan" način dođe do rešenja, znači gleda da li je vrednost nekog polja očigledna, zatim za sva ostala polja računa kandidate za vrednosti, pa na osnovu pravila (da u 1 vrsti, koloni ili maloj matrici svi elementi moraju biti različiti) poljima koja imaju samo 1 kandidata fiksira vrednost. I to se tako vrti dok ne dođe do trenutka u kome sva ne rešena polja imaju više kandidata, tako da ne postoji način da se utvrdi koje vrednosti treba fiksirati.<br />U tom trenutku u priču se uključuje backtracking algoritam, koji rekurzivno pokušava da pronađe prave vrednosti.<br />On radi tako što uzme prvo ne rešeno polje i fiksira mu prvog kandidata, zatim za takvu matricu pokušava da nadje rešenje. Ukoliko u nekom trenutku, posle primene svih gore pomenutih pravila bilo koje ne rešeno polje ostane bez kandidata, to je signal da nešto ne valja, i rekurzivna funkcija se vraća nazad i uzima prvog sledećeg kandidata.<br />Ukoliko neko želi, okačiću source , pa da ga zajedno prodiskutujemo. (naravno, pošto nisam koristio nikakav obfuscator, oni sa malo više znanja mogu i sami da vide source :))</p>Damjanhttp://www.blogger.com/profile/02201297910253584960noreply@blogger.com4tag:blogger.com,1999:blog-8138694.post-1149982159039896132006-06-11T00:40:00.000+02:002006-10-24T15:02:27.936+02:00world cupPočelo je!<br />Sećam se Italije '90... To je bilo moje prvo svetsko prvenstvo. Imao sam 8 godina i toliko sam upijao utakmice da se i danas sećam dosta detalja..<br />Prošli smo grupu... Mateus nam zagorčao život, al ipak Kolumbija i Emirati nisu toliko jaki..<br />Onda Španija... Dva predivna gola Piksija Stojkovića... Ne sećam se da sam posle ikad video da je neki naš igrač dao lepše golove od tih.<br />Onda Argentina. I čuveni penali. Šta reći.. Valjda nismo imali sreće. Nisu ni Argentinci u finalu protiv Švaba. A mnogo bi bilo da jesu :) . Penal im je došao glave.<br /><br />Ameriku '94 pamtim po Bebetu i Romariju i naravno po Robertu Bađu i čuvenom promašenom penalu u finalu. I ništa više. Valjda zbog toga što je bio rat, pa su mi sećanja iz tog perioda malo čudna..<br /><br />Onda dolazi Francuska '98.<br />Gledao sam sve utakmice do polufinala. I onda se razbolim. Dobijem mononukleozu i završim u bolnici. A tamo nema televizora. Aaaaaaaaaa. Strašno. Sećam se da sam pitao doktora kad je ujutru došao da me obiđe.. i da mi je rekao za Francuze. Bilo mi je žao Brazila. Njih sam oduvek voleo. Naši standardno dobri :) Mijatova prečka iz penala i gol Davidsa u 93 minutu su bili dovoljni za povratak kući.<br /><br />Koreje i Japana '02 skoro da se i ne sećam.. Znam se da su Senegalci dobili Francuze, da su Korejanci daleko dogurali, Turci isto, da su Brazilci dobili Švabe u finalu i da je Ronaldo bio najbolji. i to je to.<br /><br />I sada konačno Nemačka '06. Lepo je počelo. Od 7 utakmica gledao sam 4 cele, ostale po malo. Od naših očekujem da pobede Holandiju sutra. Ako njih ne dobijemo mislim da nemamo šta da tražimo dalje. Realno smo bolji. Sa Argentinom će biti gusto, ako izvučemo remi biće ok. Obale Slonovače se najviše bojim. Jesu izgubili danas od Argentine, ali su pokazali da znaju jaako dobro da igraju.<br />Drago mi je da ovaj put nemamo neke velike zvezde u timu kao ranije. Čini mi se da su momci složni. Da znaju da igraju su pokazali u kvalifikacijama. Sad je samo bitno da budu hladnih glava. Bez ikakvog straha da udju u duele. I sve će doći na svoje mesto. Petko je strašan lik. Verujem u njega da ih je dobro pripremio.<br /><br />I jedan link za kraj :)<br /><a href="http://www.largestonlinestadium.com/fan/damjan">http://www.largestonlinestadium.com/fan/damjan</a><br />oduvek su me kreativni ljudi oduševljavali. Likovi koji su pravili ovaj sajt su mnoogo dobri. Svaka im čast.<br /><br />Pridružite se ljudi. Da navijamo :)Damjanhttp://www.blogger.com/profile/02201297910253584960noreply@blogger.com0tag:blogger.com,1999:blog-8138694.post-1149636725135870642006-06-07T00:41:00.000+02:002006-10-24T15:31:11.149+02:00google - web office<div style="text-align: left;">Google nastavlja svoj put unapred.<br /></div>Nova stvarčica iz njihove kuhinje se nalazi na <a href="http://spreadsheets.google.com/">http://spreadsheets.google.com/</a> . Ne znam da li su se do sada palile crvene alarmne lampe u Redmondu, ali mi se čini da je sad krajnje vreme za to. Kupovinom <a href="http://www.writely.com/">writely</a>-a i izbacivanjem nove gore pomenute usluge MS dobija žestoku konkurenciju za svoja dva najbolja programa.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.google.com/googlespreadsheets/images/tour3.gif"><img style="margin: 0pt 10px 10px 0pt; cursor: pointer; width: 381px; height: 250px;" src="http://www.google.com/googlespreadsheets/images/tour3.gif" alt="" border="0" /></a><br /><br /><br />Realno, mogućnosti pomenutih googleovih web usluga teško da mogu da dostignu Word i Excel u nekim zahtevnijim radnjama, ali mislim da će bar 80% korisnika biti potpuno zadovoljno njima. Ja hoću :)<br />Jedva čekam da vidim šta će biti odgovor ekipe iz Redmonda, mada mi se čini da neće uspeti da odgovore na ovo. Bar ne sa ovoliko različitom filozofijom od one koju fura google. MS, kao tipična kapitalistička firma, ako želi da vlada i u narednih 20 godina, mora polako da menja strategiju. Da se što više otvori. To je, čini mi se, neminovnost.<br />Postali su jako tromi. Poput svake velike organizacije. Nisu spremni na promene koje se dešavaju mnooogo brzo.<br />Već 5 godina nisu izbacili novu verziju Internet Explorer-a!!! Sve se svodi na krpljenje postojećih stvari.Damjanhttp://www.blogger.com/profile/02201297910253584960noreply@blogger.com6tag:blogger.com,1999:blog-8138694.post-1141446607229250892006-03-04T05:25:00.000+01:002006-10-24T15:05:39.842+02:00ajax revolucija - deo drugiPošto ljudi, generalno, ne vole baš mnogo nove stvari, bar ne vole da ih probaju na svojoj koži, trebalo je dosta vremena da prođe od pojave IE5.0 koji je prvi ponudio mogućnost korišćenja ajax-a do toga da široke web developerske mase počnu da koriste isti.<br /><br />Poslednjih meseci na svim iole posećenijim forumima ili blogovima ljudi koji se bave web developing-om, skoro svaka druga napisana reč je ajax. Naravno kao i u svakoj priči, postoje ljudi koji se oduševljavaju, i oni koji na sve gledaju sa rezervom, zatim oni koji pljuju ove prve, i neki sasvim treći koji uzvraćaju , braneći prve...<br />Sve u svemu mnogo se priča o ovome. I mislim da je to dobro.<br /><br />Kao i posle svake revolucije , web developeri će , ukoliko uvide dobre strane ajax-a, početi da ga upotrebljavaju u svim situacijama. Gde je to moguće i gde je nemoguće, gde je potrebno i gde nije potrebno. Bar je tako do sada bilo sa svim novim stvarima.<br />Ono što mene lično nervira u ovakvim situacijama, je to što će , iako se zna u ovakvoj klijent-server (web browser – web server) arhitekturi šta treba da budu odgovornosti klijenta, a šta servera da nastane totalna zbrka, u kojoj se ne zna ko šta radi. Kad kažem „da se znaju odgovornosti“, naravno da mislim na postojeće design pattern-e. Kad kažem da će “da nastane zbrka”, naravno da mislim na to da će mnogi ponovo da izmišljaju toplu vodu, i da neće uspeti da je izmisle.<br />Posledica toga će biti da će više od pola započetih projekata (web aplikacija) opet da propadne, pola ostatka da se završi sa gomilom bug-ova i probijenim terminima, pola preostalog dela ostatka da se završi „uspešno” ... naravno , ne kažem da je do sada situacija u softverskom poslu bila bolja što se izvršenja zadataka tiče, već da to da je spisak tehničkih razloga za propadanje sw projekata verovatno dobio još jednog velikog člana.<br /><br />Šta je u stvari problem?<br />Pa problem je u tome kako naći pravu meru izmedju dve krajnosti. Između toga da se sve dešava na serveru (klijent samo prikazuje <span style="font-weight: bold;">od jednom</span> dobijeni sadržaj) i toga da je klijent toliko pametan da zna šta mu treba i kada mu to treba i da se u tim situacijama obrati serveru i zatraži to od njega.<br />Prva varijanta je ona od koje se krenulo. Ona koja se koristila na početku web priče. Vremenom su se odredjene odgovornosti prebacivale na stranu klijenta. JavaScript je postao idealna stvar za skoro svaku vrstu validacije, kao i za druge klijentske poslove vezane za prikaz sadržaja. Ubrzani rast sposobnosti klijentske strane da obradjuje podatke, vrši validacije, prikazuje podatke, vuče web priču ka gore pomenutoj , drugoj varijanti.<br />Druga varijanta na prvu loptu zvuči super, ali se u njoj krije dosta toga.<br />Prvo, ne možete biti sigurni da svaki browser podržava vaš pametni klijentski kood iz onog ili ovog razloga, i drugo, bitnije, je to što ovakav pristup može da dovede do narušavanja osnovnih principa klijent-server arhitekture i da bude uvod u propast.Damjanhttp://www.blogger.com/profile/02201297910253584960noreply@blogger.com3tag:blogger.com,1999:blog-8138694.post-1138421876760586992006-01-28T04:45:00.000+01:002007-09-10T22:40:45.183+02:00ajax revolucija!Mnogo prašine se diglo oko stvari zvane AJAX u poslednje vreme. Na žalost ljubitelja fudbalskog kluba iz Amsterdama, ne ponavlja se uspešna serija njihovih ljubimaca iz 60ih i 70ih godina prošlog veka kada su harali evropom na čelu sa našim Veliborom Vasovićem. Iako google na reč "ajax" odgovara sa <a href="http://www.ajax.nl/">http://www.ajax.nl/</a> na prvom mestu, jedan drugi ajax opasno preti da potisne holandjane.<br />Ja lično ne volim mnogo nove reči, tj. ne volim kad ljudi olako daju imena stvarima. Na pitanje drugara "šta je to ajax?" od pre par meseci sam naravno odgovorio sa FC, iako sam pretpostavio da su opet smislili novo ime za staru stvar. I naravno da sam bio u pravu. Ali ovoga puta je to bilo malo drugačije.<br />Ovaj AJAX je akronim od Asinhroni JavaScript i XML (Asynchronous JavaScript and XML). U stvari to je tehnika kojom se HTML strana koja se nalazi u browseru može osvežiti novim podacima bez osvežavanja cele strane. Ništa novo, rekli bi neki (da li su u pravu!?).<br />Ovo je moguće ostvariti na dva načina. Prvi je umetanjem u HTML stranu nevidljivog <span style="color: rgb(51, 102, 255);">iframe </span>taga u kome bi bila odredjena stranica koja bi se osvežavala posle određenog vremena, i koja bi na onLoad događaj pozivala neku JavaScript funkciju koja bi mogla nešto pametno da uradi. Svako ko se razume u web programiranje zna da ono što sam napisao u rečenici iznad iako zvuči prosto i nije baš tako lako izvodljivo.<br />Druga varijanta je u stvari i ona prava.<br />Ona se svodi na korišćenje <span style="">XMLHttpRequest objekta.<br />Ovaj objekat je po prvi put video svetlo dana kao ActiveX kontrola u verziji </span>5.0. Microsoft Internet Explorer-a. Veoma brzo, pošto su uočene izvanredne mogućnosti i ostali browser-i dobijaju podršku za njega.<br />Trenutno <span style="">XMLHttpRequest podržavaju svi aktuelni browser-i, bar u novijim verzijama.<br /><br />E sad, pitanje je šta je to toliko pametno, važno, dobro, loše, kako god hoćete, vezano za taj </span><span style="">XMLHttpRequest? I zašto su ljudi koji se ne razumeju u fudbal od jednom počeli toliko da pričaju o ajaxu?<br />Odgovor je, naravno, svima koji su ikada napisali (ili napravili ako više volite) par html strana koje se generišu na serveru pomoću asp-a, php-a, jsp-a ili čega već, jako prost.<br />Mene interesuje samo jedna stvar. Zašto tek sad?!!!<br />Zašto nešto tako očigledno korisno nije ranije uvedeno u web priču?<br />Pa, odgovor je opet vrlo prost. Moralo je da se desi nešto veliko. To "nešto veliko" je google !!!<br />Google je medju prvima u svim svojim servisima počeo da koristi ajax.<br />Da li zbog toga što je to </span><span style="">nešto </span><span style="">"novo i fancy"? Sumnjam.<br />LJudi su, jednostavno, uvideli (kao što mnogi sada uvidjaju) da je, ukoliko želite da sa serverom razmenite malu količinu podataka dovoljno da pošaljete zahtev za tim podacima i dobijete ih, bez ponovnog generisanja kilobajta i kilobajta HTML kooda koji je već na klijentskoj strani.<br /><br />Znači standardna priča. Mora neko da bude prvi.<br /><br /></span>Damjanhttp://www.blogger.com/profile/02201297910253584960noreply@blogger.com2