Gelöst: Exchange EWS und PHP SOAP

Update:

Habe das Problem gelöst, dass wenn nur ein Eintrag vorhanden ist dieser nicht angezeigt wird:

Problem ist das wenn nur ein Eintrag da ist das, dass Ergebnis kein Array ist.

Das wäre die richtige Funktion:

include 'ExchangeNTLMSoapClient.php';

$FindItem->Traversal = "Shallow";
$FindItem->ItemShape->BaseShape = "AllProperties";
$FindItem->ParentFolderIds->DistinguishedFolderId->Id = "calendar";
$FindItem->CalendarView->StartDate = "2010-06-01T00:00:00Z";
$FindItem->CalendarView->EndDate = "2010-06-30T00:00:00Z";
$result = $client->FindItem($FindItem);
$calendaritems = $result->ResponseMessages->FindItemResponseMessage->RootFolder->Items->CalendarItem;
var_dump($calendaritems);
if(is_array($calendaritems))
{
    foreach ($calendaritems as $item)
    {

        echo $item->Subject . "n";
    }
}else{
   echo $calendaritems->Subject . "n";
}

So sollte es klappen!

Was so ein einziger Buchstabe ausmachen kann, ich habe 2x unseren Code per Auge verglichen und keinen Unterschied gesehen.

Für den Tipp mit dem Ein-Datensatz-Problem möchte ich mich bei dir recht herzlich bedanken, jetzt kann ich meinen Terminvorlauf wieder von 7 -> 2 Tagen ändern.

Muss es in der Zeile

$s_EndTag = date('j', $s_EndDatum);

nicht

$s_EndTag = date('d', $s_EndDatum);

heissen?

d=zweistellige Tageszahl mit ggfls. führender Null.

Gruß,
kpiep

Es klappt soweit.
KalenderItems werden ausgelesen und angezeigt.
Allerdings geht nach jeder Abfrage die Fehlerkonsole beim Webfront auf:

object(stdClass)#12 (39) {
  ["ItemId"]=>
  object(stdClass)#13 (2) {
    ["Id"]=>
    string(148) "AAAVAGguc3RyYXVzc0Boc3MtbmV0ei5kZQFRAAiIzMv/FhVwAEYAAAAAIHwUFc2ZVEmwx20GJml0GgcA0azgnvwTN0mHxIvN82IYlAAAAhQnIAAA0azgnvwTN0mHxIvN82IYlAABfxIDhwAAEA=="
    ["ChangeKey"]=>
    string(40) "DwAAABYAAADRrOCe/BM3SYfEi83zYhiUAAIy/35s"
  }
  ["ParentFolderId"]=>
  object(stdClass)#14 (2) {
    ["Id"]=>
    string(100) "AAAVAGguc3RyYXVzc0Boc3MtbmV0ei5kZQAuAAAAAAAgfBQVzZlUSbDHbQYmaXQaAQDRrOCe/BM3SYfEi83zYhiUAAACFCcgAAA="
    ["ChangeKey"]=>
    string(8) "AQAAAA=="
  }
  ["ItemClass"]=>
  string(26) "IPM.Appointment.Occurrence"
  ["Subject"]=>
  string(4) "GeSa"
  ["Sensitivity"]=>
  string(6) "Normal"
  ["DateTimeReceived"]=>
  string(20) "2009-05-04T13:43:19Z"
  ["Size"]=>
  int(1218)
  ["Importance"]=>
  string(6) "Normal"
  ["IsSubmitted"]=>
  bool(false)
  ["IsDraft"]=>
  bool(false)
  ["IsFromMe"]=>
  bool(false)
  ["IsResend"]=>
  bool(false)
  ["IsUnmodified"]=>
  bool(false)
  ["DateTimeSent"]=>
  string(20) "2009-05-04T13:43:19Z"
  ["DateTimeCreated"]=>
  string(20) "2010-03-04T10:56:17Z"
  ["ReminderDueBy"]=>
  string(20) "2010-05-28T06:00:00Z"
  ["ReminderIsSet"]=>
  bool(true)
  ["ReminderMinutesBeforeStart"]=>
  string(2) "15"
  ["DisplayCc"]=>
  string(0) ""
  ["DisplayTo"]=>
  string(14) "xxxxxxxxxxxxxxx"
  ["HasAttachments"]=>
  bool(false)
  ["Culture"]=>
  string(5) "de-DE"
  ["Start"]=>
  string(20) "2010-05-28T06:00:00Z"
  ["End"]=>
  string(20) "2010-05-28T06:30:00Z"
  ["LegacyFreeBusyStatus"]=>
  string(4) "Busy"
  ["IsMeeting"]=>
  bool(false)
  ["IsRecurring"]=>
  bool(true)
  ["MeetingRequestWasSent"]=>
  bool(false)
  ["IsResponseRequested"]=>
  bool(true)
  ["CalendarItemType"]=>
  string(10) "Occurrence"
  ["MyResponseType"]=>
  string(7) "Unknown"
  ["Organizer"]=>
  object(stdClass)#15 (1) {
    ["Mailbox"]=>
    object(stdClass)#16 (1) {
      ["Name"]=>
      string(14) "XXXXXXXXXXXX"
    }
  }
  ["Duration"]=>
  string(5) "PT30M"
  ["TimeZone"]=>
  string(57) "(GMT+01:00) Amsterdam, Berlin, Bern, Rom, Stockholm, Wien"
  ["AppointmentSequenceNumber"]=>
  int(0)
  ["AppointmentState"]=>
  int(0)
  ["ConferenceType"]=>
  int(0)
  ["AllowNewTimeProposal"]=>
  bool(true)
  ["NetShowUrl"]=>
  string(0) ""
}
GeSan

