Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
die_coolen_jungs
our_own_cloud_project
Commits
39d6ddd3
Commit
39d6ddd3
authored
Dec 19, 2014
by
Morris Jobke
Browse files
Merge pull request #12865 from owncloud/files-tags-webdav
Returns tags through WebDAV
parents
028b0efd
6224e29f
Changes
8
Hide whitespace changes
Inline
Side-by-side
apps/files/appinfo/remote.php
View file @
39d6ddd3
...
...
@@ -53,6 +53,7 @@ $server->subscribeEvent('beforeMethod', function () use ($server, $objectTree) {
$rootDir
=
new
OC_Connector_Sabre_Directory
(
$view
,
$rootInfo
);
$objectTree
->
init
(
$rootDir
,
$view
,
$mountManager
);
$server
->
addPlugin
(
new
\
OC\Connector\Sabre\TagsPlugin
(
$objectTree
,
\
OC
::
$server
->
getTagManager
()));
$server
->
addPlugin
(
new
OC_Connector_Sabre_QuotaPlugin
(
$view
));
},
30
);
// priority 30: after auth (10) and acl(20), before lock(50) and handling the request
...
...
lib/private/connector/sabre/directory.php
View file @
39d6ddd3
...
...
@@ -24,6 +24,13 @@
class
OC_Connector_Sabre_Directory
extends
OC_Connector_Sabre_Node
implements
\
Sabre\DAV\ICollection
,
\
Sabre\DAV\IQuota
{
/**
* Cached directory content
*
* @var \OCP\Files\FileInfo[]
*/
private
$dirContent
;
/**
* Creates a new file in the directory
*
...
...
@@ -138,13 +145,20 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node
* @return \Sabre\DAV\INode[]
*/
public
function
getChildren
()
{
if
(
!
is_null
(
$this
->
dirContent
))
{
return
$this
->
dirContent
;
}
$folderContent
=
$this
->
fileView
->
getDirectoryContent
(
$this
->
path
);
$
folder_content
=
$this
->
fileView
->
getDirectoryContent
(
$this
->
path
);
$
properties
=
array
(
);
$paths
=
array
();
foreach
(
$folder_content
as
$info
)
{
$paths
[]
=
$this
->
path
.
'/'
.
$info
[
'name'
];
$properties
[
$this
->
path
.
'/'
.
$info
[
'name'
]][
self
::
GETETAG_PROPERTYNAME
]
=
'"'
.
$info
[
'etag'
]
.
'"'
;
foreach
(
$folderContent
as
$info
)
{
$name
=
$info
->
getName
();
$paths
[]
=
$this
->
path
.
'/'
.
$name
;
$properties
[
$this
->
path
.
'/'
.
$name
][
self
::
GETETAG_PROPERTYNAME
]
=
'"'
.
$info
->
getEtag
()
.
'"'
;
}
// TODO: move this to a beforeGetPropertiesForPath event to pre-cache properties
// TODO: only fetch the requested properties
if
(
count
(
$paths
)
>
0
)
{
//
// the number of arguments within IN conditions are limited in most databases
...
...
@@ -169,12 +183,13 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node
}
$nodes
=
array
();
foreach
(
$folder
_c
ontent
as
$info
)
{
foreach
(
$folder
C
ontent
as
$info
)
{
$node
=
$this
->
getChild
(
$info
->
getName
(),
$info
);
$node
->
setPropertyCache
(
$properties
[
$this
->
path
.
'/'
.
$info
[
'n
ame
'
]
]);
$node
->
setPropertyCache
(
$properties
[
$this
->
path
.
'/'
.
$info
->
getN
ame
()
]);
$nodes
[]
=
$node
;
}
return
$nodes
;
$this
->
dirContent
=
$nodes
;
return
$this
->
dirContent
;
}
/**
...
...
lib/private/connector/sabre/node.php
View file @
39d6ddd3
<?php
use
Sabre\DAV\URLUtil
;
use
OC\Connector\Sabre\TagList
;
/**
* ownCloud
...
...
@@ -226,6 +228,15 @@ abstract class OC_Connector_Sabre_Node implements \Sabre\DAV\INode, \Sabre\DAV\I
return
$props
;
}
/**
* Returns the cache's file id
*
* @return int
*/
public
function
getId
()
{
return
$this
->
info
->
getId
();
}
/**
* @return string|null
*/
...
...
lib/private/connector/sabre/server.php
View file @
39d6ddd3
...
...
@@ -144,6 +144,13 @@ class OC_Connector_Sabre_Server extends Sabre\DAV\Server {
$path
=
rtrim
(
$path
,
'/'
);
// This event allows people to intercept these requests early on in the
// process.
//
// We're not doing anything with the result, but this can be helpful to
// pre-fetch certain expensive live properties.
$this
->
broadCastEvent
(
'beforeGetPropertiesForPath'
,
array
(
$path
,
$propertyNames
,
$depth
));
$returnPropertyList
=
array
();
$parentNode
=
$this
->
tree
->
getNodeForPath
(
$path
);
...
...
lib/private/connector/sabre/taglist.php
0 → 100644
View file @
39d6ddd3
<?php
/**
* ownCloud
*
* @author Vincent Petry
* @copyright 2014 Vincent Petry <pvince81@owncloud.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace
OC\Connector\Sabre
;
use
Sabre\DAV
;
/**
* TagList property
*
* This property contains multiple "tag" elements, each containing a tag name.
*/
class
TagList
extends
DAV\Property
{
const
NS_OWNCLOUD
=
'http://owncloud.org/ns'
;
/**
* tags
*
* @var array
*/
private
$tags
;
/**
* @param array $tags
*/
public
function
__construct
(
array
$tags
)
{
$this
->
tags
=
$tags
;
}
/**
* Returns the tags
*
* @return array
*/
public
function
getTags
()
{
return
$this
->
tags
;
}
/**
* Serializes this property.
*
* @param DAV\Server $server
* @param \DOMElement $dom
* @return void
*/
public
function
serialize
(
DAV
\
Server
$server
,
\
DOMElement
$dom
)
{
$prefix
=
$server
->
xmlNamespaces
[
self
::
NS_OWNCLOUD
];
foreach
(
$this
->
tags
as
$tag
)
{
$elem
=
$dom
->
ownerDocument
->
createElement
(
$prefix
.
':tag'
);
$elem
->
appendChild
(
$dom
->
ownerDocument
->
createTextNode
(
$tag
));
$dom
->
appendChild
(
$elem
);
}
}
/**
* Unserializes this property from a DOM Element
*
* This method returns an instance of this class.
* It will only decode tag values.
*
* @param \DOMElement $dom
* @return \OC\Connector\Sabre\TagList
*/
static
function
unserialize
(
\
DOMElement
$dom
)
{
$tags
=
array
();
foreach
(
$dom
->
childNodes
as
$child
)
{
if
(
DAV\XMLUtil
::
toClarkNotation
(
$child
)
===
'{'
.
self
::
NS_OWNCLOUD
.
'}tag'
)
{
$tags
[]
=
$child
->
textContent
;
}
}
return
new
self
(
$tags
);
}
}
lib/private/connector/sabre/tagsplugin.php
0 → 100644
View file @
39d6ddd3
<?php
namespace
OC\Connector\Sabre
;
/**
* ownCloud
*
* @author Vincent Petry
* @copyright 2014 Vincent Petry <pvince81@owncloud.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
class
TagsPlugin
extends
\
Sabre\DAV\ServerPlugin
{
// namespace
const
NS_OWNCLOUD
=
'http://owncloud.org/ns'
;
const
TAGS_PROPERTYNAME
=
'{http://owncloud.org/ns}tags'
;
const
FAVORITE_PROPERTYNAME
=
'{http://owncloud.org/ns}favorite'
;
const
TAG_FAVORITE
=
'_$!<Favorite>!$_'
;
/**
* Reference to main server object
*
* @var \Sabre\DAV\Server
*/
private
$server
;
/**
* @var \OCP\ITagManager
*/
private
$tagManager
;
/**
* @var \OCP\ITags
*/
private
$tagger
;
/**
* Array of file id to tags array
* The null value means the cache wasn't initialized.
*
* @var array
*/
private
$cachedTags
;
/**
* @param \OCP\ITagManager $tagManager tag manager
*/
public
function
__construct
(
\
Sabre\DAV\ObjectTree
$objectTree
,
\
OCP\ITagManager
$tagManager
)
{
$this
->
objectTree
=
$objectTree
;
$this
->
tagManager
=
$tagManager
;
$this
->
tagger
=
null
;
$this
->
cachedTags
=
null
;
}
/**
* This initializes the plugin.
*
* This function is called by \Sabre\DAV\Server, after
* addPlugin is called.
*
* This method should set up the required event subscriptions.
*
* @param \Sabre\DAV\Server $server
* @return void
*/
public
function
initialize
(
\
Sabre\DAV\Server
$server
)
{
$server
->
xmlNamespaces
[
self
::
NS_OWNCLOUD
]
=
'oc'
;
$server
->
propertyMap
[
self
::
TAGS_PROPERTYNAME
]
=
'OC\\Connector\\Sabre\\TagList'
;
$this
->
server
=
$server
;
$this
->
server
->
subscribeEvent
(
'beforeGetProperties'
,
array
(
$this
,
'beforeGetProperties'
));
$this
->
server
->
subscribeEvent
(
'beforeGetPropertiesForPath'
,
array
(
$this
,
'beforeGetPropertiesForPath'
));
$this
->
server
->
subscribeEvent
(
'updateProperties'
,
array
(
$this
,
'updateProperties'
));
}
/**
* Searches and removes a value from the given array
*
* @param array $requestedProps
* @param string $propName to remove
* @return boolean true if the property was present, false otherwise
*/
private
function
findAndRemoveProperty
(
&
$requestedProps
,
$propName
)
{
$index
=
array_search
(
$propName
,
$requestedProps
);
if
(
$index
!==
false
)
{
unset
(
$requestedProps
[
$index
]);
return
true
;
}
return
false
;
}
/**
* Returns the tagger
*
* @return \OCP\ITags tagger
*/
private
function
getTagger
()
{
if
(
!
$this
->
tagger
)
{
$this
->
tagger
=
$this
->
tagManager
->
load
(
'files'
);
}
return
$this
->
tagger
;
}
/**
* Returns tags and favorites.
*
* @param integer $fileId file id
* @return array list($tags, $favorite) with $tags as tag array
* and $favorite is a boolean whether the file was favorited
*/
private
function
getTagsAndFav
(
$fileId
)
{
$isFav
=
false
;
$tags
=
$this
->
getTags
(
$fileId
);
if
(
$tags
)
{
$favPos
=
array_search
(
self
::
TAG_FAVORITE
,
$tags
);
if
(
$favPos
!==
false
)
{
$isFav
=
true
;
unset
(
$tags
[
$favPos
]);
}
}
return
array
(
$tags
,
$isFav
);
}
/**
* Returns tags for the given file id
*
* @param integer $fileId file id
* @return array list of tags for that file
*/
private
function
getTags
(
$fileId
)
{
if
(
isset
(
$this
->
cachedTags
[
$fileId
]))
{
return
$this
->
cachedTags
[
$fileId
];
}
else
{
$tags
=
$this
->
getTagger
()
->
getTagsForObjects
(
array
(
$fileId
));
if
(
$tags
)
{
return
current
(
$tags
);
}
}
return
null
;
}
/**
* Updates the tags of the given file id
*
* @param int $fileId
* @param array $tags array of tag strings
*/
private
function
updateTags
(
$fileId
,
$tags
)
{
$tagger
=
$this
->
getTagger
();
$currentTags
=
$this
->
getTags
(
$fileId
);
$newTags
=
array_diff
(
$tags
,
$currentTags
);
foreach
(
$newTags
as
$tag
)
{
if
(
$tag
===
self
::
TAG_FAVORITE
)
{
continue
;
}
$tagger
->
tagAs
(
$fileId
,
$tag
);
}
$deletedTags
=
array_diff
(
$currentTags
,
$tags
);
foreach
(
$deletedTags
as
$tag
)
{
if
(
$tag
===
self
::
TAG_FAVORITE
)
{
continue
;
}
$tagger
->
unTag
(
$fileId
,
$tag
);
}
}
/**
* Pre-fetch tags info
*
* @param string $path
* @param array $requestedProperties
* @param integer $depth
* @return void
*/
public
function
beforeGetPropertiesForPath
(
$path
,
array
$requestedProperties
,
$depth
)
{
$node
=
$this
->
objectTree
->
getNodeForPath
(
$path
);
if
(
!
(
$node
instanceof
\
OC_Connector_Sabre_Directory
))
{
return
;
}
if
(
$this
->
findAndRemoveProperty
(
$requestedProperties
,
self
::
TAGS_PROPERTYNAME
)
||
$this
->
findAndRemoveProperty
(
$requestedProperties
,
self
::
FAVORITE_PROPERTYNAME
)
)
{
$fileIds
=
array
();
// note: pre-fetching only supported for depth <= 1
$folderContent
=
$node
->
getChildren
();
// TODO: refactor somehow with the similar array that is created
// in getChildren()
foreach
(
$folderContent
as
$info
)
{
$fileIds
[]
=
$info
->
getId
();
}
$tags
=
$this
->
getTagger
()
->
getTagsForObjects
(
$fileIds
);
if
(
$tags
)
{
$this
->
cachedTags
=
$tags
;
}
}
}
/**
* Adds tags and favorites properties to the response,
* if requested.
*
* @param string $path
* @param \Sabre\DAV\INode $node
* @param array $requestedProperties
* @param array $returnedProperties
* @return void
*/
public
function
beforeGetProperties
(
$path
,
\
Sabre\DAV\INode
$node
,
array
&
$requestedProperties
,
array
&
$returnedProperties
)
{
if
(
!
(
$node
instanceof
\
OC_Connector_Sabre_Node
))
{
return
;
}
$tags
=
null
;
$isFav
=
null
;
if
(
$this
->
findAndRemoveProperty
(
$requestedProperties
,
self
::
TAGS_PROPERTYNAME
))
{
list
(
$tags
,
$isFav
)
=
$this
->
getTagsAndFav
(
$node
->
getId
());
$returnedProperties
[
200
][
self
::
TAGS_PROPERTYNAME
]
=
new
TagList
(
$tags
);
}
if
(
$this
->
findAndRemoveProperty
(
$requestedProperties
,
self
::
FAVORITE_PROPERTYNAME
))
{
if
(
is_null
(
$tags
))
{
list
(
$tags
,
$isFav
)
=
$this
->
getTagsAndFav
(
$node
->
getId
());
}
$returnedProperties
[
200
][
self
::
FAVORITE_PROPERTYNAME
]
=
$isFav
;
}
}
/**
* Updates tags and favorites properties, if applicable.
*
* @param string $path
* @param \Sabre\DAV\INode $node
* @param array $requestedProperties
* @param array $returnedProperties
* @return bool success status
*/
public
function
updateProperties
(
array
&
$properties
,
array
&
$result
,
\
Sabre\DAV\INode
$node
)
{
if
(
!
(
$node
instanceof
\
OC_Connector_Sabre_Node
))
{
return
;
}
$fileId
=
$node
->
getId
();
if
(
isset
(
$properties
[
self
::
TAGS_PROPERTYNAME
]))
{
$tagsProp
=
$properties
[
self
::
TAGS_PROPERTYNAME
];
unset
(
$properties
[
self
::
TAGS_PROPERTYNAME
]);
$this
->
updateTags
(
$fileId
,
$tagsProp
->
getTags
());
$result
[
200
][
self
::
TAGS_PROPERTYNAME
]
=
new
TagList
(
$tagsProp
->
getTags
());
}
if
(
isset
(
$properties
[
self
::
FAVORITE_PROPERTYNAME
]))
{
$favState
=
$properties
[
self
::
FAVORITE_PROPERTYNAME
];
unset
(
$properties
[
self
::
FAVORITE_PROPERTYNAME
]);
if
((
int
)
$favState
===
1
||
$favState
===
'true'
)
{
$favState
=
true
;
$this
->
getTagger
()
->
tagAs
(
$fileId
,
self
::
TAG_FAVORITE
);
}
else
{
$favState
=
false
;
$this
->
getTagger
()
->
unTag
(
$fileId
,
self
::
TAG_FAVORITE
);
}
$result
[
200
][
self
::
FAVORITE_PROPERTYNAME
]
=
$favState
;
}
return
true
;
}
}
tests/lib/connector/sabre/directory.php
View file @
39d6ddd3
...
...
@@ -101,4 +101,58 @@ class Test_OC_Connector_Sabre_Directory extends \Test\TestCase {
$dir
=
$this
->
getRootDir
();
$dir
->
delete
();
}
public
function
testGetChildren
()
{
$info1
=
$this
->
getMockBuilder
(
'OC\Files\FileInfo'
)
->
disableOriginalConstructor
()
->
getMock
();
$info2
=
$this
->
getMockBuilder
(
'OC\Files\FileInfo'
)
->
disableOriginalConstructor
()
->
getMock
();
$info1
->
expects
(
$this
->
any
())
->
method
(
'getName'
)
->
will
(
$this
->
returnValue
(
'first'
));
$info1
->
expects
(
$this
->
any
())
->
method
(
'getEtag'
)
->
will
(
$this
->
returnValue
(
'abc'
));
$info2
->
expects
(
$this
->
any
())
->
method
(
'getName'
)
->
will
(
$this
->
returnValue
(
'second'
));
$info2
->
expects
(
$this
->
any
())
->
method
(
'getEtag'
)
->
will
(
$this
->
returnValue
(
'def'
));
$this
->
view
->
expects
(
$this
->
once
())
->
method
(
'getDirectoryContent'
)
->
with
(
''
)
->
will
(
$this
->
returnValue
(
array
(
$info1
,
$info2
)));
$this
->
view
->
expects
(
$this
->
any
())
->
method
(
'getRelativePath'
)
->
will
(
$this
->
returnValue
(
''
));
$dir
=
new
OC_Connector_Sabre_Directory
(
$this
->
view
,
$this
->
info
);
$nodes
=
$dir
->
getChildren
();
$this
->
assertEquals
(
2
,
count
(
$nodes
));
// calling a second time just returns the cached values,
// does not call getDirectoryContents again
$nodes
=
$dir
->
getChildren
();
$properties
=
array
(
'testprop'
,
OC_Connector_Sabre_Node
::
GETETAG_PROPERTYNAME
);
$this
->
assertEquals
(
2
,
count
(
$nodes
));
$this
->
assertEquals
(
array
(
OC_Connector_Sabre_Node
::
GETETAG_PROPERTYNAME
=>
'"abc"'
),
$nodes
[
0
]
->
getProperties
(
$properties
)
);
$this
->
assertEquals
(
array
(
OC_Connector_Sabre_Node
::
GETETAG_PROPERTYNAME
=>
'"def"'
),
$nodes
[
1
]
->
getProperties
(
$properties
)
);
}
}
tests/lib/connector/sabre/tagsplugin.php
0 → 100644
View file @
39d6ddd3
<?php
namespace
Tests\Connector\Sabre
;
/**
* Copyright (c) 2014 Vincent Petry <pvince81@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
class
TagsPlugin
extends
\
Test\TestCase
{
const
TAGS_PROPERTYNAME
=
\
OC\Connector\Sabre\TagsPlugin
::
TAGS_PROPERTYNAME
;
const
FAVORITE_PROPERTYNAME
=
\
OC\Connector\Sabre\TagsPlugin
::
FAVORITE_PROPERTYNAME
;
const
TAG_FAVORITE
=
\
OC\Connector\Sabre\TagsPlugin
::
TAG_FAVORITE
;
/**
* @var \Sabre\DAV\Server
*/
private
$server
;
/**
* @var \Sabre\DAV\ObjectTree
*/
private
$tree
;
/**
* @var \OCP\ITagManager
*/
private
$tagManager
;
/**
* @var \OCP\ITags
*/
private
$tagger
;
/**
* @var \OC\Connector\Sabre\TagsPlugin
*/
private
$plugin
;
public
function
setUp
()
{
parent
::
setUp
();
$this
->
server
=
new
\
Sabre\DAV\Server
();
$this
->
tree
=
$this
->
getMockBuilder
(
'\Sabre\DAV\ObjectTree'
)
->
disableOriginalConstructor
()
->
getMock
();
$this
->
tagger
=
$this
->
getMock
(
'\OCP\ITags'
);
$this
->
tagManager
=
$this
->
getMock
(
'\OCP\ITagManager'
);
$this
->
tagManager
->
expects
(
$this
->
any
())
->
method
(
'load'
)
->
with
(
'files'
)
->
will
(
$this
->
returnValue
(
$this
->
tagger
));
$this
->
plugin
=
new
\
OC\Connector\Sabre\TagsPlugin
(
$this
->
tree
,
$this
->
tagManager
);
$this
->
plugin
->
initialize
(
$this
->
server
);
}