aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/dav/lib/Connector/Sabre/FilesReportPlugin.php42
-rw-r--r--apps/dav/lib/Connector/Sabre/ServerFactory.php1
-rw-r--r--apps/dav/lib/Connector/Sabre/ShareTypeList.php6
-rw-r--r--apps/dav/lib/Connector/Sabre/TagList.php6
-rw-r--r--apps/dav/tests/unit/Connector/Sabre/FilesReportPluginTest.php39
-rw-r--r--apps/files/appinfo/routes.php6
-rw-r--r--apps/files/css/upload.css1
-rw-r--r--apps/files/js/favoritesfilelist.js27
-rw-r--r--apps/files/lib/Controller/ApiController.php17
-rw-r--r--apps/files/lib/Service/TagService.php21
-rw-r--r--apps/files/tests/Controller/ApiControllerTest.php176
-rw-r--r--apps/files/tests/js/favoritesfilelistspec.js53
-rw-r--r--core/css/jquery.ocdialog.css37
-rw-r--r--core/css/mobile.css1
-rw-r--r--core/css/styles.css49
-rw-r--r--core/js/files/client.js6
-rw-r--r--core/js/integritycheck-failed-notification.js1
-rw-r--r--core/js/jquery.ocdialog.js8
-rw-r--r--core/js/oc-dialogs.js71
-rw-r--r--core/js/tests/specs/files/clientSpec.js4
-rw-r--r--core/templates/filepicker.html23
-rw-r--r--settings/img/admin.pngbin113 -> 0 bytes
-rw-r--r--settings/img/apps.pngbin114 -> 0 bytes
-rw-r--r--settings/img/help.pngbin324 -> 0 bytes
-rw-r--r--settings/img/personal.pngbin360 -> 0 bytes
-rw-r--r--settings/img/trans.pngbin185 -> 0 bytes
-rw-r--r--settings/img/users.pngbin336 -> 0 bytes
-rw-r--r--tests/lib/AppConfigTest.php15
-rw-r--r--tests/lib/AppTest.php22
-rw-r--r--tests/lib/Share/ShareTest.php20
-rw-r--r--tests/lib/TestCase.php29
-rw-r--r--tests/lib/UtilTest.php11
32 files changed, 285 insertions, 407 deletions
diff --git a/apps/dav/lib/Connector/Sabre/FilesReportPlugin.php b/apps/dav/lib/Connector/Sabre/FilesReportPlugin.php
index 5b96f4f0f64..4df5f8b80e5 100644
--- a/apps/dav/lib/Connector/Sabre/FilesReportPlugin.php
+++ b/apps/dav/lib/Connector/Sabre/FilesReportPlugin.php
@@ -39,6 +39,7 @@ use OCP\Files\Folder;
use OCP\IGroupManager;
use OCP\SystemTag\ISystemTagManager;
use OCP\SystemTag\TagNotFoundException;
+use OCP\ITagManager;
class FilesReportPlugin extends ServerPlugin {
@@ -75,6 +76,13 @@ class FilesReportPlugin extends ServerPlugin {
private $tagMapper;
/**
+ * Manager for private tags
+ *
+ * @var ITagManager
+ */
+ private $fileTagger;
+
+ /**
* @var IUserSession
*/
private $userSession;
@@ -92,11 +100,18 @@ class FilesReportPlugin extends ServerPlugin {
/**
* @param Tree $tree
* @param View $view
+ * @param ISystemTagManager $tagManager
+ * @param ISystemTagObjectMapper $tagMapper
+ * @param ITagManager $fileTagger manager for private tags
+ * @param IUserSession $userSession
+ * @param IGroupManager $groupManager
+ * @param Folder $userfolder
*/
public function __construct(Tree $tree,
View $view,
ISystemTagManager $tagManager,
ISystemTagObjectMapper $tagMapper,
+ ITagManager $fileTagger,
IUserSession $userSession,
IGroupManager $groupManager,
Folder $userFolder
@@ -105,6 +120,7 @@ class FilesReportPlugin extends ServerPlugin {
$this->fileView = $view;
$this->tagManager = $tagManager;
$this->tagMapper = $tagMapper;
+ $this->fileTagger = $fileTagger;
$this->userSession = $userSession;
$this->groupManager = $groupManager;
$this->userFolder = $userFolder;
@@ -215,12 +231,38 @@ class FilesReportPlugin extends ServerPlugin {
$ns = '{' . $this::NS_OWNCLOUD . '}';
$resultFileIds = null;
$systemTagIds = [];
+ $favoriteFilter = null;
foreach ($filterRules as $filterRule) {
if ($filterRule['name'] === $ns . 'systemtag') {
$systemTagIds[] = $filterRule['value'];
}
+ if ($filterRule['name'] === $ns . 'favorite') {
+ $favoriteFilter = true;
+ }
}
+ if ($favoriteFilter !== null) {
+ $resultFileIds = $this->fileTagger->load('files')->getFavorites();
+ if (empty($resultFileIds)) {
+ return [];
+ }
+ }
+
+ if (!empty($systemTagIds)) {
+ $fileIds = $this->getSystemTagFileIds($systemTagIds);
+ if (empty($resultFileIds)) {
+ $resultFileIds = $fileIds;
+ } else {
+ $resultFileIds = array_intersect($fileIds, $resultFileIds);
+ }
+ }
+
+ return $resultFileIds;
+ }
+
+ private function getSystemTagFileIds($systemTagIds) {
+ $resultFileIds = null;
+
// check user permissions, if applicable
if (!$this->isAdmin()) {
// check visibility/permission
diff --git a/apps/dav/lib/Connector/Sabre/ServerFactory.php b/apps/dav/lib/Connector/Sabre/ServerFactory.php
index 644f0f28f57..6d9f9b1bc8b 100644
--- a/apps/dav/lib/Connector/Sabre/ServerFactory.php
+++ b/apps/dav/lib/Connector/Sabre/ServerFactory.php
@@ -177,6 +177,7 @@ class ServerFactory {
$view,
\OC::$server->getSystemTagManager(),
\OC::$server->getSystemTagObjectMapper(),
+ \OC::$server->getTagManager(),
$this->userSession,
\OC::$server->getGroupManager(),
$userFolder
diff --git a/apps/dav/lib/Connector/Sabre/ShareTypeList.php b/apps/dav/lib/Connector/Sabre/ShareTypeList.php
index fd9ba3f7267..4d9179b6651 100644
--- a/apps/dav/lib/Connector/Sabre/ShareTypeList.php
+++ b/apps/dav/lib/Connector/Sabre/ShareTypeList.php
@@ -66,7 +66,11 @@ class ShareTypeList implements Element {
static function xmlDeserialize(Reader $reader) {
$shareTypes = [];
- foreach ($reader->parseInnerTree() as $elem) {
+ $tree = $reader->parseInnerTree();
+ if ($tree === null) {
+ return null;
+ }
+ foreach ($tree as $elem) {
if ($elem['name'] === '{' . self::NS_OWNCLOUD . '}share-type') {
$shareTypes[] = (int)$elem['value'];
}
diff --git a/apps/dav/lib/Connector/Sabre/TagList.php b/apps/dav/lib/Connector/Sabre/TagList.php
index 23576171262..2f514112a43 100644
--- a/apps/dav/lib/Connector/Sabre/TagList.php
+++ b/apps/dav/lib/Connector/Sabre/TagList.php
@@ -85,7 +85,11 @@ class TagList implements Element {
static function xmlDeserialize(Reader $reader) {
$tags = [];
- foreach ($reader->parseInnerTree() as $elem) {
+ $tree = $reader->parseInnerTree();
+ if ($tree === null) {
+ return null;
+ }
+ foreach ($tree as $elem) {
if ($elem['name'] === '{' . self::NS_OWNCLOUD . '}tag') {
$tags[] = $elem['value'];
}
diff --git a/apps/dav/tests/unit/Connector/Sabre/FilesReportPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/FilesReportPluginTest.php
index 336a33058bf..2097777c8fd 100644
--- a/apps/dav/tests/unit/Connector/Sabre/FilesReportPluginTest.php
+++ b/apps/dav/tests/unit/Connector/Sabre/FilesReportPluginTest.php
@@ -26,12 +26,15 @@ namespace OCA\DAV\Tests\unit\Connector\Sabre;
use OCA\DAV\Connector\Sabre\FilesReportPlugin as FilesReportPluginImplementation;
use OCP\IPreview;
+use OCP\ITagManager;
+use OCP\IUserSession;
use Sabre\DAV\Exception\NotFound;
use OCP\SystemTag\ISystemTagObjectMapper;
use OC\Files\View;
use OCP\Files\Folder;
use OCP\IGroupManager;
use OCP\SystemTag\ISystemTagManager;
+use OCP\ITags;
class FilesReportPluginTest extends \Test\TestCase {
/** @var \Sabre\DAV\Server|\PHPUnit_Framework_MockObject_MockObject */
@@ -46,6 +49,9 @@ class FilesReportPluginTest extends \Test\TestCase {
/** @var ISystemTagManager|\PHPUnit_Framework_MockObject_MockObject */
private $tagManager;
+ /** @var ITags|\PHPUnit_Framework_MockObject_MockObject */
+ private $privateTags;
+
/** @var \OCP\IUserSession */
private $userSession;
@@ -87,20 +93,20 @@ class FilesReportPluginTest extends \Test\TestCase {
->disableOriginalConstructor()
->getMock();
- $this->tagManager = $this->getMockBuilder('\OCP\SystemTag\ISystemTagManager')
- ->disableOriginalConstructor()
- ->getMock();
- $this->tagMapper = $this->getMockBuilder('\OCP\SystemTag\ISystemTagObjectMapper')
- ->disableOriginalConstructor()
- ->getMock();
- $this->userSession = $this->getMockBuilder('\OCP\IUserSession')
- ->disableOriginalConstructor()
- ->getMock();
-
$this->previewManager = $this->getMockBuilder('\OCP\IPreview')
->disableOriginalConstructor()
->getMock();
+ $this->tagManager = $this->createMock(ISystemTagManager::class);
+ $this->tagMapper = $this->createMock(ISystemTagObjectMapper::class);
+ $this->userSession = $this->createMock(IUserSession::class);
+ $this->privateTags = $this->createMock(ITags::class);
+ $privateTagManager = $this->createMock(ITagManager::class);
+ $privateTagManager->expects($this->any())
+ ->method('load')
+ ->with('files')
+ ->will($this->returnValue($this->privateTags));
+
$user = $this->getMockBuilder('\OCP\IUser')
->disableOriginalConstructor()
->getMock();
@@ -116,6 +122,7 @@ class FilesReportPluginTest extends \Test\TestCase {
$this->view,
$this->tagManager,
$this->tagMapper,
+ $privateTagManager,
$this->userSession,
$this->groupManager,
$this->userFolder
@@ -652,4 +659,16 @@ class FilesReportPluginTest extends \Test\TestCase {
$this->assertEquals(['222'], array_values($this->invokePrivate($this->plugin, 'processFilterRules', [$rules])));
}
+
+ public function testProcessFavoriteFilter() {
+ $rules = [
+ ['name' => '{http://owncloud.org/ns}favorite', 'value' => '1'],
+ ];
+
+ $this->privateTags->expects($this->once())
+ ->method('getFavorites')
+ ->will($this->returnValue(['456', '789']));
+
+ $this->assertEquals(['456', '789'], array_values($this->invokePrivate($this->plugin, 'processFilterRules', [$rules])));
+ }
}
diff --git a/apps/files/appinfo/routes.php b/apps/files/appinfo/routes.php
index 6237e8413ed..49dbe553435 100644
--- a/apps/files/appinfo/routes.php
+++ b/apps/files/appinfo/routes.php
@@ -45,12 +45,6 @@ $application->registerRoutes(
'requirements' => array('path' => '.+'),
),
array(
- 'name' => 'API#getFilesByTag',
- 'url' => '/api/v1/tags/{tagName}/files',
- 'verb' => 'GET',
- 'requirements' => array('tagName' => '.+'),
- ),
- array(
'name' => 'API#getRecentFiles',
'url' => '/api/v1/recent/',
'verb' => 'GET'
diff --git a/apps/files/css/upload.css b/apps/files/css/upload.css
index f0ffb9ac99e..abc09c3e99c 100644
--- a/apps/files/css/upload.css
+++ b/apps/files/css/upload.css
@@ -91,6 +91,7 @@
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
+ margin-bottom: 30px;
}
.oc-dialog .fileexists .conflict .filename,
diff --git a/apps/files/js/favoritesfilelist.js b/apps/files/js/favoritesfilelist.js
index e6532ab188c..380689be10b 100644
--- a/apps/files/js/favoritesfilelist.js
+++ b/apps/files/js/favoritesfilelist.js
@@ -75,24 +75,25 @@ $(document).ready(function() {
// there is only root
this._setCurrentDir('/', false);
- this._reloadCall = $.ajax({
- url: OC.generateUrl('/apps/files/api/v1/tags/{tagName}/files', {tagName: tagName}),
- type: 'GET',
- dataType: 'json'
- });
+ this._reloadCall = this.filesClient.getFilteredFiles(
+ {
+ favorite: true
+ },
+ {
+ properties: this._getWebdavProperties()
+ }
+ );
var callBack = this.reloadCallback.bind(this);
return this._reloadCall.then(callBack, callBack);
},
- reloadCallback: function(result) {
- delete this._reloadCall;
- this.hideMask();
-
- if (result.files) {
- this.setFiles(result.files.sort(this._sortComparator));
- return true;
+ reloadCallback: function(status, result) {
+ if (result) {
+ // prepend empty dir info because original handler
+ result.unshift({});
}
- return false;
+
+ return OCA.Files.FileList.prototype.reloadCallback.call(this, status, result);
}
});
diff --git a/apps/files/lib/Controller/ApiController.php b/apps/files/lib/Controller/ApiController.php
index 7ce83bfca15..d6f88581b96 100644
--- a/apps/files/lib/Controller/ApiController.php
+++ b/apps/files/lib/Controller/ApiController.php
@@ -174,23 +174,6 @@ class ApiController extends Controller {
}
/**
- * Returns a list of all files tagged with the given tag.
- *
- * @NoAdminRequired
- *
- * @param string $tagName tag name to filter by
- * @return DataResponse
- */
- public function getFilesByTag($tagName) {
- $nodes = $this->tagService->getFilesByTag($tagName);
- $files = $this->formatNodes($nodes);
- foreach ($files as &$file) {
- $file['tags'] = [$tagName];
- }
- return new DataResponse(['files' => $files]);
- }
-
- /**
* Returns a list of recently modifed files.
*
* @NoAdminRequired
diff --git a/apps/files/lib/Service/TagService.php b/apps/files/lib/Service/TagService.php
index 04e962b5e17..4482fb45371 100644
--- a/apps/files/lib/Service/TagService.php
+++ b/apps/files/lib/Service/TagService.php
@@ -90,26 +90,5 @@ class TagService {
// list is up to date, in case of concurrent changes ?
return $tags;
}
-
- /**
- * Get all files for the given tag
- *
- * @param string $tagName tag name to filter by
- * @return Node[] list of matching files
- * @throws \Exception if the tag does not exist
- */
- public function getFilesByTag($tagName) {
- try {
- $fileIds = $this->tagger->getIdsForTag($tagName);
- } catch (\Exception $e) {
- return [];
- }
-
- $allNodes = [];
- foreach ($fileIds as $fileId) {
- $allNodes = array_merge($allNodes, $this->homeFolder->getById((int) $fileId));
- }
- return $allNodes;
- }
}
diff --git a/apps/files/tests/Controller/ApiControllerTest.php b/apps/files/tests/Controller/ApiControllerTest.php
index 9bfc6d6f5e8..4b7bec065a0 100644
--- a/apps/files/tests/Controller/ApiControllerTest.php
+++ b/apps/files/tests/Controller/ApiControllerTest.php
@@ -103,182 +103,6 @@ class ApiControllerTest extends TestCase {
);
}
- public function testGetFilesByTagEmpty() {
- $tagName = 'MyTagName';
- $this->tagService->expects($this->once())
- ->method('getFilesByTag')
- ->with($this->equalTo([$tagName]))
- ->will($this->returnValue([]));
-
- $expected = new DataResponse(['files' => []]);
- $this->assertEquals($expected, $this->apiController->getFilesByTag([$tagName]));
- }
-
- public function testGetFilesByTagSingle() {
- $tagName = 'MyTagName';
- $fileInfo = new FileInfo(
- '/root.txt',
- $this->getMockBuilder('\OC\Files\Storage\Storage')
- ->disableOriginalConstructor()
- ->getMock(),
- '/var/www/root.txt',
- [
- 'mtime' => 55,
- 'mimetype' => 'application/pdf',
- 'permissions' => 31,
- 'size' => 1234,
- 'etag' => 'MyEtag',
- ],
- $this->getMockBuilder('\OCP\Files\Mount\IMountPoint')
- ->disableOriginalConstructor()
- ->getMock()
- );
- $node = $this->getMockBuilder('\OC\Files\Node\File')
- ->disableOriginalConstructor()
- ->getMock();
- $node->expects($this->once())
- ->method('getFileInfo')
- ->will($this->returnValue($fileInfo));
- $this->tagService->expects($this->once())
- ->method('getFilesByTag')
- ->with($this->equalTo([$tagName]))
- ->will($this->returnValue([$node]));
-
- $this->shareManager->expects($this->any())
- ->method('getSharesBy')
- ->with(
- $this->equalTo('user1'),
- $this->anything(),
- $node,
- $this->equalTo(false),
- $this->equalTo(1)
- )
- ->will($this->returnCallback(function($userId, $shareType) {
- if ($shareType === \OCP\Share::SHARE_TYPE_USER || $shareType === \OCP\Share::SHARE_TYPE_LINK) {
- return ['dummy_share'];
- }
- return [];
- }));
-
- $expected = new DataResponse([
- 'files' => [
- [
- 'id' => null,
- 'parentId' => null,
- 'mtime' => 55000,
- 'name' => 'root.txt',
- 'permissions' => 31,
- 'mimetype' => 'application/pdf',
- 'size' => 1234,
- 'type' => 'file',
- 'etag' => 'MyEtag',
- 'path' => '/',
- 'tags' => [
- [
- 'MyTagName'
- ]
- ],
- 'shareTypes' => [\OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_LINK]
- ],
- ],
- ]);
- $this->assertEquals($expected, $this->apiController->getFilesByTag([$tagName]));
- }
-
- public function testGetFilesByTagMultiple() {
- $tagName = 'MyTagName';
- $fileInfo1 = new FileInfo(
- '/root.txt',
- $this->getMockBuilder('\OC\Files\Storage\Storage')
- ->disableOriginalConstructor()
- ->getMock(),
- '/var/www/root.txt',
- [
- 'mtime' => 55,
- 'mimetype' => 'application/pdf',
- 'permissions' => 31,
- 'size' => 1234,
- 'etag' => 'MyEtag',
- ],
- $this->getMockBuilder('\OCP\Files\Mount\IMountPoint')
- ->disableOriginalConstructor()
- ->getMock()
- );
- $fileInfo2 = new FileInfo(
- '/root.txt',
- $this->getMockBuilder('\OC\Files\Storage\Storage')
- ->disableOriginalConstructor()
- ->getMock(),
- '/var/www/some/sub.txt',
- [
- 'mtime' => 999,
- 'mimetype' => 'application/binary',
- 'permissions' => 31,
- 'size' => 9876,
- 'etag' => 'SubEtag',
- ],
- $this->getMockBuilder('\OCP\Files\Mount\IMountPoint')
- ->disableOriginalConstructor()
- ->getMock()
- );
- $node1 = $this->getMockBuilder('\OC\Files\Node\File')
- ->disableOriginalConstructor()
- ->getMock();
- $node1->expects($this->once())
- ->method('getFileInfo')
- ->will($this->returnValue($fileInfo1));
- $node2 = $this->getMockBuilder('\OC\Files\Node\File')
- ->disableOriginalConstructor()
- ->getMock();
- $node2->expects($this->once())
- ->method('getFileInfo')
- ->will($this->returnValue($fileInfo2));
- $this->tagService->expects($this->once())
- ->method('getFilesByTag')
- ->with($this->equalTo([$tagName]))
- ->will($this->returnValue([$node1, $node2]));
-
- $expected = new DataResponse([
- 'files' => [
- [
- 'id' => null,
- 'parentId' => null,
- 'mtime' => 55000,
- 'name' => 'root.txt',
- 'permissions' => 31,
- 'mimetype' => 'application/pdf',
- 'size' => 1234,
- 'type' => 'file',
- 'etag' => 'MyEtag',
- 'path' => '/',
- 'tags' => [
- [
- 'MyTagName'
- ]
- ],
- ],
- [
- 'id' => null,
- 'parentId' => null,
- 'mtime' => 999000,
- 'name' => 'root.txt',
- 'permissions' => 31,
- 'mimetype' => 'application/binary',
- 'size' => 9876,
- 'type' => 'file',
- 'etag' => 'SubEtag',
- 'path' => '/',
- 'tags' => [
- [
- 'MyTagName'
- ]
- ],
- ]
- ],
- ]);
- $this->assertEquals($expected, $this->apiController->getFilesByTag([$tagName]));
- }
-
public function testUpdateFileTagsEmpty() {
$expected = new DataResponse([]);
$this->assertEquals($expected, $this->apiController->updateFileTags('/path.txt'));
diff --git a/apps/files/tests/js/favoritesfilelistspec.js b/apps/files/tests/js/favoritesfilelistspec.js
index 1c833d334e2..db890927ed4 100644
--- a/apps/files/tests/js/favoritesfilelistspec.js
+++ b/apps/files/tests/js/favoritesfilelistspec.js
@@ -41,13 +41,9 @@ describe('OCA.Files.FavoritesFileList tests', function() {
'</div>'
);
});
- afterEach(function() {
- fileList.destroy();
- fileList = undefined;
- });
describe('loading file list', function() {
- var response;
+ var fetchStub;
beforeEach(function() {
fileList = new OCA.Files.FavoritesFileList(
@@ -55,36 +51,31 @@ describe('OCA.Files.FavoritesFileList tests', function() {
);
OCA.Files.FavoritesPlugin.attach(fileList);
- fileList.reload();
-
- /* jshint camelcase: false */
- response = {
- files: [{
- id: 7,
- name: 'test.txt',
- path: '/somedir',
- size: 123,
- mtime: 11111000,
- tags: [OC.TAG_FAVORITE],
- permissions: OC.PERMISSION_ALL,
- mimetype: 'text/plain'
- }]
- };
+ fetchStub = sinon.stub(fileList.filesClient, 'getFilteredFiles');
+ });
+ afterEach(function() {
+ fetchStub.restore();
+ fileList.destroy();
+ fileList = undefined;
});
it('render files', function() {
- var request;
+ var deferred = $.Deferred();
+ fetchStub.returns(deferred.promise());
- expect(fakeServer.requests.length).toEqual(1);
- request = fakeServer.requests[0];
- expect(request.url).toEqual(
- OC.generateUrl('apps/files/api/v1/tags/{tagName}/files', {tagName: OC.TAG_FAVORITE})
- );
+ fileList.reload();
- fakeServer.requests[0].respond(
- 200,
- { 'Content-Type': 'application/json' },
- JSON.stringify(response)
- );
+ expect(fetchStub.calledOnce).toEqual(true);
+
+ deferred.resolve(207, [{
+ id: 7,
+ name: 'test.txt',
+ path: '/somedir',
+ size: 123,
+ mtime: 11111000,
+ tags: [OC.TAG_FAVORITE],
+ permissions: OC.PERMISSION_ALL,
+ mimetype: 'text/plain'
+ }]);
var $rows = fileList.$el.find('tbody tr');
var $tr = $rows.eq(0);
diff --git a/core/css/jquery.ocdialog.css b/core/css/jquery.ocdialog.css
index a19fa4c52b5..0e46ff20152 100644
--- a/core/css/jquery.ocdialog.css
+++ b/core/css/jquery.ocdialog.css
@@ -1,7 +1,7 @@
.oc-dialog {
- background: white;
- color: #333333;
- border-radius: 3px; box-shadow: 0 0 7px #888888;
+ background: #fff;
+ color: #333;
+ border-radius: 3px; box-shadow: 0 0 7px #888;
padding: 15px;
z-index: 1000;
font-size: 100%;
@@ -11,26 +11,26 @@
min-width: 200px;
}
.oc-dialog-title {
- background: white;
- font-weight: bold;
- font-size: 110%;
- margin-bottom: 5px;
- margin-top: -9px;
+ background: #fff;
+ margin-left: 12px;
}
.oc-dialog-content {
z-index: 1000;
- background: white;
}
.oc-dialog-separator {
}
.oc-dialog-buttonrow {
- background: white;
- float: right;
- position: relative;
- bottom: 5px;
display: block;
- margin-top: 10px;
+ background: transparent;
+ position: absolute;
+ right: 0;
+ bottom: 0;
+ padding: 10px;
+ box-sizing: border-box;
width: 100%;
+ background-image: linear-gradient(rgba(255, 255, 255, 0), rgba(255, 255, 255, 1));
+ border-bottom-left-radius: 3px;
+ border-bottom-right-radius: 3px;
}
/* align primary button to right, other buttons to left */
.oc-dialog-buttonrow.twobuttons button:nth-child(1) {
@@ -45,10 +45,11 @@
}
.oc-dialog-close {
- position:absolute;
- top:7px; right:7px;
- height:20px; width:20px;
- background:url('../img/actions/close.svg') no-repeat center;
+ position: absolute;
+ top: 0;
+ right: 0;
+ padding: 25px;
+ background: url('../img/actions/close.svg') no-repeat center;
}
.oc-dialog-dim {
diff --git a/core/css/mobile.css b/core/css/mobile.css
index 0ef6a08c24f..b0f8421345c 100644
--- a/core/css/mobile.css
+++ b/core/css/mobile.css
@@ -141,6 +141,7 @@ table.multiselect thead {
/* do not show dates in filepicker */
+#oc-dialog-filepicker-content .filelist .filesize,
#oc-dialog-filepicker-content .filelist .date {
display: none;
}
diff --git a/core/css/styles.css b/core/css/styles.css
index 8fa7691ca7a..94e60562ad8 100644
--- a/core/css/styles.css
+++ b/core/css/styles.css
@@ -774,39 +774,48 @@ a.bookmarklet { background-color:#ddd; border:1px solid #ccc; padding:5px;paddin
#oc-dialog-filepicker-content .dirtree span:not(:last-child) { cursor: pointer; }
#oc-dialog-filepicker-content .dirtree span:last-child { font-weight: bold; }
#oc-dialog-filepicker-content .dirtree span:not(:last-child)::after { content: '>'; padding: 3px;}
+#oc-dialog-filepicker-content .filelist-container {
+ box-sizing: border-box;
+ display: inline-block;
+ overflow-y: auto;
+ height: 321px;
+ width: 100%;
+ padding-bottom: 55px;
+}
#oc-dialog-filepicker-content .filelist {
- overflow-y:auto;
- height: 290px;
background-color:white;
width:100%;
}
-
-#oc-dialog-filepicker-content .filelist li:first-child {
- border-top: 1px solid #eee;
+#oc-dialog-filepicker-content #filestable.filelist {
+ /* prevent the filepicker to overflow */
+ min-width: initial;
}
-#oc-dialog-filepicker-content .filelist li {
- position: relative;
- height: 40px;
- padding: 5px;
+
+#oc-dialog-filepicker-content .filelist td {
+ padding: 14px;
border-bottom: 1px solid #eee;
}
+#oc-dialog-filepicker-content .filelist tr:last-child td {
+ border-bottom: none;
+}
#oc-dialog-filepicker-content .filelist .filename {
- position: absolute;
- padding-top: 9px;
- max-width: 60%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
+ background-size: 32px;
+ background-repeat: no-repeat;
+ padding-left: 51px;
+ background-position: 7px 7px;
+ cursor: pointer;
}
-#oc-dialog-filepicker-content .filelist img {
- margin: 2px 1em 0 4px;
- width: 32px;
-}
+
+#oc-dialog-filepicker-content .filelist .filesize,
#oc-dialog-filepicker-content .filelist .date {
- float: right;
- margin-right: 10px;
- margin-top: 0;
- padding-top: 9px;
+ width: 80px;
+}
+
+#oc-dialog-filepicker-content .filelist .filesize {
+ text-align: right;
}
#oc-dialog-filepicker-content .filepicker_element_selected { background-color:lightblue;}
.ui-dialog {position:fixed !important;}
diff --git a/core/js/files/client.js b/core/js/files/client.js
index a195258afbb..572f7879e17 100644
--- a/core/js/files/client.js
+++ b/core/js/files/client.js
@@ -437,6 +437,7 @@
*
* @param {Object} filter filter criteria
* @param {Object} [filter.systemTagIds] list of system tag ids to filter by
+ * @param {bool} [filter.favorite] set it to filter by favorites
* @param {Object} [options] options
* @param {Array} [options.properties] list of Webdav properties to retrieve
*
@@ -454,7 +455,7 @@
properties = options.properties;
}
- if (!filter || !filter.systemTagIds || !filter.systemTagIds.length) {
+ if (!filter || (!filter.systemTagIds && _.isUndefined(filter.favorite))) {
throw 'Missing filter argument';
}
@@ -480,6 +481,9 @@
_.each(filter.systemTagIds, function(systemTagIds) {
body += ' <oc:systemtag>' + escapeHTML(systemTagIds) + '</oc:systemtag>\n';
});
+ if (filter.favorite) {
+ body += ' <oc:favorite>' + (filter.favorite ? '1': '0') + '</oc:favorite>\n';
+ }
body += ' </oc:filter-rules>\n';
// end of root
diff --git a/core/js/integritycheck-failed-notification.js b/core/js/integritycheck-failed-notification.js
index 5bc758d54cb..9f7c59b9089 100644
--- a/core/js/integritycheck-failed-notification.js
+++ b/core/js/integritycheck-failed-notification.js
@@ -31,6 +31,7 @@ $(document).ready(function(){
OC.Notification.showHtml(
text,
{
+ type: 'error',
isHTML: true
}
);
diff --git a/core/js/jquery.ocdialog.js b/core/js/jquery.ocdialog.js
index 15b58f9e086..66c7d9bd7f1 100644
--- a/core/js/jquery.ocdialog.js
+++ b/core/js/jquery.ocdialog.js
@@ -101,9 +101,9 @@
if(this.$title) {
this.$title.text(value);
} else {
- var $title = $('<h3 class="oc-dialog-title">'
+ var $title = $('<h2 class="oc-dialog-title">'
+ value
- + '</h3>');
+ + '</h2>');
this.$title = $title.prependTo(this.$dialog);
}
this._setSizes();
@@ -184,11 +184,11 @@
if (content_height> 0) {
this.element.css({
height: content_height + 'px',
- width: this.$dialog.innerWidth()-20 + 'px'
+ width: this.$dialog.innerWidth() - 30 + 'px'
});
} else {
this.element.css({
- width : this.$dialog.innerWidth() - 20 + 'px'
+ width : this.$dialog.innerWidth() - 30 + 'px'
});
}
},
diff --git a/core/js/oc-dialogs.js b/core/js/oc-dialogs.js
index 87d16a1394a..7a7876bf30f 100644
--- a/core/js/oc-dialogs.js
+++ b/core/js/oc-dialogs.js
@@ -148,6 +148,7 @@ var OCdialogs = {
return;
}
this.filepicker.loading = true;
+ this.filepicker.filesClient = OC.Files.getClient();
$.when(this._getFilePickerTemplate()).then(function($tmpl) {
self.filepicker.loading = false;
var dialogName = 'oc-dialog-filepicker-content';
@@ -172,10 +173,10 @@ var OCdialogs = {
$('body').append(self.$filePicker);
self.$filePicker.ready(function() {
- self.$filelist = self.$filePicker.find('.filelist');
+ self.$filelist = self.$filePicker.find('.filelist tbody');
self.$dirTree = self.$filePicker.find('.dirtree');
self.$dirTree.on('click', 'div:not(:last-child)', self, self._handleTreeListSelect.bind(self));
- self.$filelist.on('click', 'li', function(event) {
+ self.$filelist.on('click', 'tr', function(event) {
self._handlePickerClick(event, $(this));
});
self._fillFilePicker('');
@@ -187,12 +188,12 @@ var OCdialogs = {
var datapath;
if (multiselect === true) {
datapath = [];
- self.$filelist.find('.filepicker_element_selected .filename').each(function(index, element) {
- datapath.push(self.$filePicker.data('path') + '/' + $(element).text());
+ self.$filelist.find('tr.filepicker_element_selected').each(function(index, element) {
+ datapath.push(self.$filePicker.data('path') + '/' + $(element).data('entryname'));
});
} else {
datapath = self.$filePicker.data('path');
- datapath += '/' + self.$filelist.find('.filepicker_element_selected .filename').text();
+ datapath += '/' + self.$filelist.find('tr.filepicker_element_selected').data('entryname');
}
callback(datapath);
self.$filePicker.ocdialog('close');
@@ -223,7 +224,11 @@ var OCdialogs = {
// Hence this is one of the approach to get the choose button.
var getOcDialog = self.$filePicker.closest('.oc-dialog');
var buttonEnableDisable = getOcDialog.find('.primary');
- buttonEnableDisable.prop("disabled", "true");
+ if (self.$filePicker.data('mimetype') === "httpd/unix-directory") {
+ buttonEnableDisable.prop("disabled", false);
+ } else {
+ buttonEnableDisable.prop("disabled", true);
+ }
if (!OC.Util.hasSVGSupport()) {
OC.Util.replaceSVG(self.$filePicker.parent());
@@ -680,7 +685,7 @@ var OCdialogs = {
var self = this;
$.get(OC.filePath('core', 'templates', 'filepicker.html'), function(tmpl) {
self.$filePickerTemplate = $(tmpl);
- self.$listTmpl = self.$filePickerTemplate.find('.filelist li:first-child').detach();
+ self.$listTmpl = self.$filePickerTemplate.find('.filelist tr:first-child').detach();
defer.resolve(self.$filePickerTemplate);
})
.fail(function(jqXHR, textStatus, errorThrown) {
@@ -723,7 +728,7 @@ var OCdialogs = {
}
return defer.promise();
},
- _getFileList: function(dir, mimeType) {
+ _getFileList: function(dir, mimeType) { //this is only used by the spreedme app atm
if (typeof(mimeType) === "string") {
mimeType = [mimeType];
}
@@ -741,50 +746,62 @@ var OCdialogs = {
* fills the filepicker with files
*/
_fillFilePicker:function(dir) {
- var dirs = [];
- var others = [];
var self = this;
this.$filelist.empty().addClass('icon-loading');
this.$filePicker.data('path', dir);
- $.when(this._getFileList(dir, this.$filePicker.data('mimetype'))).then(function(response) {
-
- $.each(response.data.files, function(index, file) {
- if (file.type === 'dir') {
- dirs.push(file);
+ var filter = this.$filePicker.data('mimetype');
+ if (typeof(filter) === "string") {
+ filter = [filter];
+ }
+ self.filepicker.filesClient.getFolderContents(dir).then(function(status, files) {
+ if (filter) {
+ files = files.filter(function (file) {
+ return filter == [] || file.type === 'dir' || filter.indexOf(file.mimetype) !== -1;
+ });
+ }
+ files = files.sort(function(a, b) {
+ if (a.type === 'dir' && b.type !== 'dir') {
+ return -1;
+ } else if(a.type !== 'dir' && b.type === 'dir') {
+ return 1;
} else {
- others.push(file);
+ return 0;
}
});
self._fillSlug();
- var sorted = dirs.concat(others);
- $.each(sorted, function(idx, entry) {
+ $.each(files, function(idx, entry) {
entry.icon = OC.MimeType.getIconUrl(entry.mimetype);
- var $li = self.$listTmpl.octemplate({
+ if (typeof(entry.size) !== 'undefined' && entry.size >= 0) {
+ var simpleSize = humanFileSize(parseInt(entry.size, 10), true);
+ var sizeColor = Math.round(160 - Math.pow((entry.size / (1024 * 1024)), 2));
+ } else {
+ simpleSize = t('files', 'Pending');
+ }
+ var $row = self.$listTmpl.octemplate({
type: entry.type,
dir: dir,
filename: entry.name,
- date: OC.Util.relativeModifiedDate(entry.mtime)
+ date: OC.Util.relativeModifiedDate(entry.mtime),
+ size: simpleSize,
+ sizeColor: sizeColor,
+ icon: entry.icon
});
if (entry.type === 'file') {
var urlSpec = {
file: dir + '/' + entry.name,
};
- $li.find('img').attr('src', OC.MimeType.getIconUrl(entry.mimetype));
var img = new Image();
var previewUrl = OC.generateUrl('/core/preview.png?') + $.param(urlSpec);
img.onload = function() {
if (img.width > 5) {
- $li.find('img').attr('src', previewUrl);
+ $row.find('td.filename').attr('style', 'background-image:url(' + previewUrl + ')');
}
};
img.src = previewUrl;
}
- else {
- $li.find('img').attr('src', OC.Util.replaceSVGIcon(entry.icon));
- }
- self.$filelist.append($li);
+ self.$filelist.append($row);
});
self.$filelist.removeClass('icon-loading');
@@ -829,7 +846,7 @@ var OCdialogs = {
self._fillFilePicker(dir);
var getOcDialog = (event.target).closest('.oc-dialog');
var buttonEnableDisable = $('.primary', getOcDialog);
- if (this.$filePicker.data('mimetype') === "http/unix-directory") {
+ if (this.$filePicker.data('mimetype') === "httpd/unix-directory") {
buttonEnableDisable.prop("disabled", false);
} else {
buttonEnableDisable.prop("disabled", true);
diff --git a/core/js/tests/specs/files/clientSpec.js b/core/js/tests/specs/files/clientSpec.js
index 7673ec6e0fc..f75998029a9 100644
--- a/core/js/tests/specs/files/clientSpec.js
+++ b/core/js/tests/specs/files/clientSpec.js
@@ -461,9 +461,7 @@ describe('OC.Files.Client tests', function() {
it('throws exception if arguments are missing', function() {
var thrown = null;
try {
- client.getFilteredFiles({
- systemTagIds: []
- });
+ client.getFilteredFiles({});
} catch (e) {
thrown = true;
}
diff --git a/core/templates/filepicker.html b/core/templates/filepicker.html
index 46d1ca3d52d..b665ca26893 100644
--- a/core/templates/filepicker.html
+++ b/core/templates/filepicker.html
@@ -1,10 +1,19 @@
<div id="{dialog_name}" title="{title}">
<span class="dirtree breadcrumb"></span>
- <ul class="filelist">
- <li data-entryname="{filename}" data-type="{type}">
- <img />
- <span class="filename">{filename}</span>
- <span class="date">{date}</span>
- </li>
- </ul>
+ <div class="filelist-container">
+ <table id="filestable" class="filelist">
+ <tbody>
+ <tr data-entryname="{filename}" data-type="{type}">
+ <td class="filename"
+ style="background-image:url({icon})">{filename}
+ </td>
+ <td class="filesize"
+ style="color:rgb({sizeColor}, {sizeColor}, {sizeColor})">
+ {size}
+ </td>
+ <td class="date">{date}</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
</div>
diff --git a/settings/img/admin.png b/settings/img/admin.png
deleted file mode 100644
index 2a1f1eb257b..00000000000
--- a/settings/img/admin.png
+++ /dev/null
Binary files differ
diff --git a/settings/img/apps.png b/settings/img/apps.png
deleted file mode 100644
index 9afec98a460..00000000000
--- a/settings/img/apps.png
+++ /dev/null
Binary files differ
diff --git a/settings/img/help.png b/settings/img/help.png
deleted file mode 100644
index 69cb269d91b..00000000000
--- a/settings/img/help.png
+++ /dev/null
Binary files differ
diff --git a/settings/img/personal.png b/settings/img/personal.png
deleted file mode 100644
index 10e502faa9f..00000000000
--- a/settings/img/personal.png
+++ /dev/null
Binary files differ
diff --git a/settings/img/trans.png b/settings/img/trans.png
deleted file mode 100644
index ef57510d530..00000000000
--- a/settings/img/trans.png
+++ /dev/null
Binary files differ
diff --git a/settings/img/users.png b/settings/img/users.png
deleted file mode 100644
index 14a0d5d03c2..00000000000
--- a/settings/img/users.png
+++ /dev/null
Binary files differ
diff --git a/tests/lib/AppConfigTest.php b/tests/lib/AppConfigTest.php
index 520dbf66d36..c4da7507752 100644
--- a/tests/lib/AppConfigTest.php
+++ b/tests/lib/AppConfigTest.php
@@ -40,7 +40,7 @@ class AppConfigTest extends TestCase {
$sql->delete('appconfig');
$sql->execute();
- $this->registerAppConfig(new \OC\AppConfig($this->connection));
+ $this->overwriteService('AppConfig', new \OC\AppConfig($this->connection));
$sql = $this->connection->getQueryBuilder();
$sql->insert('appconfig')
@@ -130,21 +130,10 @@ class AppConfigTest extends TestCase {
$sql->execute();
}
- $this->registerAppConfig(new \OC\AppConfig(\OC::$server->getDatabaseConnection()));
+ $this->restoreService('AppConfig');
parent::tearDown();
}
- /**
- * Register an app config object for testing purposes.
- *
- * @param \OCP\IAppConfig $appConfig
- */
- protected function registerAppConfig($appConfig) {
- \OC::$server->registerService('AppConfig', function () use ($appConfig) {
- return $appConfig;
- });
- }
-
public function testGetApps() {
$config = new \OC\AppConfig(\OC::$server->getDatabaseConnection());
diff --git a/tests/lib/AppTest.php b/tests/lib/AppTest.php
index ac4fb913beb..ea4f328b63a 100644
--- a/tests/lib/AppTest.php
+++ b/tests/lib/AppTest.php
@@ -493,24 +493,22 @@ class AppTest extends \Test\TestCase {
* @param IAppConfig $appConfig app config mock
*/
private function registerAppConfig(IAppConfig $appConfig) {
- \OC::$server->registerService('AppConfig', function ($c) use ($appConfig) {
- return $appConfig;
- });
- \OC::$server->registerService('AppManager', function (\OC\Server $c) use ($appConfig) {
- return new \OC\App\AppManager($c->getUserSession(), $appConfig, $c->getGroupManager(), $c->getMemCacheFactory(), $c->getEventDispatcher());
- });
+ $this->overwriteService('AppConfig', $appConfig);
+ $this->overwriteService('AppManager', new \OC\App\AppManager(
+ \OC::$server->getUserSession(),
+ $appConfig,
+ \OC::$server->getGroupManager(),
+ \OC::$server->getMemCacheFactory(),
+ \OC::$server->getEventDispatcher()
+ ));
}
/**
* Restore the original app config service.
*/
private function restoreAppConfig() {
- \OC::$server->registerService('AppConfig', function (\OC\Server $c) {
- return new \OC\AppConfig($c->getDatabaseConnection());
- });
- \OC::$server->registerService('AppManager', function (\OC\Server $c) {
- return new \OC\App\AppManager($c->getUserSession(), $c->getAppConfig(), $c->getGroupManager(), $c->getMemCacheFactory(), $c->getEventDispatcher());
- });
+ $this->restoreService('AppConfig');
+ $this->restoreService('AppManager');
// Remove the cache of the mocked apps list with a forceRefresh
\OC_App::getEnabledApps();
diff --git a/tests/lib/Share/ShareTest.php b/tests/lib/Share/ShareTest.php
index 42adee21d0c..63289529843 100644
--- a/tests/lib/Share/ShareTest.php
+++ b/tests/lib/Share/ShareTest.php
@@ -119,12 +119,6 @@ class ShareTest extends \Test\TestCase {
parent::tearDown();
}
- protected function setHttpHelper($httpHelper) {
- \OC::$server->registerService('HTTPHelper', function () use ($httpHelper) {
- return $httpHelper;
- });
- }
-
public function testShareInvalidShareType() {
$message = 'Share type foobar is not valid for test.txt';
try {
@@ -1046,11 +1040,10 @@ class ShareTest extends \Test\TestCase {
* @param string $urlHost
*/
public function testRemoteShareUrlCalls($shareWith, $urlHost) {
- $oldHttpHelper = \OC::$server->query('HTTPHelper');
- $httpHelperMock = $this->getMockBuilder('OC\HttpHelper')
+ $httpHelperMock = $this->getMockBuilder('OC\HTTPHelper')
->disableOriginalConstructor()
->getMock();
- $this->setHttpHelper($httpHelperMock);
+ $this->overwriteService('HTTPHelper', $httpHelperMock);
$httpHelperMock->expects($this->at(0))
->method('post')
@@ -1075,7 +1068,7 @@ class ShareTest extends \Test\TestCase {
->willReturn(['success' => true, 'result' => json_encode(['ocs' => ['meta' => ['statuscode' => 100]]])]);
\OCP\Share::unshare('test', 'test.txt', \OCP\Share::SHARE_TYPE_REMOTE, $shareWith);
- $this->setHttpHelper($oldHttpHelper);
+ $this->restoreService('HTTPHelper');
}
/**
@@ -1473,11 +1466,10 @@ class ShareTest extends \Test\TestCase {
* Make sure that a user cannot have multiple identical shares to remote users
*/
public function testOnlyOneRemoteShare() {
- $oldHttpHelper = \OC::$server->query('HTTPHelper');
- $httpHelperMock = $this->getMockBuilder('OC\HttpHelper')
+ $httpHelperMock = $this->getMockBuilder('OC\HTTPHelper')
->disableOriginalConstructor()
->getMock();
- $this->setHttpHelper($httpHelperMock);
+ $this->overwriteService('HTTPHelper', $httpHelperMock);
$httpHelperMock->expects($this->at(0))
->method('post')
@@ -1502,7 +1494,7 @@ class ShareTest extends \Test\TestCase {
->willReturn(['success' => true, 'result' => json_encode(['ocs' => ['meta' => ['statuscode' => 100]]])]);
\OCP\Share::unshare('test', 'test.txt', \OCP\Share::SHARE_TYPE_REMOTE, 'foo@localhost');
- $this->setHttpHelper($oldHttpHelper);
+ $this->restoreService('HTTPHelper');
}
/**
diff --git a/tests/lib/TestCase.php b/tests/lib/TestCase.php
index 7ccff382357..61372d4ae90 100644
--- a/tests/lib/TestCase.php
+++ b/tests/lib/TestCase.php
@@ -102,6 +102,16 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase {
return false;
}
+ public function restoreAllServices() {
+ if (!empty($this->services)) {
+ if (!empty($this->services)) {
+ foreach ($this->services as $name => $service) {
+ $this->restoreService($name);
+ }
+ }
+ }
+ }
+
protected function getTestTraits() {
$traits = [];
$class = $this;
@@ -132,9 +142,7 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase {
// overwrite the command bus with one we can run ourselves
$this->commandBus = new QueueBus();
- \OC::$server->registerService('AsyncCommandBus', function () {
- return $this->commandBus;
- });
+ $this->overwriteService('AsyncCommandBus', $this->commandBus);
$traits = $this->getTestTraits();
foreach ($traits as $trait) {
@@ -145,7 +153,22 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase {
}
}
+ protected function onNotSuccessfulTest($e) {
+ $this->restoreAllServices();
+
+ // restore database connection
+ if (!$this->IsDatabaseAccessAllowed()) {
+ \OC::$server->registerService('DatabaseConnection', function () {
+ return self::$realDatabase;
+ });
+ }
+
+ parent::onNotSuccessfulTest($e);
+ }
+
protected function tearDown() {
+ $this->restoreAllServices();
+
// restore database connection
if (!$this->IsDatabaseAccessAllowed()) {
\OC::$server->registerService('DatabaseConnection', function () {
diff --git a/tests/lib/UtilTest.php b/tests/lib/UtilTest.php
index 619963a2abf..a1671191ab8 100644
--- a/tests/lib/UtilTest.php
+++ b/tests/lib/UtilTest.php
@@ -87,24 +87,17 @@ class UtilTest extends \Test\TestCase {
function testFormatDateWithTZFromSession($offset, $expected, $expectedTimeZone) {
date_default_timezone_set("UTC");
- $oldDateTimeFormatter = \OC::$server->query('DateTimeFormatter');
\OC::$server->getSession()->set('timezone', $offset);
$selectedTimeZone = \OC::$server->getDateTimeZone()->getTimeZone(1350129205);
$this->assertEquals($expectedTimeZone, $selectedTimeZone->getName());
$newDateTimeFormatter = new \OC\DateTimeFormatter($selectedTimeZone, new \OC_L10N('lib', 'en'));
- $this->setDateFormatter($newDateTimeFormatter);
+ $this->overwriteService('DateTimeFormatter', $newDateTimeFormatter);
$result = OC_Util::formatDate(1350129205, false);
$this->assertEquals($expected, $result);
- $this->setDateFormatter($oldDateTimeFormatter);
- }
-
- protected function setDateFormatter($formatter) {
- \OC::$server->registerService('DateTimeFormatter', function ($c) use ($formatter) {
- return $formatter;
- });
+ $this->restoreService('DateTimeFormatter');
}
function testSanitizeHTML() {