How to get all subfolders from "root" recursively via SOAP from Exchange? - php

After searching the web for hours you are my last hope:
I have to build a system which reads sent and incoming mails from a Microsoft Exchange Server. I found the following script for that:
Exchange-Web-Services-for-PHP (Heartspring)
https://github.com/Heartspring/Exchange-Web-Services-for-PHP
The existing get_messages() function returns all messages for a folder, for example "inbox". So far everything is clear. My problem starts when I want to get all messages from "sent" - Folder - i've tried many words, from "send" to "Sent Items"; without any result (mailbox not available)
My idea was to get all subfolders for the folder "root" and wrote this:
include "init.php";
$ec = new ExchangeClient();
$ec->init("bambullis#123.de", "", NULL, "https://amxprd3610.outlook.com/EWS/Services.wsdl");
$folders = $ec->get_subfolders("root");
foreach($folders as $folder) {
print_r($folder);
}
This is what I get:
stdClass Object
(
[FolderId] => stdClass Object
(
[Id] => AAAeAGJhbWJ1bGxpc0BzdHVrZSbi5kZQAuAAAAAABw352p5E4yS5voYF9ELBmiAQBXYPdO6NZAQ6T9C3xviT7xAAAAC1iXAAA=
[ChangeKey] => AQAAABYAAABXYPdO6NZAQ6T9C3xviAALNCey
)
[DisplayName] => Oberste Ebene des Informationsspeichers
[TotalCount] => 0
[ChildFolderCount] => 16
[UnreadCount] => 0
)
(I know that FolderId->Id is base64 encoded, I've modified the string above for security reasons ;o))
Now I tried to list the subfolders for this directory (I added a mailbox to see, if the value "ChildFolderCount" will change, it does):
...
print_r($folder);
print_r($ec->get_subfolders($folder->FolderId->Id));
...
This is the error I get:
The request failed schema validation: The 'Id' attribute is invalid
What did I do wrong? How to get all subfolders from "root" recursively? Thanks to this lovely guy who can help me!

The EWS-PHP get_subfolders method uses by default a TraversalType "Shallow", so it searches only the identified folder and returns only the folder IDs for items that have not been deleted.
To search in all subfolders of the identified parent folder and return only the folder IDs for items that have not been deleted you should use the "Deep" TraversalType.
For example:
<?php
include "init.php";
class myExchangeClient extends ExchangeClient {
public function get_subfolders_deep($ParentFolderId = "inbox", $Distinguished = TRUE) {
$this->setup();
$FolderItem = new stdClass();
$FolderItem->FolderShape = new stdClass();
$FolderItem->ParentFolderIds = new stdClass();
$FolderItem->FolderShape->BaseShape = "Default";
/*
** See http://msdn.microsoft.com/en-us/library/exchange/exchangewebservices.folderquerytraversaltype(v=exchg.140).aspx
** Deep Traversal: Searches in all subfolders of the identified parent folder and returns only the folder IDs for items that
** have not been deleted.
*/
$FolderItem->Traversal = "Deep";
if ($Distinguished) {
$FolderItem->ParentFolderIds->DistinguishedFolderId = new stdClass();
$FolderItem->ParentFolderIds->DistinguishedFolderId->Id = $ParentFolderId;
} else {
$FolderItem->ParentFolderIds->FolderId = new stdClass();
$FolderItem->ParentFolderIds->FolderId->Id = $ParentFolderId;
}
$response = $this->client->FindFolder($FolderItem);
if ($response->ResponseMessages->FindFolderResponseMessage->ResponseCode == "NoError") {
$folders = array();
if (!is_array($response->ResponseMessages->FindFolderResponseMessage->RootFolder->Folders->Folder)) {
$folders[] = $response->ResponseMessages->FindFolderResponseMessage->RootFolder->Folders->Folder;
} else {
$folders = $response->ResponseMessages->FindFolderResponseMessage->RootFolder->Folders->Folder;
}
return $folders;
} else {
$this->lastError = $response->ResponseMessages->FindFolderResponseMessage->ResponseCode;
}
}
}
$ec = new myExchangeClient();
$ec->init("bambullis#123.de", "", NULL, "https://amxprd3610.outlook.com/EWS/Services.wsdl");
$folders = $ec->get_subfolders_deep("root");
echo "<pre>".print_r($folders,true)."</pre>\n";
?>
Anyway, looking at the ExchangeClient class source code, the FolderID for the sent items should be "sentitems".

Related

PhalconPHP - How to return return single typed Raw SQL Statement Result

I have a relatively average (however basic) knowledge with PHP but I'm fairly new to PhalconPHP. I've learned of PhalconPHP while browsing a specific online course website and that this framework is rather fast and unique than other PHP Frameworks, plus it has a Micro package for easy development of REST API.
So as it is, I'm trying to learn how to develop an API with the help of PhalconPHP Framework. And as it is, for beginners like me, I'd like to start with using raw SQL statements rather than the provided phql syntax because I'm not entirely developing an MVC Application (and I'm really having a hard time learning the entire concept of PHQL itself, I need a separate time to learn this).
I have managed to make things work when working with multi-column query results that map to the class much like how it is supposed to be with PhalconPHP, with below sample code:
<?php
namespace MyApplication\Db\ShortLinks\Services;
use MyApplication\Db\ShortLinks\DatabaseTables;
use Phalcon\Mvc\Model\Resultset\Simple as ResultSet;
class VisitorService
{
function getVisitorLogByLinkId($id)
{
$sqlQry = "SELECT * FROM visitors v WHERE v.link_id = $id ORDER BY v.visitor_id DESC;";
$visitor = new DatabaseTables\Visitor();
$resultSet = new ResultSet(null, $visitor, $visitor->getReadConnection()->query($sqlQry));
$data = [];
foreach ($resultSet as $result) {
$data[] = [
"visitor_name" => $result->visitor_name,
"access_timestamp" => $result->access_timestamp
];
}
return $data;
}
}
?>
However, I cannot seem to get the concept on how to work this out with single-columned query results such as getting the SUM, COUNT, or even ids and specific data type. I've tried below code as I've tried to recreate provided answer here:
function isLinkIdValid($id)
{
$sqlQry = "SELECT IF(COUNT(l.link_id) = 0, 0, 1) 'is_exist' FROM links l WHERE l.link_id = $id;";
$result = $app->di->db->query($sqlQry);
$result->setFetchMode(Phalcon\Db::FETCH_ASSOC);
$result = $result->fetchAll($result);
return $result;
}
But, as i have expected, it would not work since $app (which is an instance of Phalcon\Mvc\Micro which is in index.php) is not accessible from the function.
Also, its best to explain to everyone that while I do have models for the database tables, I've separated all database logic from the model object to keep it clean and serve its only purpose - being a model. How I've done this is I've created a new folder named /service and creating service for each model that there is (for example: VisitorService).
To show everyone my code, I basically patterned it after PhalconPHP's REST Tutorial, below is a snippet of my code:
index.php:
<?php
use Phalcon\Loader;
use Phalcon\Mvc\Micro;
use Phalcon\Di\FactoryDefault;
use Phalcon\Db\Adapter\Pdo\Mysql as PdoMySql;
use Phalcon\Http\Response;
# Loader initialization and namespace registration
$loader = new Loader();
$loader->registerNamespaces([
"MyApplication\Db\ShortLinks\DatabaseTables" => __DIR__ . "/models/",
"MyApplication\Db\ShortLinks\Services" => __DIR__ . "/services/"
]);
$loader->register();
# DependencyInjector initialization and instantiation of database connection
$di = new FactoryDefault();
$di->set("db", function() {
return new PdoMySql([
"host" => "127.0.0.1",
"username" => "username",
"password" => "password",
"dbname" => "short_links"
]);
});
# Micro initialization, route definition
$app = new Micro($di);
$app->notFound(function() use ($app) {
$response = new Response();
$response->setStatusCode(404, "Not Found");
$response->setJsonContent([
"status" => 404,
"message" => "The specified route was not found, or does not exist",
"data" => ""
]);
return $response;
});
require "routes/visitor_route.php";
$app->handle();
?>
routes/visitor_route.php
<?php
use Phalcon\Http\Response;
use MyApplication\Db\ShortLinks\Services;
require "services/visitor_service.php";
$app->get('/v1/visitors/log/{id:[0-9]+}', function($id) use ($app) {
$response = new Response();
# validate id parameter
if($id <= 0) {
$response->setStatusCode(422, "Unprocessable Entity");
$response->setJsonContent([
"status" => 422,
"message" => "Invalid link id specified.",
"data" => ""
]);
return $response;
}
$service = new Services\VisitorService();
$response->setStatusCode(200, "OK");
$response->setJsonContent([
"status" => 200,
"message" => "",
"data" => $service->getVisitorLogByLinkId($id)
]);
# This partially works, except that it does not return whether a link id is valid, so I'm trying to create a logic for one.
// return $response;
$tempService = new Services\LinkService();
$tempResult = $tempService->isLinkIdValid($id);
return print_r($tempResult);
});
?>
service/visitor_service.php
<?php
namespace MyApplication\Db\ShortLinks\Services;
use MyApplication\Db\ShortLinks\DatabaseTables;
use Phalcon\Mvc\Model\Resultset\Simple as ResultSet;
class VisitorService
{
# technically belongs to 'services/link_service.php' but placed here to shorten attached codes.
function getAllLinks()
{
$sqlQry = "SELECT * FROM links;";
$link = new DatabaseTables\Link();
$resultSet = new ResultSet(null, $link, $link->getReadConnection()->query($sqlQry));
$data = [];
foreach ($resultSet as $result) {
$data[] = [
"link_id" => $result->link_id,
"description" => $result->description,
"original_url" => $result->original_url,
"short_url" => $result->short_url
];
}
return $data;
}
# technically belongs to 'services/link_service.php' but placed here to shorten attached codes.
function createNewShortLink($link)
{
$sqlQry = "INSERT INTO links VALUES (DEFAULT, '$link->description', '$link->original_url', $link->base_url, '$link->unique_key', DEFAULT, DEFAULT);";
$link = new DatabaseTables\Link();
$link->getReadConnection()->query($sqlQry);
}
# technically belongs to 'services/link_service.php' but placed here to shorten attached codes.
# This is the experimental code i am working with
function isLinkIdValid($id)
{
/*
As it is, everything here doesn't work.
*/
$sqlQry = "SELECT IF(COUNT(l.link_id) = 0, 0, 1) 'is_exist' FROM links l WHERE l.link_id = $id;";
$result = $app->di->db->query($sqlQry);
$result->setFetchMode(Phalcon\Db::FETCH_ASSOC);
$result = $result->fetchAll($result);
return $result;
}
function getVisitorLogByLinkId($id)
{
$sqlQry = "SELECT * FROM visitors v WHERE v.link_id = $id ORDER BY v.visitor_id DESC;";
$visitor = new DatabaseTables\Visitor();
$resultSet = new ResultSet(null, $visitor, $visitor->getReadConnection()->query($sqlQry));
$data = [];
foreach ($resultSet as $result) {
$data[] = [
"visitor_name" => $result->visitor_name,
"access_timestamp" => $result->access_timestamp
];
}
return $data;
}
}
?>
To summarize, I'm looking for a way to fetch single-columned data from my database using RAW SQL Statements inside PhalconPHP. I've dug up several resources but there's just too few of them, if not lacking.
EDIT 1: i know that how i implemented the service is not perfect, and that is prone to security attacks. but please focus on the issue main issue. i'll resolve other issues afterwards. also, what i'm looking for is to be able to use the PdoMysql stored in $app->di["db"] from the index.php much like how i managed to in getAllLinks() and createNewShortLink($link) functions. however, i made a "temporary" solution by passing in the PdoMysql object from the $app->di["db"] to the function like below:
function isLinkIdValid($dbConn, $id)
{
$sqlQry = "SELECT IF(COUNT(l.link_id) = 0, 0, 1) 'is_exist' FROM links l WHERE l.link_id = $id;";
$isExist = $dbConn->fetchAll($sqlQry, \Phalcon\Db::FETCH_ASSOC);
return $isExist;
}

