Drupal Beitrag mit Services Module in .NET erstellen / bearbeiten (Remote)

Fr, 30.04.2010 - 11:46 -- admin

In Drupal gibt es ein Module Services, hiermit lässt sich eine Drupalwebseite über eine Remote-Schnittstelle ansprechen. So können ohne Drupal selbst zu nutzen Beiträge erstellt werden. Eine simple Windows-Anwendung kann dann z.B. einen Beitrag in Drupal anlegen, welcher dann von Drupal verwaltet wird.
Zur Kommunikation kann man unter anderem dass XML-RPC Protokoll nutzen (andere lassen sich auch nutzen), somit kann man aus einer ganzen Hand von Programmiersprachen und Entwicklungsumgebungen auf seinen Drupal Beiträge zugreifen. So kann man mittels Visual Studio und VB.NET mit relativ wenig Quelltext auf eine Drupalseite Connecten und diverse Operationen durchführen

Fertige Bibliotheken

Möchte man nicht selber eine Schnittellen zu Drupal programmieren, kann man sich auch fertige Bibliotheken runterladen. Mittels einfacher Funktionsaufrufe kann man so einen node editieren / ändern, Dateien hochladen (inkl. CCK), Taxonomy-System nutzen, ... . Ich habe bisher zwei fertige Bibliotheken gefunden, welche beide in C# geschrieben sind. Einmal DrupalXmlRpc .NET (Entwicklerseite) und DrutNet - Drupal .NET API .

Drupal Services Module