Wo kann das Problem sein?
Ich tippe auf die includete class-Datei vom Anfang des Threads- finde aber die Ausgabe der o.a. Daten nicht. Die echos habe ich testweise mal auskommentiert. Die sind es nicht.
Hat jemand eine Idee?

Gruß,
kpiep

So wie das aussieht hast Du in einer der Dateien ein print_r(); drin. Denke an die Klasse, da stand im Original ein print_r am Ende.

@wgreipl
Das habe ich auch gedacht und den print_r-Befehl rausgenommen und testweise mal die echos in der class-Datei auskommentiert.
Hat aber nix gebracht.

Hänge hier mal die class-Datei und deine Datei mit den Modifizierungen von bl4cky an.
Vielleicht kannst Du mal rüber schauen - ich sehe da keinen relevanten print_r, echo oder so mehr…

<?

 /*
*******************************
 IP-SYMCON Event Scripting
*******************************
File     : class_ex_?????.ips.php
Trigger  :
Interval :

Kurze Erklärung:
Man braucht natürlich php_soap.dll und php_curl.dll und noch die ssleay32.dll 

Im Verzeichnis "scripts" fehlen jetzt noch drei Dateien.
Services.wsdl, types.xsd und messages.xsd
Alle drei runter zu laden vom Exchange Server:
https://exchange.example.com/EWS/types.xsd
https://exchange.example.com/EWS/messages.xsd
https://exchange.example.com/EWS/Services.wsdl

Bei Services.wsdl noch das nächste Textblock VOR der letzten Zeile hinzufügen:

Code:
		<wsdl:service name="ExchangeServices">
			<wsdl:port name="ExchangeServicePort" binding="tns:ExchangeServiceBinding">
				<soap:address location="https://exchange.example.com/EWS/Exchange.asmx"/>
			</wsdl:port>
		</wsdl:service>


*/

class NTLMSoapClient extends SoapClient {
    function __doRequest($request, $location, $action, $version) {
        $headers = array(
            'Method: POST',
            'Connection: Keep-Alive',
            'User-Agent: PHP-SOAP-CURL',
            'Content-Type: text/xml; charset=utf-8',
            'SOAPAction: "'.$action.'"',
        );
        $this->__last_request_headers = $headers;
        $ch = curl_init($location);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_POST, true );
        curl_setopt($ch, CURLOPT_POSTFIELDS, $request);
        curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
        curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_NTLM);
        curl_setopt($ch, CURLOPT_USERPWD, $this->user.':'.$this->password);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);

        $response = curl_exec($ch);
        return $response;
    }
    function __getLastRequestHeaders() {
        return implode("n", $this->__last_request_headers)."n";
    }
}



