Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@
}
],
"require": {
"guzzlehttp/guzzle": "^6.3",
"symfony/options-resolver": "^3.4"
"guzzlehttp/guzzle": "^7.0",
"symfony/options-resolver": "^3.4",
"ext-simplexml": "*"
},
"autoload": {
"psr-4": {"NextcloudApiWrapper\\": "src/NextcloudApiWrapper"}
Expand Down
32 changes: 32 additions & 0 deletions src/NextcloudApiWrapper/WebDAV/AbstractClient.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

namespace NextcloudApiWrapper\WebDAV;


use DOMDocument;

abstract class AbstractClient extends \NextcloudApiWrapper\AbstractClient
{
/**
* @var Connection
*/
protected $connection;

public function __construct(Connection $connection)
{
parent::__construct($connection);
}

/**
* @param SimpleXMLElement $simpleXMLElement
* @return string
*/
function formatXml($simpleXMLElement)
{
$xmlDocument = new DOMDocument('1.0');
$xmlDocument->preserveWhiteSpace = false;
$xmlDocument->formatOutput = true;
$xmlDocument->loadXML($simpleXMLElement->asXML());
return $xmlDocument->saveXML();
}
}
106 changes: 106 additions & 0 deletions src/NextcloudApiWrapper/WebDAV/Connection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<?php


namespace NextcloudApiWrapper\WebDAV;


use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\RequestOptions;
use NextcloudApiWrapper\NCException;
use NextcloudApiWrapper\NextcloudResponse;
use Psr\Http\Message\ResponseInterface;

class Connection extends \NextcloudApiWrapper\Connection
{

const URL_SUFIX = "remote.php/dav";

const PROFIND = "PROFIND";
const PROPPATH = "PROPPATH";
const REPORT = "REPORT";

const MKCOL = "MKCOL";
const MOVE = "MOVE";
const COPY = "COPY";

/**
* @param string $basePath The base path to nextcloud api, IE. 'http://nextcloud.mydomain.com'
* @param string $username The username of the user performing api calls
* @param string $password The password of the user performing api calls
*/
public function __construct($basePath, $username, $password)
{
parent::__construct($basePath . "/" . self::URL_SUFIX . "/", $username, $password);
}

/**
* @param $method
* @param $uri
* @param $xml
* @param array $header
* @return NextcloudResponse
* @throws \GuzzleHttp\Exception\GuzzleException
* @throws \NextcloudApiWrapper\NCException
*/
public function xmlRequest($method, $uri, $xml, $headers = []) {

return $this->request($method, $uri, null, $xml, $headers);
}

/**
* @param $method
* @param $uri
* @param null $params
* @param null $data
* @param array $headers
* @return bool
* @throws NCException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function noBodyRequest($method, $uri, $params = null, $data = null, $headers = []) {

$response = $this->rawRequest($method, $uri, $params, $data, $headers);
switch ($response->getStatusCode()) {
default:
throw new NCException($response);
case 201:
case 202:
case 204:
return true;
}
}

/**
* @param $method
* @param $uri
* @param null $params
* @param null $data
* @param array $headers
* @return NextcloudResponse
* @throws \GuzzleHttp\Exception\GuzzleException
* @throws \NextcloudApiWrapper\NCException
*/
public function request($method, $uri, $params = null, $data = null, $headers = []) {

$response = $this->rawRequest($method, $uri, $params, $data, $headers);

return new NextcloudResponse($response);
}

/**
* @param $method
* @param $uri
* @param null $params
* @param null $data
* @param array $headers
* @return ResponseInterface
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function rawRequest($method, $uri, $params = null, $data = null, $headers = []) {

$params = $params === null ? $this->getBaseRequestParams() : $params;
$request = new Request($method, $uri, $headers, $data);
return $this->guzzle->send($request, $params);
}
}
169 changes: 169 additions & 0 deletions src/NextcloudApiWrapper/WebDAV/FileClient.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
<?php


namespace NextcloudApiWrapper\WebDAV;

use NextcloudApiWrapper\NextcloudResponse;
use SimpleXMLElement;

class FileClient extends AbstractClient
{
const FILE_PART = 'files';

/**
* @param $pathToFolder
* @param array $params
* @return NextcloudResponse
* @throws \GuzzleHttp\Exception\GuzzleException
* @throws \NextcloudApiWrapper\NCException
*/
public function listFolder($pathToFolder, array $params = [], $resursive = true) {
if (empty($params)) {
return $this->connection->request(Connection::PROFIND, self::FILE_PART . '/' . $pathToFolder);
}
$xml = new SimpleXMLElement('<?xml version="1.0"?>
<d:propfind ' . XMLTypesEnum::XML_TYPE_ALIASES . '>
</d:propfind>');

$xmlProp = $xml->addChild("d:prop");
foreach ($params as $param) {
if (key_exists($param, XMLTypesEnum::FOLDER_PARAMS_TO_XML_TYPE)) {
$xmlProp->addChild(XMLTypesEnum::FOLDER_PARAMS_TO_XML_TYPE[$param]);
}
}

$header = $resursive === false ? ["Depth" => 0] : [];

return $this->connection->xmlRequest(Connection::PROFIND, self::FILE_PART . '/' . $pathToFolder, $xml->asXML(), $header);
}

/**
* @param $pathToFile
* @return \Psr\Http\Message\ResponseInterface
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function downloadFile($pathToFile) {
return $this->connection->rawRequest(Connection::GET, $pathToFile);
}

/**
* @param $pathToFile
* @param $fileContent
* @throws \GuzzleHttp\Exception\GuzzleException
* @throws \NextcloudApiWrapper\NCException
*/
public function uploadFile($pathToFile, $fileContent) {
$this->connection->noBodyRequest(Connection::PUT, $pathToFile, null, $fileContent);
}

/**
* @param $pathToFolder
* @return bool
* @throws \GuzzleHttp\Exception\GuzzleException
* @throws \NextcloudApiWrapper\NCException
*/
public function createFolder($pathToFolder) {

$this->connection->noBodyRequest(Connection::MKCOL, self::FILE_PART . '/' . $pathToFolder);
}

/**
* @param $pathToFolder
* @throws \GuzzleHttp\Exception\GuzzleException
* @throws \NextcloudApiWrapper\NCException
*/
public function deleteFolder($pathToFolder) {

$this->connection->noBodyRequest(Connection::DELETE, self::FILE_PART . '/' . $pathToFolder);
}

/**
* @param $sourcePath
* @param $destinationPath
* @param null $name - use when you do not want to rename file or folder
* - when not null it append after sourcePath and also destinationPath
* @throws \GuzzleHttp\Exception\GuzzleException
* @throws \NextcloudApiWrapper\NCException
*/
public function move($sourcePath, $destinationPath, $name = null) {

if ($name !== null) {
$sourcePath .= "/" . $name;
$destinationPath .= "/" . $name;
}
$this->connection->noBodyRequest(Connection::MOVE, self::FILE_PART . '/' . $sourcePath, null, null, ["Destination" => $destinationPath]);
}

/**
* @param $sourcePath
* @param $destinationPath
* @param null $name - use when you do not want to rename file or folder
* - when not null it append after sourcePath and also destinationPath
* @throws \GuzzleHttp\Exception\GuzzleException
* @throws \NextcloudApiWrapper\NCException
*/
public function copy($sourcePath, $destinationPath, $name = null) {

if ($name !== null) {
$sourcePath .= "/" . $name;
$destinationPath .= "/" . $name;
}
$this->connection->noBodyRequest(Connection::COPY, self::FILE_PART . '/' . $sourcePath, null, null, ["Destination" => $destinationPath]);
}

/**
* @param $path
* @param array $params
* @return NextcloudResponse
* @throws \GuzzleHttp\Exception\GuzzleException
* @throws \NextcloudApiWrapper\NCException
*/
public function favourite($path, $params = []) {
$xml = new SimpleXMLElement('<?xml version="1.0"?>
<d:propertyupdate ' . XMLTypesEnum::XML_TYPE_ALIASES . '>
</d:propertyupdate>');

$xmlProp = $xml->addChild("d:set")->addChild("d:prop");
foreach ($params as $key=>$value) {
if (key_exists($key, XMLTypesEnum::FAVOURITE_PARAMS_TO_XML_TYPE)) {
if (is_bool($value)) {
$value = $value ? 1 : 0;
}
$xmlProp->addChild(XMLTypesEnum::FAVOURITE_PARAMS_TO_XML_TYPE[$key], $value);
}
}

return $this->connection->xmlRequest(Connection::PROFIND, self::FILE_PART . '/' . $path, $xml->asXML());
}

/**
* @param $path
* @param array $params
* @return NextcloudResponse
* @throws \GuzzleHttp\Exception\GuzzleException
* @throws \NextcloudApiWrapper\NCException
*/
public function filter($path, $params = []) {
$xml = new SimpleXMLElement('<?xml version="1.0"?>
<oc:filter-files ' . XMLTypesEnum::XML_TYPE_ALIASES . '>
</oc:filter-files>');

$xmlFilterRules = $xml->addChild("oc:filter-rules");
foreach ($params as $key=>$value) {
if (key_exists($key, XMLTypesEnum::FAVOURITE_PARAMS_TO_XML_TYPE)) {
if (is_bool($value)) {
$value = $value ? 1 : 0;
}
$xmlFilterRules->addChild(XMLTypesEnum::FAVOURITE_PARAMS_TO_XML_TYPE[$key], $value);
} else if (key_exists($key, XMLTypesEnum::FOLDER_PARAMS_TO_XML_TYPE)) {
if (is_bool($value)) {
$value = $value ? 1 : 0;
}
$xmlFilterRules->addChild(XMLTypesEnum::FOLDER_PARAMS_TO_XML_TYPE[$key], $value);
}
}

return $this->connection->xmlRequest(Connection::PROFIND, self::FILE_PART . '/' . $path, $xml->asXML());
}

}
35 changes: 35 additions & 0 deletions src/NextcloudApiWrapper/WebDAV/XMLTypesEnum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php


namespace NextcloudApiWrapper\WebDAV;


final class XMLTypesEnum
{

const XML_TYPE_ALIASES = 'xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns" xmlns:nc="http://nextcloud.org/ns"';
const FOLDER_PARAMS_TO_XML_TYPE =
[
"getlastmodified" => "d:getlastmodified",
"getetag" => "d:getetag",
"getcontenttype" => "d:getcontenttype",
"resourcetype" => "d:resourcetype",
"getcontentlength" => "d:getcontentlength",
"id" => "oc:id", // The fileid namespaced by the instance id, globally unique
"fileid" => "oc:fileid", // fileid The unique id for the file within the instance
"favorite" => "oc:favorite",
"comments-href" => "oc:comments-href",
"comments-count" => "oc:comments-count",
"comments-unread" => "oc:comments-unread",
"owner-id" => "oc:owner-id", // The user id of the owner of a shared file
"owner-display-name" => "oc:owner-display-name", // The display name of the owner of a shared file
"share-types" => "oc:share-types",
"checksums" => "oc:checksums",
"has-preview" => "ns:has-preview",
"size" => "oc:size", // Unlike getcontentlength, this property also works for folders reporting the size of everything in the folder.
];

const FAVOURITE_PARAMS_TO_XML_TYPE = [
"favorite" => "oc:favourite",
];
}