jamesiarmes / php-ews

PHP Exchange Web Services
http://jamesarmes.com/php-ews/
MIT License
567 stars 302 forks source link

Help Wanted: Enumerating Exch2010 Public Folder structure #528

Open pantsmanuk opened 5 years ago

pantsmanuk commented 5 years ago

Version (e.g. 1.0, dev-master): 1.0 PHP version: 7.0 Microsoft Exchange version: 2010 SP3

Looking for a little info from anyone that understands EWS better than I at present. I'm retooling a script I wrote for my employer - the old script automagically copied emails from staff Inbox and Sent Items into the Public Folders structure (Exchange 2003) using WEBDAV and Troy Wolf's screen scraping classes for HTTP and XML. It wasn't pretty or elegant (or fast), but it worked.

We've now upgraded to Exchange 2010, and I'm having to revisit code I wrote the best part of a decade ago. I'm beginning to get my head around php-ews (and it's great James, BTW), but...

I'm starting out with trying to enumerate the Public Folders structure (eventually into a DB table, but to screen to start with). I munged the Find Items example with some code from StackExchange and got the first level PF contents (Display Name and ID) no problem, but now I'm trying to get the code iterating I'm hitting the hurdle - the ID I get back in the response message (FolderId->Id) isn't the same as the ID that DistinguishedFolderId->Id is expecting to receive. Have I gotten things very messed up in my head? I kinda hoped that FolderId->Id would have "just worked".

pantsmanuk commented 5 years ago

This is the working code to get the first level public folders...

$client = new Client($host, $username, $password, $version);
$client->setCurlOptions(array(CURLOPT_SSL_VERIFYPEER => false)); // Shouldn't be required, but...
$client->setTimezone($timezone);

// Start building the find folder request
$request = new FindFolderType();
$request->Traversal = FolderQueryTraversalType::SHALLOW;
$request->FolderShape = new FolderResponseShapeType();
$request->FolderShape->BaseShape = DefaultShapeNamesType::ALL_PROPERTIES;

// Configure the view
$request->IndexedPageFolderView = new IndexedPageViewType();
$request->IndexedPageFolderView->BasePoint = 'Beginning';
$request->IndexedPageFolderView->Offset = 0;

// Set the starting folder
$request->ParentFolderIds = new NonEmptyArrayOfBaseFolderIdsType();
$request->ParentFolderIds->DistinguishedFolderId = new DistinguishedFolderIdNameType();
$request->ParentFolderIds->DistinguishedFolderId->Id = DistinguishedFolderIdNameType::PUBLIC_FOLDERS_ROOT;

// Request
$response = $client->FindFolder($request);
pantsmanuk commented 5 years ago

Answering my own query, but after reading the EWS docs it seemed simple enough... Just needed to dig in James' code a chunk more. Specifically, the code to enumerate sub-folders is basically the same as above, but not using DistinguishedFolderId in any subfolder. I've written a function that will do it for my application:-

/**
 * @param null $FolderId The Exchange ID value of the folder to start enumerating
 * @return FindFolderType The FindFolder request
 */
function enumerate($FolderId) {
    // Start building the find folder request
    $request = new FindFolderType();
    $request->Traversal = FolderQueryTraversalType::SHALLOW;
    $request->FolderShape = new FolderResponseShapeType();
    $request->FolderShape->BaseShape = DefaultShapeNamesType::ALL_PROPERTIES;

    // Configure the view
    $request->IndexedPageFolderView = new IndexedPageViewType();
    $request->IndexedPageFolderView->BasePoint = 'Beginning';
    $request->IndexedPageFolderView->Offset = 0;

    // Set the starting folder
    $request->ParentFolderIds = new NonEmptyArrayOfBaseFolderIdsType();
    $request->ParentFolderIds->FolderId = array('Id'=>$FolderId);

    // Request
    return $request;
}

Just call it in place of $request in $response = $client->FindFolder($request); for subfolders of the first-level folders that $response = $client->FindFolder($request); returns; $sub_response = $client->FindFolder(enumerate($pf_id))'.