class ExchangeNTLMSoapClient extends NTLMSoapClient {
    protected $user = 'xxxxxxx';
    protected $password = 'xxxxxxxx';
}


class NTLMStream {
    private $path;
    private $mode;
    private $options;
    private $opened_path;
    private $buffer;
    private $pos;

    public function stream_open($path, $mode, $options, $opened_path) {
        echo "[NTLMStream::stream_open] $path , mode=$mode n";
        $this->path = $path;
        $this->mode = $mode;
        $this->options = $options;
        $this->opened_path = $opened_path;
        $this->createBuffer($path);
        return true;
    }

    public function stream_close() {
        echo "[NTLMStream::stream_close] n";
        curl_close($this->ch);
    }

    public function stream_read($count) {
        echo "[NTLMStream::stream_read] $count n";
        if(strlen($this->buffer) == 0) {
            return false;
        }
        $read = substr($this->buffer,$this->pos, $count);
        $this->pos += $count;
        return $read;
    }

    public function stream_write($data) {
        echo "[NTLMStream::stream_write] n";
        if(strlen($this->buffer) == 0) {
            return false;
        }
        return true;
    }

    public function stream_eof() {
        echo "[NTLMStream::stream_eof] ";
        if($this->pos > strlen($this->buffer)) {
            echo "true n";
            return true;
        }
        echo "false n";
        return false;
    }

    /* return the position of the current read pointer */
    public function stream_tell() {
        echo "[NTLMStream::stream_tell] n";
        return $this->pos;
    }

    public function stream_flush() {
        echo "[NTLMStream::stream_flush] n";
        $this->buffer = null;
        $this->pos = null;
    }

    public function stream_stat() {
        echo "[NTLMStream::stream_stat] n";
        $this->createBuffer($this->path);
        $stat = array(
            'size' => strlen($this->buffer),
        );
        return $stat;
    }

    public function url_stat($path, $flags) {
        echo "[NTLMStream::url_stat] n";
        $this->createBuffer($path);
        $stat = array(
            'size' => strlen($this->buffer),
        );
        return $stat;
    }

