Get all public folders / shared contacts from EWS office365 with PHP - php

I'm building a simple contact book from office 365, that lists all the shared contacts of my company.
I was trying with Graph and EWS too, but i can't figure out what is wrong.
Searching in the Microsoft Graph explorer seems to be no chance to see my "Other Contacts" -> "All Contacts" folder.
I've been trying with "/me/contactFolders" endpoint and with "/people" endpoint. Non of them gave me results.
I also used a php-ews library (the project is build on Laravel) to access to the folders via Exchange, with no luck.
using this example, i'm able to list just my contact, without any chance to see other folders or other kind of contacts.
Does anybody have any tip for a newbbie?!
thanks in advance.
EDIT
this is the Controller that works with PHP-EWS library
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use garethp\ews\ContactsAPI as ContactsAPI;
use garethp\ews\API;
use garethp\ews\API\Enumeration;
use garethp\ews\API\Type;
use garethp\ews\API\ExchangeWebServices;
//use garethp\ews\API\ExchangeAutodiscover;
//use garethp\ews\API\Exception\AutodiscoverFailed;
class SharedContatctsController extends Controller
{
//
public function index()
{
# code...
$mail='developeremail#example.com';
$pwd='password';
$version='Exchange2016';
//$apiS = ExchangeAutodiscover::getAPI($mail, $pwd);
//$server=$apiS->getClient()->getServer();
$server='mail.example.com;
$api = ContactsAPI::withUsernameAndPassword($server, $mail, $pwd);
$contacts = $api->getContacts();
//return print($api->getFolderId());
//If you want to get contacts within a folder
//$folder = $api->getFolderByDisplayName('Altri Contatti', 'contacts');
//$contacts = $api->getContacts($folder->getFolderId());
return view('shared',array('contacts'=>$contacts,'displayName'=>$contacts['displayName']));
}
}
This is the Controller that (quite works) displays the "BackupContacts" folder that is in the same directory as "Contact"
<?php
namespace App\Http\Controllers;
use Microsoft\Graph\Graph;
use Microsoft\Graph\Model;
use App\Http\Controllers\Controller;
class OutlookController extends Controller
{
public function contacts()
{
if (session_status() == PHP_SESSION_NONE) {
session_start();
}
$tokenCache = new \App\TokenStore\TokenCache;
$graph = new Graph();
$graph->setAccessToken($tokenCache->getAccessToken());
$user = $graph->createRequest('GET', '/me')
->setReturnType(Model\User::class)
->execute();
$contactsQueryParams = array (
// // Only return givenName, surname, and emailAddresses fields
//"\$select" => "displayName,scoredEmailAddresses",
// Sort by given name
//"\$orderby" => "givenName ASC",
// Return at most 10 results
"\$orderby"=>"displayName",
"\$top" => "1000"
);
$getContactsUrl = '/me/contactFolders/{BackuPlderId-retrieved-with-Graph}/contacts/?'.http_build_query($contactsQueryParams);
$contacts = $graph->createRequest('GET', $getContactsUrl)
->addHeaders(array ('X-AnchorMailbox' => $user->getMail()))
->setReturnType(Model\Contact::class)
->execute();
return view('contacts', array(
'username' => $user->getdisplayName(),
'usermail' => $user->getMail(),
'contacts' => $contacts
));
}
}