Amazon MWS (PHP) - Report Request API functions return without data, no error thrown

I am currently working with the Amazon MWS to integrate some features into wordpress via a plugin. I am using the client libraries provided by amazon found here:
https://developer.amazonservices.com/api.html?group=bde&section=reports&version=latest
Using these client libraries and the sample php files included I have set up my plugin to make two API calls. The first is requestReport
public function requestInventoryReport() {
AWI_Amazon_Config::defineCredentials(); // Defines data for API Call
$serviceUrl = "https://mws.amazonservices.com";
$config = array (
'ServiceURL' => $serviceUrl,
'ProxyHost' => null,
'ProxyPort' => -1,
'MaxErrorRetry' => 3,
);
$service = new MarketplaceWebService_Client(
AWS_ACCESS_KEY_ID,
AWS_SECRET_ACCESS_KEY,
$config,
APPLICATION_NAME,
APPLICATION_VERSION);
$request = new MarketplaceWebService_Model_RequestReportRequest();
$request->setMerchant(MERCHANT_ID);
$request->setReportType('_GET_MERCHANT_LISTINGS_DATA_');
self::invokeRequestReport($service, $request);
}
private function invokeRequestReport(MarketplaceWebService_Interface $service, $request) {
try {
$response = $service->requestReport($request);
if ($response->isSetRequestReportResult()) {
// Print Out Data
}
} catch (MarketplaceWebService_Exception $ex) {
// Print Out Error
}
}
and the second is getReportRequestList which has code similar to the first function. I am able to run these functions without any errors. The issue that I am having is that $response->isSetRequestReportResult() returns false. From my understanding and looking into the response object, this would suggest that the response object does not have the result. (Upon printing out the response object I can see that the FieldValue of the result array is NULL.) The call, however, does not throw an error but neither does it have the result.
I did some digging through the code and found that the result does actually get returned from the api call but never gets set to the return object when the library attempts to parse it from XML. I've tracked the error down to this block of code (This code is untouched by me and directly from the amazon mws reports library).
private function fromDOMElement(DOMElement $dom)
{
$xpath = new DOMXPath($dom->ownerDocument);
$xpath->registerNamespace('a', 'http://mws.amazonaws.com/doc/2009-01-01/');
foreach ($this->fields as $fieldName => $field) {
$fieldType = $field['FieldType'];
if (is_array($fieldType)) {
if ($this->isComplexType($fieldType[0])) {
// Handle Data
} else {
// Handle Data
}
} else {
if ($this->isComplexType($fieldType)) {
// Handle Data
} else {
$element = $xpath->query("./a:$fieldName/text()", $dom);
$data = null;
if ($element->length == 1) {
switch($this->fields[$fieldName]['FieldType']) {
case 'DateTime':
$data = new DateTime($element->item(0)->data,
new DateTimeZone('UTC'));
break;
case 'bool':
$value = $element->item(0)->data;
$data = $value === 'true' ? true : false;
break;
default:
$data = $element->item(0)->data;
break;
}
$this->fields[$fieldName]['FieldValue'] = $data;
}
}
}
}
}
The data that should go into the RequestReportResult exists at the beginning of this function as a node in the dom element. The flow of logic takes it into the last else statement inside the foreach. The code runs its query and returns $element however $element->length = 13 in my case which causes it to fail the if statement and never set the data to the object. I have also looked into $element->item(0) to see what was in it and it appears to be a dom object itself matching the original dom object but with a bunch of empty strings.
Now, I'm new to working with the MWS and my gut feeling is that I am missing a parameter somewhere in my api call that is messing up how the data is returned and is causing this weird error, but I'm out of ideas at this point. If anyone has any ideas or could point me in the right direction, I would greatly appreciate it.
Thanks for your time!
** Also as a side note, Amazon Scratchpad does return everything properly using the same parameters that I am using in my code **
These works for me, check if you are missing anything.
For RequestReportRequest i am doing this:
$request = new MarketplaceWebService_Model_RequestReportRequest();
$marketplaceIdArray = array("Id" => array($pos_data['marketplace_id']));
$request->setMarketplaceIdList($marketplaceIdArray);
$request->setMerchant($pos_data['merchant_id']);
$request->setReportType($this->report_type);
For GetReportRequestList i am doing this:
$service = new MarketplaceWebService_Client($pos_data['aws_access_key'], $pos_data['aws_secret_access_key'], $pos_data['config'], $pos_data['application_name'], $pos_data['application_version']);
$report_request = new MarketplaceWebService_Model_GetReportRequestListRequest();
$report_request->setMerchant($pos_data["merchant_id"]);
$report_type_request = new MarketplaceWebService_Model_TypeList();
$report_type_request->setType($this->report_type);
$report_request->setReportTypeList($report_type_request);
$report_request_status = $this->invokeGetReportRequestList($service, $report_request, $report_requestID);

