21Apr

Gelegentlich werden in Spalten von ListItems auch Benutzerinformationen gespeichert - als Beispiel sollen hier “erstellt von” und “geändert von” dienen. Die zugehörigen Felder des Items beinhalten meist Zeichenketten im Format <BenutzerID>;#<Benutzername>.

Bevor man sich nun in String-Operationen versucht - z.B. durch Split an der Stelle “;#” und Erzeugung der Objekte über die extrahierte ID - sollte man sich mit den Klassen SPFieldUser bzw. SPFieldUserValue auseinandersetzen, die hier viel Arbeit und Fehleranfälligkeit abnehmen können, da hier sehr einfach Objekte vom Typ SPUser bzw. SPGroup zurückgegeben werden können.

Im Folgenden 2 beispielhafte Methoden, die den Zugriff verdeutlichen sollen:

private SPUser GetSPUser(SPListItem item, Guid fieldid)
{
    SPUser retval = null; 

    #region check params
    if (item == null)
    {
        throw new ArgumentNullException("item");
    }
    if (fieldid == Guid.Empty)
    {
        throw new ArgumentException("Empty Guid is not allowed.", "fieldid");
    }
    #endregion 

    try
    {
        SPFieldUser field = item.Fields[fieldid] as SPFieldUser;
        if (field != null)
        {
            SPFieldUserValue fieldValue = field.GetFieldValue(item[fieldid].ToString()) as SPFieldUserValue;
            if (fieldValue != null)
            {
                retval = fieldValue.User;
            }
        }
    }
    catch (Exception ex)
    {
        throw ex;
    } 

    return retval;
}
private SPGroup GetSPGroup(SPListItem item, Guid fieldid)
{
    SPGroup retval = null; 

    #region check params
    if (item == null)
    {
        throw new ArgumentNullException("item");
    }
    if (fieldid == Guid.Empty)
    {
        throw new ArgumentException("Empty Guid is not allowed.", "fieldid");
    }
    #endregion 

    try
    {
        SPFieldUser field = item.Fields[fieldid] as SPFieldUser;
        if (field != null)
        {
            SPFieldUserValue fieldValue = field.GetFieldValue(item[fieldid].ToString()) as SPFieldUserValue;
            if (fieldValue != null)
            {
                string groupName = fieldValue.LookupValue;
                retval = item.Web.SiteGroups[groupName];
            }
        }
    }
    catch (Exception ex)
    {
        throw ex;
    } 

    return retval;
}

Der Aufruf ist nun recht trivial:

SPUser modifiedbyuser = GetSPUser(tasklistitem, SPBuiltInFieldId.Editor);
DateTime modifiedat = Convert.ToDateTime(tasklistitem[SPBuiltInFieldId.Modified]);
SPUser assignedto = GetSPUser(tasklistitem, SPBuiltInFieldId.AssignedTo);
Technorati Tags: , ,

17Apr

Bei der Entwicklung komplexer Workflows für den SharePoint spielen auch Workflowaufgaben für die Benutzerinteraktion während der Ausführung des Workflows eine nicht unbedeutende Rolle.

Eine solche Aufgabe hat nicht nur die Standardeigenschaften wie einen Titel oder eine Beschreibung sondern auch erweiterte Eigenschaften. Das praktische daran ist, dass diese kein eigenes Feld in der Liste benötigen sondern die Daten in den bereits vorhandenen Feldern gespeichert werden.

Und wenn man weiß, wie es geht, ist der Zugriff auf diese Daten auch sehr einfach.

1.) bei der Erstellung
Während der Erstellung der Aufgaben verwendet man in der Regel den Typ WssTaskActivity. Dieser verfügt bereits über eine Eigenschaft ExtendedProperties, die vom Typ Hashtable ist und in die man seine gewünschten Daten schreiben kann.

createTask.TaskId = Guid.NewGuid();
createTask.TaskProperties.Title = "Bitte genehmigen";
createTask.TaskProperties.ExtendedProperties["AdditionalText"]
    = "Bitte persönlich bearbeiten!";

2.) über den Item der TaskList