The /me/contacts will return only the contacts in the current user's default contact folder. Similarly, the /me/contactFolders will only return the contact folders for the current user's mailbox (and an empty result if there isn't anything beyond their default folder.
The key here is the /me element. This is synonymous with currently authenticated user. So if the user who authenticated was person#company.com both /me/contacts and /users/person#company.com/contacts would return the exact same results.
The currently isn't a method for retrieving Organization Contacts (i.e. contacts stored in your GAL) from the /v1.0 API set. There is however an orgContact object in the /beta API so support is forthcoming.

Related

Laravel Feature testing, how do i test if a profile page has been created with the user?

I created my feature test;
ProfilesControllerTest.php
<?php
namespace Tests\Feature;
use App\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;
class ProfileControllerTest extends TestCase
{
use RefreshDatabase;
/**
* A basic feature test example.
*
* #return void
*/
public function test_the_profile_page_is_rendered()
{
// First The user is created
$user = User::factory()->create();
//act as user
$this->actingAs($user);
// Then we want to make sure a profile page is created
$response = $this->get('/profile/{user}');
//
$response->assertStatus(200);
}
}
web.php
Route::get('/profile/{user}', 'ProfilesController#index')->name('profiles.show');
But it keeps returning an error. I Suspect it is because of the profile link, however I am unsure of how to show it. I have attempted a few variations and i have not managed to get it to work.
I realised factory was not working so instead I tried this;
ProfilesControllerTest.php
public function test_the_profile_page_is_rendered()
{
// First The user is created
$user = User::make([
'name' => 'John Doe',
'username' => 'johnnyd',
'email' => 'johndoe#email.com'
]);
//act as user
$this->actingAs($user);
// Then we want to make sure a profile page is created
$response = $this->get('/profile/{$user}');
$response->assertStatus(200);
}
And I kept getting the error:
Error
php artisan test
PASS Tests\Unit\ExampleTest
✓ basic test
PASS Tests\Unit\UserTest
✓ login form
✓ user duplication
PASS Tests\Feature\ExampleTest
✓ basic test
FAIL Tests\Feature\ProfileControllerTest
✕ the profile page is rendered
Tests: 1 failed, 4 passed, 1 pending
Expected status code 200 but received 404. Failed asserting that 200 is identical to 404.
at tests/Feature/ProfileControllerTest.php:34
30| // Then we want to make sure a profile page is created
31| $response = $this->get('/profile/{$user');
32|
33| //
> 34| $response->assertStatus(200);
35| }
36| }
37|
Profile controller for index was written as follows:
ProfilesController.php
class ProfilesController extends Controller
{
public function index(User $user)
{
$postCount = Cache::remember(
'count.posts.' . $user->id,
now()->addSeconds(30),
function () use ($user) {
return $user->posts->count();
}
);
return view('profiles.index', compact('user', 'postCount'));
}
}
Your first test isn't working because you're attempting to access the wrong URL. You're attempting to go to http://localhost/profile/{user}. That URL is not correct, as there is no user with an id of "{user}". The URL you want to access is http://localhost/profile/1, to see the profile of the user with id 1.
To fix the first test, fix the URL:
// bad
// $response = $this->get('/profile/{user}');
// good
$response = $this->get('/profile/'.$user->id);
Your second test is failing for two reasons:
User::make() will make a new instance of the User model, but it will not persist anything to the database. Since the User won't exist in the database, it does not have a profile URL you can visit.
Again, as in the first test, the profile URL you're trying to visit is wrong.
So, go back to the first test, correct the URL, and you should be good.

How to show an alert message to a user with Event Listener

