воскресенье, 19 декабря 2010 г.

Web: Tree-graph visualization in javascript

Возникло желание нарисовать на web диаграмму в виде дерева, в стиле "Исаак родил Иакова; Иаков родил Иуду". И при этом позволить перетаскивать элементы дерева, и сохранять расположение нод.

Оказалось что задача вовсе не тривальна и состоит как минимум из трех частей:
  • Раскладка графа в виде дерева
  • Рисование соединительных линии между блоками
  • Возможность сохранения и восстановления результата.


Я попытался найти готовую JavaScript библиотеку для этого. Вот что из этого вышло:

Dracula Graph Library

Рисует используя SVG и обладает очень простым синтаксисом. К сожалению библиотека представляет лишь один метод визуализации графа:



При этом синтаксис очень прост:
window.onload = function() {
    var g = new Graph();
    g.addNode("Root");
    g.addNode("Vasy");
    g.addNode("Pety");
    g.addNode("Sasha");
    g.addNode("Masha");
    g.addNode("Katy");
    g.addNode("Misha");
    g.addEdge("Root", "Vasy");
    g.addEdge("Root", "Pety");
    g.addEdge("Vasy", "Sasha");
    g.addEdge("Vasy", "Masha");
    g.addEdge("Pety", "Katy");
    g.addEdge("Pety", "Misha");
    var layouter = new Graph.Layout.Spring(g);
    layouter.layout();
    var renderer = new Graph.Renderer.Raphael('canvas', g, width, height);
    renderer.draw();
 


Если бы была возможность раскладывать граф в дерево и сохранять в JSON расположение нод, то это была бы идеальная библиотека для моих целей.


js-graph-it



Тут сами ноды и соединения задается в виде набора html тэгов с предопределенными классами.

<p class="block draggable" style="top: 100px; left: 10px; width: 300px;" id="par_1">
Ioan
</p>
<p class="block draggable" style="top: 200px; left: 100px; width: 300px;" id="par_2">
Yakov
</p>
<p class="block draggable" style="top: 300px; left: 100px; width: 300px;" id="par_3">
Iuda
</p>
<p class="block draggable" style="top: 400px; left: 100px; width: 300px;" id="par_4">
Vasy
</p>
<div class="connector par_1 par_2">
    <img class="connector-end" src="arrow.gif">
</div>
<div class="connector par_2 par_3">
    <img class="connector-end" src="arrow.gif">
</div>
<div class="connector par_2 par_4">
    <img class="connector-end" src="arrow.gif">
</div>
 

К сожалению библиотека требует, чтобы в разметке уже была рассчитана начальная раскаладка графа. Это удобно лишь для отрисовки статических графов, но для рисования динамических нужно самому реализовывать расчет местоположения всех нод.
К плюсам можно отнести то, что все соединения рисуются стандартными div'ами т.е. работать будет почти везде, но при этом все связи будут состоять из вертикальных или горизонтальных линии, кривых и диагональных нет.

Пока найти 100% подходящую под мои требования библиотеку, мне не удалось. :(
Вот обсуждение на StackOferflow этих и других библиотек.

среда, 15 декабря 2010 г.

SharePoint: PowerShell script for creating big list with look up fields

В комментариях к моему предыдущему посту где я писал утилитку для создания больших листов c лукап полями на C#, высказали мнение о том, что неплохо бы такие вещи писать на PowerShell. Ну я и попробовал, вот версия на PowerShell:

PARAM ($url, $toCount, $toListItemsCount, $lookUpItemsCount)

$snapin = Get-PSSnapin | Where-Object {$_.Name -eq 'Microsoft.SharePoint.Powershell'}
if ($snapin -eq $null){
    Add-PSSnapin Microsoft.SharePoint.Powershell
    [Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")
}

$spWeb = Get-SPWeb -Identity $url
$toListsIds = @()

for($iList=0; $iList -lt $toCount; $iList++)
{
    $toListName = "ToList" + $iList
    Write-Host "Creating to list " $toListName
   
    $lstId = $spWeb.Lists.Add($toListName, "test to list",100);
    $spLst = $spWeb.Lists.GetList($lstId, $true);  
    $toListsIds+=$lstId
   
    for ($iItem=0; $iItem -lt $toListItemsCount; $iItem++)
    {
        $spItem = $spLst.Items.Add()
        $spItem["Title"] = [string]::Format("List {0} item {1}", $toListName, $iItem)
        $spItem.Update()
        Write-Host "\tadd item " $iItem
    }
}

Write-Host "Creating look up list"
   
$lookUplstId = $spWeb.Lists.Add("LookUpList", "test to list",100); 
$splookUpLst = $spWeb.Lists.GetList($lookUplstId, $true);  

for ($iField=0; $iField -lt $toCount; $iField++)
{
    $fldIntName = $splookUpLst.Fields.AddLookup("LookUpField"+$iField,  $toListsIds[$iField], $false)
    $spFld = $splookUpLst.Fields[$fldIntName]
    $spLookUpFld = $spFld -as [Microsoft.SharePoint.SPFieldLookup]
    $spLookUpFld.ShowInDisplayForm = $true
    $spLookUpFld.LookupField = "Title"
    $spLookUpFld.Update()
    Write-Host "\tadd field " $iField
}

Write-Host "Creating look up items " $lookUpItemsCount
   
$rand = New-Object system.random

for ($iItem=0; $iItem -lt $lookUpItemsCount; $iItem++)
{
    $spItem = $splookUpLst.Items.Add()
    $spItem["Title"] = "Item " +  $iItem

    for ($iField=0; $iField -lt $toCount; $iField++)
    {
        $fldName = "LookUpField"+$iField
       
        if ($iField -eq 0)
        {
             $spItem[$fldName] = $null
        }
        else
        {
            $rId = $rand.Next(0, $toListItemsCount)
             
            if ($rId -eq 0)
            {
                $spItem[$fldName] = $null;
            }
            else
            {
                $spItem[$fldName] = $rId;
            }
        }
    }
    $spItem.Update()   
    Write-Host "Create item " $iItem
}

Write-Host "DONE!"


В написании скрипта мне очень помог бесплатный редактор PowerGUI

вторник, 14 декабря 2010 г.

SharePoint: Creating big list with look up fields

Понадобилось мне тут проверить, как будет вести себя мой продукт с большими листами содержащими LookUp поля. Создавать руками это не наш метод, потому набросал код который в определенном сайте создает листы и лист с лукапами на них.

Вот такой запуск:
LookUpCreator http://server/site1 10 10 100

создаст 10 листов "ToList" с 10 итемами вида "Item " и лист "LookUpList" содержащий 10 лукапных колонок на вышеозначенные листы и содержащий 100 итемов.

Хочу отметить что первая лукапная колонка не содержит значении специально. И так же часть значении не заполнены. Этим я имитировал некоторые нужные мне ситуации.

        static void Main(string[] args)
        {
            Console.WriteLine("LookUps creator usage <site url> <to lists count> <to lists items> <lookup items count>");

            var siteUrl = args[0];

            var toCount          = int.Parse(args[1]); //   10;
            var toListItemsCount = int.Parse(args[2]); //   10;
            var lookUpItemsCount = int.Parse(args[3]); // 7000;

            var listToBaseName = "ToList";
            var lookUplistName = "LookUpList";

            using (var site = new SPSite(siteUrl))
            {
                using (var web = site.OpenWeb())
                {
                    Console.WriteLine("Creating to {0} lists with {1} items", toCount, toListItemsCount);
                    var toLists = new List<SPList>(toCount);
                    for (int iList=0; iList<toCount; iList++)
                    {
                        var toListName = listToBaseName + iList.ToString();
                        Console.WriteLine("Creating to list '{0}'", toListName);

                        var lstId = web.Lists.Add(toListName, "test to list",SPListTemplateType.GenericList);
                        var spList = web.Lists.GetList(lstId, true);

                        toLists.Add(spList);
                       
                        for (int iItem = 0; iItem < toListItemsCount; iItem++)
                        {
                            var item = spList.Items.Add();
                            item["Title"] = string.Format("List '{0}' item {1}",toListName,iItem );
                            item.Update();
                            Console.WriteLine("\tadd item {0}", iItem);
                        }
                    }

                    Console.WriteLine("Creating look up list {0}", lookUplistName);

                    var lookUplstId = web.Lists.Add(lookUplistName, "test lookup list", SPListTemplateType.GenericList);
                    var spLookUpList = web.Lists.GetList(lookUplstId, true);

                    for (int iField = 0; iField<toCount; iField++)
                    {
                        var fldName = "LookUpField"+iField.ToString();
                        var fldIntName = spLookUpList.Fields.AddLookup(fldName, toLists[iField].ID, false);
                        var fldLookUp = spLookUpList.Fields[fldIntName] as SPFieldLookup;                        
                        fldLookUp.ShowInDisplayForm = true;
                        fldLookUp.LookupField = "Title";
                        fldLookUp.Update();
                        Console.WriteLine("\tadd column '{0}'", fldIntName);
                    }

                    Console.WriteLine("Creating {0} look up items", lookUpItemsCount);

                    var Rnd = new Random();
                    for (int iItem = 0; iItem < lookUpItemsCount; iItem++)
                    {
                        var spItem = spLookUpList.Items.Add();

                        spItem["Title"] = "Item " + iItem.ToString();

                        for (int iField = 0; iField < toCount; iField++)
                        {
                            var fldName = "LookUpField" + iField.ToString();

                            if (iField == 0)
                                spItem[fldName] = null;
                            else
                            {
                                var rId = Rnd.Next(0, toListItemsCount);
                                if (rId == 0)
                                    spItem[fldName] = null;
                                else
                                    spItem[fldName] = rId;
                            }
                        }
                        spItem.Update();
                        Console.WriteLine("\t" + spItem.Title);
                    }

                    Console.WriteLine("Done!");
                }
            }
        }
 


Может быть, кому-нибудь пригодится или послужит примером. UPD: Вариант этого же кода, но на PowerShell тут

четверг, 9 декабря 2010 г.

SharePoint: How to delete folder with items using web services.

Достаточно часто пишу небольшие примерчики работы с SharePoint, на которых проверяю те или иные механизмы SharePoint. Возможно кому то эти примеры могут пригодится.

Сегодня надо было удалить folder с файлами внутри Document Library используя Web Services. Для этого есть Lists.UpdateListItems

Вот что у меня вышло:
var url = "http://server/subsite/_vti_bin/Lists.asmx";

var xml = "<Batch OnError=\"Continue\" PreCalc=\"TRUE\" ListVersion=\"0\">"+
                "<Method ID=\"1\" Cmd=\"Delete\">"+
                "<Field Name=\"ID\">10</Field>"+
                "<Field Name=\"FileRef\">http://server/subsite/DocLib/Folder</Field>" +
                "</Method>"+
                "</Batch>";

var listWebSvc = new ListsSvc.Lists();
listWebSvc.Url = url;
listWebSvc.Credentials = System.Net.CredentialCache.DefaultCredentials;
listWebSvc.PreAuthenticate = true;

var xmlDoc = new System.Xml.XmlDocument();
xmlDoc.LoadXml(xml);

var res = listWebSvc.UpdateListItems("DocLib",xmlDoc);
 

Хочу обратить внимание на то что в FileRef нельзя указывать путь относительно сайта ("/DocLib/Folder"), я на это сразу не обратил внимание, и потратил лишнее время в поисках почему оно не работает.