Series
In this series, we will build a file manager in Laravel that reads and writes to Dropbox.
Application Register
Before we start on the code we need a Dropbox App registered at https://www.dropbox.com/developers/apps/create
- Choose Dropbox API.
- For the type of access select Full App.
- Give your app a name.
- Select which Account to create the app in (you may only have one option)
- Click Create App
Now you have an app registered, there are a few settings that are required, take notice of:
- App Key
- App Secret
They will both be required in Laravel. There are two options for our integration either use the app with your account only or let users access their Dropbox account.
Option 1 Use your own account only
This is the simpler option all you need to do is click Generate Access Token and copy and paste the code into a .env file:
DROPBOX_ACCESS_TOKEN=
This means you will only ever be able to use your own account for the integration.
Option 2 Let users access their own account
Let users connect to their own account using Oauth2. This means a user goes to a URL and then is redirected to Dropbox to approve the connection once complete an Access Token will be available to store against the user. The token can be revoked at any time directly from Dropbox.
Install a Dropbox Laravel package
Open a Laravel project (new or existing), we're going to install my Laravel Dropbox package
The complete docs for this package can be found at http://docs.dcblog.dev/laravel-dropbox
composer require daveismyname/laravel-dropbox
In Laravel 5.5 the service provider will automatically get registered. In older versions of the framework just add the service provider in config/app.php file
'providers' => [
// ...
Daveismyname\Dropbox\DropboxServiceProvider::class,
];
You can publish the config file with:
php artisan vendor:publish --provider="Daveismyname\Dropbox\DropboxServiceProvider" --tag="config"
You can publish the migration with: (this is required to store the access token)
php artisan vendor:publish --provider="Daveismyname\Dropbox\DropboxServiceProvider" --tag="migrations"
After the migration has been published you can create the tokens tables by running the migration:
php artisan migrate
Ensure you've set the following in your .env file:
DROPBOX_CLIENT_ID=
DROPBOX_SECRET_ID=
DROPBOX_OAUTH_URL=https://domain.com/filemanager/oauth
DROPBOX_LANDING_URL=https://domain.com/filemanager
Change the urls above as needed I'm choosing to use domain.com/filemanager as my project url.
Use Oauth2 to connect to a users Dropbox Account
Note you should already have authentication setup in Laravel and be logged in. See https://laravel.com/docs/5.8/authentication
Go back to Dropbox App Console, a redirect URL needs setting up.
Go to the Redirect URIs section and add this URL:
https://domain.com/filemanager/oauth
This tells Dropbox where to redirect to.
Back in Laravel
In order to start the integration, we need a route that will redirect to Dropbox to start the Oauth2 process. In routes/web.php add:
Route::get('filemanager/oauth', function(){
return Dropbox::connect();
});
Now going to /filemanager/oauth in the browser will redirect you to Dropbox.
Since the app on Dropbox has not been published you will see this warning:
Click continue to carry on.
You will then see a screen like this:
Select an account to connect to. You will then be redirected to the URL set in your .env file:
DROPBOX_LANDING_URL=https://domain.com/filemanager
This route has not yet been setup, all routes at this point should only be available when authenticated with Dropbox the Laravel Dropbox package has a simple way of taking care of this with middleware:
Route::group(['middleware' => ['auth', 'DropboxAuthenticated']], function() {
Route::get('filemanager/{path?}', 'FileManagerController@index')->where('path', '(.*)');
});
This closure uses a DropboxAuthenticated middleware which ensures you are connected to Dropbox otherwise you will be redirected for approval.
Next, a new route is defined filemanager/{path?} this is a wildcard route meaning it will respond to not only /filemanager but also paths beyond like /filemanager/foldername
Create a new controller called FileManagerController
<?php
namespace App\Http\Controllers;
use Daveismyname\Dropbox\Facades\Dropbox;
use Exception;
class FileManagerController extends Controller
{
public function index($path = '')
{
$results = Dropbox::files()->listContents($path);
return view('filemanager.index', compact('results'));
}
}
Inside the controller we can access the list of folders and files for a given path by calling:
Dropbox::files()->listContents($path)
The $path comes from the route directly.
Pass this to a view called filemanager/index.blade.php and add:
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<table class="table">
@foreach($results['entries'] as $row)
@php
$type = $row['.tag'] == 'folder' ? 'folder' : 'file';
$name = $row['name'];
$pathLower = $row['path_lower'];
@endphp
<tr>
<td>
@if ($type == 'folder')
<a href='{{ url('filemanager'.$pathLower) }}'><i class="fa fa-folder"></i> {{ $name }}</a><br>
@else
{{ $name }}
@endif
</td>
</tr>
@endforeach
</table>
</div>
</div>
</div>
@endsection
This forms the layout of the File Manager, let's go through it.
We loop through the files and folders by doing a foreach loop on $results['entries']
Inside the data is an attribute called .tag this will be either file or folder to let's add that to a $type
$type = $row['.tag'] == 'folder' ? 'folder' : 'file';
We also want the name of the file/folder and its path:
$name = $row['name'];
$pathLower = $row['path_lower'];
Listing the files/folders if it's a folder then we should add a link so it's clickable otherwise just show the name.
@if ($type == 'folder')
<a href='{{ url('filemanager'.$pathLower) }}'><i class="fa fa-folder"></i> {{ $name }}</a><br>
@else
{{ $name }}
@endif
Thanks to the wildcard route for filemanager/{path?) we can access any folder by going to /filemanager/foldername as long as the folder path exists on the dropbox account its contents will be displayed.