Möchte man nun später noch einmal darauf zugreifen, sieht man sich vor die Aufgaben gestellt, die Daten wieder aus dem SPListItem der TaskList zu lesen und nach der Änderung auch wieder zu schreiben. Sieht man sich das SPListItem genauer an, das die Aufgabe repräsentiert, so erkennt man, dass es ein Feld ExtendedProperties enthält, in dem die Daten auch enthalten sind - allerdings sind hier einfach die XML-Attribute die die Eigenschaften repräsentieren angegeben:

"AdditionalText='Bitte persönlich bearbeiten!' Department='Einkauf'"

Die große Frage ist jetzt, wie man einfach auf diese Daten zugreift, diese verändert und wieder abspeichert. Die Hashtable die bei der Erstellung wäre hierfür ein adäquates Objekt, das hier aber wie es zunächst scheint nicht vorhanden ist. Aber man muss hier nicht verzagen - auch dafür gibt es eine bereits vorhandene Lösung, auf die man aber erst kommen muss. Der Typ SPWorkflowTask aus dem NameSpace Microsoft.SharePoint.Workflow verfügt über 2 interessante statische Methoden: GetExtendedPropertiesAsHashtable hat als Rückgabewert genau die eben noch vermisste Hashtable und AlterTask verfügt über die Möglichkeiten, den Item mit den geänderten Daten auch wieder zu aktualisieren.

// get the extended properties hashtable
Hashtable taskItemExtProps = SPWorkflowTask.GetExtendedPropertiesAsHashtable(this.TaskListItem);
// write new values to the hashtable
taskItemExtProps["AdditionalText"] = "Mein Kommentar: genehmigt!";
// update the task item with new values
SPWorkflowTask.AlterTask(this.TaskListItem, taskItemExtProps, true);
Technorati Tags: , , , , ,

08Jan

Dafür gibt es (theoretisch) eine denkbar einfache Lösung: SPListItem bietet eine Methode CopyTo(destinationUrl) an (http://msdn2.microsoft.com/en-us/library/microsoft.sharepoint.splistitem.copyto.aspx) - leider scheint diese aber nicht (in jedem Fall?) zu funktionieren. Zumindest in meinem Fall (benutzerdefinierte Liste mit benutzerdefiniertem Inhaltstyp UND Attachments - vielleicht erwarte ich einfach auch zu viel von Sharepoint…) tat sie es nicht. Stattdessen erhielt ich folgende Exception: “Source item cannot be found. Verify that the item exist and that you have permission to read it.” Eine rasche Recherche bei Google brachte mir die Erkenntnis, dass andere Leute das gleiche Problem auch schon hatten - leider ohne verwertbare Lösungsvorschläge…

Also erstellen wir uns eben selbst eine kleine statische Methode, die das gewünschte tut:

Die Methodensignatur erwartet ein Quellelement und einen Listenname und gibt das kopierte Zielelement zurück:

image

Zuerst erstellen wir das Zielelement in der angegebenen Liste. Dann gehen wir Schritt für Schritt alle Fields des Quellelementes durch und kopieren diese zum Zielelement:

image

Achtung! Wir sollten nicht versuchen, readonly Fields zu kopieren und auch die Attachments lassen sich nicht auf diese Weise “abfertigen”. Diese behandeln wir folgendermaßen:

image

Nun noch schnell das Zielelement gespeichert und zurückgegeben - fertig :-)

image

So könnte z.B. der Aufruf der Methode aussehen:

image

Zum besseren Kopieren hier das Ganze nochmal als Text:

public static SPListItem CopyItem(SPListItem sourceItem, string destinationListName)
{
//copy sourceItem to destinationList
SPList destinationList = sourceItem.Web.Lists[destinationListName];
SPListItem targetItem = destinationList.Items.Add();
foreach (SPField f in sourceItem.Fields)
{
if (!f.ReadOnlyField && f.InternalName != “Attachments”)
{
targetItem[f.InternalName] = sourceItem[f.InternalName];
}
}
//copy attachments
foreach (string fileName in sourceItem.Attachments)
{
SPFile file = sourceItem.ParentList.ParentWeb.GetFile(sourceItem.Attachments.UrlPrefix + fileName);
byte[] imageData = file.OpenBinary();
targetItem.Attachments.Add(fileName, imageData);
}
targetItem.Update();
return targetItem;
}