Wir erwähnt stellt uns dass Drupal Module Services die nötige Schnittstelle bereit. Nach der Installation sollten wir die Drupal Module XMLRPC Server, Node Service und User Service aktivieren. Zusätzliche sollten wir einmal auf die Drupal Berechtigungen achten. Loggt man sich nicht mit dem admin Account ein muss man dem "Remote-User" die nötigen Services Rechte geben (z.B. node_service-Modul -> load node data.

Services mit .NET

Zum Ansprechen der Services-Schnittstelle mittels .NET brauchen wir die XML-RPC Funktionen, welche leider nicht im .NET Framework enthalten sind. Es muss also die XML-RPC.NET DLL nachgerüstet werden (Datei nicht mit dem Windows-ZIP Programm entpacken, sonst fehlt der bin Ordner).
Hat man ein Verweise auf CookComputing.XmlRpc gesetzt kann man loslegen, man sollte allerdings bedenken, dass VB.NET nicht unbedingt dafür geeignet und etwas umständlicher ist, es wäre einfache C# zu nutzen.

VB.NET Beispiel

Ich habe eine einfache Klassen um Lesen und Erstellen eines Drupal Node erstellt, die kurz aufzeigt wie man es macht. Zum Teil habe ich die vorhanden C# Beispiele Bibliotheken genutzt und portiert als auch ein paar VB.NET Optimierungen vorgenommen. Direkt auf der Drupal Seite gibt es auch ein paar Beispiele: Example: User login with API key using C#

Dim DrupalPage As New DrupalXmlRpcInterface
 
'connect to a Drupal page
DrupalPage.Connect()
 
'login to page; you need a valid account
DrupalPage.Login("admin", "yourpw")
 
'the the content of the node 17
MsgBox(DrupalPage.GetNode(17).Body)
 
'create a drupal node template, save it into drupal an return the generated nodeid (nid)
Dim NewNode As DrupalXmlRpcInterface.DrupalNode = DrupalXmlRpcInterface.CreateNodeTemplate("test", "testbody", "page")
Dim nid As Integer = DrupalPage.SaveNode(NewNode)
MsgBox(nid)

DrupalXmlRpcInterface

Imports CookComputing.XmlRpc
Imports System.Reflection
''' <summary>
''' An simple example of using the "Drupal Services Module" to get some nodes or to create some new one
''' </summary>
''' <remarks></remarks>
Public Class DrupalXmlRpcInterface
    <XmlRpcUrl("http://localhost/services/xmlrpc")> _
    Interface IDrupalServices
        <XmlRpcMethod("node.get")> Function NodeGet(ByVal nid As Integer, ByVal fields() As String) As DrupalNode
        <XmlRpcMethod("node.save")> Function NodeSave(ByVal node As XmlRpcStruct)
        <XmlRpcMethod("user.login")> Function Login(ByVal username As String, ByVal password As String)
    End Interface
    Private _IDrupalServices As IDrupalServices
    Private _loggedInUser As String = ""
 
    ''' <summary>
    ''' If Service Module needs a Authenfication call it after connect
    ''' needs Drupal Module "User Service"
    ''' </summary>
    ''' <param name="Username"></param>
    ''' <param name="Password"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Function Login(ByVal Username As String, ByVal Password As String)
        Try
            Dim l As XmlRpcStruct = _IDrupalServices.Login(Username, Password)
            _loggedInUser = Username
            Return True
        Catch ex As Exception
            MsgBox(ex.Message)
            Return False
        End Try
    End Function
    ''' <summary>
    ''' Connect to a Drupal site with enabled Service Module
    ''' </summary>
    ''' <remarks></remarks>
    Sub Connect()
        _IDrupalServices = CType(XmlRpcProxyGen.Create(GetType(IDrupalServices)), IDrupalServices)
    End Sub
    ''' <summary>
    ''' This will save a Drupal node
    ''' needs Drupal Module "Node Service"
    ''' </summary>
    ''' <param name="Node"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Function SaveNode(ByVal Node As DrupalNode)
        Dim XmlRpcNode As XmlRpcStruct = ConvertToXmlRpcStruct(Of DrupalNode)(Node)
        Return _IDrupalServices.NodeSave(XmlRpcNode)
    End Function
    ''' <summary>
    ''' Create a full Drupal node
    ''' needs Drupal Module "Node Service"
    ''' </summary>
    ''' <param name="nid"></param>
    ''' <param name="fields"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Function GetNode(ByVal nid As Integer, Optional ByVal fields() As String = Nothing) As DrupalNode
        If fields Is Nothing Then fields = New String() {}
        Return _IDrupalServices.NodeGet(nid, fields)
    End Function
    ''' <summary>
    ''' This will create a Drupal Node template that we can use to save it
    ''' </summary>
    ''' <param name="Title"></param>
    ''' <param name="Body"></param>
    ''' <param name="ContentType"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Shared Function CreateNodeTemplate(ByVal Title As String, ByVal Body As String, ByVal ContentType As String) As DrupalNode
        Dim b As New DrupalNode
        b.Title = Title
        b.Body = Body
        b.ContentType = ContentType
        Return b
    End Function
    ''' <summary>
    ''' This Function will convert a Class with XmlRpcMember Functions to a XmlRpcStruct
    ''' It walks to all Properties of a Class which has values and convert it to a XmlRpcStruct Object
    ''' </summary>
    ''' <typeparam name="T"></typeparam>
    ''' <param name="node"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function ConvertToXmlRpcStruct(Of T As DrupalNode)(ByVal node As T) As XmlRpcStruct
        Dim xStruct As New XmlRpcStruct()
        Dim ext As New KrmSoftware.ClassExtender(node)
        Try
            For Each pi As PropertyInfo In node.[GetType]().GetProperties()
                Try
                    Dim att As XmlRpcMemberAttribute = TryCast(Attribute.GetCustomAttribute(pi, GetType(XmlRpcMemberAttribute)), XmlRpcMemberAttribute)
                    Dim value As Object = ext.GetValue(pi.Name)
                    If value IsNot Nothing Then
                        xStruct.Add(att.Member, value)
                    End If
                Catch ex As Exception
                End Try
            Next
            Return xStruct
        Catch ex As Exception
            Throw ex
        End Try
    End Function
    ''' <summary>
    ''' A Drupal Node Object Class
    ''' </summary>
    ''' <remarks></remarks>
    <XmlRpcMissingMapping(MappingAction.Ignore)> _
    Public Class DrupalNode
 
        Private _NodeID As String
        Private _Title As String
        Private _Body As String
        Private _Author As String
        Private _ContentType As String
        Public Sub New()
            For Each pi As PropertyInfo In Me.[GetType]().GetProperties()
                If pi.PropertyType Is GetType(XmlRpcStruct()) Then
                    pi.SetValue(Me, New XmlRpcStruct(0) {}, New Object() {})
                End If
            Next
        End Sub
        <XmlRpcMember(Description:="The NodeID", Member:="nid")> _
        Public Property NodeID() As String
            Get
                Return _NodeID
            End Get
            Set(ByVal value As String)
                _NodeID = value
            End Set
        End Property
        <XmlRpcMember(Description:="", Member:="title")> _
        Public Property Title() As String
            Get
                Return _Title
            End Get
            Set(ByVal value As String)
                _Title = value
            End Set
        End Property
        <XmlRpcMember(Description:="", Member:="body")> _
         Public Property Body() As String
            Get
                Return _Body
            End Get
            Set(ByVal value As String)
                _Body = value
            End Set
        End Property
        <XmlRpcMember(Description:="", Member:="name")> _
         Public Property Author() As String
            Get
                Return _Author
            End Get
            Set(ByVal value As String)
                _Author = value
            End Set
        End Property
        <XmlRpcMember(Description:="", Member:="type")> _
         Public Property ContentType() As String
            Get
                Return _ContentType
            End Get
            Set(ByVal value As String)
                _ContentType = value
            End Set
        End Property
 
    End Class
End Class