I have created an Event called UserWalletNewTransaction.php and added this to it:
public $transaction;
public function __construct($transaction) {
$this->$transaction = $transaction;
}
Now in order to fire this event at the Controller, I coded this:
$newTransaction = UserWalletTransaction::create(['user_id' => $user_id, 'wallet_id' => $wallet_id, 'creator_id' => $creator_id, 'amount' => $amount_add_value, 'description' => $trans_desc]);
event(new UserWalletNewTransaction($newTransaction));
Then at the Listener, UserWalletNotification.php, I tried:
public function handle(UserWalletNewTransaction $event) {
$uid = $event->transaction->user_id;
$user = User::find($uid);
// now sends alert message to the user
}
So the scenario is, when Admins create a new Transaction for a custom user, a new alert message must be sent for him/her to let him/her know that new transaction was added for him/her.
But I don't really know how to do that.. So if you know, please let me know, I would really appreciate that...
Thanks in advance.
If by alert you mean showing a message on the web interface, use flash data.
https://laravel.com/docs/5.8/session#flash-data
$newTransaction = UserWalletTransaction::create(...);
event(new UserWalletNewTransaction($newTransaction));
$request->session()->flash('status', 'Transaction done.');
return view(...)
<span>{{ session('status') }}</span>
If you mean sending an email, just use the Mail facade in your listener to send a mailable.
https://laravel.com/docs/5.8/mail#sending-mail
public function handle(UserWalletNewTransaction $event) {
$uid = $event->transaction->user_id;
$user = User::find($uid);
Mail::to($user)->send(new TransactionDoneMail($event->transaction)); // TransactionDoneMail being your mailable class, made with "php artisan make:email TransactionDoneMail"
}
There are nice examples on how to build a mailable class in the documentation.
https://laravel.com/docs/5.8/mail#writing-mailables
There are many different things you can do in terms of "alerting" the customer.
One route would be to send an email or text message in your event listener. See https://laravel.com/docs/5.8/mail for help doing it via email.
Another way would be using browser push notifications. You could use OneSignal for this. You would setup the front end to display an alert to a customer user asking if they would like to subscribe to push notifications. When they subscribe, you will get back an ID for that specific user. Make an API call to your Laravel app, and store that ID in the users table (you will need a migration). Then from within your event listener, you can make a call to OneSignal's API and send the user a notification, which will popup on their computer.
Here is an example of using OneSignal to send an event to a user via the API:
Your OneSignal service:
<?php
namespace App\Services;
use App\User;
use GuzzleHttp\Client;
class OneSignalService
{
public function sendNotificationToUser(User $user, string $title, string $message, string $url, string $subtitle = null)
{
if (!$user->one_signal_id) {
return;
}
$fields = [
'app_id' => config('services.onesignal.app_id'),
'include_player_ids' => [$user->one_signal_id],
'headings' => ['en' => $title],
'contents' => ['en' => $message],
'url' => $url,
];
if ($subtitle) {
$fields['subtitle'] = ['en' => $subtitle];
}
$client = new Client([
'base_uri' => 'https://onesignal.com/api/v1/',
'headers' => [
'Content-Type' => 'application/json; charset=utf-8',
'Authorization' => 'Basic <<API_KEY>>',
]
]);
$client->request('POST', 'notifications', [
'json' => $fields
])
}
}
UserWalletNotification:
public function handle(UserWalletNewTransaction $event) {
$uid = $event->transaction->user_id;
$user = User::find($uid);
// now sends alert message to the user
$oneSignal = new OneSignalService();
$oneSignal->sendNotificationToUser($user, 'New Transaction', 'You have a new transaction', 'yourwebsite.com');
}
The way I would go about this would be via broadcasting, which would use websockets to instantly send the customer user an alert to their browser, in which you could then display a popup of some sort. You could install Laravel Echo Server, but to keep things simple you can use Pusher. Follow the guide to install on the front end of your website.
Then, create a private channel specific to a customer user "transaction.created.{{USER ID}}" and listen for it on your front end.
Within Laravel you will install the PHP Pusher SDK via composer.
Then within your .env file set:
BROADCAST_DRIVER=pusher
Next, open up channels.php within your routes directory in Laravel and add:
Broadcast::channel('transaction.created.{id}', function ($user, $id) {
return (int) $user->id === (int) $id;
});
This will verify authentication for your user to the private channel.
Create an Laravel Event:
<?php
namespace App\Events;
use App\User;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class TransactionCreated implements ShouldBroadcastNow
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $user = null;
public $transaction = null;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct(User $user, UserWalletTransaction $transaction)
{
$this->user = $user;
$this->transaction = $transaction;
}
public function broadcastWith(): array
{
return $this->transaction->toArray(); //Or whatever information you want to send to the front end
}
public function broadcastAs(): string
{
return 'TransactionCreated';
}
/**
* Get the channels the event should broadcast on.
*
* #return Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('transaction.created.' . $this->user->id);
}
}
Fire the event from UserWalletNotification:
public function handle(UserWalletNewTransaction $event) {
$uid = $event->transaction->user_id;
$user = User::find($uid);
// now sends alert message to the user
event(new TransactionCreated($user, $event->transaction));
}
Lastly, create some sort of popup and display it on the front end when your callback function for the private channel is hit.
If you need anymore help, feel free to comment.
What you want to do I believe, is asynchronous notifications.
Well, if you really mean flash messages - those who are stored in session - it will not be so easy.
Normal steps are create flash message for the user currently logged in on a website, stored in session that is unique for the current user. It can be shown only for this user.
What you want is to create flash message as the admin (from admin perspective) , then only to admin it can be shown.
I would do this, create new table, when these notification messages will be stored. Some table with columns like id, user_id, message, type, created_date, shown_date. Admins will put alert/notification messages for each user. Then create class (can be in controller for example) that will check this table for each user and if there is new not already shown message, show it normally in flash message for that current user. Dont forget to mark that message as shown. That is it.
So much for custom solution. I belive there must be some for example jQuery/other Jvascript plugins or Laravel plugins for asynchronous notifications, please check those.

Method App\Http\Controllers\XeroController::data does not exist

Hello i am having xero API
i am trying to integrate it with my laravel project , i am getting above error I am using following laravel package for same.
github package link : https://github.com/webfox/laravel-xero-oauth2/
----------------routes-----------------
Route::get('/manage/xero', [XeroController::class, 'index'])->name('xero.auth.success');
Route::get('xero/auth/callback', [XeroController::class, 'data'])->name('xero.auth.callback');
---controller-----------------
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Webfox\Xero\OauthCredentialManager;
class XeroController extends Controller
{
public function index(Request $request, OauthCredentialManager $xeroCredentials)
{
try {
// Check if we've got any stored credentials
if ($xeroCredentials->exists()) {
/*
* We have stored credentials so we can resolve the AccountingApi,
* If we were sure we already had some stored credentials then we could just resolve this through the controller
* But since we use this route for the initial authentication we cannot be sure!
*/
$xero = resolve(\XeroAPI\XeroPHP\Api\AccountingApi::class);
$organisationName = $xero->getOrganisations($xeroCredentials->getTenantId())->getOrganisations()[0]->getName();
$user = $xeroCredentials->getUser();
$username = "{$user['given_name']} {$user['family_name']} ({$user['username']})";
}
} catch (\throwable $e) {
// This can happen if the credentials have been revoked or there is an error with the organisation (e.g. it's expired)
$error = $e->getMessage();
}
return view('xero', [
'connected' => $xeroCredentials->exists(),
'error' => $error ?? null,
'organisationName' => $organisationName ?? null,
'username' => $username ?? null
]);
}
}
Your xero/auth/callback route is routed to the XeroController::data() function, which does not exist.
Looking at that package, it looks like it already registers a route for xero/auth/callback, pointing to the AuthorizationCallbackController in the package. I'm assuming you just need to remove your manually defined route.