PHP - get defined constants for a given namespace

I have a set of classes for data storage. It currently have a working connector for Google Drive, Dropbox, Box, FTP and local connection. I have designed this to construct a file manager where a client can manage multiple data storage sources.
Here comes my question. Currently, my implementation of the addConnection() method is the following:
// addConnection - adds a storage connection to the storage manager
public function addConnection($service, $credentials) {
if (!empty($service) && !empty($credentials)) {
if (in_array($service, [Connectors\Box, Connectors\Dropbox, Connectors\FTP, Connectors\GoogleDrive, Connectors\Local], true)) {
// rest goes here...
}
}
}
I have my constants in a separate namespace like this:
namespace Services\Storage\Connectors {
const Box = "Box";
const Dropbox = "Dropbox";
const FTP = "FTP";
const GoogleDrive = "GoogleDrive";
const Local = "Local";
}
Is there a way for me to get all defined constant for a given namespace? That way, I could construct a function to get all defined constants and write something more elegant like in_array($service, getConstants($this->getNamespace()), true).
Can you give me a hint?
You can get all user defined constants with get_defined_constants and then check the namespace using strpos:
$constants = get_defined_constants(TRUE)['user'];
foreach ($constants as $name => $value)
{
if (0 === strpos($name, 'services\storage\connectors'))
{
$services_storage_connectors[] = $name;
}
}
echo '<pre>' . print_r($services_storage_connectors, TRUE) . '</pre>';
/*
Array
(
[0] => services\storage\connectors\Box
[1] => services\storage\connectors\Dropbox
[2] => services\storage\connectors\FTP
[3] => services\storage\connectors\GoogleDrive
[4] => services\storage\connectors\Local
)
*/
Note that $name is lowercase, even if the namespace is defined with uppercase letters.
Edit: In case you'd like it in fewer lines
$constants = array_filter(array_keys(get_defined_constants(TRUE)['user']), function ($name)
{
return 0 === strpos($name, 'services\storage\connectors');
}
);