    /* Create the buffer by requesting the url through cURL */
    private function createBuffer($path) {
        if($this->buffer) {
            return;
        }
        echo "[NTLMStream::createBuffer] create buffer from : $pathn";
        $this->ch = curl_init($path);
        curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($this->ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
        curl_setopt($this->ch, CURLOPT_HTTPAUTH, CURLAUTH_NTLM);
        curl_setopt($this->ch, CURLOPT_USERPWD, $this->user.':'.$this->password);
        echo $this->buffer = curl_exec($this->ch);
        echo "[NTLMStream::createBuffer] buffer size : ".strlen($this->buffer)."bytesn";
        $this->pos = 0;
    }
}


class ExchangeNTLMStream extends NTLMStream {
    protected $user = 'xxxxxxxxx';
    protected $password = 'xxxxxxxxx';
}


//verändert: erst wenn https registriert ist, unregistrieren
$existed = in_array("https", stream_get_wrappers());
if ($existed) {
    stream_wrapper_unregister("https");

    stream_wrapper_register('https', 'ExchangeNTLMStream') or die("Failed to register protocol");
    $wsdl = "services.wsdl";
    $client = new ExchangeNTLMSoapClient($wsdl);

    /* Do something with the web service connection */
    stream_wrapper_restore('https');
    }
    else {
        stream_wrapper_register('https', 'ExchangeNTLMStream') or die("Failed to register protocol");
        $wsdl = "services.wsdl";
        $client = new ExchangeNTLMSoapClient($wsdl);
}


?>
<?

// Debug-Modus ein/aus
$debug = false;

// Pfad für Sprach- bzw. Erinnerungston
$path = IPS_GetKernelDir().'media/';

// Vorlauf der Kalenderansicht (heute plus x-Tage)
// Hilfs-Integervariable mit Profil anlegen - Assoziation 2, 7, 14 und 31 Tage

if($IPS_SENDER == "WebFront")
{
SetValue($IPS_VARIABLE, $IPS_VALUE);
 $vorlauf = $IPS_VALUE;
}
if(isset($IPS_VALUE)) {
 // NIX
} else {
 $vorlauf = 2;
 SetValue(33148 /*[System\Termine\Kalendervorlauf]*/, $vorlauf);
}

// Datum zusammensetzen der Startzeit für Abfrage Exchange
// alle Termine ab heute/jetzt
$s_jahr = date('Y');
$s_monat = date('m');
$s_tag   = date('j');
$s_start = $s_jahr."-".$s_monat."-".$s_tag."T00:00:01Z";
if($debug) { echo $s_start."
"; }

// Datum zusammensetzen der Endzeit für Abfrage Exchange
$s_EndDatum = time()+(60*60*24*$vorlauf);
$s_EndTag = date('d', $s_EndDatum);
$s_EndMonat = date('m', $s_EndDatum);
$s_EndJahr = date('Y', $s_EndDatum);
$s_ende = $s_EndJahr."-".$s_EndMonat."-".$s_EndTag."T23:59:59Z";
if($debug) { echo $s_ende."
"; }

// Klasse für den entsprechenden Benutzer laden
include "18301.ips.php";

//SOAP-Anfrage an Exchange starten
$FindItem->Traversal = "Shallow";
$FindItem->ItemShape->BaseShape = "AllProperties";
$FindItem->ParentFolderIds->DistinguishedFolderId->Id = "calendar";
$FindItem->CalendarView->StartDate  = $s_start;
$FindItem->CalendarView->EndDate  = $s_ende;
$result = $client->FindItem($FindItem);
$calendaritems = $result->ResponseMessages->FindItemResponseMessage->RootFolder->Items->CalendarItem;

if($debug) { print_r($calendaritems); }

var_dump($calendaritems);
if(is_array($calendaritems))
{
foreach ($calendaritems as $item)
    {
//echo $item->Subject . "n";

 // Startzeit des Termines aus Exchange zerlegen für Anzeige in Datumsform (Unix-Timestamp)
 $stunde  = substr ( $item->Start, 11, 2 );
 $minute  = substr ( $item->Start, 14, 2 );
 $sekunde = substr ( $item->Start, 17, 2 );
 $tag     = substr ( $item->Start, 8, 2 );
 $monat   = substr ( $item->Start, 5, 2 );
 $jahr    = substr ( $item->Start, 0, 4 );

 // String für Anzeigedatum und Uhrzeit generieren und Zeitzone hinzufügen
 $unix = mktime ( $stunde+date('I')+1, $minute, $sekunde, $monat, $tag, $jahr );
 if(date('d.m.Y, H:i', $unix-(($item->ReminderMinutesBeforeStart)*60)) == date('d.m.Y, H:i', time())) {
  WFC_SendNotification(28059 /*[WebFront Configurator]*/, "Terminwarnung", utf8_decode($item->Subject), "Glocke", 0);
		// WAC_PlayFile(30880 /*[Objekt #30880 existiert nicht]*/, $path."bell.wav");
      // In der kommenden Version wird der Termin vorgelesen
 }

 if($debug) { echo date ( 'd.m.Y, H:i', $unix) . ", " . utf8_decode($item->Subject)."<br>"; }
 if($debug) { echo $unix . "-". utf8_decode($item->Location) . "<br><br>"; }

 // Auswertung des Feldes ORT zur Steuerung von verschiedenen Aufgaben, z.B. FHT_Bad
 /*if (utf8_decode($item->Location) == "FHT_Bad") {
    echo "Heizung Bad";
 }*/

 // Array für Anzeige in Stringvariable in Webfront erzeugen
 $ausgabe[$x] = date ( 'd.m.Y, H:i', $unix) . " Uhr - " . utf8_decode($item->Subject);
 $x++;
}
if($debug) { print_r($ausgabe); }

// Array für Webfront aufbereiten und in String-Variable schreiben
// die im Webfront angezeigt wird. Profil ~Textbox
//echo $ausgabe_wf;

$ausgabe_wf = implode("
", $ausgabe);
SetValueString(11817 /*[System\Termine\Termine]*/, $ausgabe_wf);

}
else{
//   echo $calendaritems->Subject . "n";
}

?>

Hallo,

nimm, den Eintrag

var_dump($calendaritems); 

aus dem IPS-Skript raus, dann sollte es ohne Ausgabe laufen :slight_smile:

jepp, das wars.

Wunderbar und vielen Dank!!!

Hat bei mir auch auf Anhieb geklappt.

Kann man das auch mit Öffentlichen Ordnern machen?
So könnte man sich ja prima einen Geburtstagskalender erstellen und sich die immer anzeigen lassen.

Loerdy

Hallo zusammen,

Nach stundenlanger suche bin ich auf diesen Post gestossen. Alles funktioniert super, hab aber eine kleine Frage bezueglich des Codes.

Ich moechte gerne ein Termin erstellen. Das funktioniert auch alles bestens. Jedoch moechte ich nebsts Time start/end, subject, location, noch ein weiteres Feld hinzufuegen:

$CreateItem->Items->CalendarItem[$i]->Body = „example text“;

jedoch gibt mir dies wie zu erwarten ein error, da ich noch BodyType = „Text“ einfuegen muss. Also das sollte dann in xml so aussehen:

<Body BodyType=„Text“>example text</Body>

wie muss ich nun den code aendern, dass dieses BodyType im body tag korrekt erscheint?

Besten Dank
Pascal

Hi Leute!

Ich habe das Ganze jetzt auch mal versucht mit einem Exchange, ich bekomme jedoch noch folgende Fehlermeldung:


Fatal error:  Uncaught SoapFault exception: [WSDL] SOAP-ERROR: Parsing WSDL: Couldn't load from 'services.wsdl' : failed to load external entity "services.wsdl"
 in C:\IP-Symcon\scripts\ipsuser.ips.php:159
Stack trace:
#0 C:\IP-Symcon\scripts\ipsuser.ips.php(159): SoapClient->SoapClient('services.wsdl')
#1 C:\IP-Symcon\scripts\30097.ips.php(41): include('C:\IP-Symcon\sc...')
#2 {main}
  thrown in C:\IP-Symcon\scripts\ipsuser.ips.php on line 159

Was kann daran schuld sein?

Niemand eine Idee??? Steh da echt daneben…

Kann es mir heute Abend mal ansehen.

Ich gehe davon aus das Du alle benötigten Dateien vom Exchange geholt hast. Waren glaube ich 2 Stück. In einer mußten Anpassungen gemacht werden.

Bin mir nicht sicher ob ich das noch aus dem Stehgreif kann. Meins läuft schon eine weile ohne Probleme.

Ich habe eigentlich alles genauso gemacht wie beschrieben wurde. Die 3 Dateien in den Scripts-Ordner. In der services.wdsl die Anpassungen gemacht und den Pfad zu meinem Server eingetragen. Habe auch schon ein wenig herumexperimentiert, leider ohne Erfolg. Wäre toll wenn du mir helfen könntest. Werde inzwischen weiterprobieren, vielleicht komme ich selber noch dahinter :o

Hallo Leute… (könnte auch das Problem von Hofimax sein)

Bei der 2.4 kann er die SSLEAY32 nicht einbinden kommt die Meldung

Erweiterung ssleay32.dll nicht geladen. Version stimmt nicht überein: PHP: 5.3.1, EXT: 0.9.8

Welche müsste man da nehmen das es wieder klappt?

Vor allem wo muss ich sie hingeben und muss ich sie auch über die php.ini einbinden? Ich hatte diese Fehlermeldung noch nirgends gesehen!

die gehört auch nicht in ext Ordner, sondern ins IPS_Verzeichniss!

Diese Datei ssleay32.dll habe ich im IPS-Ordner. Die php_soap und die php_curl habe ich im ext Ordner und in der php.ini als Extension eingetragen.

Die php_soap und die php_curl habe ich im ext Ordner und in der php.ini als Extension eingetragen.

das passt, allerding werden sie in der ini automatisch übernommen. Du brauchst sie also nicht nochmal einbinden. :slight_smile:

Okay, dann sollte aber automatisch in meiner php.ini ein Pfad auf die curl-Datei verweisen oder? Oder ist dies gar nicht notwendig, weil ja der Pfad in den ext-Ordner angegeben ist?