Laravel 5.2 and Braintree Custom Webhooks

I'm using Braintree to collect payments for a SaaS subscription site built using Laravel 5.2 and testing it on my localhost using ngrok.io. The signup and initial charges work fine. I can cancel and resume subscriptions, as well. However, I can't get the custom webhooks to work. I'm following the Laravel docs. Here's my code:
routes.php
Route::post('/webhook/braintree', 'BraintreeWebhookController#handleWebhook');
BraintreeWebhookController.php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use Braintree\WebhookNotification;
use Laravel\Cashier\Http\Controllers\WebhookController;
use App\Transaction;
class BraintreeWebhookController extends WebhookController
{
//
public function handleSubscriptionChargedSuccessfully(WebhookNotification $notification)
{
$transaction = new Transaction();
$transaction->subscript_id = $notification->subscription->id;
$transaction->name = $notification->subscription->name;
$transaction->next_bill_date = $notification->subscription->next_bill_date;
$transaction->price = $notification->subscription->price;
$transaction->save();
return new Response('Webhook Handled', 200);
}
public function handleSubscriptionChargedUnsuccessfully(WebhookNotification $notification)
{
//
}
public function handleSubscriptionTrialEnded(WebhookNotification $notification)
{
//
}
}
The Braintree Webhooks Test URL link returns "Success! Server responded with 200." I tested the Transaction model using a test form and it creates a database record just fine. Is the Subscription_Charged_Successfully webhook not making it through to the controller or am I missing something in the handle function? Any help here would be greatly appreciated.
I'm pretty much doing the same as what you've got here. I'm using ngrok to test my local setup with braintree and I've figured out there's an exception with your code.
ErrorException: Undefined property on Braintree\Subscription: name in file /var/www/vendor/braintree/braintree_php/lib/Braintree/Base.php on line 49
Looks like name is not a property on subscription object
I figured out that you can get your local subscription like so
$localSubscription = Subscription::where('braintree_id', $notification->subscription->id)
->first();
Transaction::create([
'subscription_id' => $notification->subscription->id,
'name' => $localSubscription->name,
'next_bill_date' => $notification->subscription-> nextBillingDate,
'price' => $notification->subscription->price,
'user_id' => $localSubscription->user->id,
]);
Where Subscription is Laravel\Cashier\Subscription.
EDIT
Also next_bill_date should be nextBillingDate as seen above.