Codeigniter - Get several files, rename them and then zip it

I'm making a website, using codeigniter, that will enable users to download files a bit like gmail. By that I mean that the user can download only 1 file or all files in a zip folder.
Because there will be many files, I have encoded their names to avoid duplicates and stored their original names in a database which returns me an array like this:
Array
(
[0] => Array
(
[file_id] => 2
[file_name] => v6_copy.pdf
[file_path] => uploads/4/d5/67697ff58d09d3fb25d563bf85d3f1ac.pdf
)
[1] => Array
(
[file_id] => 3
[file_name] => v4_copy.pdf
[file_path] => uploads/7/cf/38212079635e93a8f8f4d4a3fc2a11ff.pdf
)
)
What I need to do is, get each file, rename them to their original names and then zip it in one zip. I'm currently trying to use the codeigniter zip helper, but I can't seem to be able to rename the files.
foreach ($query->result() as $row) // This returns what you see above
{
// I need to rename the file somewhere here
$this->zip->read_file($row->filename);
}
$this->zip->download('files_backup.zip');
Is there a way to do this without creating manually a directory, copying the files, renaming them and then zipping the file?
Any help most appreciated.
CodeIgniter's Zip Class apparently does not offer any means to rename entries. You can use PHP's native Zip Extension, which allows you to change the name when adding the file to the archive (and also later).
Example from PHP Manual
$zip = new ZipArchive;
if ($zip->open('test.zip') === TRUE) {
$zip->addFile('/path/to/index.txt', 'newname.txt');
$zip->close();
echo 'ok';
} else {
echo 'failed';
}
Thanks to #Gordon 's answer, I found a solution.
He is completely right about Codeigniter not being able to rename a file, but I found a really quick change to the library and it seems to be working.
If you go to your system>librairies->Zip.php like mentioned by #Gordon, search for "read_file" and you'll find the function.
Then I simply added a argument to the function and modified some of the code thereafter, see below:
function read_file($path, $preserve_filepath = FALSE, $name = NULL) // Added $name
{
if ( ! file_exists($path))
{
return FALSE;
}
if (FALSE !== ($data = file_get_contents($path)))
{
if($name == NULL){ // Added a verification to see if it is set, if not set, then it does it's normal thing, if it is set, it uses the defined var.
$name = str_replace("\\", "/", $path);
if ($preserve_filepath === FALSE)
{
$name = preg_replace("|.*/(.+)|", "\\1", $name);
}
}
$this->add_data($name, $data);
return TRUE;
}
return FALSE;
}
I hope this helps others. Thanks again #Gordon

