Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/server.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/apps/dav
diff options
context:
space:
mode:
authorArthur Schiwon <blizzz@arthur-schiwon.de>2016-06-11 16:34:43 +0300
committerArthur Schiwon <blizzz@arthur-schiwon.de>2016-06-11 16:34:43 +0300
commit42c66efea5ef512d3a3442112f820168e6499265 (patch)
tree97ef44632d653656608e71e096fd537bbd609936 /apps/dav
parent75f37f550bb7895757325d3f9a3215bcc4471065 (diff)
parent52a0c939ab8674857bbfe9a9fb0ee7308eee960e (diff)
Merge branch 'master' of https://github.com/owncloud/core into downstream-160611
Diffstat (limited to 'apps/dav')
-rw-r--r--apps/dav/appinfo/database.xml6
-rw-r--r--apps/dav/appinfo/info.xml7
-rw-r--r--apps/dav/appinfo/v1/publicwebdav.php9
-rw-r--r--apps/dav/lib/AppInfo/Application.php10
-rw-r--r--apps/dav/lib/CalDAV/CalDavBackend.php90
-rw-r--r--apps/dav/lib/CalDAV/Calendar.php77
-rw-r--r--apps/dav/lib/CalDAV/CalendarObject.php92
-rw-r--r--apps/dav/lib/Connector/PublicAuth.php9
-rw-r--r--apps/dav/lib/Connector/Sabre/Auth.php4
-rw-r--r--apps/dav/lib/Connector/Sabre/FilesPlugin.php23
-rw-r--r--apps/dav/lib/Connector/Sabre/ServerFactory.php4
-rw-r--r--apps/dav/lib/Migration/Classification.php93
-rw-r--r--apps/dav/lib/Server.php1
-rw-r--r--apps/dav/tests/unit/CalDAV/AbstractCalDavBackendTest.php163
-rw-r--r--apps/dav/tests/unit/CalDAV/CalDavBackendTest.php141
-rw-r--r--apps/dav/tests/unit/CalDAV/CalendarTest.php150
-rw-r--r--apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php66
-rw-r--r--apps/dav/tests/unit/Connector/Sabre/FilesReportPluginTest.php3
-rw-r--r--apps/dav/tests/unit/Migration/ClassificationTest.php75
19 files changed, 857 insertions, 166 deletions
diff --git a/apps/dav/appinfo/database.xml b/apps/dav/appinfo/database.xml
index f79ea07ae76..9578526a705 100644
--- a/apps/dav/appinfo/database.xml
+++ b/apps/dav/appinfo/database.xml
@@ -272,6 +272,12 @@ CREATE TABLE calendarobjects (
<type>text</type>
<length>255</length>
</field>
+ <field>
+ <comments>0 - public, 1 - private, 2 - confidential</comments>
+ <name>classification</name>
+ <type>integer</type>
+ <default>0</default>
+ </field>
<index>
<name>calobjects_index</name>
<unique>true</unique>
diff --git a/apps/dav/appinfo/info.xml b/apps/dav/appinfo/info.xml
index ca456b03089..26e37e6bb86 100644
--- a/apps/dav/appinfo/info.xml
+++ b/apps/dav/appinfo/info.xml
@@ -5,7 +5,7 @@
<description>ownCloud WebDAV endpoint</description>
<licence>AGPL</licence>
<author>owncloud.org</author>
- <version>0.2.4</version>
+ <version>0.2.5</version>
<default_enable/>
<types>
<filesystem/>
@@ -20,4 +20,9 @@
<background-jobs>
<job>OCA\DAV\CardDAV\Sync\SyncJob</job>
</background-jobs>
+ <repair-steps>
+ <post-migration>
+ <job>OCA\DAV\Migration\Classification</job>
+ </post-migration>
+ </repair-steps>
</info>
diff --git a/apps/dav/appinfo/v1/publicwebdav.php b/apps/dav/appinfo/v1/publicwebdav.php
index c6c319aa36d..261a4d4b96d 100644
--- a/apps/dav/appinfo/v1/publicwebdav.php
+++ b/apps/dav/appinfo/v1/publicwebdav.php
@@ -66,7 +66,6 @@ $server = $serverFactory->createServer($baseuri, $requestUri, $authBackend, func
$share = $authBackend->getShare();
$owner = $share->getShareOwner();
- $isWritable = $share->getPermissions() & (\OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_CREATE);
$isReadable = $share->getPermissions() & \OCP\Constants::PERMISSION_READ;
$fileId = $share->getNodeId();
@@ -74,11 +73,9 @@ $server = $serverFactory->createServer($baseuri, $requestUri, $authBackend, func
return false;
}
- if (!$isWritable) {
- \OC\Files\Filesystem::addStorageWrapper('readonly', function ($mountPoint, $storage) {
- return new \OC\Files\Storage\Wrapper\PermissionsMask(array('storage' => $storage, 'mask' => \OCP\Constants::PERMISSION_READ + \OCP\Constants::PERMISSION_SHARE));
- });
- }
+ \OC\Files\Filesystem::addStorageWrapper('sharePermissions', function ($mountPoint, $storage) use ($share) {
+ return new \OC\Files\Storage\Wrapper\PermissionsMask(array('storage' => $storage, 'mask' => $share->getPermissions() | \OCP\Constants::PERMISSION_SHARE));
+ });
OC_Util::setupFS($owner);
$ownerView = \OC\Files\Filesystem::getView();
diff --git a/apps/dav/lib/AppInfo/Application.php b/apps/dav/lib/AppInfo/Application.php
index ba0ef421f97..9e0d2da4e17 100644
--- a/apps/dav/lib/AppInfo/Application.php
+++ b/apps/dav/lib/AppInfo/Application.php
@@ -31,10 +31,12 @@ use OCA\DAV\CardDAV\SyncService;
use OCA\DAV\Connector\Sabre\Principal;
use OCA\DAV\DAV\GroupPrincipalBackend;
use OCA\DAV\HookManager;
+use OCA\DAV\Migration\Classification;
use \OCP\AppFramework\App;
use OCP\AppFramework\IAppContainer;
use OCP\Contacts\IManager;
use OCP\IUser;
+use Sabre\VObject\Reader;
use Symfony\Component\EventDispatcher\GenericEvent;
class Application extends App {
@@ -106,6 +108,14 @@ class Application extends App {
$g
);
});
+
+ $container->registerService('OCA\DAV\Migration\Classification', function ($c) {
+ /** @var IAppContainer $c */
+ return new Classification(
+ $c->query('CalDavBackend'),
+ $c->getServer()->getUserManager()
+ );
+ });
}
/**
diff --git a/apps/dav/lib/CalDAV/CalDavBackend.php b/apps/dav/lib/CalDAV/CalDavBackend.php
index 64fdf0f7ebe..ce494082976 100644
--- a/apps/dav/lib/CalDAV/CalDavBackend.php
+++ b/apps/dav/lib/CalDAV/CalDavBackend.php
@@ -37,10 +37,11 @@ use Sabre\CalDAV\Xml\Property\ScheduleCalendarTransp;
use Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet;
use Sabre\DAV;
use Sabre\DAV\Exception\Forbidden;
+use Sabre\DAV\PropPatch;
use Sabre\HTTP\URLUtil;
use Sabre\VObject\DateTimeParser;
use Sabre\VObject\Reader;
-use Sabre\VObject\RecurrenceIterator;
+use Sabre\VObject\Recur\EventIterator;
/**
* Class CalDavBackend
@@ -61,6 +62,10 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
*/
const MAX_DATE = '2038-01-01';
+ const CLASSIFICATION_PUBLIC = 0;
+ const CLASSIFICATION_PRIVATE = 1;
+ const CLASSIFICATION_CONFIDENTIAL = 2;
+
/**
* List of CalDAV properties, and how they map to database field names
* Add your own properties by simply adding on to this array.
@@ -395,10 +400,10 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
*
* Read the PropPatch documentation for more info and examples.
*
- * @param \Sabre\DAV\PropPatch $propPatch
+ * @param PropPatch $propPatch
* @return void
*/
- function updateCalendar($calendarId, \Sabre\DAV\PropPatch $propPatch) {
+ function updateCalendar($calendarId, PropPatch $propPatch) {
$supportedProperties = array_keys($this->propertyMap);
$supportedProperties[] = '{' . Plugin::NS_CALDAV . '}schedule-calendar-transp';
@@ -484,7 +489,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
*/
function getCalendarObjects($calendarId) {
$query = $this->db->getQueryBuilder();
- $query->select(['id', 'uri', 'lastmodified', 'etag', 'calendarid', 'size', 'componenttype'])
+ $query->select(['id', 'uri', 'lastmodified', 'etag', 'calendarid', 'size', 'componenttype', 'classification'])
->from('calendarobjects')
->where($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId)));
$stmt = $query->execute();
@@ -499,6 +504,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
'calendarid' => $row['calendarid'],
'size' => (int)$row['size'],
'component' => strtolower($row['componenttype']),
+ 'classification'=> (int)$row['classification']
];
}
@@ -524,7 +530,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
function getCalendarObject($calendarId, $objectUri) {
$query = $this->db->getQueryBuilder();
- $query->select(['id', 'uri', 'lastmodified', 'etag', 'calendarid', 'size', 'calendardata', 'componenttype'])
+ $query->select(['id', 'uri', 'lastmodified', 'etag', 'calendarid', 'size', 'calendardata', 'componenttype', 'classification'])
->from('calendarobjects')
->where($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId)))
->andWhere($query->expr()->eq('uri', $query->createNamedParameter($objectUri)));
@@ -542,6 +548,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
'size' => (int)$row['size'],
'calendardata' => $this->readBlob($row['calendardata']),
'component' => strtolower($row['componenttype']),
+ 'classification'=> (int)$row['classification']
];
}
@@ -559,7 +566,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
*/
function getMultipleCalendarObjects($calendarId, array $uris) {
$query = $this->db->getQueryBuilder();
- $query->select(['id', 'uri', 'lastmodified', 'etag', 'calendarid', 'size', 'calendardata', 'componenttype'])
+ $query->select(['id', 'uri', 'lastmodified', 'etag', 'calendarid', 'size', 'calendardata', 'componenttype', 'classification'])
->from('calendarobjects')
->where($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId)))
->andWhere($query->expr()->in('uri', $query->createParameter('uri')))
@@ -579,6 +586,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
'size' => (int)$row['size'],
'calendardata' => $this->readBlob($row['calendardata']),
'component' => strtolower($row['componenttype']),
+ 'classification' => (int)$row['classification']
];
}
@@ -618,6 +626,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
'componenttype' => $query->createNamedParameter($extraData['componentType']),
'firstoccurence' => $query->createNamedParameter($extraData['firstOccurence']),
'lastoccurence' => $query->createNamedParameter($extraData['lastOccurence']),
+ 'classification' => $query->createNamedParameter($extraData['classification']),
'uid' => $query->createNamedParameter($extraData['uid']),
])
->execute();
@@ -657,6 +666,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
->set('componenttype', $query->createNamedParameter($extraData['componentType']))
->set('firstoccurence', $query->createNamedParameter($extraData['firstOccurence']))
->set('lastoccurence', $query->createNamedParameter($extraData['lastOccurence']))
+ ->set('classification', $query->createNamedParameter($extraData['classification']))
->set('uid', $query->createNamedParameter($extraData['uid']))
->where($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId)))
->andWhere($query->expr()->eq('uri', $query->createNamedParameter($objectUri)))
@@ -668,6 +678,23 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
}
/**
+ * @param int $calendarObjectId
+ * @param int $classification
+ */
+ public function setClassification($calendarObjectId, $classification) {
+ if (!in_array($classification, [
+ self::CLASSIFICATION_PUBLIC, self::CLASSIFICATION_PRIVATE, self::CLASSIFICATION_CONFIDENTIAL
+ ])) {
+ throw new \InvalidArgumentException();
+ }
+ $query = $this->db->getQueryBuilder();
+ $query->update('calendarobjects')
+ ->set('classification', $query->createNamedParameter($classification))
+ ->where($query->expr()->eq('id', $query->createNamedParameter($calendarObjectId)))
+ ->execute();
+ }
+
+ /**
* Deletes an existing calendar object.
*
* The object uri is only the basename, or filename and not a full path.
@@ -1086,10 +1113,10 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
* Read the PropPatch documentation for more info and examples.
*
* @param mixed $subscriptionId
- * @param \Sabre\DAV\PropPatch $propPatch
+ * @param PropPatch $propPatch
* @return void
*/
- function updateSubscription($subscriptionId, DAV\PropPatch $propPatch) {
+ function updateSubscription($subscriptionId, PropPatch $propPatch) {
$supportedProperties = array_keys($this->subscriptionPropertyMap);
$supportedProperties[] = '{http://calendarserver.org/ns/}source';
@@ -1280,14 +1307,15 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
* @param string $calendarData
* @return array
*/
- protected function getDenormalizedData($calendarData) {
+ public function getDenormalizedData($calendarData) {
$vObject = Reader::read($calendarData);
$componentType = null;
$component = null;
- $firstOccurence = null;
- $lastOccurence = null;
+ $firstOccurrence = null;
+ $lastOccurrence = null;
$uid = null;
+ $classification = self::CLASSIFICATION_PUBLIC;
foreach($vObject->getComponents() as $component) {
if ($component->name!=='VTIMEZONE') {
$componentType = $component->name;
@@ -1299,27 +1327,27 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
throw new \Sabre\DAV\Exception\BadRequest('Calendar objects must have a VJOURNAL, VEVENT or VTODO component');
}
if ($componentType === 'VEVENT' && $component->DTSTART) {
- $firstOccurence = $component->DTSTART->getDateTime()->getTimeStamp();
+ $firstOccurrence = $component->DTSTART->getDateTime()->getTimeStamp();
// Finding the last occurrence is a bit harder
if (!isset($component->RRULE)) {
if (isset($component->DTEND)) {
- $lastOccurence = $component->DTEND->getDateTime()->getTimeStamp();
+ $lastOccurrence = $component->DTEND->getDateTime()->getTimeStamp();
} elseif (isset($component->DURATION)) {
$endDate = clone $component->DTSTART->getDateTime();
$endDate->add(DateTimeParser::parse($component->DURATION->getValue()));
- $lastOccurence = $endDate->getTimeStamp();
+ $lastOccurrence = $endDate->getTimeStamp();
} elseif (!$component->DTSTART->hasTime()) {
$endDate = clone $component->DTSTART->getDateTime();
$endDate->modify('+1 day');
- $lastOccurence = $endDate->getTimeStamp();
+ $lastOccurrence = $endDate->getTimeStamp();
} else {
- $lastOccurence = $firstOccurence;
+ $lastOccurrence = $firstOccurrence;
}
} else {
- $it = new RecurrenceIterator($vObject, (string)$component->UID);
+ $it = new EventIterator($vObject, (string)$component->UID);
$maxDate = new \DateTime(self::MAX_DATE);
if ($it->isInfinite()) {
- $lastOccurence = $maxDate->getTimeStamp();
+ $lastOccurrence = $maxDate->getTimeStamp();
} else {
$end = $it->getDtEnd();
while($it->valid() && $end < $maxDate) {
@@ -1327,19 +1355,31 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
$it->next();
}
- $lastOccurence = $end->getTimeStamp();
+ $lastOccurrence = $end->getTimeStamp();
}
}
}
+ if ($component->CLASS) {
+ $classification = CalDavBackend::CLASSIFICATION_PRIVATE;
+ switch ($component->CLASS->getValue()) {
+ case 'PUBLIC':
+ $classification = CalDavBackend::CLASSIFICATION_PUBLIC;
+ break;
+ case 'CONFIDENTIAL':
+ $classification = CalDavBackend::CLASSIFICATION_CONFIDENTIAL;
+ break;
+ }
+ }
return [
- 'etag' => md5($calendarData),
- 'size' => strlen($calendarData),
- 'componentType' => $componentType,
- 'firstOccurence' => is_null($firstOccurence) ? null : max(0, $firstOccurence),
- 'lastOccurence' => $lastOccurence,
- 'uid' => $uid,
+ 'etag' => md5($calendarData),
+ 'size' => strlen($calendarData),
+ 'componentType' => $componentType,
+ 'firstOccurence' => is_null($firstOccurrence) ? null : max(0, $firstOccurrence),
+ 'lastOccurence' => $lastOccurrence,
+ 'uid' => $uid,
+ 'classification' => $classification
];
}
diff --git a/apps/dav/lib/CalDAV/Calendar.php b/apps/dav/lib/CalDAV/Calendar.php
index 73b3957a9b0..785bb5699e2 100644
--- a/apps/dav/lib/CalDAV/Calendar.php
+++ b/apps/dav/lib/CalDAV/Calendar.php
@@ -26,6 +26,7 @@ use OCA\DAV\DAV\Sharing\IShareable;
use OCP\IL10N;
use Sabre\CalDAV\Backend\BackendInterface;
use Sabre\DAV\Exception\Forbidden;
+use Sabre\DAV\Exception\NotFound;
use Sabre\DAV\PropPatch;
class Calendar extends \Sabre\CalDAV\Calendar implements IShareable {
@@ -162,6 +163,78 @@ class Calendar extends \Sabre\CalDAV\Calendar implements IShareable {
parent::propPatch($propPatch);
}
+ function getChild($name) {
+
+ $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'], $name);
+
+ if (!$obj) {
+ throw new NotFound('Calendar object not found');
+ }
+
+ if ($this->isShared() && $obj['classification'] === CalDavBackend::CLASSIFICATION_PRIVATE) {
+ throw new NotFound('Calendar object not found');
+ }
+
+ $obj['acl'] = $this->getChildACL();
+
+ return new CalendarObject($this->caldavBackend, $this->calendarInfo, $obj);
+
+ }
+
+ function getChildren() {
+
+ $objs = $this->caldavBackend->getCalendarObjects($this->calendarInfo['id']);
+ $children = [];
+ foreach ($objs as $obj) {
+ if ($this->isShared() && $obj['classification'] === CalDavBackend::CLASSIFICATION_PRIVATE) {
+ continue;
+ }
+ $obj['acl'] = $this->getChildACL();
+ $children[] = new CalendarObject($this->caldavBackend, $this->calendarInfo, $obj);
+ }
+ return $children;
+
+ }
+
+ function getMultipleChildren(array $paths) {
+
+ $objs = $this->caldavBackend->getMultipleCalendarObjects($this->calendarInfo['id'], $paths);
+ $children = [];
+ foreach ($objs as $obj) {
+ if ($this->isShared() && $obj['classification'] === CalDavBackend::CLASSIFICATION_PRIVATE) {
+ continue;
+ }
+ $obj['acl'] = $this->getChildACL();
+ $children[] = new CalendarObject($this->caldavBackend, $this->calendarInfo, $obj);
+ }
+ return $children;
+
+ }
+
+ function childExists($name) {
+ $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'], $name);
+ if (!$obj) {
+ return false;
+ }
+ if ($this->isShared() && $obj['classification'] === CalDavBackend::CLASSIFICATION_PRIVATE) {
+ return false;
+ }
+
+ return true;
+ }
+
+ function calendarQuery(array $filters) {
+
+ $uris = $this->caldavBackend->calendarQuery($this->calendarInfo['id'], $filters);
+ if ($this->isShared()) {
+ return array_filter($uris, function ($uri) {
+ return $this->childExists($uri);
+ });
+ }
+
+ return $uris;
+ }
+
private function canWrite() {
if (isset($this->calendarInfo['{http://owncloud.org/ns}read-only'])) {
return !$this->calendarInfo['{http://owncloud.org/ns}read-only'];
@@ -169,4 +242,8 @@ class Calendar extends \Sabre\CalDAV\Calendar implements IShareable {
return true;
}
+ private function isShared() {
+ return isset($this->calendarInfo['{http://owncloud.org/ns}owner-principal']);
+ }
+
}
diff --git a/apps/dav/lib/CalDAV/CalendarObject.php b/apps/dav/lib/CalDAV/CalendarObject.php
new file mode 100644
index 00000000000..b4a58b52093
--- /dev/null
+++ b/apps/dav/lib/CalDAV/CalendarObject.php
@@ -0,0 +1,92 @@
+<?php
+/**
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+
+namespace OCA\DAV\CalDAV;
+
+
+use Sabre\VObject\Component;
+use Sabre\VObject\Property;
+use Sabre\VObject\Reader;
+
+class CalendarObject extends \Sabre\CalDAV\CalendarObject {
+
+ /**
+ * @inheritdoc
+ */
+ function get() {
+ $data = parent::get();
+ if ($this->isShared() && $this->objectData['classification'] === CalDavBackend::CLASSIFICATION_CONFIDENTIAL) {
+ return $this->createConfidentialObject($data);
+ }
+ return $data;
+ }
+
+ private function isShared() {
+ return isset($this->calendarInfo['{http://owncloud.org/ns}owner-principal']);
+ }
+
+ /**
+ * @param string $calData
+ * @return string
+ */
+ private static function createConfidentialObject($calData) {
+
+ $vObject = Reader::read($calData);
+
+ /** @var Component $vElement */
+ $vElement = null;
+ if(isset($vObject->VEVENT)) {
+ $vElement = $vObject->VEVENT;
+ }
+ if(isset($vObject->VJOURNAL)) {
+ $vElement = $vObject->VJOURNAL;
+ }
+ if(isset($vObject->VTODO)) {
+ $vElement = $vObject->VTODO;
+ }
+ if(!is_null($vElement)) {
+ foreach ($vElement->children as &$property) {
+ /** @var Property $property */
+ switch($property->name) {
+ case 'CREATED':
+ case 'DTSTART':
+ case 'RRULE':
+ case 'DURATION':
+ case 'DTEND':
+ case 'CLASS':
+ case 'UID':
+ break;
+ case 'SUMMARY':
+ $property->setValue('Busy');
+ break;
+ default:
+ $vElement->__unset($property->name);
+ unset($property);
+ break;
+ }
+ }
+ }
+
+ return $vObject->serialize();
+ }
+
+}
diff --git a/apps/dav/lib/Connector/PublicAuth.php b/apps/dav/lib/Connector/PublicAuth.php
index 2716ca29107..4e63ca1d29e 100644
--- a/apps/dav/lib/Connector/PublicAuth.php
+++ b/apps/dav/lib/Connector/PublicAuth.php
@@ -31,13 +31,14 @@ use OCP\IRequest;
use OCP\ISession;
use OCP\Share\Exceptions\ShareNotFound;
use OCP\Share\IManager;
+use Sabre\DAV\Auth\Backend\AbstractBasic;
/**
* Class PublicAuth
*
* @package OCA\DAV\Connector
*/
-class PublicAuth extends \Sabre\DAV\Auth\Backend\AbstractBasic {
+class PublicAuth extends AbstractBasic {
/** @var \OCP\Share\IShare */
private $share;
@@ -62,6 +63,10 @@ class PublicAuth extends \Sabre\DAV\Auth\Backend\AbstractBasic {
$this->request = $request;
$this->shareManager = $shareManager;
$this->session = $session;
+
+ // setup realm
+ $defaults = new \OC_Defaults();
+ $this->realm = $defaults->getName();
}
/**
@@ -99,7 +104,7 @@ class PublicAuth extends \Sabre\DAV\Auth\Backend\AbstractBasic {
if (in_array('XMLHttpRequest', explode(',', $this->request->getHeader('X-Requested-With')))) {
// do not re-authenticate over ajax, use dummy auth name to prevent browser popup
http_response_code(401);
- header('WWW-Authenticate', 'DummyBasic real="ownCloud"');
+ header('WWW-Authenticate','DummyBasic realm="' . $this->realm . '"');
throw new \Sabre\DAV\Exception\NotAuthenticated('Cannot authenticate over ajax calls');
}
return false;
diff --git a/apps/dav/lib/Connector/Sabre/Auth.php b/apps/dav/lib/Connector/Sabre/Auth.php
index 27900cc1cad..653da10bc3c 100644
--- a/apps/dav/lib/Connector/Sabre/Auth.php
+++ b/apps/dav/lib/Connector/Sabre/Auth.php
@@ -74,6 +74,10 @@ class Auth extends AbstractBasic {
$this->twoFactorManager = $twoFactorManager;
$this->request = $request;
$this->principalPrefix = $principalPrefix;
+
+ // setup realm
+ $defaults = new \OC_Defaults();
+ $this->realm = $defaults->getName();
}
/**
diff --git a/apps/dav/lib/Connector/Sabre/FilesPlugin.php b/apps/dav/lib/Connector/Sabre/FilesPlugin.php
index dc47416cca8..0a2e6713cb4 100644
--- a/apps/dav/lib/Connector/Sabre/FilesPlugin.php
+++ b/apps/dav/lib/Connector/Sabre/FilesPlugin.php
@@ -42,6 +42,7 @@ use \Sabre\HTTP\RequestInterface;
use \Sabre\HTTP\ResponseInterface;
use OCP\Files\StorageNotAvailableException;
use OCP\IConfig;
+use OCP\IRequest;
class FilesPlugin extends ServerPlugin {
@@ -96,19 +97,28 @@ class FilesPlugin extends ServerPlugin {
private $config;
/**
+ * @var IRequest
+ */
+ private $request;
+
+ /**
* @param Tree $tree
* @param View $view
+ * @param IConfig $config
+ * @param IRequest $request
* @param bool $isPublic
* @param bool $downloadAttachment
*/
public function __construct(Tree $tree,
View $view,
IConfig $config,
+ IRequest $request,
$isPublic = false,
$downloadAttachment = true) {
$this->tree = $tree;
$this->fileView = $view;
$this->config = $config;
+ $this->request = $request;
$this->isPublic = $isPublic;
$this->downloadAttachment = $downloadAttachment;
}
@@ -225,7 +235,18 @@ class FilesPlugin extends ServerPlugin {
// adds a 'Content-Disposition: attachment' header
if ($this->downloadAttachment) {
- $response->addHeader('Content-Disposition', 'attachment');
+ $filename = $node->getName();
+ if ($this->request->isUserAgent(
+ [
+ \OC\AppFramework\Http\Request::USER_AGENT_IE,
+ \OC\AppFramework\Http\Request::USER_AGENT_ANDROID_MOBILE_CHROME,
+ \OC\AppFramework\Http\Request::USER_AGENT_FREEBOX,
+ ])) {
+ $response->addHeader('Content-Disposition', 'attachment; filename="' . rawurlencode($filename) . '"');
+ } else {
+ $response->addHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . rawurlencode($filename)
+ . '; filename="' . rawurlencode($filename) . '"');
+ }
}
if ($node instanceof \OCA\DAV\Connector\Sabre\File) {
diff --git a/apps/dav/lib/Connector/Sabre/ServerFactory.php b/apps/dav/lib/Connector/Sabre/ServerFactory.php
index b193bfc76c7..c5b4f6a9352 100644
--- a/apps/dav/lib/Connector/Sabre/ServerFactory.php
+++ b/apps/dav/lib/Connector/Sabre/ServerFactory.php
@@ -100,10 +100,9 @@ class ServerFactory {
$server->setBaseUri($baseUri);
// Load plugins
- $defaults = new \OC_Defaults();
$server->addPlugin(new \OCA\DAV\Connector\Sabre\MaintenancePlugin($this->config));
$server->addPlugin(new \OCA\DAV\Connector\Sabre\BlockLegacyClientPlugin($this->config));
- $server->addPlugin(new \Sabre\DAV\Auth\Plugin($authBackend, $defaults->getName()));
+ $server->addPlugin(new \Sabre\DAV\Auth\Plugin($authBackend));
// FIXME: The following line is a workaround for legacy components relying on being able to send a GET to /
$server->addPlugin(new \OCA\DAV\Connector\Sabre\DummyGetResponsePlugin());
$server->addPlugin(new \OCA\DAV\Connector\Sabre\ExceptionLoggerPlugin('webdav', $this->logger));
@@ -144,6 +143,7 @@ class ServerFactory {
$objectTree,
$view,
$this->config,
+ $this->request,
false,
!$this->config->getSystemValue('debug', false)
)
diff --git a/apps/dav/lib/Migration/Classification.php b/apps/dav/lib/Migration/Classification.php
new file mode 100644
index 00000000000..b793f790af5
--- /dev/null
+++ b/apps/dav/lib/Migration/Classification.php
@@ -0,0 +1,93 @@
+<?php
+/**
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+
+namespace OCA\DAV\Migration;
+
+use OCA\DAV\CalDAV\CalDavBackend;
+use OCP\IUser;
+use OCP\IUserManager;
+use OCP\Migration\IOutput;
+use OCP\Migration\IRepairStep;
+
+class Classification implements IRepairStep {
+
+ /** @var CalDavBackend */
+ private $calDavBackend;
+
+ /** @var IUserManager */
+ private $userManager;
+
+ /**
+ * Classification constructor.
+ *
+ * @param CalDavBackend $calDavBackend
+ */
+ public function __construct(CalDavBackend $calDavBackend, IUserManager $userManager) {
+ $this->calDavBackend = $calDavBackend;
+ $this->userManager = $userManager;
+ }
+
+ /**
+ * @param IUser $user
+ */
+ public function runForUser($user) {
+ $principal = 'principals/users/' . $user->getUID();
+ $calendars = $this->calDavBackend->getCalendarsForUser($principal);
+ foreach ($calendars as $calendar) {
+ $objects = $this->calDavBackend->getCalendarObjects($calendar['id']);
+ foreach ($objects as $object) {
+ $calObject = $this->calDavBackend->getCalendarObject($calendar['id'], $object['uri']);
+ $classification = $this->extractClassification($calObject['calendardata']);
+ $this->calDavBackend->setClassification($object['id'], $classification);
+ }
+ }
+ }
+
+ /**
+ * @param $calendarData
+ * @return integer
+ * @throws \Sabre\DAV\Exception\BadRequest
+ */
+ protected function extractClassification($calendarData) {
+ return $this->calDavBackend->getDenormalizedData($calendarData)['classification'];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function getName() {
+ return 'Fix classification for calendar objects';
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function run(IOutput $output) {
+ $output->startProgress();
+ $this->userManager->callForAllUsers(function($user) use ($output) {
+ /** @var IUser $user */
+ $output->advance(1, $user->getDisplayName());
+ $this->runForUser($user);
+ });
+ $output->finishProgress();
+ }
+}
diff --git a/apps/dav/lib/Server.php b/apps/dav/lib/Server.php
index 179558e97ae..e150f441b82 100644
--- a/apps/dav/lib/Server.php
+++ b/apps/dav/lib/Server.php
@@ -141,6 +141,7 @@ class Server {
$this->server->tree,
$view,
\OC::$server->getConfig(),
+ $this->request,
false,
!\OC::$server->getConfig()->getSystemValue('debug', false)
)
diff --git a/apps/dav/tests/unit/CalDAV/AbstractCalDavBackendTest.php b/apps/dav/tests/unit/CalDAV/AbstractCalDavBackendTest.php
new file mode 100644
index 00000000000..49e5e5a2bcc
--- /dev/null
+++ b/apps/dav/tests/unit/CalDAV/AbstractCalDavBackendTest.php
@@ -0,0 +1,163 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\DAV\Tests\unit\CalDAV;
+
+use DateTime;
+use DateTimeZone;
+use OCA\DAV\CalDAV\CalDavBackend;
+use OCA\DAV\CalDAV\Calendar;
+use OCA\DAV\Connector\Sabre\Principal;
+use OCP\IL10N;
+use Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet;
+use Sabre\DAV\PropPatch;
+use Sabre\DAV\Xml\Property\Href;
+use Sabre\DAVACL\IACL;
+use Test\TestCase;
+
+/**
+ * Class CalDavBackendTest
+ *
+ * @group DB
+ *
+ * @package OCA\DAV\Tests\unit\CalDAV
+ */
+abstract class AbstractCalDavBackendTest extends TestCase {
+
+ /** @var CalDavBackend */
+ protected $backend;
+
+ /** @var Principal | \PHPUnit_Framework_MockObject_MockObject */
+ protected $principal;
+
+ const UNIT_TEST_USER = 'principals/users/caldav-unit-test';
+ const UNIT_TEST_USER1 = 'principals/users/caldav-unit-test1';
+ const UNIT_TEST_GROUP = 'principals/groups/caldav-unit-test-group';
+
+ public function setUp() {
+ parent::setUp();
+
+ $this->principal = $this->getMockBuilder('OCA\DAV\Connector\Sabre\Principal')
+ ->disableOriginalConstructor()
+ ->setMethods(['getPrincipalByPath', 'getGroupMembership'])
+ ->getMock();
+ $this->principal->expects($this->any())->method('getPrincipalByPath')
+ ->willReturn([
+ 'uri' => 'principals/best-friend'
+ ]);
+ $this->principal->expects($this->any())->method('getGroupMembership')
+ ->withAnyParameters()
+ ->willReturn([self::UNIT_TEST_GROUP]);
+
+ $db = \OC::$server->getDatabaseConnection();
+ $this->backend = new CalDavBackend($db, $this->principal);
+
+ $this->tearDown();
+ }
+
+ public function tearDown() {
+ parent::tearDown();
+
+ if (is_null($this->backend)) {
+ return;
+ }
+ $books = $this->backend->getCalendarsForUser(self::UNIT_TEST_USER);
+ foreach ($books as $book) {
+ $this->backend->deleteCalendar($book['id']);
+ }
+ $subscriptions = $this->backend->getSubscriptionsForUser(self::UNIT_TEST_USER);
+ foreach ($subscriptions as $subscription) {
+ $this->backend->deleteSubscription($subscription['id']);
+ }
+ }
+
+ protected function createTestCalendar() {
+ $this->backend->createCalendar(self::UNIT_TEST_USER, 'Example', [
+ '{http://apple.com/ns/ical/}calendar-color' => '#1C4587FF'
+ ]);
+ $calendars = $this->backend->getCalendarsForUser(self::UNIT_TEST_USER);
+ $this->assertEquals(1, count($calendars));
+ $this->assertEquals(self::UNIT_TEST_USER, $calendars[0]['principaluri']);
+ /** @var SupportedCalendarComponentSet $components */
+ $components = $calendars[0]['{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set'];
+ $this->assertEquals(['VEVENT','VTODO'], $components->getValue());
+ $color = $calendars[0]['{http://apple.com/ns/ical/}calendar-color'];
+ $this->assertEquals('#1C4587FF', $color);
+ $this->assertEquals('Example', $calendars[0]['uri']);
+ $this->assertEquals('Example', $calendars[0]['{DAV:}displayname']);
+ $calendarId = $calendars[0]['id'];
+
+ return $calendarId;
+ }
+
+ protected function createEvent($calendarId, $start = '20130912T130000Z', $end = '20130912T140000Z') {
+
+ $calData = <<<EOD
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:ownCloud Calendar
+BEGIN:VEVENT
+CREATED;VALUE=DATE-TIME:20130910T125139Z
+UID:47d15e3ec8
+LAST-MODIFIED;VALUE=DATE-TIME:20130910T125139Z
+DTSTAMP;VALUE=DATE-TIME:20130910T125139Z
+SUMMARY:Test Event
+DTSTART;VALUE=DATE-TIME:$start
+DTEND;VALUE=DATE-TIME:$end
+CLASS:PUBLIC
+END:VEVENT
+END:VCALENDAR
+EOD;
+ $uri0 = $this->getUniqueID('event');
+ $this->backend->createCalendarObject($calendarId, $uri0, $calData);
+
+ return $uri0;
+ }
+
+ protected function assertAcl($principal, $privilege, $acl) {
+ foreach($acl as $a) {
+ if ($a['principal'] === $principal && $a['privilege'] === $privilege) {
+ $this->assertTrue(true);
+ return;
+ }
+ }
+ $this->fail("ACL does not contain $principal / $privilege");
+ }
+
+ protected function assertNotAcl($principal, $privilege, $acl) {
+ foreach($acl as $a) {
+ if ($a['principal'] === $principal && $a['privilege'] === $privilege) {
+ $this->fail("ACL contains $principal / $privilege");
+ return;
+ }
+ }
+ $this->assertTrue(true);
+ }
+
+ protected function assertAccess($shouldHaveAcl, $principal, $privilege, $acl) {
+ if ($shouldHaveAcl) {
+ $this->assertAcl($principal, $privilege, $acl);
+ } else {
+ $this->assertNotAcl($principal, $privilege, $acl);
+ }
+ }
+}
diff --git a/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php b/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php
index c3e32e436d9..977bdf15c8e 100644
--- a/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php
+++ b/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php
@@ -26,13 +26,10 @@ use DateTime;
use DateTimeZone;
use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\CalDAV\Calendar;
-use OCA\DAV\Connector\Sabre\Principal;
use OCP\IL10N;
-use Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet;
use Sabre\DAV\PropPatch;
use Sabre\DAV\Xml\Property\Href;
use Sabre\DAVACL\IACL;
-use Test\TestCase;
/**
* Class CalDavBackendTest
@@ -41,54 +38,7 @@ use Test\TestCase;
*
* @package OCA\DAV\Tests\unit\CalDAV
*/
-class CalDavBackendTest extends TestCase {
-
- /** @var CalDavBackend */
- private $backend;
-
- /** @var Principal | \PHPUnit_Framework_MockObject_MockObject */
- private $principal;
-
- const UNIT_TEST_USER = 'principals/users/caldav-unit-test';
- const UNIT_TEST_USER1 = 'principals/users/caldav-unit-test1';
- const UNIT_TEST_GROUP = 'principals/groups/caldav-unit-test-group';
-
- public function setUp() {
- parent::setUp();
-
- $this->principal = $this->getMockBuilder('OCA\DAV\Connector\Sabre\Principal')
- ->disableOriginalConstructor()
- ->setMethods(['getPrincipalByPath', 'getGroupMembership'])
- ->getMock();
- $this->principal->expects($this->any())->method('getPrincipalByPath')
- ->willReturn([
- 'uri' => 'principals/best-friend'
- ]);
- $this->principal->expects($this->any())->method('getGroupMembership')
- ->withAnyParameters()
- ->willReturn([self::UNIT_TEST_GROUP]);
-
- $db = \OC::$server->getDatabaseConnection();
- $this->backend = new CalDavBackend($db, $this->principal);
-
- $this->tearDown();
- }
-
- public function tearDown() {
- parent::tearDown();
-
- if (is_null($this->backend)) {
- return;
- }
- $books = $this->backend->getCalendarsForUser(self::UNIT_TEST_USER);
- foreach ($books as $book) {
- $this->backend->deleteCalendar($book['id']);
- }
- $subscriptions = $this->backend->getSubscriptionsForUser(self::UNIT_TEST_USER);
- foreach ($subscriptions as $subscription) {
- $this->backend->deleteSubscription($subscription['id']);
- }
- }
+class CalDavBackendTest extends AbstractCalDavBackendTest {
public function testCalendarOperations() {
@@ -232,6 +182,7 @@ EOD;
$calendarObjects = $this->backend->getCalendarObjects($calendarId);
$this->assertEquals(1, count($calendarObjects));
$this->assertEquals($calendarId, $calendarObjects[0]['calendarid']);
+ $this->assertArrayHasKey('classification', $calendarObjects[0]);
// get the cards
$calendarObject = $this->backend->getCalendarObject($calendarId, $uri);
@@ -241,6 +192,7 @@ EOD;
$this->assertArrayHasKey('lastmodified', $calendarObject);
$this->assertArrayHasKey('etag', $calendarObject);
$this->assertArrayHasKey('size', $calendarObject);
+ $this->assertArrayHasKey('classification', $calendarObject);
$this->assertEquals($calData, $calendarObject['calendardata']);
// update the card
@@ -310,6 +262,7 @@ EOD;
$this->assertArrayHasKey('lastmodified', $card);
$this->assertArrayHasKey('etag', $card);
$this->assertArrayHasKey('size', $card);
+ $this->assertArrayHasKey('classification', $card);
$this->assertEquals($calData, $card['calendardata']);
}
@@ -363,49 +316,6 @@ EOD;
];
}
- private function createTestCalendar() {
- $this->backend->createCalendar(self::UNIT_TEST_USER, 'Example', [
- '{http://apple.com/ns/ical/}calendar-color' => '#1C4587FF'
- ]);
- $calendars = $this->backend->getCalendarsForUser(self::UNIT_TEST_USER);
- $this->assertEquals(1, count($calendars));
- $this->assertEquals(self::UNIT_TEST_USER, $calendars[0]['principaluri']);
- /** @var SupportedCalendarComponentSet $components */
- $components = $calendars[0]['{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set'];
- $this->assertEquals(['VEVENT','VTODO'], $components->getValue());
- $color = $calendars[0]['{http://apple.com/ns/ical/}calendar-color'];
- $this->assertEquals('#1C4587FF', $color);
- $this->assertEquals('Example', $calendars[0]['uri']);
- $this->assertEquals('Example', $calendars[0]['{DAV:}displayname']);
- $calendarId = $calendars[0]['id'];
-
- return $calendarId;
- }
-
- private function createEvent($calendarId, $start = '20130912T130000Z', $end = '20130912T140000Z') {
-
- $calData = <<<EOD
-BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:ownCloud Calendar
-BEGIN:VEVENT
-CREATED;VALUE=DATE-TIME:20130910T125139Z
-UID:47d15e3ec8
-LAST-MODIFIED;VALUE=DATE-TIME:20130910T125139Z
-DTSTAMP;VALUE=DATE-TIME:20130910T125139Z
-SUMMARY:Test Event
-DTSTART;VALUE=DATE-TIME:$start
-DTEND;VALUE=DATE-TIME:$end
-CLASS:PUBLIC
-END:VEVENT
-END:VCALENDAR
-EOD;
- $uri0 = $this->getUniqueID('event');
- $this->backend->createCalendarObject($calendarId, $uri0, $calData);
-
- return $uri0;
- }
-
public function testSyncSupport() {
$calendarId = $this->createTestCalendar();
@@ -464,43 +374,20 @@ EOD;
/**
* @dataProvider providesCalDataForGetDenormalizedData
*/
- public function testGetDenormalizedData($expectedFirstOccurance, $calData) {
- $actual = $this->invokePrivate($this->backend, 'getDenormalizedData', [$calData]);
- $this->assertEquals($expectedFirstOccurance, $actual['firstOccurence']);
+ public function testGetDenormalizedData($expected, $key, $calData) {
+ $actual = $this->backend->getDenormalizedData($calData);
+ $this->assertEquals($expected, $actual[$key]);
}
public function providesCalDataForGetDenormalizedData() {
return [
- [0, "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Sabre//Sabre VObject 3.5.0//EN\r\nCALSCALE:GREGORIAN\r\nBEGIN:VEVENT\r\nUID:413F269B-B51B-46B1-AFB6-40055C53A4DC\r\nDTSTAMP:20160309T095056Z\r\nDTSTART;VALUE=DATE:16040222\r\nDTEND;VALUE=DATE:16040223\r\nRRULE:FREQ=YEARLY\r\nSUMMARY:SUMMARY\r\nTRANSP:TRANSPARENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"],
- [null, "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Sabre//Sabre VObject 3.5.0//EN\r\nCALSCALE:GREGORIAN\r\nBEGIN:VEVENT\r\nUID:413F269B-B51B-46B1-AFB6-40055C53A4DC\r\nDTSTAMP:20160309T095056Z\r\nRRULE:FREQ=YEARLY\r\nSUMMARY:SUMMARY\r\nTRANSP:TRANSPARENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"]
+ 'first occurrence before unix epoch starts' => [0, 'firstOccurence', "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Sabre//Sabre VObject 3.5.0//EN\r\nCALSCALE:GREGORIAN\r\nBEGIN:VEVENT\r\nUID:413F269B-B51B-46B1-AFB6-40055C53A4DC\r\nDTSTAMP:20160309T095056Z\r\nDTSTART;VALUE=DATE:16040222\r\nDTEND;VALUE=DATE:16040223\r\nRRULE:FREQ=YEARLY\r\nSUMMARY:SUMMARY\r\nTRANSP:TRANSPARENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"],
+ 'no first occurrence because yearly' => [null, 'firstOccurence', "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Sabre//Sabre VObject 3.5.0//EN\r\nCALSCALE:GREGORIAN\r\nBEGIN:VEVENT\r\nUID:413F269B-B51B-46B1-AFB6-40055C53A4DC\r\nDTSTAMP:20160309T095056Z\r\nRRULE:FREQ=YEARLY\r\nSUMMARY:SUMMARY\r\nTRANSP:TRANSPARENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"],
+ 'CLASS:PRIVATE' => [CalDavBackend::CLASSIFICATION_PRIVATE, 'classification', "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//dmfs.org//mimedir.icalendar//EN\r\nBEGIN:VTIMEZONE\r\nTZID:Europe/Berlin\r\nX-LIC-LOCATION:Europe/Berlin\r\nBEGIN:DAYLIGHT\r\nTZOFFSETFROM:+0100\r\nTZOFFSETTO:+0200\r\nTZNAME:CEST\r\nDTSTART:19700329T020000\r\nRRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU\r\nEND:DAYLIGHT\r\nBEGIN:STANDARD\r\nTZOFFSETFROM:+0200\r\nTZOFFSETTO:+0100\r\nTZNAME:CET\r\nDTSTART:19701025T030000\r\nRRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU\r\nEND:STANDARD\r\nEND:VTIMEZONE\r\nBEGIN:VEVENT\r\nDTSTART;TZID=Europe/Berlin:20160419T130000\r\nSUMMARY:Test\r\nCLASS:PRIVATE\r\nTRANSP:OPAQUE\r\nSTATUS:CONFIRMED\r\nDTEND;TZID=Europe/Berlin:20160419T140000\r\nLAST-MODIFIED:20160419T074202Z\r\nDTSTAMP:20160419T074202Z\r\nCREATED:20160419T074202Z\r\nUID:2e468c48-7860-492e-bc52-92fa0daeeccf.1461051722310\r\nEND:VEVENT\r\nEND:VCALENDAR"],
+ 'CLASS:PUBLIC' => [CalDavBackend::CLASSIFICATION_PUBLIC, 'classification', "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//dmfs.org//mimedir.icalendar//EN\r\nBEGIN:VTIMEZONE\r\nTZID:Europe/Berlin\r\nX-LIC-LOCATION:Europe/Berlin\r\nBEGIN:DAYLIGHT\r\nTZOFFSETFROM:+0100\r\nTZOFFSETTO:+0200\r\nTZNAME:CEST\r\nDTSTART:19700329T020000\r\nRRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU\r\nEND:DAYLIGHT\r\nBEGIN:STANDARD\r\nTZOFFSETFROM:+0200\r\nTZOFFSETTO:+0100\r\nTZNAME:CET\r\nDTSTART:19701025T030000\r\nRRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU\r\nEND:STANDARD\r\nEND:VTIMEZONE\r\nBEGIN:VEVENT\r\nDTSTART;TZID=Europe/Berlin:20160419T130000\r\nSUMMARY:Test\r\nCLASS:PUBLIC\r\nTRANSP:OPAQUE\r\nSTATUS:CONFIRMED\r\nDTEND;TZID=Europe/Berlin:20160419T140000\r\nLAST-MODIFIED:20160419T074202Z\r\nDTSTAMP:20160419T074202Z\r\nCREATED:20160419T074202Z\r\nUID:2e468c48-7860-492e-bc52-92fa0daeeccf.1461051722310\r\nEND:VEVENT\r\nEND:VCALENDAR"],
+ 'CLASS:CONFIDENTIAL' => [CalDavBackend::CLASSIFICATION_CONFIDENTIAL, 'classification', "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//dmfs.org//mimedir.icalendar//EN\r\nBEGIN:VTIMEZONE\r\nTZID:Europe/Berlin\r\nX-LIC-LOCATION:Europe/Berlin\r\nBEGIN:DAYLIGHT\r\nTZOFFSETFROM:+0100\r\nTZOFFSETTO:+0200\r\nTZNAME:CEST\r\nDTSTART:19700329T020000\r\nRRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU\r\nEND:DAYLIGHT\r\nBEGIN:STANDARD\r\nTZOFFSETFROM:+0200\r\nTZOFFSETTO:+0100\r\nTZNAME:CET\r\nDTSTART:19701025T030000\r\nRRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU\r\nEND:STANDARD\r\nEND:VTIMEZONE\r\nBEGIN:VEVENT\r\nDTSTART;TZID=Europe/Berlin:20160419T130000\r\nSUMMARY:Test\r\nCLASS:CONFIDENTIAL\r\nTRANSP:OPAQUE\r\nSTATUS:CONFIRMED\r\nDTEND;TZID=Europe/Berlin:20160419T140000\r\nLAST-MODIFIED:20160419T074202Z\r\nDTSTAMP:20160419T074202Z\r\nCREATED:20160419T074202Z\r\nUID:2e468c48-7860-492e-bc52-92fa0daeeccf.1461051722310\r\nEND:VEVENT\r\nEND:VCALENDAR"],
+ 'no class set -> public' => [CalDavBackend::CLASSIFICATION_PUBLIC, 'classification', "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//dmfs.org//mimedir.icalendar//EN\r\nBEGIN:VTIMEZONE\r\nTZID:Europe/Berlin\r\nX-LIC-LOCATION:Europe/Berlin\r\nBEGIN:DAYLIGHT\r\nTZOFFSETFROM:+0100\r\nTZOFFSETTO:+0200\r\nTZNAME:CEST\r\nDTSTART:19700329T020000\r\nRRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU\r\nEND:DAYLIGHT\r\nBEGIN:STANDARD\r\nTZOFFSETFROM:+0200\r\nTZOFFSETTO:+0100\r\nTZNAME:CET\r\nDTSTART:19701025T030000\r\nRRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU\r\nEND:STANDARD\r\nEND:VTIMEZONE\r\nBEGIN:VEVENT\r\nDTSTART;TZID=Europe/Berlin:20160419T130000\r\nSUMMARY:Test\r\nTRANSP:OPAQUE\r\nDTEND;TZID=Europe/Berlin:20160419T140000\r\nLAST-MODIFIED:20160419T074202Z\r\nDTSTAMP:20160419T074202Z\r\nCREATED:20160419T074202Z\r\nUID:2e468c48-7860-492e-bc52-92fa0daeeccf.1461051722310\r\nEND:VEVENT\r\nEND:VCALENDAR"],
+ 'unknown class -> private' => [CalDavBackend::CLASSIFICATION_PRIVATE, 'classification', "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//dmfs.org//mimedir.icalendar//EN\r\nBEGIN:VTIMEZONE\r\nTZID:Europe/Berlin\r\nX-LIC-LOCATION:Europe/Berlin\r\nBEGIN:DAYLIGHT\r\nTZOFFSETFROM:+0100\r\nTZOFFSETTO:+0200\r\nTZNAME:CEST\r\nDTSTART:19700329T020000\r\nRRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU\r\nEND:DAYLIGHT\r\nBEGIN:STANDARD\r\nTZOFFSETFROM:+0200\r\nTZOFFSETTO:+0100\r\nTZNAME:CET\r\nDTSTART:19701025T030000\r\nRRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU\r\nEND:STANDARD\r\nEND:VTIMEZONE\r\nBEGIN:VEVENT\r\nDTSTART;TZID=Europe/Berlin:20160419T130000\r\nSUMMARY:Test\r\nCLASS:VERTRAULICH\r\nTRANSP:OPAQUE\r\nSTATUS:CONFIRMED\r\nDTEND;TZID=Europe/Berlin:20160419T140000\r\nLAST-MODIFIED:20160419T074202Z\r\nDTSTAMP:20160419T074202Z\r\nCREATED:20160419T074202Z\r\nUID:2e468c48-7860-492e-bc52-92fa0daeeccf.1461051722310\r\nEND:VEVENT\r\nEND:VCALENDAR"],
];
}
-
- private function assertAcl($principal, $privilege, $acl) {
- foreach($acl as $a) {
- if ($a['principal'] === $principal && $a['privilege'] === $privilege) {
- $this->assertTrue(true);
- return;
- }
- }
- $this->fail("ACL does not contain $principal / $privilege");
- }
-
- private function assertNotAcl($principal, $privilege, $acl) {
- foreach($acl as $a) {
- if ($a['principal'] === $principal && $a['privilege'] === $privilege) {
- $this->fail("ACL contains $principal / $privilege");
- return;
- }
- }
- $this->assertTrue(true);
- }
-
- private function assertAccess($shouldHaveAcl, $principal, $privilege, $acl) {
- if ($shouldHaveAcl) {
- $this->assertAcl($principal, $privilege, $acl);
- } else {
- $this->assertNotAcl($principal, $privilege, $acl);
- }
- }
}
diff --git a/apps/dav/tests/unit/CalDAV/CalendarTest.php b/apps/dav/tests/unit/CalDAV/CalendarTest.php
index 73d85e82bbc..56a2d4fcba7 100644
--- a/apps/dav/tests/unit/CalDAV/CalendarTest.php
+++ b/apps/dav/tests/unit/CalDAV/CalendarTest.php
@@ -27,6 +27,7 @@ use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\CalDAV\Calendar;
use OCP\IL10N;
use Sabre\DAV\PropPatch;
+use Sabre\VObject\Reader;
use Test\TestCase;
class CalendarTest extends TestCase {
@@ -189,4 +190,153 @@ class CalendarTest extends TestCase {
'birthday calendar' => [false, false, false, BirthdayService::BIRTHDAY_CALENDAR_URI]
];
}
+
+ /**
+ * @dataProvider providesConfidentialClassificationData
+ * @param $expectedChildren
+ * @param $isShared
+ */
+ public function testPrivateClassification($expectedChildren, $isShared) {
+
+ $calObject0 = ['uri' => 'event-0', 'classification' => CalDavBackend::CLASSIFICATION_PUBLIC];
+ $calObject1 = ['uri' => 'event-1', 'classification' => CalDavBackend::CLASSIFICATION_CONFIDENTIAL];
+ $calObject2 = ['uri' => 'event-2', 'classification' => CalDavBackend::CLASSIFICATION_PRIVATE];
+
+ /** @var \PHPUnit_Framework_MockObject_MockObject | CalDavBackend $backend */
+ $backend = $this->getMockBuilder('OCA\DAV\CalDAV\CalDavBackend')->disableOriginalConstructor()->getMock();
+ $backend->expects($this->any())->method('getCalendarObjects')->willReturn([
+ $calObject0, $calObject1, $calObject2
+ ]);
+ $backend->expects($this->any())->method('getMultipleCalendarObjects')
+ ->with(666, ['event-0', 'event-1', 'event-2'])
+ ->willReturn([
+ $calObject0, $calObject1, $calObject2
+ ]);
+ $backend->expects($this->any())->method('getCalendarObject')
+ ->willReturn($calObject2)->with(666, 'event-2');
+
+ $calendarInfo = [
+ 'principaluri' => 'user2',
+ 'id' => 666,
+ 'uri' => 'cal',
+ ];
+
+ if ($isShared) {
+ $calendarInfo['{http://owncloud.org/ns}owner-principal'] = 'user1';
+
+ }
+ $c = new Calendar($backend, $calendarInfo, $this->l10n);
+ $children = $c->getChildren();
+ $this->assertEquals($expectedChildren, count($children));
+ $children = $c->getMultipleChildren(['event-0', 'event-1', 'event-2']);
+ $this->assertEquals($expectedChildren, count($children));
+
+ $this->assertEquals(!$isShared, $c->childExists('event-2'));
+ }
+
+ /**
+ * @dataProvider providesConfidentialClassificationData
+ * @param $expectedChildren
+ * @param $isShared
+ */
+ public function testConfidentialClassification($expectedChildren, $isShared) {
+ $start = '20160609';
+ $end = '20160610';
+
+ $calData = <<<EOD
+BEGIN:VCALENDAR
+PRODID:-//ownCloud calendar v1.2.2
+BEGIN:VEVENT
+CREATED:20160602T133732
+DTSTAMP:20160602T133732
+LAST-MODIFIED:20160602T133732
+UID:wej2z68l9h
+SUMMARY:Test Event
+LOCATION:Somewhere ...
+ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;CUTYPE=INDIVIDUAL;CN=de
+ epdiver:MAILTO:thomas.mueller@tmit.eu
+ORGANIZER;CN=deepdiver:MAILTO:thomas.mueller@tmit.eu
+DESCRIPTION:maybe ....
+DTSTART;TZID=Europe/Berlin;VALUE=DATE:$start
+DTEND;TZID=Europe/Berlin;VALUE=DATE:$end
+RRULE:FREQ=DAILY
+BEGIN:VALARM
+ACTION:AUDIO
+TRIGGER:-PT15M
+END:VALARM
+END:VEVENT
+BEGIN:VTIMEZONE
+TZID:Europe/Berlin
+BEGIN:DAYLIGHT
+DTSTART:19810329T020000
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
+TZNAME:MESZ
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0200
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:19961027T030000
+RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
+TZNAME:MEZ
+TZOFFSETFROM:+0200
+TZOFFSETTO:+0100
+END:STANDARD
+END:VTIMEZONE
+END:VCALENDAR
+EOD;
+
+ $calObject0 = ['uri' => 'event-0', 'classification' => CalDavBackend::CLASSIFICATION_PUBLIC];
+ $calObject1 = ['uri' => 'event-1', 'classification' => CalDavBackend::CLASSIFICATION_CONFIDENTIAL, 'calendardata' => $calData];
+ $calObject2 = ['uri' => 'event-2', 'classification' => CalDavBackend::CLASSIFICATION_PRIVATE];
+
+ /** @var \PHPUnit_Framework_MockObject_MockObject | CalDavBackend $backend */
+ $backend = $this->getMockBuilder('OCA\DAV\CalDAV\CalDavBackend')->disableOriginalConstructor()->getMock();
+ $backend->expects($this->any())->method('getCalendarObjects')->willReturn([
+ $calObject0, $calObject1, $calObject2
+ ]);
+ $backend->expects($this->any())->method('getMultipleCalendarObjects')
+ ->with(666, ['event-0', 'event-1', 'event-2'])
+ ->willReturn([
+ $calObject0, $calObject1, $calObject2
+ ]);
+ $backend->expects($this->any())->method('getCalendarObject')
+ ->willReturn($calObject1)->with(666, 'event-1');
+
+ $calendarInfo = [
+ 'principaluri' => 'user2',
+ 'id' => 666,
+ 'uri' => 'cal',
+ ];
+
+ if ($isShared) {
+ $calendarInfo['{http://owncloud.org/ns}owner-principal'] = 'user1';
+
+ }
+ $c = new Calendar($backend, $calendarInfo, $this->l10n);
+
+ // test private event
+ $privateEvent = $c->getChild('event-1');
+ $calData = $privateEvent->get();
+ $event = Reader::read($calData);
+
+ $this->assertEquals($start, $event->VEVENT->DTSTART->getValue());
+ $this->assertEquals($end, $event->VEVENT->DTEND->getValue());
+
+ if ($isShared) {
+ $this->assertEquals('Busy', $event->VEVENT->SUMMARY->getValue());
+ $this->assertArrayNotHasKey('ATTENDEE', $event->VEVENT);
+ $this->assertArrayNotHasKey('LOCATION', $event->VEVENT);
+ $this->assertArrayNotHasKey('DESCRIPTION', $event->VEVENT);
+ $this->assertArrayNotHasKey('ORGANIZER', $event->VEVENT);
+ } else {
+ $this->assertEquals('Test Event', $event->VEVENT->SUMMARY->getValue());
+ }
+ }
+
+ public function providesConfidentialClassificationData() {
+ return [
+ [3, false],
+ [2, true]
+ ];
+ }
}
diff --git a/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php
index 80f284e470e..2b3f3e15d1a 100644
--- a/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php
+++ b/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php
@@ -73,6 +73,11 @@ class FilesPluginTest extends TestCase {
*/
private $config;
+ /**
+ * @var \OCP\IRequest | \PHPUnit_Framework_MockObject_MockObject
+ */
+ private $request;
+
public function setUp() {
parent::setUp();
$this->server = $this->getMockBuilder('\Sabre\DAV\Server')
@@ -88,11 +93,13 @@ class FilesPluginTest extends TestCase {
$this->config->expects($this->any())->method('getSystemValue')
->with($this->equalTo('data-fingerprint'), $this->equalTo(''))
->willReturn('my_fingerprint');
+ $this->request = $this->getMock('\OCP\IRequest');
$this->plugin = new FilesPlugin(
$this->tree,
$this->view,
- $this->config
+ $this->config,
+ $this->request
);
$this->plugin->initialize($this->server);
}
@@ -268,6 +275,7 @@ class FilesPluginTest extends TestCase {
$this->tree,
$this->view,
$this->config,
+ $this->getMock('\OCP\IRequest'),
true);
$this->plugin->initialize($this->server);
@@ -484,4 +492,60 @@ class FilesPluginTest extends TestCase {
$this->plugin->checkMove('FolderA/test.txt', 'test.txt');
}
+
+ public function downloadHeadersProvider() {
+ return [
+ [
+ false,
+ 'attachment; filename*=UTF-8\'\'somefile.xml; filename="somefile.xml"'
+ ],
+ [
+ true,
+ 'attachment; filename="somefile.xml"'
+ ],
+ ];
+ }
+
+ /**
+ * @dataProvider downloadHeadersProvider
+ */
+ public function testDownloadHeaders($isClumsyAgent, $contentDispositionHeader) {
+ $request = $this->getMockBuilder('Sabre\HTTP\RequestInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $response = $this->getMockBuilder('Sabre\HTTP\ResponseInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $request
+ ->expects($this->once())
+ ->method('getPath')
+ ->will($this->returnValue('test/somefile.xml'));
+
+ $node = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\File')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $node
+ ->expects($this->once())
+ ->method('getName')
+ ->will($this->returnValue('somefile.xml'));
+
+ $this->tree
+ ->expects($this->once())
+ ->method('getNodeForPath')
+ ->with('test/somefile.xml')
+ ->will($this->returnValue($node));
+
+ $this->request
+ ->expects($this->once())
+ ->method('isUserAgent')
+ ->will($this->returnValue($isClumsyAgent));
+
+ $response
+ ->expects($this->once())
+ ->method('addHeader')
+ ->with('Content-Disposition', $contentDispositionHeader);
+
+ $this->plugin->httpGet($request, $response);
+ }
}
diff --git a/apps/dav/tests/unit/Connector/Sabre/FilesReportPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/FilesReportPluginTest.php
index 41d44efd89c..baf4259b215 100644
--- a/apps/dav/tests/unit/Connector/Sabre/FilesReportPluginTest.php
+++ b/apps/dav/tests/unit/Connector/Sabre/FilesReportPluginTest.php
@@ -343,7 +343,8 @@ class FilesReportPluginTest extends \Test\TestCase {
new \OCA\DAV\Connector\Sabre\FilesPlugin(
$this->tree,
$this->view,
- $config
+ $config,
+ $this->getMock('\OCP\IRequest')
)
);
$this->plugin->initialize($this->server);
diff --git a/apps/dav/tests/unit/Migration/ClassificationTest.php b/apps/dav/tests/unit/Migration/ClassificationTest.php
new file mode 100644
index 00000000000..5c7fa627226
--- /dev/null
+++ b/apps/dav/tests/unit/Migration/ClassificationTest.php
@@ -0,0 +1,75 @@
+<?php
+/**
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\DAV\Tests\unit\DAV\Migration;
+
+use OCA\DAV\CalDAV\CalDavBackend;
+use OCA\DAV\Migration\Classification;
+use OCA\DAV\Tests\unit\CalDAV\AbstractCalDavBackendTest;
+use OCP\IUser;
+
+/**
+ * Class ClassificationTest
+ *
+ * @group DB
+ *
+ * @package OCA\DAV\Tests\unit\DAV
+ */
+class ClassificationTest extends AbstractCalDavBackendTest {
+
+ /** @var \PHPUnit_Framework_MockObject_MockObject | \OCP\IUserManager */
+ private $userManager;
+
+ public function setUp() {
+ parent::setUp();
+
+ $this->userManager = $this->getMockBuilder('OCP\IUserManager')
+ ->disableOriginalConstructor()->getMock();
+ }
+
+ public function test() {
+ // setup data
+ $calendarId = $this->createTestCalendar();
+ $eventUri = $this->createEvent($calendarId, '20130912T130000Z', '20130912T140000Z');
+ $object = $this->backend->getCalendarObject($calendarId, $eventUri);
+
+ // assert proper classification
+ $this->assertEquals(CalDavBackend::CLASSIFICATION_PUBLIC, $object['classification']);
+ $this->backend->setClassification($object['id'], CalDavBackend::CLASSIFICATION_CONFIDENTIAL);
+ $object = $this->backend->getCalendarObject($calendarId, $eventUri);
+ $this->assertEquals(CalDavBackend::CLASSIFICATION_CONFIDENTIAL, $object['classification']);
+
+ // run migration
+ $c = new Classification($this->backend, $this->userManager);
+
+ /** @var IUser | \PHPUnit_Framework_MockObject_MockObject $user */
+ $user = $this->getMockBuilder('OCP\IUser')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $user->expects($this->once())->method('getUID')->willReturn('caldav-unit-test');
+
+ $c->runForUser($user);
+
+ // assert classification after migration
+ $object = $this->backend->getCalendarObject($calendarId, $eventUri);
+ $this->assertEquals(CalDavBackend::CLASSIFICATION_PUBLIC, $object['classification']);
+ }
+}