Android App interaction with Laravel Action methods

I already have Laravel web pages where i can add/update/delete/Read records from MySQL Database. Laravel version is 5.2.15
Now, I have to integrate Database with Android App. In order to do that I have to post and read Json Data.
Here question is: Should I have 2 public action methods? First for web page that will show records on webpage and second will return json data in Android.
I meant, when I return data to webPage..I will have to write the below code.
return View("View-Path", array("Data" => $Data));
but in case of Android App, I will have to request Json Data.
Please suggest the right approach.
You should develop a simple API to access your APP data from an android client:
Routes
First of all you need to create some specific routes for the API through which you'll serve your data in JSON format
Authentication
The API's routes should handle authentication in a different way in respect on what you're doing now: you can't use the classic session-based approach. Instead you have to use a basic or token-based approach. You've different alternatives, these are some of the most used (from the simplest, to the most complicated )
Laravel HTTP Basic Authentication
Laravel Json Web Token Authentication
Laravel OAUTH2
Data Acess
Once you've setup your routes and authentication, you have to serve your data via the API routes. Since you use the same data in your APP routes and API routes, you can wrap the logic of data building and retrieving in services, and use the services to get the data both in your APP routes and API routes.
Using different controllers for API and APP routes, you have:
//APP Controller method for route: www.app.com/app-route/users
public function getUsers()
{
//wrap the logic to build the data inside the service
$service = App::make('your_service');
//once is ready, get the built data from the service
$data = $service->getData();
return View("View-Path", array("Data" => $data));
}
//API Controller method for route: www.app.com/api/users
public function getUsers()
{
//use the same service to build and get the data you need
$service = App::make('your_service');
$data = $service->getData();
return response()->json( $data );
}
This way you can:
Encapsulate data building and retrieveng in services, so that you don't have the need to duplicate code for data retrieving between APP and API routes
Have different controllers to access APP or API routes; so you can get the data, transform it as you need and serve it to either views or api clients
About the Service class
Regarding the service class i've mentioned, it could be simply one or multiple wrapper classes that you use both in API and APP controllers to build and get the data without repeting code. The structure of such classes depends on how your app work.
For example let's suppose you need to compute some data for each user's project, store it in a variable and then send it to the viev:
public function getUsers($request)
{
$user = Users::with('projects')->find( $request->user_id )->get();
$data = [];
foreach ( $user->projects as $p )
{
//compute some data you need and store it in $data;
}
return View("View-Path", array("Data" => $data));
}
Now if want to make the same thing in the API controller, you'd need to repete this code to get the projects and create the data. To avoid this, you could 'wrap' the data access in a service class, and use the same class in boh controllers:
Service class
public class UserDataBuilder
{
protected $user;
public function setUser( Authenticatable $user )
{
$this->user = $user;
}
public function getData()
{
$user = Users::with('projects')->find( $this-user->id )->get();
$data = [];
foreach ( $user->projects as $p )
{
//compute some data you need and store it in $data;
}
return $data;
}
}
and use the same class in both API and APP controllers:
//APP controller: get the data and pass to the view
public function getUsers($request)
{
$service = App::make( UserDataBuilder::class );
$service->setUser( User::find( $request->user_id )->get() );
return View("View-Path", array("Data" => $service->getData() );
}
//API controller: get the data and convert to JSON
public function getUsers($request)
{
$service = App::make( UserDataBuilder::class );
$service->setUser( User::find(1)->get() );
return response()->json( $data );
}
For android you have to write a separate web service.
You can use one method to do that. Example
public function someMethod(Request $request){
$Data = ['laravel','is','awesome'];
// If the request has the content type is json return a json response
// So you android request will be true for this request
// else return data to webpage
if($request->isJson()){
return response()->json($Data);
}
return View("View-Path", array("Data" => $Data));
}

Categories