aboutsummaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
Diffstat (limited to 'apps')
-rw-r--r--apps/dav/appinfo/application.php42
-rw-r--r--apps/dav/appinfo/install.php1
-rw-r--r--apps/dav/appinfo/register_command.php3
-rw-r--r--apps/dav/command/migrateaddressbooks.php1
-rw-r--r--apps/dav/command/migratecalendars.php66
-rw-r--r--apps/dav/lib/caldav/caldavbackend.php124
-rw-r--r--apps/dav/lib/connector/sabre/filesreportplugin.php15
-rw-r--r--apps/dav/lib/migration/calendaradapter.php83
-rw-r--r--apps/dav/lib/migration/migratecalendars.php94
-rw-r--r--apps/dav/tests/unit/connector/sabre/filesreportplugin.php117
-rw-r--r--apps/dav/tests/unit/migration/calendar_schema.xml191
-rw-r--r--apps/dav/tests/unit/migration/calendaradaptertest.php131
-rw-r--r--apps/dav/tests/unit/migration/migratecalendartest.php76
-rw-r--r--apps/federatedfilesharing/lib/federatedshareprovider.php2
-rw-r--r--apps/files/appinfo/register_command.php10
-rw-r--r--apps/files/command/transferownership.php225
-rw-r--r--apps/files/js/filelist.js4
-rw-r--r--apps/files/tests/controller/ViewControllerTest.php25
-rw-r--r--apps/files/tests/js/filelistSpec.js6
-rw-r--r--apps/files_external/command/import.php4
-rw-r--r--apps/files_external/command/listcommand.php2
-rw-r--r--apps/files_external/lib/auth/password/globalauth.php4
-rw-r--r--apps/files_external/tests/auth/password/globalauth.php117
-rw-r--r--apps/files_external/tests/command/listcommandtest.php69
-rw-r--r--apps/files_sharing/lib/controllers/sharecontroller.php63
-rw-r--r--apps/files_sharing/tests/controller/sharecontroller.php20
-rw-r--r--apps/systemtags/appinfo/app.php14
-rw-r--r--apps/systemtags/css/systemtagsfilelist.css29
-rw-r--r--apps/systemtags/img/tag.pngbin0 -> 293 bytes
-rw-r--r--apps/systemtags/img/tag.svg5
-rw-r--r--apps/systemtags/js/app.js87
-rw-r--r--apps/systemtags/js/filesplugin.js3
-rw-r--r--apps/systemtags/js/systemtagsfilelist.js240
-rw-r--r--apps/systemtags/list.php25
-rw-r--r--apps/systemtags/templates/list.php38
-rw-r--r--apps/systemtags/tests/js/systemtagsfilelistSpec.js226
36 files changed, 2111 insertions, 51 deletions
diff --git a/apps/dav/appinfo/application.php b/apps/dav/appinfo/application.php
index 07905db7368..11ab384d75d 100644
--- a/apps/dav/appinfo/application.php
+++ b/apps/dav/appinfo/application.php
@@ -20,12 +20,16 @@
*/
namespace OCA\Dav\AppInfo;
+use OCA\DAV\CalDAV\CalDavBackend;
+use OCA\DAV\CardDAV\CardDavBackend;
use OCA\DAV\CardDAV\ContactsManager;
use OCA\DAV\CardDAV\SyncJob;
use OCA\DAV\CardDAV\SyncService;
use OCA\DAV\HookManager;
use OCA\Dav\Migration\AddressBookAdapter;
+use OCA\Dav\Migration\CalendarAdapter;
use OCA\Dav\Migration\MigrateAddressbooks;
+use OCA\Dav\Migration\MigrateCalendars;
use \OCP\AppFramework\App;
use OCP\AppFramework\IAppContainer;
use OCP\Contacts\IManager;
@@ -73,7 +77,17 @@ class Application extends App {
$c->getServer()->getUserManager(),
$c->getServer()->getGroupManager()
);
- return new \OCA\DAV\CardDAV\CardDavBackend($db, $principal, $logger);
+ return new CardDavBackend($db, $principal, $logger);
+ });
+
+ $container->registerService('CalDavBackend', function($c) {
+ /** @var IAppContainer $c */
+ $db = $c->getServer()->getDatabaseConnection();
+ $principal = new \OCA\DAV\Connector\Sabre\Principal(
+ $c->getServer()->getUserManager(),
+ $c->getServer()->getGroupManager()
+ );
+ return new CalDavBackend($db, $principal);
});
$container->registerService('MigrateAddressbooks', function($c) {
@@ -84,6 +98,15 @@ class Application extends App {
$c->query('CardDavBackend')
);
});
+
+ $container->registerService('MigrateCalendars', function($c) {
+ /** @var IAppContainer $c */
+ $db = $c->getServer()->getDatabaseConnection();
+ return new MigrateCalendars(
+ new CalendarAdapter($db),
+ $c->query('CalDavBackend')
+ );
+ });
}
/**
@@ -112,8 +135,8 @@ class Application extends App {
}
public function migrateAddressbooks() {
-
try {
+ /** @var MigrateAddressbooks $migration */
$migration = $this->getContainer()->query('MigrateAddressbooks');
$migration->setup();
$userManager = $this->getContainer()->getServer()->getUserManager();
@@ -127,4 +150,19 @@ class Application extends App {
}
}
+ public function migrateCalendars() {
+ try {
+ /** @var MigrateCalendars $migration */
+ $migration = $this->getContainer()->query('MigrateCalendars');
+ $migration->setup();
+ $userManager = $this->getContainer()->getServer()->getUserManager();
+
+ $userManager->callForAllUsers(function($user) use($migration) {
+ /** @var IUser $user */
+ $migration->migrateForUser($user->getUID());
+ });
+ } catch (\Exception $ex) {
+ $this->getContainer()->getServer()->getLogger()->logException($ex);
+ }
+ }
}
diff --git a/apps/dav/appinfo/install.php b/apps/dav/appinfo/install.php
index f6ef533958e..a7a3220b90f 100644
--- a/apps/dav/appinfo/install.php
+++ b/apps/dav/appinfo/install.php
@@ -24,3 +24,4 @@ use OCA\Dav\AppInfo\Application;
$app = new Application();
$app->setupCron();
$app->migrateAddressbooks();
+$app->migrateCalendars();
diff --git a/apps/dav/appinfo/register_command.php b/apps/dav/appinfo/register_command.php
index e8ca370f84f..4981cab9264 100644
--- a/apps/dav/appinfo/register_command.php
+++ b/apps/dav/appinfo/register_command.php
@@ -23,6 +23,7 @@ use OCA\Dav\AppInfo\Application;
use OCA\DAV\Command\CreateAddressBook;
use OCA\DAV\Command\CreateCalendar;
use OCA\Dav\Command\MigrateAddressbooks;
+use OCA\Dav\Command\MigrateCalendars;
use OCA\DAV\Command\SyncSystemAddressBook;
$config = \OC::$server->getConfig();
@@ -44,4 +45,6 @@ if ($config->getSystemValue('debug', false)){
$app = new \OCA\Dav\AppInfo\Application();
$migration = $app->getContainer()->query('MigrateAddressbooks');
$application->add(new MigrateAddressbooks($userManager, $migration));
+ $migration = $app->getContainer()->query('MigrateCalendars');
+ $application->add(new MigrateCalendars($userManager, $migration));
}
diff --git a/apps/dav/command/migrateaddressbooks.php b/apps/dav/command/migrateaddressbooks.php
index 2ab7113ab1f..f37c29e7ab3 100644
--- a/apps/dav/command/migrateaddressbooks.php
+++ b/apps/dav/command/migrateaddressbooks.php
@@ -68,6 +68,7 @@ class MigrateAddressbooks extends Command {
}
$output->writeln("Start migration for $user");
$this->service->migrateForUser($user);
+ return;
}
$output->writeln("Start migration of all known users ...");
$p = new ProgressBar($output);
diff --git a/apps/dav/command/migratecalendars.php b/apps/dav/command/migratecalendars.php
new file mode 100644
index 00000000000..eda4f5fb417
--- /dev/null
+++ b/apps/dav/command/migratecalendars.php
@@ -0,0 +1,66 @@
+<?php
+
+namespace OCA\Dav\Command;
+
+use OCP\IUser;
+use OCP\IUserManager;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Helper\ProgressBar;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class MigrateCalendars extends Command {
+
+ /** @var IUserManager */
+ protected $userManager;
+
+ /** @var \OCA\Dav\Migration\MigrateCalendars */
+ private $service;
+
+ /**
+ * @param IUserManager $userManager
+ * @param \OCA\Dav\Migration\MigrateCalendars $service
+ */
+ function __construct(IUserManager $userManager,
+ \OCA\Dav\Migration\MigrateCalendars $service
+ ) {
+ parent::__construct();
+ $this->userManager = $userManager;
+ $this->service = $service;
+ }
+
+ protected function configure() {
+ $this
+ ->setName('dav:migrate-calendars')
+ ->setDescription('Migrate calendars from the calendar app to core')
+ ->addArgument('user',
+ InputArgument::OPTIONAL,
+ 'User for whom all calendars will be migrated');
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $this->service->setup();
+
+ if ($input->hasArgument('user')) {
+ $user = $input->getArgument('user');
+ if (!$this->userManager->userExists($user)) {
+ throw new \InvalidArgumentException("User <$user> in unknown.");
+ }
+ $output->writeln("Start migration for $user");
+ $this->service->migrateForUser($user);
+ return;
+ }
+ $output->writeln("Start migration of all known users ...");
+ $p = new ProgressBar($output);
+ $p->start();
+ $this->userManager->callForAllUsers(function($user) use ($p) {
+ $p->advance();
+ /** @var IUser $user */
+ $this->service->migrateForUser($user->getUID());
+ });
+
+ $p->finish();
+ $output->writeln('');
+ }
+}
diff --git a/apps/dav/lib/caldav/caldavbackend.php b/apps/dav/lib/caldav/caldavbackend.php
index 52b4812b05b..775612487f9 100644
--- a/apps/dav/lib/caldav/caldavbackend.php
+++ b/apps/dav/lib/caldav/caldavbackend.php
@@ -22,9 +22,11 @@
namespace OCA\DAV\CalDAV;
+use OCA\DAV\DAV\Sharing\IShareable;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCA\DAV\Connector\Sabre\Principal;
use OCA\DAV\DAV\Sharing\Backend;
+use OCP\IDBConnection;
use Sabre\CalDAV\Backend\AbstractBackend;
use Sabre\CalDAV\Backend\SchedulingSupport;
use Sabre\CalDAV\Backend\SubscriptionSupport;
@@ -59,7 +61,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
const MAX_DATE = '2038-01-01';
/**
- * List of CalDAV properties, and how they map to database fieldnames
+ * List of CalDAV properties, and how they map to database field names
* Add your own properties by simply adding on to this array.
*
* Note that only string-based properties are supported here.
@@ -75,7 +77,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
];
/**
- * List of subscription properties, and how they map to database fieldnames.
+ * List of subscription properties, and how they map to database field names.
*
* @var array
*/
@@ -89,7 +91,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
'{http://calendarserver.org/ns/}subscribed-strip-attachments' => 'stripattachments',
];
- /** @var \OCP\IDBConnection */
+ /** @var IDBConnection */
private $db;
/** @var Backend */
@@ -101,9 +103,10 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
/**
* CalDavBackend constructor.
*
- * @param \OCP\IDBConnection $db
+ * @param IDBConnection $db
+ * @param Principal $principalBackend
*/
- public function __construct(\OCP\IDBConnection $db, Principal $principalBackend) {
+ public function __construct(IDBConnection $db, Principal $principalBackend) {
$this->db = $db;
$this->principalBackend = $principalBackend;
$this->sharingBackend = new Backend($this->db, $principalBackend, 'calendar');
@@ -232,6 +235,95 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
return array_values($calendars);
}
+ public function getCalendarByUri($principal, $uri) {
+ $fields = array_values($this->propertyMap);
+ $fields[] = 'id';
+ $fields[] = 'uri';
+ $fields[] = 'synctoken';
+ $fields[] = 'components';
+ $fields[] = 'principaluri';
+ $fields[] = 'transparent';
+
+ // Making fields a comma-delimited list
+ $query = $this->db->getQueryBuilder();
+ $query->select($fields)->from('calendars')
+ ->where($query->expr()->eq('uri', $query->createNamedParameter($uri)))
+ ->andWhere($query->expr()->eq('principaluri', $query->createNamedParameter($principal)))
+ ->setMaxResults(1);
+ $stmt = $query->execute();
+
+ $row = $stmt->fetch(\PDO::FETCH_ASSOC);
+ $stmt->closeCursor();
+ if ($row === false) {
+ return null;
+ }
+
+ $components = [];
+ if ($row['components']) {
+ $components = explode(',',$row['components']);
+ }
+
+ $calendar = [
+ 'id' => $row['id'],
+ 'uri' => $row['uri'],
+ 'principaluri' => $row['principaluri'],
+ '{' . Plugin::NS_CALENDARSERVER . '}getctag' => 'http://sabre.io/ns/sync/' . ($row['synctoken']?$row['synctoken']:'0'),
+ '{http://sabredav.org/ns}sync-token' => $row['synctoken']?$row['synctoken']:'0',
+ '{' . Plugin::NS_CALDAV . '}supported-calendar-component-set' => new SupportedCalendarComponentSet($components),
+ '{' . Plugin::NS_CALDAV . '}schedule-calendar-transp' => new ScheduleCalendarTransp($row['transparent']?'transparent':'opaque'),
+ ];
+
+ foreach($this->propertyMap as $xmlName=>$dbName) {
+ $calendar[$xmlName] = $row[$dbName];
+ }
+
+ return $calendar;
+ }
+
+ public function getCalendarById($calendarId) {
+ $fields = array_values($this->propertyMap);
+ $fields[] = 'id';
+ $fields[] = 'uri';
+ $fields[] = 'synctoken';
+ $fields[] = 'components';
+ $fields[] = 'principaluri';
+ $fields[] = 'transparent';
+
+ // Making fields a comma-delimited list
+ $query = $this->db->getQueryBuilder();
+ $query->select($fields)->from('calendars')
+ ->where($query->expr()->eq('id', $query->createNamedParameter($calendarId)))
+ ->setMaxResults(1);
+ $stmt = $query->execute();
+
+ $row = $stmt->fetch(\PDO::FETCH_ASSOC);
+ $stmt->closeCursor();
+ if ($row === false) {
+ return null;
+ }
+
+ $components = [];
+ if ($row['components']) {
+ $components = explode(',',$row['components']);
+ }
+
+ $calendar = [
+ 'id' => $row['id'],
+ 'uri' => $row['uri'],
+ 'principaluri' => $row['principaluri'],
+ '{' . Plugin::NS_CALENDARSERVER . '}getctag' => 'http://sabre.io/ns/sync/' . ($row['synctoken']?$row['synctoken']:'0'),
+ '{http://sabredav.org/ns}sync-token' => $row['synctoken']?$row['synctoken']:'0',
+ '{' . Plugin::NS_CALDAV . '}supported-calendar-component-set' => new SupportedCalendarComponentSet($components),
+ '{' . Plugin::NS_CALDAV . '}schedule-calendar-transp' => new ScheduleCalendarTransp($row['transparent']?'transparent':'opaque'),
+ ];
+
+ foreach($this->propertyMap as $xmlName=>$dbName) {
+ $calendar[$xmlName] = $row[$dbName];
+ }
+
+ return $calendar;
+ }
+
/**
* Creates a new calendar for a principal.
*
@@ -241,7 +333,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
* @param string $principalUri
* @param string $calendarUri
* @param array $properties
- * @return void
+ * @return int
*/
function createCalendar($principalUri, $calendarUri, array $properties) {
$values = [
@@ -278,6 +370,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
$query->setValue($column, $query->createNamedParameter($value));
}
$query->execute();
+ return $query->getLastInsertId();
}
/**
@@ -1249,16 +1342,29 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
return $cardData;
}
+ /**
+ * @param IShareable $shareable
+ * @param array $add
+ * @param array $remove
+ */
public function updateShares($shareable, $add, $remove) {
$this->sharingBackend->updateShares($shareable, $add, $remove);
}
+ /**
+ * @param int $resourceId
+ * @return array
+ */
public function getShares($resourceId) {
return $this->sharingBackend->getShares($resourceId);
}
- public function applyShareAcl($addressBookId, $acl) {
- return $this->sharingBackend->applyShareAcl($addressBookId, $acl);
+ /**
+ * @param int $resourceId
+ * @param array $acl
+ * @return array
+ */
+ public function applyShareAcl($resourceId, $acl) {
+ return $this->sharingBackend->applyShareAcl($resourceId, $acl);
}
-
}
diff --git a/apps/dav/lib/connector/sabre/filesreportplugin.php b/apps/dav/lib/connector/sabre/filesreportplugin.php
index 5bdd7a71ddc..141b684360e 100644
--- a/apps/dav/lib/connector/sabre/filesreportplugin.php
+++ b/apps/dav/lib/connector/sabre/filesreportplugin.php
@@ -211,7 +211,7 @@ class FilesReportPlugin extends ServerPlugin {
*/
public function processFilterRules($filterRules) {
$ns = '{' . $this::NS_OWNCLOUD . '}';
- $resultFileIds = [];
+ $resultFileIds = null;
$systemTagIds = [];
foreach ($filterRules as $filterRule) {
if ($filterRule['name'] === $ns . 'systemtag') {
@@ -239,11 +239,22 @@ class FilesReportPlugin extends ServerPlugin {
foreach ($systemTagIds as $systemTagId) {
$fileIds = $this->tagMapper->getObjectIdsForTags($systemTagId, 'files');
- if (empty($resultFileIds)) {
+ if (empty($fileIds)) {
+ // This tag has no files, nothing can ever show up
+ return [];
+ }
+
+ // first run ?
+ if ($resultFileIds === null) {
$resultFileIds = $fileIds;
} else {
$resultFileIds = array_intersect($resultFileIds, $fileIds);
}
+
+ if (empty($resultFileIds)) {
+ // Empty intersection, nothing can show up anymore
+ return [];
+ }
}
return $resultFileIds;
}
diff --git a/apps/dav/lib/migration/calendaradapter.php b/apps/dav/lib/migration/calendaradapter.php
new file mode 100644
index 00000000000..39faf2cec37
--- /dev/null
+++ b/apps/dav/lib/migration/calendaradapter.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace OCA\Dav\Migration;
+
+use OCP\IDBConnection;
+
+class CalendarAdapter {
+
+ /** @var \OCP\IDBConnection */
+ protected $dbConnection;
+
+ /** @var string */
+ private $sourceCalendarTable;
+
+ /** @var string */
+ private $sourceCalObjTable;
+
+ /**
+ * @param IDBConnection $dbConnection
+ * @param string $sourceCalendarTable
+ * @param string $sourceCalObjTable
+ */
+ function __construct(IDBConnection $dbConnection,
+ $sourceCalendarTable = 'clndr_calendars',
+ $sourceCalObjTable = 'clndr_objects') {
+ $this->dbConnection = $dbConnection;
+ $this->sourceCalendarTable = $sourceCalendarTable;
+ $this->sourceCalObjTable = $sourceCalObjTable;
+ }
+
+ /**
+ * @param string $user
+ * @param \Closure $callBack
+ */
+ public function foreachCalendar($user, \Closure $callBack) {
+ // get all calendars of that user
+ $query = $this->dbConnection->getQueryBuilder();
+ $stmt = $query->select('*')->from($this->sourceCalendarTable)
+ ->where($query->expr()->eq('userid', $query->createNamedParameter($user)))
+ ->execute();
+
+ while($row = $stmt->fetch()) {
+ $callBack($row);
+ }
+ }
+
+ public function setup() {
+ if (!$this->dbConnection->tableExists($this->sourceCalendarTable)) {
+ throw new \DomainException('Calendar tables are missing. Nothing to do.');
+ }
+ }
+
+ /**
+ * @param int $calendarId
+ * @param \Closure $callBack
+ */
+ public function foreachCalendarObject($calendarId, \Closure $callBack) {
+ $query = $this->dbConnection->getQueryBuilder();
+ $stmt = $query->select('*')->from($this->sourceCalObjTable)
+ ->where($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId)))
+ ->execute();
+
+ while($row = $stmt->fetch()) {
+ $callBack($row);
+ }
+ }
+
+ /**
+ * @param int $addressBookId
+ * @return array
+ */
+ public function getShares($addressBookId) {
+ $query = $this->dbConnection->getQueryBuilder();
+ $shares = $query->select('*')->from('share')
+ ->where($query->expr()->eq('item_source', $query->createNamedParameter($addressBookId)))
+ ->andWhere($query->expr()->eq('item_type', $query->expr()->literal('calendar')))
+ ->andWhere($query->expr()->in('share_type', [ $query->expr()->literal(0), $query->expr()->literal(1)]))
+ ->execute()
+ ->fetchAll();
+
+ return $shares;
+ }
+}
diff --git a/apps/dav/lib/migration/migratecalendars.php b/apps/dav/lib/migration/migratecalendars.php
new file mode 100644
index 00000000000..33f8a105180
--- /dev/null
+++ b/apps/dav/lib/migration/migratecalendars.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace OCA\Dav\Migration;
+
+use OCA\DAV\CalDAV\CalDavBackend;
+use OCA\DAV\CalDAV\Calendar;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class MigrateCalendars {
+
+ /** @var CalendarAdapter */
+ protected $adapter;
+
+ /** @var CalDavBackend */
+ private $backend;
+
+ /**
+ * @param CalendarAdapter $adapter
+ * @param CalDavBackend $backend
+ */
+ function __construct(CalendarAdapter $adapter,
+ CalDavBackend $backend
+ ) {
+ $this->adapter = $adapter;
+ $this->backend = $backend;
+ }
+
+ /**
+ * @param string $user
+ */
+ public function migrateForUser($user) {
+
+ $this->adapter->foreachCalendar($user, function($calendar) use ($user) {
+ $principal = "principals/users/$user";
+ $calendarByUri = $this->backend->getCalendarByUri($principal, $calendar['uri']);
+ if (!is_null($calendarByUri)) {
+ return;
+ }
+
+ $newId = $this->backend->createCalendar($principal, $calendar['uri'], [
+ '{DAV:}displayname' => $calendar['displayname'],
+ '{urn:ietf:params:xml:ns:caldav}calendar-description' => $calendar['displayname'],
+ '{urn:ietf:params:xml:ns:caldav}calendar-timezone' => $calendar['timezone'],
+ '{http://apple.com/ns/ical/}calendar-order' => $calendar['calendarorder'],
+ '{http://apple.com/ns/ical/}calendar-color' => $calendar['calendarcolor'],
+ ]);
+
+ $this->migrateCalendar($calendar['id'], $newId);
+ $this->migrateShares($calendar['id'], $newId);
+ });
+ }
+
+ public function setup() {
+ $this->adapter->setup();
+ }
+
+ /**
+ * @param int $calendarId
+ * @param int $newCalendarId
+ */
+ private function migrateCalendar($calendarId, $newCalendarId) {
+ $this->adapter->foreachCalendarObject($calendarId, function($calObject) use ($newCalendarId) {
+ $this->backend->createCalendarObject($newCalendarId, $calObject['uri'], $calObject['calendardata']);
+ });
+ }
+
+ /**
+ * @param int $calendarId
+ * @param int $newCalendarId
+ */
+ private function migrateShares($calendarId, $newCalendarId) {
+ $shares =$this->adapter->getShares($calendarId);
+ if (empty($shares)) {
+ return;
+ }
+
+ $add = array_map(function($s) {
+ $prefix = 'principal:principals/users/';
+ if ($s['share_type'] === 1) {
+ $prefix = 'principal:principals/groups/';
+ }
+ return [
+ 'href' => $prefix . $s['share_with']
+ ];
+ }, $shares);
+
+ $newCalendar = $this->backend->getCalendarById($newCalendarId);
+ $calendar = new Calendar($this->backend, $newCalendar);
+ $this->backend->updateShares($calendar, $add, []);
+ }
+}
diff --git a/apps/dav/tests/unit/connector/sabre/filesreportplugin.php b/apps/dav/tests/unit/connector/sabre/filesreportplugin.php
index 853e52f5039..b528e2d2427 100644
--- a/apps/dav/tests/unit/connector/sabre/filesreportplugin.php
+++ b/apps/dav/tests/unit/connector/sabre/filesreportplugin.php
@@ -30,16 +30,16 @@ use OCP\IGroupManager;
use OCP\SystemTag\ISystemTagManager;
class FilesReportPlugin extends \Test\TestCase {
- /** @var \Sabre\DAV\Server */
+ /** @var \Sabre\DAV\Server|\PHPUnit_Framework_MockObject_MockObject */
private $server;
- /** @var \Sabre\DAV\Tree */
+ /** @var \Sabre\DAV\Tree|\PHPUnit_Framework_MockObject_MockObject */
private $tree;
- /** @var ISystemTagObjectMapper */
+ /** @var ISystemTagObjectMapper|\PHPUnit_Framework_MockObject_MockObject */
private $tagMapper;
- /** @var ISystemTagManager */
+ /** @var ISystemTagManager|\PHPUnit_Framework_MockObject_MockObject */
private $tagManager;
/** @var \OCP\IUserSession */
@@ -48,13 +48,13 @@ class FilesReportPlugin extends \Test\TestCase {
/** @var FilesReportPluginImplementation */
private $plugin;
- /** @var View **/
+ /** @var View|\PHPUnit_Framework_MockObject_MockObject **/
private $view;
- /** @var IGroupManager **/
+ /** @var IGroupManager|\PHPUnit_Framework_MockObject_MockObject **/
private $groupManager;
- /** @var Folder **/
+ /** @var Folder|\PHPUnit_Framework_MockObject_MockObject **/
private $userFolder;
public function setUp() {
@@ -254,6 +254,7 @@ class FilesReportPlugin extends \Test\TestCase {
->with('222')
->will($this->returnValue([$filesNode2]));
+ /** @var \OCA\DAV\Connector\Sabre\Directory|\PHPUnit_Framework_MockObject_MockObject $reportTargetNode */
$result = $this->plugin->findNodesByFileIds($reportTargetNode, ['111', '222']);
$this->assertCount(2, $result);
@@ -304,6 +305,7 @@ class FilesReportPlugin extends \Test\TestCase {
->with('222')
->will($this->returnValue([$filesNode2]));
+ /** @var \OCA\DAV\Connector\Sabre\Directory|\PHPUnit_Framework_MockObject_MockObject $reportTargetNode */
$result = $this->plugin->findNodesByFileIds($reportTargetNode, ['111', '222']);
$this->assertCount(2, $result);
@@ -361,10 +363,14 @@ class FilesReportPlugin extends \Test\TestCase {
->method('isAdmin')
->will($this->returnValue(true));
- $this->tagMapper->expects($this->once())
+ $this->tagMapper->expects($this->exactly(1))
->method('getObjectIdsForTags')
- ->with('123')
- ->will($this->returnValue(['111', '222']));
+ ->withConsecutive(
+ ['123', 'files']
+ )
+ ->willReturnMap([
+ ['123', 'files', ['111', '222']],
+ ]);
$rules = [
['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
@@ -378,14 +384,16 @@ class FilesReportPlugin extends \Test\TestCase {
->method('isAdmin')
->will($this->returnValue(true));
- $this->tagMapper->expects($this->at(0))
- ->method('getObjectIdsForTags')
- ->with('123')
- ->will($this->returnValue(['111', '222']));
- $this->tagMapper->expects($this->at(1))
+ $this->tagMapper->expects($this->exactly(2))
->method('getObjectIdsForTags')
- ->with('456')
- ->will($this->returnValue(['222', '333']));
+ ->withConsecutive(
+ ['123', 'files'],
+ ['456', 'files']
+ )
+ ->willReturnMap([
+ ['123', 'files', ['111', '222']],
+ ['456', 'files', ['222', '333']],
+ ]);
$rules = [
['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
@@ -395,6 +403,81 @@ class FilesReportPlugin extends \Test\TestCase {
$this->assertEquals(['222'], array_values($this->plugin->processFilterRules($rules)));
}
+ public function testProcessFilterRulesAndConditionWithOneEmptyResult() {
+ $this->groupManager->expects($this->any())
+ ->method('isAdmin')
+ ->will($this->returnValue(true));
+
+ $this->tagMapper->expects($this->exactly(2))
+ ->method('getObjectIdsForTags')
+ ->withConsecutive(
+ ['123', 'files'],
+ ['456', 'files']
+ )
+ ->willReturnMap([
+ ['123', 'files', ['111', '222']],
+ ['456', 'files', []],
+ ]);
+
+ $rules = [
+ ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
+ ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'],
+ ];
+
+ $this->assertEquals([], array_values($this->plugin->processFilterRules($rules)));
+ }
+
+ public function testProcessFilterRulesAndConditionWithFirstEmptyResult() {
+ $this->groupManager->expects($this->any())
+ ->method('isAdmin')
+ ->will($this->returnValue(true));
+
+ $this->tagMapper->expects($this->exactly(1))
+ ->method('getObjectIdsForTags')
+ ->withConsecutive(
+ ['123', 'files'],
+ ['456', 'files']
+ )
+ ->willReturnMap([
+ ['123', 'files', []],
+ ['456', 'files', ['111', '222']],
+ ]);
+
+ $rules = [
+ ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
+ ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'],
+ ];
+
+ $this->assertEquals([], array_values($this->plugin->processFilterRules($rules)));
+ }
+
+ public function testProcessFilterRulesAndConditionWithEmptyMidResult() {
+ $this->groupManager->expects($this->any())
+ ->method('isAdmin')
+ ->will($this->returnValue(true));
+
+ $this->tagMapper->expects($this->exactly(2))
+ ->method('getObjectIdsForTags')
+ ->withConsecutive(
+ ['123', 'files'],
+ ['456', 'files'],
+ ['789', 'files']
+ )
+ ->willReturnMap([
+ ['123', 'files', ['111', '222']],
+ ['456', 'files', ['333']],
+ ['789', 'files', ['111', '222']],
+ ]);
+
+ $rules = [
+ ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
+ ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'],
+ ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '789'],
+ ];
+
+ $this->assertEquals([], array_values($this->plugin->processFilterRules($rules)));
+ }
+
public function testProcessFilterRulesInvisibleTagAsAdmin() {
$this->groupManager->expects($this->any())
->method('isAdmin')
diff --git a/apps/dav/tests/unit/migration/calendar_schema.xml b/apps/dav/tests/unit/migration/calendar_schema.xml
new file mode 100644
index 00000000000..6c88b596a3f
--- /dev/null
+++ b/apps/dav/tests/unit/migration/calendar_schema.xml
@@ -0,0 +1,191 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<database>
+
+ <name>*dbname*</name>
+ <create>true</create>
+ <overwrite>false</overwrite>
+
+ <charset>utf8</charset>
+
+ <table>
+
+ <name>*dbprefix*clndr_objects</name>
+
+ <declaration>
+
+ <field>
+ <name>id</name>
+ <type>integer</type>
+ <default>0</default>
+ <notnull>true</notnull>
+ <autoincrement>1</autoincrement>
+ <unsigned>true</unsigned>
+ <length>4</length>
+ </field>
+
+ <field>
+ <name>calendarid</name>
+ <type>integer</type>
+ <default></default>
+ <notnull>true</notnull>
+ <unsigned>true</unsigned>
+ <length>4</length>
+ </field>
+
+ <field>
+ <name>objecttype</name>
+ <type>text</type>
+ <default></default>
+ <notnull>true</notnull>
+ <length>40</length>
+ </field>
+
+ <field>
+ <name>startdate</name>
+ <type>timestamp</type>
+ <default>1970-01-01 00:00:00</default>
+ <notnull>false</notnull>
+ </field>
+
+ <field>
+ <name>enddate</name>
+ <type>timestamp</type>
+ <default>1970-01-01 00:00:00</default>
+ <notnull>false</notnull>
+ </field>
+
+ <field>
+ <name>repeating</name>
+ <type>integer</type>
+ <default></default>
+ <notnull>false</notnull>
+ <length>4</length>
+ </field>
+
+ <field>
+ <name>summary</name>
+ <type>text</type>
+ <default></default>
+ <notnull>false</notnull>
+ <length>255</length>
+ </field>
+
+ <field>
+ <name>calendardata</name>
+ <type>clob</type>
+ <notnull>false</notnull>
+ </field>
+
+ <field>
+ <name>uri</name>
+ <type>text</type>
+ <default></default>
+ <notnull>false</notnull>
+ <length>255</length>
+ </field>
+
+ <field>
+ <name>lastmodified</name>
+ <type>integer</type>
+ <default></default>
+ <notnull>false</notnull>
+ <length>4</length>
+ </field>
+
+ </declaration>
+
+ </table>
+
+ <table>
+
+ <name>*dbprefix*clndr_calendars</name>
+
+ <declaration>
+
+ <field>
+ <name>id</name>
+ <type>integer</type>
+ <default>0</default>
+ <notnull>true</notnull>
+ <autoincrement>1</autoincrement>
+ <unsigned>true</unsigned>
+ <length>4</length>
+ </field>
+
+ <field>
+ <name>userid</name>
+ <type>text</type>
+ <default></default>
+ <notnull>false</notnull>
+ <length>255</length>
+ </field>
+
+ <field>
+ <name>displayname</name>
+ <type>text</type>
+ <default></default>
+ <notnull>false</notnull>
+ <length>100</length>
+ </field>
+
+ <field>
+ <name>uri</name>
+ <type>text</type>
+ <default></default>
+ <notnull>false</notnull>
+ <length>255</length>
+ </field>
+
+ <field>
+ <name>active</name>
+ <type>integer</type>
+ <default>1</default>
+ <notnull>true</notnull>
+ <length>4</length>
+ </field>
+
+ <field>
+ <name>ctag</name>
+ <type>integer</type>
+ <default>0</default>
+ <notnull>true</notnull>
+ <unsigned>true</unsigned>
+ <length>4</length>
+ </field>
+
+ <field>
+ <name>calendarorder</name>
+ <type>integer</type>
+ <default>0</default>
+ <notnull>true</notnull>
+ <unsigned>true</unsigned>
+ <length>4</length>
+ </field>
+
+ <field>
+ <name>calendarcolor</name>
+ <type>text</type>
+ <default></default>
+ <notnull>false</notnull>
+ <length>10</length>
+ </field>
+
+ <field>
+ <name>timezone</name>
+ <type>clob</type>
+ <notnull>false</notnull>
+ </field>
+
+ <field>
+ <name>components</name>
+ <type>text</type>
+ <default></default>
+ <notnull>false</notnull>
+ <length>100</length>
+ </field>
+
+ </declaration>
+
+ </table>
+
+</database>
diff --git a/apps/dav/tests/unit/migration/calendaradaptertest.php b/apps/dav/tests/unit/migration/calendaradaptertest.php
new file mode 100644
index 00000000000..f92774ef6ad
--- /dev/null
+++ b/apps/dav/tests/unit/migration/calendaradaptertest.php
@@ -0,0 +1,131 @@
+<?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\Migration;
+
+use DomainException;
+use OCA\Dav\Migration\AddressBookAdapter;
+use OCA\Dav\Migration\CalendarAdapter;
+use OCP\IDBConnection;
+use Test\TestCase;
+
+/**
+ * Class CalendarAdapterTest
+ *
+ * @group DB
+ *
+ * @package OCA\DAV\Tests\Unit\Migration
+ */
+class CalendarAdapterTest extends TestCase {
+
+ /** @var IDBConnection */
+ private $db;
+ /** @var CalendarAdapter */
+ private $adapter;
+ /** @var array */
+ private $cals = [];
+ /** @var array */
+ private $calObjs = [];
+
+ public function setUp() {
+ parent::setUp();
+ $this->db = \OC::$server->getDatabaseConnection();
+
+ $manager = new \OC\DB\MDB2SchemaManager($this->db);
+ $manager->createDbFromStructure(__DIR__ . '/calendar_schema.xml');
+
+ $this->adapter = new CalendarAdapter($this->db);
+ }
+
+ public function tearDown() {
+ $this->db->dropTable('clndr_calendars');
+ $this->db->dropTable('clndr_objects');
+ parent::tearDown();
+ }
+
+ /**
+ * @expectedException DomainException
+ */
+ public function testOldTablesDoNotExist() {
+ $adapter = new AddressBookAdapter(\OC::$server->getDatabaseConnection(), 'crazy_table_that_does_no_exist');
+ $adapter->setup();
+ }
+
+ public function test() {
+
+ // insert test data
+ $builder = $this->db->getQueryBuilder();
+ $builder->insert('clndr_calendars')
+ ->values([
+ 'userid' => $builder->createNamedParameter('test-user-666'),
+ 'displayname' => $builder->createNamedParameter('Display Name'),
+ 'uri' => $builder->createNamedParameter('events'),
+ 'ctag' => $builder->createNamedParameter('112233'),
+ 'active' => $builder->createNamedParameter('1')
+ ])
+ ->execute();
+ $builder = $this->db->getQueryBuilder();
+ $builder->insert('clndr_objects')
+ ->values([
+ 'calendarid' => $builder->createNamedParameter(6666),
+ 'objecttype' => $builder->createNamedParameter('VEVENT'),
+ 'startdate' => $builder->createNamedParameter(new \DateTime(), 'datetime'),
+ 'enddate' => $builder->createNamedParameter(new \DateTime(), 'datetime'),
+ 'repeating' => $builder->createNamedParameter(0),
+ 'summary' => $builder->createNamedParameter('Something crazy will happen'),
+ 'uri' => $builder->createNamedParameter('event.ics'),
+ 'lastmodified' => $builder->createNamedParameter('112233'),
+ ])
+ ->execute();
+ $builder = $this->db->getQueryBuilder();
+ $builder->insert('share')
+ ->values([
+ 'share_type' => $builder->createNamedParameter(1),
+ 'share_with' => $builder->createNamedParameter('user01'),
+ 'uid_owner' => $builder->createNamedParameter('user02'),
+ 'item_type' => $builder->createNamedParameter('calendar'),
+ 'item_source' => $builder->createNamedParameter(6666),
+ 'item_target' => $builder->createNamedParameter('Contacts (user02)'),
+ ])
+ ->execute();
+
+ // test the adapter
+ $this->adapter->foreachCalendar('test-user-666', function($row) {
+ $this->cals[] = $row;
+ });
+ $this->assertArrayHasKey('id', $this->cals[0]);
+ $this->assertEquals('test-user-666', $this->cals[0]['userid']);
+ $this->assertEquals('Display Name', $this->cals[0]['displayname']);
+ $this->assertEquals('events', $this->cals[0]['uri']);
+ $this->assertEquals('112233', $this->cals[0]['ctag']);
+
+ $this->adapter->foreachCalendarObject(6666, function($row) {
+ $this->calObjs[]= $row;
+ });
+ $this->assertArrayHasKey('id', $this->calObjs[0]);
+ $this->assertEquals(6666, $this->calObjs[0]['calendarid']);
+
+ // test getShares
+ $shares = $this->adapter->getShares(6666);
+ $this->assertEquals(1, count($shares));
+
+ }
+
+}
diff --git a/apps/dav/tests/unit/migration/migratecalendartest.php b/apps/dav/tests/unit/migration/migratecalendartest.php
new file mode 100644
index 00000000000..1058773ffff
--- /dev/null
+++ b/apps/dav/tests/unit/migration/migratecalendartest.php
@@ -0,0 +1,76 @@
+<?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\Migration;
+
+use OCA\DAV\CalDAV\CalDavBackend;
+use OCA\Dav\Migration\CalendarAdapter;
+use Test\TestCase;
+
+class MigrateCalendarTest extends TestCase {
+
+ public function testMigration() {
+ /** @var CalendarAdapter | \PHPUnit_Framework_MockObject_MockObject $adapter */
+ $adapter = $this->mockAdapter();
+
+ /** @var CalDavBackend | \PHPUnit_Framework_MockObject_MockObject $cardDav */
+ $cardDav = $this->getMockBuilder('\OCA\Dav\CalDAV\CalDAVBackend')->disableOriginalConstructor()->getMock();
+ $cardDav->method('createCalendar')->willReturn(666);
+ $cardDav->expects($this->once())->method('createCalendar')->with('principals/users/test01', 'test_contacts');
+ $cardDav->expects($this->once())->method('createCalendarObject')->with(666, '63f0dd6c-39d5-44be-9d34-34e7a7441fc2.ics', 'BEGIN:VCARD');
+
+ $m = new \OCA\Dav\Migration\MigrateCalendars($adapter, $cardDav);
+ $m->migrateForUser('test01');
+ }
+
+ /**
+ * @return \PHPUnit_Framework_MockObject_MockObject
+ */
+ private function mockAdapter($shares = []) {
+ $adapter = $this->getMockBuilder('\OCA\Dav\Migration\CalendarAdapter')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $adapter->method('foreachCalendar')->willReturnCallback(function ($user, \Closure $callBack) {
+ $callBack([
+ // calendarorder | calendarcolor | timezone | components
+ 'id' => 0,
+ 'userid' => $user,
+ 'displayname' => 'Test Contacts',
+ 'uri' => 'test_contacts',
+ 'ctag' => 1234567890,
+ 'active' => 1,
+ 'calendarorder' => '0',
+ 'calendarcolor' => '#b3dc6c',
+ 'timezone' => null,
+ 'components' => 'VEVENT,VTODO,VJOURNAL'
+ ]);
+ });
+ $adapter->method('foreachCalendarObject')->willReturnCallback(function ($addressBookId, \Closure $callBack) {
+ $callBack([
+ 'userid' => $addressBookId,
+ 'uri' => '63f0dd6c-39d5-44be-9d34-34e7a7441fc2.ics',
+ 'calendardata' => 'BEGIN:VCARD'
+ ]);
+ });
+ $adapter->method('getShares')->willReturn($shares);
+ return $adapter;
+ }
+
+}
diff --git a/apps/federatedfilesharing/lib/federatedshareprovider.php b/apps/federatedfilesharing/lib/federatedshareprovider.php
index 0825a0e69bc..0e6089bde07 100644
--- a/apps/federatedfilesharing/lib/federatedshareprovider.php
+++ b/apps/federatedfilesharing/lib/federatedshareprovider.php
@@ -220,6 +220,8 @@ class FederatedShareProvider implements IShareProvider {
$qb->update('share')
->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
->set('permissions', $qb->createNamedParameter($share->getPermissions()))
+ ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
+ ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
->execute();
return $share;
diff --git a/apps/files/appinfo/register_command.php b/apps/files/appinfo/register_command.php
index 4aaf49df9e2..0ec2fe1a584 100644
--- a/apps/files/appinfo/register_command.php
+++ b/apps/files/appinfo/register_command.php
@@ -21,5 +21,11 @@
*
*/
-$application->add(new OCA\Files\Command\Scan(\OC::$server->getUserManager()));
-$application->add(new OCA\Files\Command\DeleteOrphanedFiles(\OC::$server->getDatabaseConnection()));
+$dbConnection = \OC::$server->getDatabaseConnection();
+$userManager = OC::$server->getUserManager();
+$shareManager = \OC::$server->getShareManager();
+
+/** @var Symfony\Component\Console\Application $application */
+$application->add(new OCA\Files\Command\Scan($userManager));
+$application->add(new OCA\Files\Command\DeleteOrphanedFiles($dbConnection));
+$application->add(new OCA\Files\Command\TransferOwnership($userManager, $shareManager));
diff --git a/apps/files/command/transferownership.php b/apps/files/command/transferownership.php
new file mode 100644
index 00000000000..4cc2f34c3a3
--- /dev/null
+++ b/apps/files/command/transferownership.php
@@ -0,0 +1,225 @@
+<?php
+/**
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2015, 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\Files\Command;
+
+use OC\Files\Filesystem;
+use OC\Files\View;
+use OCP\Files\FileInfo;
+use OCP\Files\Folder;
+use OCP\IUserManager;
+use OCP\Share\IManager;
+use OCP\Share\IShare;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Helper\ProgressBar;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class TransferOwnership extends Command {
+
+ /** @var IUserManager $userManager */
+ private $userManager;
+
+ /** @var IManager */
+ private $shareManager;
+
+ /** @var FileInfo[] */
+ private $allFiles = [];
+
+ /** @var FileInfo[] */
+ private $encryptedFiles = [];
+
+ /** @var IShare[] */
+ private $shares = [];
+
+ /** @var string */
+ private $sourceUser;
+
+ /** @var string */
+ private $destinationUser;
+
+ /** @var string */
+ private $finalTarget;
+
+ public function __construct(IUserManager $userManager, IManager $shareManager) {
+ $this->userManager = $userManager;
+ $this->shareManager = $shareManager;
+ parent::__construct();
+ }
+
+ protected function configure() {
+ $this
+ ->setName('files:transfer-ownership')
+ ->setDescription('All files and folders are move to another user - shares are moved as well.')
+ ->addArgument(
+ 'source-user',
+ InputArgument::REQUIRED,
+ 'owner of files which shall be moved'
+ )
+ ->addArgument(
+ 'destination-user',
+ InputArgument::REQUIRED,
+ 'user who will be the new owner of the files'
+ );
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $this->sourceUser = $input->getArgument('source-user');
+ $this->destinationUser = $input->getArgument('destination-user');
+ if (!$this->userManager->userExists($this->sourceUser)) {
+ $output->writeln("<error>Unknown source user $this->sourceUser</error>");
+ return;
+ }
+ if (!$this->userManager->userExists($this->destinationUser)) {
+ $output->writeln("<error>Unknown destination user $this->destinationUser</error>");
+ return;
+ }
+
+ $date = date('c');
+ $this->finalTarget = "$this->destinationUser/files/transferred from $this->sourceUser on $date";
+
+ // setup filesystem
+ Filesystem::initMountPoints($this->sourceUser);
+ Filesystem::initMountPoints($this->destinationUser);
+
+ // analyse source folder
+ $this->analyse($output);
+
+ // collect all the shares
+ $this->collectUsersShares($output);
+
+ // transfer the files
+ $this->transfer($output);
+
+ // restore the shares
+ $this->restoreShares($output);
+ }
+
+ private function walkFiles(View $view, $path, \Closure $callBack) {
+ foreach ($view->getDirectoryContent($path) as $fileInfo) {
+ if (!$callBack($fileInfo)) {
+ return;
+ }
+ if ($fileInfo->getType() === FileInfo::TYPE_FOLDER) {
+ $this->walkFiles($view, $fileInfo->getPath(), $callBack);
+ }
+ }
+ }
+
+ /**
+ * @param OutputInterface $output
+ * @throws \Exception
+ */
+ protected function analyse(OutputInterface $output) {
+ $view = new View();
+ $output->writeln("Analysing files of $this->sourceUser ...");
+ $progress = new ProgressBar($output);
+ $progress->start();
+ $self = $this;
+ $this->walkFiles($view, "$this->sourceUser/files",
+ function (FileInfo $fileInfo) use ($progress, $self) {
+ if ($fileInfo->getType() === FileInfo::TYPE_FOLDER) {
+ return true;
+ }
+ $progress->advance();
+ $this->allFiles[] = $fileInfo;
+ if ($fileInfo->isEncrypted()) {
+ $this->encryptedFiles[] = $fileInfo;
+ }
+ return true;
+ });
+ $progress->finish();
+ $output->writeln('');
+
+ // no file is allowed to be encrypted
+ if (!empty($this->encryptedFiles)) {
+ $output->writeln("<error>Some files are encrypted - please decrypt them first</error>");
+ foreach($this->encryptedFiles as $encryptedFile) {
+ /** @var FileInfo $encryptedFile */
+ $output->writeln(" " . $encryptedFile->getPath());
+ }
+ throw new \Exception('Execution terminated.');
+ }
+
+ }
+
+ /**
+ * @param OutputInterface $output
+ */
+ private function collectUsersShares(OutputInterface $output) {
+ $output->writeln("Collecting all share information for files and folder of $this->sourceUser ...");
+
+ $progress = new ProgressBar($output, count($this->shares));
+ foreach([\OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_GROUP, \OCP\Share::SHARE_TYPE_LINK, \OCP\Share::SHARE_TYPE_REMOTE] as $shareType) {
+ $offset = 0;
+ while (true) {
+ $sharePage = $this->shareManager->getSharesBy($this->sourceUser, $shareType, null, true, 50, $offset);
+ $progress->advance(count($sharePage));
+ if (empty($sharePage)) {
+ break;
+ }
+ $this->shares = array_merge($this->shares, $sharePage);
+ $offset += 50;
+ }
+ }
+
+ $progress->finish();
+ $output->writeln('');
+ }
+
+ /**
+ * @param OutputInterface $output
+ */
+ protected function transfer(OutputInterface $output) {
+ $view = new View();
+ $output->writeln("Transferring files to $this->finalTarget ...");
+ $view->rename("$this->sourceUser/files", $this->finalTarget);
+ // because the files folder is moved away we need to recreate it
+ $view->mkdir("$this->sourceUser/files");
+ }
+
+ /**
+ * @param OutputInterface $output
+ */
+ private function restoreShares(OutputInterface $output) {
+ $output->writeln("Restoring shares ...");
+ $progress = new ProgressBar($output, count($this->shares));
+
+ foreach($this->shares as $share) {
+ if ($share->getSharedWith() === $this->destinationUser) {
+ $this->shareManager->deleteShare($share);
+ } else {
+ if ($share->getShareOwner() === $this->sourceUser) {
+ $share->setShareOwner($this->destinationUser);
+ }
+ if ($share->getSharedBy() === $this->sourceUser) {
+ $share->setSharedBy($this->destinationUser);
+ }
+
+ $this->shareManager->updateShare($share);
+ }
+ $progress->advance();
+ }
+ $progress->finish();
+ $output->writeln('');
+ }
+}
diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js
index 35999b5d0ee..1a6f38d3d7c 100644
--- a/apps/files/js/filelist.js
+++ b/apps/files/js/filelist.js
@@ -815,6 +815,10 @@
if (mountType) {
data.mountType = mountType;
}
+ var path = $el.attr('data-path');
+ if (path) {
+ data.path = path;
+ }
return data;
},
diff --git a/apps/files/tests/controller/ViewControllerTest.php b/apps/files/tests/controller/ViewControllerTest.php
index b5df3cfc904..28e2f0c2c9a 100644
--- a/apps/files/tests/controller/ViewControllerTest.php
+++ b/apps/files/tests/controller/ViewControllerTest.php
@@ -168,6 +168,15 @@ class ViewControllerTest extends TestCase {
'icon' => '',
],
2 => [
+ 'id' => 'systemtagsfilter',
+ 'appname' => 'systemtags',
+ 'script' => 'list.php',
+ 'order' => 9,
+ 'name' => new \OC_L10N_String(new \OC_L10N('systemtags'), 'Tags', []),
+ 'active' => false,
+ 'icon' => '',
+ ],
+ 3 => [
'id' => 'sharingin',
'appname' => 'files_sharing',
'script' => 'list.php',
@@ -176,7 +185,7 @@ class ViewControllerTest extends TestCase {
'active' => false,
'icon' => '',
],
- 3 => [
+ 4 => [
'id' => 'sharingout',
'appname' => 'files_sharing',
'script' => 'list.php',
@@ -185,7 +194,7 @@ class ViewControllerTest extends TestCase {
'active' => false,
'icon' => '',
],
- 4 => [
+ 5 => [
'id' => 'sharinglinks',
'appname' => 'files_sharing',
'script' => 'list.php',
@@ -194,7 +203,7 @@ class ViewControllerTest extends TestCase {
'active' => false,
'icon' => '',
],
- 5 => [
+ 6 => [
'id' => 'trashbin',
'appname' => 'files_trashbin',
'script' => 'list.php',
@@ -227,18 +236,22 @@ class ViewControllerTest extends TestCase {
'content' => null,
],
2 => [
- 'id' => 'sharingin',
+ 'id' => 'systemtagsfilter',
'content' => null,
],
3 => [
- 'id' => 'sharingout',
+ 'id' => 'sharingin',
'content' => null,
],
4 => [
- 'id' => 'sharinglinks',
+ 'id' => 'sharingout',
'content' => null,
],
5 => [
+ 'id' => 'sharinglinks',
+ 'content' => null,
+ ],
+ 6 => [
'id' => 'trashbin',
'content' => null,
],
diff --git a/apps/files/tests/js/filelistSpec.js b/apps/files/tests/js/filelistSpec.js
index 1b2dd12213b..0091a9ee6e4 100644
--- a/apps/files/tests/js/filelistSpec.js
+++ b/apps/files/tests/js/filelistSpec.js
@@ -2521,6 +2521,12 @@ describe('OCA.Files.FileList tests', function() {
expect(fileInfo.size).toEqual(12);
expect(fileInfo.mimetype).toEqual('text/plain');
expect(fileInfo.type).toEqual('file');
+ expect(fileInfo.path).not.toBeDefined();
+ });
+ it('adds path attribute if available', function() {
+ $tr.attr('data-path', '/subdir');
+ var fileInfo = fileList.elementToFile($tr);
+ expect(fileInfo.path).toEqual('/subdir');
});
});
describe('new file menu', function() {
diff --git a/apps/files_external/command/import.php b/apps/files_external/command/import.php
index fe27051359c..28032c207b5 100644
--- a/apps/files_external/command/import.php
+++ b/apps/files_external/command/import.php
@@ -194,8 +194,8 @@ class Import extends Base {
$mount = new StorageConfig($data['mount_id']);
$mount->setMountPoint($data['mount_point']);
$mount->setBackend($this->getBackendByClass($data['storage']));
- $authBackends = $this->backendService->getAuthMechanismsByScheme([$data['authentication_type']]);
- $mount->setAuthMechanism(current($authBackends));
+ $authBackend = $this->backendService->getAuthMechanism($data['authentication_type']);
+ $mount->setAuthMechanism($authBackend);
$mount->setBackendOptions($data['configuration']);
$mount->setMountOptions($data['options']);
$mount->setApplicableUsers(isset($data['applicable_users']) ? $data['applicable_users'] : []);
diff --git a/apps/files_external/command/listcommand.php b/apps/files_external/command/listcommand.php
index c978ae5cfcb..5a0794be4c5 100644
--- a/apps/files_external/command/listcommand.php
+++ b/apps/files_external/command/listcommand.php
@@ -146,7 +146,7 @@ class ListCommand extends Base {
$config->getId(),
$config->getMountPoint(),
$config->getBackend()->getStorageClass(),
- $config->getAuthMechanism()->getScheme(),
+ $config->getAuthMechanism()->getIdentifier(),
$config->getBackendOptions(),
$config->getMountOptions()
];
diff --git a/apps/files_external/lib/auth/password/globalauth.php b/apps/files_external/lib/auth/password/globalauth.php
index b1e52fb53ab..c6faee06109 100644
--- a/apps/files_external/lib/auth/password/globalauth.php
+++ b/apps/files_external/lib/auth/password/globalauth.php
@@ -21,8 +21,6 @@
namespace OCA\Files_External\Lib\Auth\Password;
-use OCA\Files_External\Lib\Auth\IUserProvided;
-use OCA\Files_External\Lib\DefinitionParameter;
use OCA\Files_External\Service\BackendService;
use OCP\IL10N;
use OCP\IUser;
@@ -74,6 +72,8 @@ class GlobalAuth extends AuthMechanism {
public function manipulateStorageConfig(StorageConfig &$storage, IUser $user = null) {
if ($storage->getType() === StorageConfig::MOUNT_TYPE_ADMIN) {
$uid = '';
+ } elseif (is_null($user)) {
+ throw new InsufficientDataForMeaningfulAnswerException('No credentials saved');
} else {
$uid = $user->getUID();
}
diff --git a/apps/files_external/tests/auth/password/globalauth.php b/apps/files_external/tests/auth/password/globalauth.php
new file mode 100644
index 00000000000..912bfd1574d
--- /dev/null
+++ b/apps/files_external/tests/auth/password/globalauth.php
@@ -0,0 +1,117 @@
+<?php
+/**
+ * @author Robin Appelman <icewind@owncloud.com>
+ *
+ * @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\Files_External\Tests\Auth\Password;
+
+use OCA\Files_External\Lib\Auth\Password\GlobalAuth;
+use OCA\Files_external\Lib\StorageConfig;
+use Test\TestCase;
+
+class GlobalAuthTest extends TestCase {
+ /**
+ * @var \OCP\IL10N|\PHPUnit_Framework_MockObject_MockObject
+ */
+ private $l10n;
+
+ /**
+ * @var \OCP\Security\ICredentialsManager|\PHPUnit_Framework_MockObject_MockObject
+ */
+ private $credentialsManager;
+
+ /**
+ * @var GlobalAuth
+ */
+ private $instance;
+
+ protected function setUp() {
+ parent::setUp();
+ $this->l10n = $this->getMock('\OCP\IL10N');
+ $this->credentialsManager = $this->getMock('\OCP\Security\ICredentialsManager');
+ $this->instance = new GlobalAuth($this->l10n, $this->credentialsManager);
+ }
+
+ private function getStorageConfig($type, $config = []) {
+ /** @var \OCA\Files_External\Lib\StorageConfig|\PHPUnit_Framework_MockObject_MockObject $storageConfig */
+ $storageConfig = $this->getMock('\OCA\Files_External\Lib\StorageConfig');
+ $storageConfig->expects($this->any())
+ ->method('getType')
+ ->will($this->returnValue($type));
+ $storageConfig->expects($this->any())
+ ->method('getBackendOptions')
+ ->will($this->returnCallback(function () use (&$config) {
+ return $config;
+ }));
+ $storageConfig->expects($this->any())
+ ->method('getBackendOption')
+ ->will($this->returnCallback(function ($key) use (&$config) {
+ return $config[$key];
+ }));
+ $storageConfig->expects($this->any())
+ ->method('setBackendOption')
+ ->will($this->returnCallback(function ($key, $value) use (&$config) {
+ $config[$key] = $value;
+ }));
+
+ return $storageConfig;
+ }
+
+ public function testNoCredentials() {
+ $this->credentialsManager->expects($this->once())
+ ->method('retrieve')
+ ->will($this->returnValue(null));
+
+ $storage = $this->getStorageConfig(StorageConfig::MOUNT_TYPE_ADMIN);
+
+ $this->instance->manipulateStorageConfig($storage);
+ $this->assertEquals([], $storage->getBackendOptions());
+ }
+
+ public function testSavedCredentials() {
+ $this->credentialsManager->expects($this->once())
+ ->method('retrieve')
+ ->will($this->returnValue([
+ 'user' => 'a',
+ 'password' => 'b'
+ ]));
+
+ $storage = $this->getStorageConfig(StorageConfig::MOUNT_TYPE_ADMIN);
+
+ $this->instance->manipulateStorageConfig($storage);
+ $this->assertEquals([
+ 'user' => 'a',
+ 'password' => 'b'
+ ], $storage->getBackendOptions());
+ }
+
+ /**
+ * @expectedException \OCA\Files_External\Lib\InsufficientDataForMeaningfulAnswerException
+ */
+ public function testNoCredentialsPersonal() {
+ $this->credentialsManager->expects($this->never())
+ ->method('retrieve');
+
+ $storage = $this->getStorageConfig(StorageConfig::MOUNT_TYPE_PERSONAl);
+
+ $this->instance->manipulateStorageConfig($storage);
+ $this->assertEquals([], $storage->getBackendOptions());
+ }
+
+}
diff --git a/apps/files_external/tests/command/listcommandtest.php b/apps/files_external/tests/command/listcommandtest.php
new file mode 100644
index 00000000000..338ddb7593e
--- /dev/null
+++ b/apps/files_external/tests/command/listcommandtest.php
@@ -0,0 +1,69 @@
+<?php
+/**
+ * @author Robin Appelman <icewind@owncloud.com>
+ *
+ * @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\Files_External\Tests\Command;
+
+use OCA\Files_External\Command\ListCommand;
+use OCA\Files_External\Lib\Auth\NullMechanism;
+use OCA\Files_External\Lib\Auth\Password\Password;
+use OCA\Files_External\Lib\Auth\Password\UserProvided;
+use OCA\Files_External\Lib\Backend\Local;
+use OCA\Files_external\Lib\StorageConfig;
+use Symfony\Component\Console\Output\BufferedOutput;
+
+class ListCommandTest extends CommandTest {
+ /**
+ * @return \OCA\Files_External\Command\ListCommand|\PHPUnit_Framework_MockObject_MockObject
+ */
+ private function getInstance() {
+ /** @var \OCA\Files_external\Service\GlobalStoragesService|\PHPUnit_Framework_MockObject_MockObject $globalService */
+ $globalService = $this->getMock('\OCA\Files_external\Service\GlobalStoragesService', null, [], '', false);
+ /** @var \OCA\Files_external\Service\UserStoragesService|\PHPUnit_Framework_MockObject_MockObject $userService */
+ $userService = $this->getMock('\OCA\Files_external\Service\UserStoragesService', null, [], '', false);
+ /** @var \OCP\IUserManager|\PHPUnit_Framework_MockObject_MockObject $userManager */
+ $userManager = $this->getMock('\OCP\IUserManager');
+ /** @var \OCP\IUserSession|\PHPUnit_Framework_MockObject_MockObject $userSession */
+ $userSession = $this->getMock('\OCP\IUserSession');
+
+ return new ListCommand($globalService, $userService, $userSession, $userManager);
+ }
+
+ public function testListAuthIdentifier() {
+ $l10n = $this->getMock('\OC_L10N', null, [], '', false);
+ $credentialsManager = $this->getMock('\OCP\Security\ICredentialsManager');
+ $instance = $this->getInstance();
+ $mount1 = new StorageConfig();
+ $mount1->setAuthMechanism(new Password($l10n));
+ $mount1->setBackend(new Local($l10n, new NullMechanism($l10n)));
+ $mount2 = new StorageConfig();
+ $mount2->setAuthMechanism(new UserProvided($l10n, $credentialsManager));
+ $mount2->setBackend(new Local($l10n, new NullMechanism($l10n)));
+ $input = $this->getInput($instance, [], [
+ 'output' => 'json'
+ ]);
+ $output = new BufferedOutput();
+
+ $instance->listMounts('', [$mount1, $mount2], $input, $output);
+ $output = json_decode($output->fetch(), true);
+
+ $this->assertNotEquals($output[0]['authentication_type'], $output[1]['authentication_type']);
+ }
+}
diff --git a/apps/files_sharing/lib/controllers/sharecontroller.php b/apps/files_sharing/lib/controllers/sharecontroller.php
index bbe68096b52..dae61a3537b 100644
--- a/apps/files_sharing/lib/controllers/sharecontroller.php
+++ b/apps/files_sharing/lib/controllers/sharecontroller.php
@@ -30,7 +30,6 @@
namespace OCA\Files_Sharing\Controllers;
use OC;
-use OC\Files\Filesystem;
use OC_Files;
use OC_Util;
use OCP;
@@ -71,7 +70,7 @@ class ShareController extends Controller {
protected $logger;
/** @var OCP\Activity\IManager */
protected $activityManager;
- /** @var OC\Share20\Manager */
+ /** @var OCP\Share\IManager */
protected $shareManager;
/** @var ISession */
protected $session;
@@ -88,7 +87,7 @@ class ShareController extends Controller {
* @param IUserManager $userManager
* @param ILogger $logger
* @param OCP\Activity\IManager $activityManager
- * @param \OC\Share20\Manager $shareManager
+ * @param \OCP\Share\IManager $shareManager
* @param ISession $session
* @param IPreview $previewManager
* @param IRootFolder $rootFolder
@@ -100,7 +99,7 @@ class ShareController extends Controller {
IUserManager $userManager,
ILogger $logger,
\OCP\Activity\IManager $activityManager,
- \OC\Share20\Manager $shareManager,
+ \OCP\Share\IManager $shareManager,
ISession $session,
IPreview $previewManager,
IRootFolder $rootFolder) {
@@ -177,6 +176,7 @@ class ShareController extends Controller {
if ($this->shareManager->checkPassword($share, $password)) {
$this->session->set('public_link_authenticated', (string)$share->getId());
} else {
+ $this->emitAccessShareHook($share, 403, 'Wrong password');
return false;
}
} else {
@@ -190,6 +190,44 @@ class ShareController extends Controller {
}
/**
+ * throws hooks when a share is attempted to be accessed
+ *
+ * @param \OCP\Share\IShare|string $share the Share instance if available,
+ * otherwise token
+ * @param int $errorCode
+ * @param string $errorMessage
+ * @throws OC\HintException
+ * @throws OC\ServerNotAvailableException
+ */
+ protected function emitAccessShareHook($share, $errorCode = 200, $errorMessage = '') {
+ $itemType = $itemSource = $uidOwner = '';
+ $token = $share;
+ $exception = null;
+ if($share instanceof \OCP\Share\IShare) {
+ try {
+ $token = $share->getToken();
+ $uidOwner = $share->getSharedBy();
+ $itemType = $share->getNodeType();
+ $itemSource = $share->getNodeId();
+ } catch (\Exception $e) {
+ // we log what we know and pass on the exception afterwards
+ $exception = $e;
+ }
+ }
+ \OC_Hook::emit('OCP\Share', 'share_link_access', [
+ 'itemType' => $itemType,
+ 'itemSource' => $itemSource,
+ 'uidOwner' => $uidOwner,
+ 'token' => $token,
+ 'errorCode' => $errorCode,
+ 'errorMessage' => $errorMessage,
+ ]);
+ if(!is_null($exception)) {
+ throw $exception;
+ }
+ }
+
+ /**
* @PublicPage
* @NoCSRFRequired
*
@@ -205,6 +243,7 @@ class ShareController extends Controller {
try {
$share = $this->shareManager->getShareByToken($token);
} catch (ShareNotFound $e) {
+ $this->emitAccessShareHook($token, 404, 'Share not found');
return new NotFoundResponse();
}
@@ -215,8 +254,14 @@ class ShareController extends Controller {
}
// We can't get the path of a file share
- if ($share->getNode() instanceof \OCP\Files\File && $path !== '') {
- throw new NotFoundException();
+ try {
+ if ($share->getNode() instanceof \OCP\Files\File && $path !== '') {
+ $this->emitAccessShareHook($share, 404, 'Share not found');
+ throw new NotFoundException();
+ }
+ } catch (\Exception $e) {
+ $this->emitAccessShareHook($share, 404, 'Share not found');
+ throw $e;
}
$rootFolder = null;
@@ -227,6 +272,7 @@ class ShareController extends Controller {
try {
$path = $rootFolder->get($path);
} catch (\OCP\Files\NotFoundException $e) {
+ $this->emitAccessShareHook($share, 404, 'Share not found');
throw new NotFoundException();
}
}
@@ -287,6 +333,8 @@ class ShareController extends Controller {
$response = new TemplateResponse($this->appName, 'public', $shareTmpl, 'base');
$response->setContentSecurityPolicy($csp);
+ $this->emitAccessShareHook($share);
+
return $response;
}
@@ -344,6 +392,7 @@ class ShareController extends Controller {
try {
$node = $node->get($path);
} catch (NotFoundException $e) {
+ $this->emitAccessShareHook($share, 404, 'Share not found');
return new NotFoundResponse();
}
}
@@ -409,6 +458,8 @@ class ShareController extends Controller {
setcookie('ocDownloadStarted', $downloadStartSecret, time() + 20, '/');
}
+ $this->emitAccessShareHook($share);
+
// download selected files
if (!is_null($files)) {
// FIXME: The exit is required here because otherwise the AppFramework is trying to add headers as well
diff --git a/apps/files_sharing/tests/controller/sharecontroller.php b/apps/files_sharing/tests/controller/sharecontroller.php
index 22e15972cd6..11dc082390c 100644
--- a/apps/files_sharing/tests/controller/sharecontroller.php
+++ b/apps/files_sharing/tests/controller/sharecontroller.php
@@ -219,7 +219,11 @@ class ShareControllerTest extends \Test\TestCase {
public function testAuthenticateInvalidPassword() {
$share = \OC::$server->getShareManager()->newShare();
- $share->setId(42);
+ $share->setNodeId(100)
+ ->setNodeType('file')
+ ->setToken('token')
+ ->setSharedBy('initiator')
+ ->setId(42);
$this->shareManager
->expects($this->once())
@@ -237,6 +241,20 @@ class ShareControllerTest extends \Test\TestCase {
->expects($this->never())
->method('set');
+ $hookListner = $this->getMockBuilder('Dummy')->setMethods(['access'])->getMock();
+ \OCP\Util::connectHook('OCP\Share', 'share_link_access', $hookListner, 'access');
+
+ $hookListner->expects($this->once())
+ ->method('access')
+ ->with($this->callback(function(array $data) {
+ return $data['itemType'] === 'file' &&
+ $data['itemSource'] === 100 &&
+ $data['uidOwner'] === 'initiator' &&
+ $data['token'] === 'token' &&
+ $data['errorCode'] === 403 &&
+ $data['errorMessage'] === 'Wrong password';
+ }));
+
$response = $this->shareController->authenticate('token', 'invalidpassword');
$expectedResponse = new TemplateResponse($this->appName, 'authenticate', array('wrongpw' => true), 'guest');
$this->assertEquals($expectedResponse, $response);
diff --git a/apps/systemtags/appinfo/app.php b/apps/systemtags/appinfo/app.php
index 0bb57e1227b..00663d5082b 100644
--- a/apps/systemtags/appinfo/app.php
+++ b/apps/systemtags/appinfo/app.php
@@ -39,9 +39,11 @@ $eventDispatcher->addListener(
\OCP\Util::addScript('systemtags/systemtagscollection');
\OCP\Util::addScript('systemtags/systemtagsinputfield');
\OCP\Util::addScript('systemtags', 'app');
+ \OCP\Util::addScript('systemtags', 'systemtagsfilelist');
\OCP\Util::addScript('systemtags', 'filesplugin');
\OCP\Util::addScript('systemtags', 'systemtagsinfoview');
\OCP\Util::addStyle('systemtags');
+ \OCP\Util::addStyle('systemtags', 'systemtagsfilelist');
}
);
@@ -73,3 +75,15 @@ $mapperListener = function(MapperEvent $event) use ($activityManager) {
$eventDispatcher->addListener(MapperEvent::EVENT_ASSIGN, $mapperListener);
$eventDispatcher->addListener(MapperEvent::EVENT_UNASSIGN, $mapperListener);
+
+$l = \OC::$server->getL10N('systemtags');
+
+\OCA\Files\App::getNavigationManager()->add(
+ array(
+ 'id' => 'systemtagsfilter',
+ 'appname' => 'systemtags',
+ 'script' => 'list.php',
+ 'order' => 9,
+ 'name' => $l->t('Tags')
+ )
+);
diff --git a/apps/systemtags/css/systemtagsfilelist.css b/apps/systemtags/css/systemtagsfilelist.css
new file mode 100644
index 00000000000..e8fb665e26b
--- /dev/null
+++ b/apps/systemtags/css/systemtagsfilelist.css
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2016
+ *
+ * This file is licensed under the Affero General Public License version 3
+ * or later.
+ *
+ * See the COPYING-README file.
+ *
+ */
+#app-content-systemtagsfilter .select2-container {
+ width: 30%;
+}
+
+#app-content-systemtagsfilter .select2-choices {
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ background: #fff;
+ color: #555;
+ box-sizing: content-box;
+ border-radius: 3px;
+ border: 1px solid #ddd;
+ margin: 3px 3px 3px 0;
+ padding: 0;
+ min-height: auto;
+}
+
+.nav-icon-systemtagsfilter {
+ background-image: url('../img/tag.svg');
+}
diff --git a/apps/systemtags/img/tag.png b/apps/systemtags/img/tag.png
new file mode 100644
index 00000000000..5f4767a6f46
--- /dev/null
+++ b/apps/systemtags/img/tag.png
Binary files differ
diff --git a/apps/systemtags/img/tag.svg b/apps/systemtags/img/tag.svg
new file mode 100644
index 00000000000..6024607dd0a
--- /dev/null
+++ b/apps/systemtags/img/tag.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="16" width="16" version="1.0" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <rect style="color:#000000" fill-opacity="0" height="97.986" width="163.31" y="-32.993" x="-62.897"/>
+ <path opacity=".5" style="color:#000000" d="m6 1c-2.7614 0-5 2.2386-5 5s2.2386 5 5 5c0.98478 0 1.8823-0.28967 2.6562-0.78125l4.4688 4.625c0.09558 0.10527 0.22619 0.16452 0.375 0.15625 0.14882-0.0083 0.3031-0.07119 0.40625-0.1875l0.9375-1.0625c0.19194-0.22089 0.19549-0.53592 0-0.71875l-4.594-4.406c0.478-0.7663 0.75-1.6555 0.75-2.625 0-2.7614-2.2386-5-5-5zm0 2c1.6569 0 3 1.3431 3 3s-1.3431 3-3 3-3-1.3431-3-3 1.3431-3 3-3z"/>
+</svg>
diff --git a/apps/systemtags/js/app.js b/apps/systemtags/js/app.js
index f55aa5c9a6e..d28514358c1 100644
--- a/apps/systemtags/js/app.js
+++ b/apps/systemtags/js/app.js
@@ -16,5 +16,92 @@
OCA.SystemTags = {};
}
+ OCA.SystemTags.App = {
+
+ initFileList: function($el) {
+ if (this._fileList) {
+ return this._fileList;
+ }
+
+ this._fileList = new OCA.SystemTags.FileList(
+ $el,
+ {
+ id: 'systemtags',
+ scrollContainer: $('#app-content'),
+ fileActions: this._createFileActions()
+ }
+ );
+
+ this._fileList.appName = t('systemtags', 'Tags');
+ return this._fileList;
+ },
+
+ removeFileList: function() {
+ if (this._fileList) {
+ this._fileList.$fileList.empty();
+ }
+ },
+
+ _createFileActions: function() {
+ // inherit file actions from the files app
+ var fileActions = new OCA.Files.FileActions();
+ // note: not merging the legacy actions because legacy apps are not
+ // compatible with the sharing overview and need to be adapted first
+ fileActions.registerDefaultActions();
+ fileActions.merge(OCA.Files.fileActions);
+
+ if (!this._globalActionsInitialized) {
+ // in case actions are registered later
+ this._onActionsUpdated = _.bind(this._onActionsUpdated, this);
+ OCA.Files.fileActions.on('setDefault.app-systemtags', this._onActionsUpdated);
+ OCA.Files.fileActions.on('registerAction.app-systemtags', this._onActionsUpdated);
+ this._globalActionsInitialized = true;
+ }
+
+ // when the user clicks on a folder, redirect to the corresponding
+ // folder in the files app instead of opening it directly
+ fileActions.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename, context) {
+ OCA.Files.App.setActiveView('files', {silent: true});
+ OCA.Files.App.fileList.changeDirectory(OC.joinPaths(context.$file.attr('data-path'), filename), true, true);
+ });
+ fileActions.setDefault('dir', 'Open');
+ return fileActions;
+ },
+
+ _onActionsUpdated: function(ev) {
+ if (!this._fileList) {
+ return;
+ }
+
+ if (ev.action) {
+ this._fileList.fileActions.registerAction(ev.action);
+ } else if (ev.defaultAction) {
+ this._fileList.fileActions.setDefault(
+ ev.defaultAction.mime,
+ ev.defaultAction.name
+ );
+ }
+ },
+
+ /**
+ * Destroy the app
+ */
+ destroy: function() {
+ OCA.Files.fileActions.off('setDefault.app-systemtags', this._onActionsUpdated);
+ OCA.Files.fileActions.off('registerAction.app-systemtags', this._onActionsUpdated);
+ this.removeFileList();
+ this._fileList = null;
+ delete this._globalActionsInitialized;
+ }
+ };
+
})();
+$(document).ready(function() {
+ $('#app-content-systemtagsfilter').on('show', function(e) {
+ OCA.SystemTags.App.initFileList($(e.target));
+ });
+ $('#app-content-systemtagsfilter').on('hide', function() {
+ OCA.SystemTags.App.removeFileList();
+ });
+});
diff --git a/apps/systemtags/js/filesplugin.js b/apps/systemtags/js/filesplugin.js
index 471440c2e09..588037455ae 100644
--- a/apps/systemtags/js/filesplugin.js
+++ b/apps/systemtags/js/filesplugin.js
@@ -23,7 +23,8 @@
OCA.SystemTags.FilesPlugin = {
allowedLists: [
'files',
- 'favorites'
+ 'favorites',
+ 'systemtagsfilter'
],
attach: function(fileList) {
diff --git a/apps/systemtags/js/systemtagsfilelist.js b/apps/systemtags/js/systemtagsfilelist.js
new file mode 100644
index 00000000000..56838018a2c
--- /dev/null
+++ b/apps/systemtags/js/systemtagsfilelist.js
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2016 Vincent Petry <pvince81@owncloud.com>
+ *
+ * This file is licensed under the Affero General Public License version 3
+ * or later.
+ *
+ * See the COPYING-README file.
+ *
+ */
+(function() {
+ /**
+ * @class OCA.SystemTags.FileList
+ * @augments OCA.Files.FileList
+ *
+ * @classdesc SystemTags file list.
+ * Contains a list of files filtered by system tags.
+ *
+ * @param $el container element with existing markup for the #controls
+ * and a table
+ * @param [options] map of options, see other parameters
+ * @param {Array.<string>} [options.systemTagIds] array of system tag ids to
+ * filter by
+ */
+ var FileList = function($el, options) {
+ this.initialize($el, options);
+ };
+ FileList.prototype = _.extend({}, OCA.Files.FileList.prototype,
+ /** @lends OCA.SystemTags.FileList.prototype */ {
+ id: 'systemtagsfilter',
+ appName: t('systemtags', 'Tagged files'),
+
+ /**
+ * Array of system tag ids to filter by
+ *
+ * @type Array.<string>
+ */
+ _systemTagIds: [],
+
+ _clientSideSort: true,
+ _allowSelection: false,
+
+ _filterField: null,
+
+ /**
+ * @private
+ */
+ initialize: function($el, options) {
+ OCA.Files.FileList.prototype.initialize.apply(this, arguments);
+ if (this.initialized) {
+ return;
+ }
+
+ if (options && options.systemTagIds) {
+ this._systemTagIds = options.systemTagIds;
+ }
+
+ OC.Plugins.attach('OCA.SystemTags.FileList', this);
+
+ var $controls = this.$el.find('#controls').empty();
+
+ this._initFilterField($controls);
+ },
+
+ destroy: function() {
+ this.$filterField.remove();
+
+ OCA.Files.FileList.prototype.destroy.apply(this, arguments);
+ },
+
+ _initFilterField: function($container) {
+ this.$filterField = $('<input type="hidden" name="tags"/>');
+ $container.append(this.$filterField);
+ this.$filterField.select2({
+ placeholder: t('systemtags', 'Select tags to filter by'),
+ allowClear: false,
+ multiple: true,
+ separator: ',',
+ query: _.bind(this._queryTagsAutocomplete, this),
+
+ id: function(tag) {
+ return tag.id;
+ },
+
+ initSelection: function(element, callback) {
+ var val = $(element).val().trim();
+ if (val) {
+ var tagIds = val.split(','),
+ tags = [];
+
+ OC.SystemTags.collection.fetch({
+ success: function() {
+ _.each(tagIds, function(tagId) {
+ var tag = OC.SystemTags.collection.get(tagId);
+ if (!_.isUndefined(tag)) {
+ tags.push(tag.toJSON());
+ }
+ });
+
+ callback(tags);
+ }
+ });
+ } else {
+ callback([]);
+ }
+ },
+
+ formatResult: function (tag) {
+ return OC.SystemTags.getDescriptiveTag(tag);
+ },
+
+ formatSelection: function (tag) {
+ return OC.SystemTags.getDescriptiveTag(tag)[0].outerHTML;
+ },
+
+ escapeMarkup: function(m) {
+ // prevent double markup escape
+ return m;
+ }
+ });
+ this.$filterField.on('change', _.bind(this._onTagsChanged, this));
+ return this.$filterField;
+ },
+
+ /**
+ * Autocomplete function for dropdown results
+ *
+ * @param {Object} query select2 query object
+ */
+ _queryTagsAutocomplete: function(query) {
+ OC.SystemTags.collection.fetch({
+ success: function() {
+ var results = OC.SystemTags.collection.filterByName(query.term);
+
+ query.callback({
+ results: _.invoke(results, 'toJSON')
+ });
+ }
+ });
+ },
+
+ /**
+ * Event handler for when the URL changed
+ */
+ _onUrlChanged: function(e) {
+ if (e.dir) {
+ var tags = _.filter(e.dir.split('/'), function(val) { return val.trim() !== ''; });
+ this.$filterField.select2('val', tags || []);
+ this._systemTagIds = tags;
+ this.reload();
+ }
+ },
+
+ _onTagsChanged: function(ev) {
+ var val = $(ev.target).val().trim();
+ if (val !== '') {
+ this._systemTagIds = val.split(',');
+ } else {
+ this._systemTagIds = [];
+ }
+
+ this.$el.trigger(jQuery.Event('changeDirectory', {
+ dir: this._systemTagIds.join('/')
+ }));
+ this.reload();
+ },
+
+ updateEmptyContent: function() {
+ var dir = this.getCurrentDirectory();
+ if (dir === '/') {
+ // root has special permissions
+ if (!this._systemTagIds.length) {
+ // no tags selected
+ this.$el.find('#emptycontent').html('<div class="icon-systemtags"></div>' +
+ '<h2>' + t('systemtags', 'Please select tags to filter by') + '</h2>');
+ } else {
+ // tags selected but no results
+ this.$el.find('#emptycontent').html('<div class="icon-systemtags"></div>' +
+ '<h2>' + t('systemtags', 'No files found for the selected tags') + '</h2>');
+ }
+ this.$el.find('#emptycontent').toggleClass('hidden', !this.isEmpty);
+ this.$el.find('#filestable thead th').toggleClass('hidden', this.isEmpty);
+ }
+ else {
+ OCA.Files.FileList.prototype.updateEmptyContent.apply(this, arguments);
+ }
+ },
+
+ getDirectoryPermissions: function() {
+ return OC.PERMISSION_READ | OC.PERMISSION_DELETE;
+ },
+
+ updateStorageStatistics: function() {
+ // no op because it doesn't have
+ // storage info like free space / used space
+ },
+
+ reload: function() {
+ if (!this._systemTagIds.length) {
+ // don't reload
+ this.updateEmptyContent();
+ this.setFiles([]);
+ return $.Deferred().resolve();
+ }
+
+ this._selectedFiles = {};
+ this._selectionSummary.clear();
+ if (this._currentFileModel) {
+ this._currentFileModel.off();
+ }
+ this._currentFileModel = null;
+ this.$el.find('.select-all').prop('checked', false);
+ this.showMask();
+ this._reloadCall = this.filesClient.getFilteredFiles(
+ {
+ systemTagIds: this._systemTagIds
+ },
+ {
+ properties: this._getWebdavProperties()
+ }
+ );
+ if (this._detailsView) {
+ // close sidebar
+ this._updateDetailsView(null);
+ }
+ var callBack = this.reloadCallback.bind(this);
+ return this._reloadCall.then(callBack, callBack);
+ },
+
+ reloadCallback: function(status, result) {
+ if (result) {
+ // prepend empty dir info because original handler
+ result.unshift({});
+ }
+
+ return OCA.Files.FileList.prototype.reloadCallback.call(this, status, result);
+ }
+ });
+
+ OCA.SystemTags.FileList = FileList;
+})();
diff --git a/apps/systemtags/list.php b/apps/systemtags/list.php
new file mode 100644
index 00000000000..dd4fe01e767
--- /dev/null
+++ b/apps/systemtags/list.php
@@ -0,0 +1,25 @@
+<?php
+/**
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @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/>
+ *
+ */
+// Check if we are a user
+OCP\User::checkLoggedIn();
+
+$tmpl = new OCP\Template('systemtags', 'list', '');
+$tmpl->printPage();
diff --git a/apps/systemtags/templates/list.php b/apps/systemtags/templates/list.php
new file mode 100644
index 00000000000..841ce7b5b6d
--- /dev/null
+++ b/apps/systemtags/templates/list.php
@@ -0,0 +1,38 @@
+<div id="controls">
+</div>
+
+<div id="emptycontent" class="hidden">
+ <div class="icon-folder"></div>
+ <h2><?php p($l->t('No files in here')); ?></h2>
+ <p class="uploadmessage hidden"></p>
+</div>
+
+<div class="nofilterresults emptycontent hidden">
+ <div class="icon-search"></div>
+ <h2><?php p($l->t('No entries found in this folder')); ?></h2>
+ <p></p>
+</div>
+
+<table id="filestable" data-preview-x="32" data-preview-y="32">
+ <thead>
+ <tr>
+ <th id='headerName' class="hidden column-name">
+ <div id="headerName-container">
+ <a class="name sort columntitle" data-sort="name"><span><?php p($l->t( 'Name' )); ?></span><span class="sort-indicator"></span></a>
+ </div>
+ </th>
+ <th id="headerSize" class="hidden column-size">
+ <a class="size sort columntitle" data-sort="size"><span><?php p($l->t('Size')); ?></span><span class="sort-indicator"></span></a>
+ </th>
+ <th id="headerDate" class="hidden column-mtime">
+ <a id="modified" class="columntitle" data-sort="mtime"><span><?php p($l->t( 'Modified' )); ?></span><span class="sort-indicator"></span></a>
+ </th>
+ </tr>
+ </thead>
+ <tbody id="fileList">
+ </tbody>
+ <tfoot>
+ </tfoot>
+</table>
+<input type="hidden" name="dir" id="dir" value="" />
+
diff --git a/apps/systemtags/tests/js/systemtagsfilelistSpec.js b/apps/systemtags/tests/js/systemtagsfilelistSpec.js
new file mode 100644
index 00000000000..ba41d347ca4
--- /dev/null
+++ b/apps/systemtags/tests/js/systemtagsfilelistSpec.js
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2016 Vincent Petry <pvince81@owncloud.com>
+ *
+ * This file is licensed under the Affero General Public License version 3
+ * or later.
+ *
+ * See the COPYING-README file.
+ *
+ */
+
+describe('OCA.SystemTags.FileList tests', function() {
+ var FileInfo = OC.Files.FileInfo;
+ var fileList;
+
+ beforeEach(function() {
+ // init parameters and test table elements
+ $('#testArea').append(
+ '<div id="app-content-container">' +
+ // init horrible parameters
+ '<input type="hidden" id="dir" value="/"></input>' +
+ '<input type="hidden" id="permissions" value="31"></input>' +
+ '<div id="controls"></div>' +
+ // dummy table
+ // TODO: at some point this will be rendered by the fileList class itself!
+ '<table id="filestable">' +
+ '<thead><tr>' +
+ '<th id="headerName" class="hidden column-name">' +
+ '<input type="checkbox" id="select_all_files" class="select-all">' +
+ '<a class="name columntitle" data-sort="name"><span>Name</span><span class="sort-indicator"></span></a>' +
+ '<span class="selectedActions hidden">' +
+ '</th>' +
+ '<th class="hidden column-mtime">' +
+ '<a class="columntitle" data-sort="mtime"><span class="sort-indicator"></span></a>' +
+ '</th>' +
+ '</tr></thead>' +
+ '<tbody id="fileList"></tbody>' +
+ '<tfoot></tfoot>' +
+ '</table>' +
+ '<div id="emptycontent">Empty content message</div>' +
+ '</div>'
+ );
+ });
+ afterEach(function() {
+ fileList.destroy();
+ fileList = undefined;
+ });
+
+ describe('filter field', function() {
+ var select2Stub, oldCollection, fetchTagsStub;
+ var $tagsField;
+
+ beforeEach(function() {
+ fetchTagsStub = sinon.stub(OC.SystemTags.SystemTagsCollection.prototype, 'fetch');
+ select2Stub = sinon.stub($.fn, 'select2');
+ oldCollection = OC.SystemTags.collection;
+ OC.SystemTags.collection = new OC.SystemTags.SystemTagsCollection([
+ {
+ id: '123',
+ name: 'abc'
+ },
+ {
+ id: '456',
+ name: 'def'
+ }
+ ]);
+
+ fileList = new OCA.SystemTags.FileList(
+ $('#app-content-container'), {
+ systemTagIds: []
+ }
+ );
+ $tagsField = fileList.$el.find('[name=tags]');
+ });
+ afterEach(function() {
+ select2Stub.restore();
+ fetchTagsStub.restore();
+ OC.SystemTags.collection = oldCollection;
+ });
+ it('inits select2 on filter field', function() {
+ expect(select2Stub.calledOnce).toEqual(true);
+ });
+ it('uses global system tags collection', function() {
+ var callback = sinon.stub();
+ var opts = select2Stub.firstCall.args[0];
+
+ $tagsField.val('123');
+
+ opts.initSelection($tagsField, callback);
+
+ expect(callback.notCalled).toEqual(true);
+ expect(fetchTagsStub.calledOnce).toEqual(true);
+
+ fetchTagsStub.yieldTo('success', fetchTagsStub.thisValues[0]);
+
+ expect(callback.calledOnce).toEqual(true);
+ expect(callback.lastCall.args[0]).toEqual([
+ OC.SystemTags.collection.get('123').toJSON()
+ ]);
+ });
+ it('fetches tag list from the global collection', function() {
+ var callback = sinon.stub();
+ var opts = select2Stub.firstCall.args[0];
+
+ $tagsField.val('123');
+
+ opts.query({
+ term: 'de',
+ callback: callback
+ });
+
+ expect(fetchTagsStub.calledOnce).toEqual(true);
+ expect(callback.notCalled).toEqual(true);
+ fetchTagsStub.yieldTo('success', fetchTagsStub.thisValues[0]);
+
+ expect(callback.calledOnce).toEqual(true);
+ expect(callback.lastCall.args[0]).toEqual({
+ results: [
+ OC.SystemTags.collection.get('456').toJSON()
+ ]
+ });
+ });
+ it('reloads file list after selection', function() {
+ var reloadStub = sinon.stub(fileList, 'reload');
+ $tagsField.val('456,123').change();
+ expect(reloadStub.calledOnce).toEqual(true);
+ reloadStub.restore();
+ });
+ it('updates URL after selection', function() {
+ var handler = sinon.stub();
+ fileList.$el.on('changeDirectory', handler);
+ $tagsField.val('456,123').change();
+
+ expect(handler.calledOnce).toEqual(true);
+ expect(handler.lastCall.args[0].dir).toEqual('456/123');
+ });
+ it('updates tag selection when url changed', function() {
+ fileList.$el.trigger(new $.Event('urlChanged', {dir: '456/123'}));
+
+ expect(select2Stub.lastCall.args[0]).toEqual('val');
+ expect(select2Stub.lastCall.args[1]).toEqual(['456', '123']);
+ });
+ });
+
+ describe('loading results', function() {
+ var getFilteredFilesSpec, requestDeferred;
+
+ beforeEach(function() {
+ requestDeferred = new $.Deferred();
+ getFilteredFilesSpec = sinon.stub(OC.Files.Client.prototype, 'getFilteredFiles')
+ .returns(requestDeferred.promise());
+ });
+ afterEach(function() {
+ getFilteredFilesSpec.restore();
+ });
+
+ it('renders empty message when no tags were set', function() {
+ fileList = new OCA.SystemTags.FileList(
+ $('#app-content-container'), {
+ systemTagIds: []
+ }
+ );
+
+ fileList.reload();
+
+ expect(fileList.$el.find('#emptycontent').hasClass('hidden')).toEqual(false);
+
+ expect(getFilteredFilesSpec.notCalled).toEqual(true);
+ });
+
+ it('render files', function() {
+ fileList = new OCA.SystemTags.FileList(
+ $('#app-content-container'), {
+ systemTagIds: ['123', '456']
+ }
+ );
+
+ fileList.reload();
+
+ expect(getFilteredFilesSpec.calledOnce).toEqual(true);
+ expect(getFilteredFilesSpec.lastCall.args[0].systemTagIds).toEqual(['123', '456']);
+
+ var testFiles = [new FileInfo({
+ id: 1,
+ type: 'file',
+ name: 'One.txt',
+ mimetype: 'text/plain',
+ mtime: 123456789,
+ size: 12,
+ etag: 'abc',
+ permissions: OC.PERMISSION_ALL
+ }), new FileInfo({
+ id: 2,
+ type: 'file',
+ name: 'Two.jpg',
+ mimetype: 'image/jpeg',
+ mtime: 234567890,
+ size: 12049,
+ etag: 'def',
+ permissions: OC.PERMISSION_ALL
+ }), new FileInfo({
+ id: 3,
+ type: 'file',
+ name: 'Three.pdf',
+ mimetype: 'application/pdf',
+ mtime: 234560000,
+ size: 58009,
+ etag: '123',
+ permissions: OC.PERMISSION_ALL
+ }), new FileInfo({
+ id: 4,
+ type: 'dir',
+ name: 'somedir',
+ mimetype: 'httpd/unix-directory',
+ mtime: 134560000,
+ size: 250,
+ etag: '456',
+ permissions: OC.PERMISSION_ALL
+ })];
+
+ requestDeferred.resolve(207, testFiles);
+
+ expect(fileList.$el.find('#emptycontent').hasClass('hidden')).toEqual(true);
+ expect(fileList.$el.find('tbody>tr').length).toEqual(4);
+ });
+ });
+});