check if object exists in Cloud Files (PHP API)

I've just started working with the PHP API for Rackspace Cloud Files. So far so good-- but I am using it as sort of a poor man's memcache, storing key/value pairs of serialized data.
My app attempts to grab the existing cached object by its key ('name' in the API language) using something like this:
$obj = $this->container->get_object($key);
The problem is, if the object doesn't exist, the API throws a fatal error rather than simply returning false. The "right" way to do this by the API would probably be to do a
$objs = $this->container->list_objects();
and then check for my $key value in that list. However, this seems way more time/CPU intensive than just returning false from the get_object request.
Is there a way to do a "search for object" or "check if object exists" in Cloud Files?
Thanks
I sent them a pull request and hope it'll get included.
https://github.com/rackspace/php-cloudfiles/pull/35
My pull-request includes an example, for you it would be similar to this:
$object = new CF_Object($this->container, 'key');
if ($object->exists() === false) {
echo "The object '{$object->name}' does not exist.";
}
I have more general way to check if object exists:
try {
$this->_container->get_object($path);
$booExists = true;
} catch (Exception $e) {
$booExists = false;
}
If you dump the $object, you'll see that content_length is zero. Or, last modified will be a zero length string.
Example:
$object = new CF_Object($container, 'thisdocaintthere.pdf');
print_r($object->content_length);
There is also, deep in the dumped parent object, a 404 that will return, but it's private, so you'd need to some hackin' to get at it.
To see this, do the following:
$object = new CF_Object($container, 'thisdocaintthere.pdf');
print_r($object->container->cfs_http);
You'll see inside that object a response_status that is 404
[response_status:CF_Http:private] => 404
I know I'm a little late to the party, but hopefully this will help someone in the future: you can use the objectExists() method to test if an object is available.
public static function getObject($container, $filename, $expirationTime = false)
{
if ($container->objectExists($filename)) {
$object = $container->getPartialObject($filename);
// return a private, temporary url
if ($expirationTime) {
return $object->getTemporaryUrl($expirationTime, 'GET');
}
// return a public url
return $object->getPublicUrl();
}
// object does not exist
return '';
}
Use like...
// public CDN file
$photo = self::getObject($container, 'myPublicfile.jpg');
// private file; temporary link expires after 60 seconds
$photo = self::getObject($container, 'myPrivatefile.jpg', 60);
If you do not want to import opencloud to perform this check you can use the following:
$url = 'YOUR CDN URL';
$code = FALSE;
$options['http'] = array(
'method' => "HEAD",
'ignore_errors' => 1,
'max_redirects' => 0
);
$body = file_get_contents($url, NULL, stream_context_create($options));
sscanf($http_response_header[0], 'HTTP/%*d.%*d %d', $code);
if($code!='200') {
echo 'failed';
} else {
echo 'exists';
}

Categories