: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* Simple elFinder driver for OneDrive
* @author Dmitry (dio) Levashov
* @author Cem (discofever)
class elFinderVolumeOneDrive extends elFinderVolumeDriver
* Must be started from letter and contains [a-z0-9]
* Used as part of volume id.
protected $driverId = 'od';
* @var string The base URL for API requests
const API_URL = 'https://graph.microsoft.com/v1.0/me/drive/items/';
* @var string The base URL for authorization requests
const AUTH_URL = 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize';
* @var string The base URL for token requests
const TOKEN_URL = 'https://login.microsoftonline.com/common/oauth2/v2.0/token';
* Directory for tmp files
* If not set driver will try to use tmbDir as tmpDir.
public $netMountKey = '';
protected $tmbPrefix = '';
protected $HasdirsCache = array();
* Query options of API call.
protected $queryOptions = array();
* Path to access token file for permanent mount
private $aTokenFile = '';
* Extend options with required fields.
* @author Dmitry (dio) Levashov
* @author Cem (DiscoFever)
public function __construct()
'root' => 'OneDrive.com',
'OneDriveApiClient' => '',
'acceptedName' => '#^[^/\\?*:|"<>]*[^./\\?*:|"<>]$#',
'rootCssClass' => 'elfinder-navbar-root-onedrive',
'useApiThumbnail' => true,
$this->options = array_merge($this->options, $opts);
$this->options['mimeDetect'] = 'internal';
/*********************************************************************/
/*********************************************************************/
* Obtains a new access token from OAuth. This token is valid for one hour.
* @param string $code The code returned by OneDrive after
* @throws Exception Thrown if the redirect URI of this Client instance's
protected function _od_obtainAccessToken($client_id, $client_secret, $code, $nodeid)
if (null === $client_id) {
return 'The client ID must be set to call obtainAccessToken()';
if (null === $client_secret) {
return 'The client Secret must be set to call obtainAccessToken()';
$redirect = elFinder::getConnectorUrl();
if (strpos($redirect, '/netmount/onedrive/') === false) {
$redirect .= '/netmount/onedrive/' . ($nodeid === 'elfinder'? '1' : $nodeid);
$fields = http_build_query(
'client_id' => $client_id,
'redirect_uri' => $redirect,
'client_secret' => $client_secret,
'grant_type' => 'authorization_code',
curl_setopt_array($curl, array(
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POSTFIELDS => $fields,
CURLOPT_HTTPHEADER => array(
'Content-Length: ' . strlen($fields),
$result = elFinder::curlExec($curl);
$decoded = json_decode($result);
throw new \Exception('json_decode() failed');
if (!empty($decoded->error)) {
$error = $decoded->error;
if (!empty($decoded->error_description)) {
$error .= ': ' . $decoded->error_description;
throw new \Exception($error);
'expires' => time() + $decoded->expires_in - 30,
if (!empty($decoded->refresh_token)) {
$res->initialToken = md5($client_id . $decoded->refresh_token);
* Get token and auto refresh.
protected function _od_refreshToken()
if (!property_exists($this->token, 'expires') || $this->token->expires < time()) {
if (!$this->options['client_id']) {
$this->options['client_id'] = ELFINDER_ONEDRIVE_CLIENTID;
if (!$this->options['client_secret']) {
$this->options['client_secret'] = ELFINDER_ONEDRIVE_CLIENTSECRET;
if (empty($this->token->data->refresh_token)) {
throw new \Exception(elFinder::ERROR_REAUTH_REQUIRE);
$refresh_token = $this->token->data->refresh_token;
$initialToken = $this->_od_getInitialToken();
curl_setopt_array($curl, array(
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true, // i am sending post data
CURLOPT_POSTFIELDS => 'client_id=' . urlencode($this->options['client_id'])
. '&client_secret=' . urlencode($this->options['client_secret'])
. '&grant_type=refresh_token'
. '&refresh_token=' . urlencode($this->token->data->refresh_token),
$result = elFinder::curlExec($curl);
$decoded = json_decode($result);
throw new \Exception('json_decode() failed');
if (empty($decoded->access_token)) {
if (is_file($this->aTokenFile)) {
unlink($this->aTokenFile);
$err = property_exists($decoded, 'error')? ' ' . $decoded->error : '';
$err .= property_exists($decoded, 'error_description')? ' ' . $decoded->error_description : '';
throw new \Exception($err? $err : elFinder::ERROR_REAUTH_REQUIRE);
'expires' => time() + $decoded->expires_in - 30,
'initialToken' => $initialToken,
$json = json_encode($token);
if (!empty($decoded->refresh_token)) {
if (empty($this->options['netkey']) && $this->aTokenFile) {
file_put_contents($this->aTokenFile, json_encode($token));
$this->options['accessToken'] = $json;
} else if (!empty($this->options['netkey'])) {
// OAuth2 refresh token can be used only once,
// so update it if it is the same as the token file
$aTokenFile = $this->_od_getATokenFile();
if ($aTokenFile && is_file($aTokenFile)) {
if ($_token = json_decode(file_get_contents($aTokenFile))) {
if ($_token->data->refresh_token === $refresh_token) {
file_put_contents($aTokenFile, $json);
$this->options['accessToken'] = $json;
elFinder::$instance->updateNetVolumeOption($this->options['netkey'], 'accessToken', $this->options['accessToken']);
$this->session->set('OneDriveTokens', $token);
throw new \Exception(elFinder::ERROR_CREATING_TEMP_DIR);
* Get Parent ID, Item ID, Parent Path as an array from path.
protected function _od_splitPath($path)
$path = trim($path, '/');
$paths = explode('/', trim($path, '/'));
$parent = '/' . implode('/', $paths);
$pid = array_pop($paths);
return array($pid, $id, $parent);
* Creates a base cURL object which is compatible with the OneDrive API.
* @return resource A compatible cURL object
protected function _od_prepareCurl($url = null)
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => array(
'Content-Type: application/json',
'Authorization: Bearer ' . $this->token->data->access_token,
curl_setopt_array($curl, $defaultOptions);
* Creates a base cURL object which is compatible with the OneDrive API.
* @param string $path The path of the API call (eg. me/skydrive)
* @return resource A compatible cURL object
* @throws elFinderAbortException
protected function _od_createCurl($path, $contents = false)
elFinder::checkAborted();
$curl = $this->_od_prepareCurl($path);
$res = elFinder::curlExec($curl);
$result = json_decode(elFinder::curlExec($curl));
if (isset($result->value)) {
$result = (array)$result;
if (!empty($result['@odata.nextLink'])) {
$nextRes = $this->_od_createCurl($result['@odata.nextLink'], false);
if (is_array($nextRes)) {
$res = array_merge($res, $nextRes);
* Drive query and fetchAll.
* @param bool $fetch_self
* @throws elFinderAbortException
protected function _od_query($itemId, $fetch_self = false, $recursive = false, $options = array())
if ($fetch_self == true) {
$path = $itemId . '/children';
if (isset($options['query'])) {
$path .= '?' . http_build_query($options['query']);
$url = self::API_URL . $path;
$res = $this->_od_createCurl($url);
if (!$fetch_self && $recursive && is_array($res)) {
foreach ($res as $file) {
if (!empty($file->folder)) {
$result = array_merge($result, $this->_od_query($file->id, false, true, $options));
return isset($result->error) ? array() : $result;
* Parse line from onedrive metadata output and return file stat (array).
* @param object $raw line from ftp_rawlist() output
* @author Dmitry Levashov
protected function _od_parseRaw($raw)
$folder = isset($raw->folder) ? $raw->folder : null;
$stat['rev'] = isset($raw->id) ? $raw->id : 'root';
$stat['name'] = $raw->name;
if (isset($raw->lastModifiedDateTime)) {
$stat['ts'] = strtotime($raw->lastModifiedDateTime);
$stat['mime'] = 'directory';
if (empty($folder->childCount)) {
if (isset($raw->file->mimeType)) {
$stat['mime'] = $raw->file->mimeType;
$stat['size'] = (int)$raw->size;
if (!$this->disabledGetUrl) {
if (isset($raw->image) && $img = $raw->image) {
isset($img->width) ? $stat['width'] = $img->width : $stat['width'] = 0;
isset($img->height) ? $stat['height'] = $img->height : $stat['height'] = 0;
if (!empty($raw->thumbnails)) {
if ($raw->thumbnails[0]->small->url) {
$stat['tmb'] = substr($raw->thumbnails[0]->small->url, 8); // remove "https://"
} elseif (!empty($raw->file->processingMetadata)) {
* Get raw data(onedrive metadata) from OneDrive.
* @return array|object onedrive metadata
protected function _od_getFileRaw($path)
list(, $itemId) = $this->_od_splitPath($path);
$res = $this->_od_query($itemId, true, false, $this->queryOptions);