From 9c1196d73e0dc57f1f20a57e459ce053264172b8 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 10 Feb 2013 12:05:41 +0100 Subject: [PATCH 01/33] Cache: add seccond mtime field --- db_structure.xml | 8 ++++++++ lib/util.php | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/db_structure.xml b/db_structure.xml index fc7f1082ff..a86e5e6a8f 100644 --- a/db_structure.xml +++ b/db_structure.xml @@ -261,6 +261,14 @@ 4 + + storage_mtime + integer + + true + 4 + + encrypted integer diff --git a/lib/util.php b/lib/util.php index a5fe4cb175..7950586b58 100755 --- a/lib/util.php +++ b/lib/util.php @@ -74,7 +74,7 @@ class OC_Util { */ public static function getVersion() { // hint: We only can count up. So the internal version number of ownCloud 4.5 will be 4.90.0. This is not visible to the user - return array(4, 91, 9); + return array(4, 91, 10); } /** From 3e70d563a6774567ec77e9d9adf6b9ccb1e9619d Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 10 Feb 2013 12:27:35 +0100 Subject: [PATCH 02/33] Cache: bookkeeping of storage_mtime --- lib/files/cache/cache.php | 17 ++++++++++++++--- lib/files/cache/scanner.php | 1 + lib/files/cache/watcher.php | 2 +- tests/lib/files/cache/cache.php | 18 ++++++++++++++++++ tests/lib/files/cache/watcher.php | 10 +++++----- tests/lib/files/view.php | 2 +- 6 files changed, 40 insertions(+), 10 deletions(-) diff --git a/lib/files/cache/cache.php b/lib/files/cache/cache.php index dcb6e8fd39..d696f003c5 100644 --- a/lib/files/cache/cache.php +++ b/lib/files/cache/cache.php @@ -114,7 +114,7 @@ class Cache { $params = array($file); } $query = \OC_DB::prepare( - 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted`, `etag` + 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `storage_mtime`, `encrypted`, `etag` FROM `*PREFIX*filecache` ' . $where); $result = $query->execute($params); $data = $result->fetchRow(); @@ -133,6 +133,9 @@ class Cache { $data['storage'] = $this->storageId; $data['mimetype'] = $this->getMimetype($data['mimetype']); $data['mimepart'] = $this->getMimetype($data['mimepart']); + if ($data['storage_mtime'] == 0) { + $data['storage_mtime'] = $data['mtime']; + } } return $data; @@ -148,13 +151,16 @@ class Cache { $fileId = $this->getId($folder); if ($fileId > -1) { $query = \OC_DB::prepare( - 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted`, `etag` + 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `storage_mtime`, `encrypted`, `etag` FROM `*PREFIX*filecache` WHERE parent = ? ORDER BY `name` ASC'); $result = $query->execute(array($fileId)); $files = $result->fetchAll(); foreach ($files as &$file) { $file['mimetype'] = $this->getMimetype($file['mimetype']); $file['mimepart'] = $this->getMimetype($file['mimepart']); + if ($file['storage_mtime'] == 0) { + $file['storage_mtime'] = $file['mtime']; + } } return $files; } else { @@ -226,7 +232,7 @@ class Cache { * @return array */ function buildParts(array $data) { - $fields = array('path', 'parent', 'name', 'mimetype', 'size', 'mtime', 'encrypted', 'etag'); + $fields = array('path', 'parent', 'name', 'mimetype', 'size', 'mtime', 'storage_mtime', 'encrypted', 'etag'); $params = array(); $queryParts = array(); foreach ($data as $name => $value) { @@ -238,6 +244,11 @@ class Cache { $params[] = $this->getMimetypeId(substr($value, 0, strpos($value, '/'))); $queryParts[] = '`mimepart`'; $value = $this->getMimetypeId($value); + } elseif ($name === 'storage_mtime') { + if (!isset($data['mtime'])) { + $params[] = $value; + $queryParts[] = '`mtime`'; + } } $params[] = $value; $queryParts[] = '`' . $name . '`'; diff --git a/lib/files/cache/scanner.php b/lib/files/cache/scanner.php index 9a5546dce3..2623a167e9 100644 --- a/lib/files/cache/scanner.php +++ b/lib/files/cache/scanner.php @@ -51,6 +51,7 @@ class Scanner { $data['size'] = $this->storage->filesize($path); } $data['etag'] = $this->storage->getETag($path); + $data['storage_mtime'] = $data['mtime']; return $data; } diff --git a/lib/files/cache/watcher.php b/lib/files/cache/watcher.php index 31059ec7f5..8bfd4602f3 100644 --- a/lib/files/cache/watcher.php +++ b/lib/files/cache/watcher.php @@ -43,7 +43,7 @@ class Watcher { */ public function checkUpdate($path) { $cachedEntry = $this->cache->get($path); - if ($this->storage->hasUpdated($path, $cachedEntry['mtime'])) { + if ($this->storage->hasUpdated($path, $cachedEntry['storage_mtime'])) { if ($this->storage->is_dir($path)) { $this->scanner->scan($path, Scanner::SCAN_SHALLOW); } else { diff --git a/tests/lib/files/cache/cache.php b/tests/lib/files/cache/cache.php index c466fbb63e..794664c889 100644 --- a/tests/lib/files/cache/cache.php +++ b/tests/lib/files/cache/cache.php @@ -204,6 +204,23 @@ class Cache extends \PHPUnit_Framework_TestCase { $this->assertEquals(array($storageId, 'foo'), \OC\Files\Cache\Cache::getById($id)); } + function testStorageMTime() { + $data = array('size' => 1000, 'mtime' => 20, 'mimetype' => 'foo/file'); + $this->cache->put('foo', $data); + $cachedData = $this->cache->get('foo'); + $this->assertEquals($data['mtime'], $cachedData['storage_mtime']);//if no storage_mtime is saved, mtime should be used + + $this->cache->put('foo', array('storage_mtime' => 30));//when setting storage_mtime, mtime is also set + $cachedData = $this->cache->get('foo'); + $this->assertEquals(30, $cachedData['storage_mtime']); + $this->assertEquals(30, $cachedData['mtime']); + + $this->cache->put('foo', array('mtime' => 25));//setting mtime does not change storage_mtime + $cachedData = $this->cache->get('foo'); + $this->assertEquals(30, $cachedData['storage_mtime']); + $this->assertEquals(25, $cachedData['mtime']); + } + public function tearDown() { $this->cache->clear(); } @@ -213,3 +230,4 @@ class Cache extends \PHPUnit_Framework_TestCase { $this->cache = new \OC\Files\Cache\Cache($this->storage); } } + diff --git a/tests/lib/files/cache/watcher.php b/tests/lib/files/cache/watcher.php index e8a1689cab..1ea0c2eb47 100644 --- a/tests/lib/files/cache/watcher.php +++ b/tests/lib/files/cache/watcher.php @@ -35,7 +35,7 @@ class Watcher extends \PHPUnit_Framework_TestCase { $updater = $storage->getWatcher(); //set the mtime to the past so it can detect an mtime change - $cache->put('', array('mtime' => 10)); + $cache->put('', array('storage_mtime' => 10)); $this->assertTrue($cache->inCache('folder/bar.txt')); $this->assertTrue($cache->inCache('folder/bar2.txt')); @@ -47,14 +47,14 @@ class Watcher extends \PHPUnit_Framework_TestCase { $cachedData = $cache->get('bar.test'); $this->assertEquals(3, $cachedData['size']); - $cache->put('bar.test', array('mtime' => 10)); + $cache->put('bar.test', array('storage_mtime' => 10)); $storage->file_put_contents('bar.test', 'test data'); $updater->checkUpdate('bar.test'); $cachedData = $cache->get('bar.test'); $this->assertEquals(9, $cachedData['size']); - $cache->put('folder', array('mtime' => 10)); + $cache->put('folder', array('storage_mtime' => 10)); $storage->unlink('folder/bar2.txt'); $updater->checkUpdate('folder'); @@ -69,7 +69,7 @@ class Watcher extends \PHPUnit_Framework_TestCase { $updater = $storage->getWatcher(); //set the mtime to the past so it can detect an mtime change - $cache->put('', array('mtime' => 10)); + $cache->put('', array('storage_mtime' => 10)); $storage->unlink('foo.txt'); $storage->rename('folder', 'foo.txt'); @@ -86,7 +86,7 @@ class Watcher extends \PHPUnit_Framework_TestCase { $updater = $storage->getWatcher(); //set the mtime to the past so it can detect an mtime change - $cache->put('foo.txt', array('mtime' => 10)); + $cache->put('foo.txt', array('storage_mtime' => 10)); $storage->unlink('foo.txt'); $storage->rename('folder', 'foo.txt'); diff --git a/tests/lib/files/view.php b/tests/lib/files/view.php index a064e44f3e..c9a8a0fb08 100644 --- a/tests/lib/files/view.php +++ b/tests/lib/files/view.php @@ -220,7 +220,7 @@ class View extends \PHPUnit_Framework_TestCase { $cachedData = $rootView->getFileInfo('foo.txt'); $this->assertEquals(16, $cachedData['size']); - $rootView->putFileInfo('foo.txt', array('mtime' => 10)); + $rootView->putFileInfo('foo.txt', array('storage_mtime' => 10)); $storage1->file_put_contents('foo.txt', 'foo'); clearstatcache(); From 9738fae3cf1ad18593d21eb62e138e00c01f5f36 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 10 Feb 2013 12:44:27 +0100 Subject: [PATCH 03/33] Emulate touch() for backends that don't support it --- lib/files/view.php | 16 ++++++++++------ tests/lib/files/view.php | 34 ++++++++++++++++++++++++++++++++-- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/lib/files/view.php b/lib/files/view.php index 1a234228ea..69e2f1ad34 100644 --- a/lib/files/view.php +++ b/lib/files/view.php @@ -242,7 +242,11 @@ class View { if (!is_null($mtime) and !is_numeric($mtime)) { $mtime = strtotime($mtime); } - return $this->basicOperation('touch', $path, array('write'), $mtime); + $result = $this->basicOperation('touch', $path, array('write'), $mtime); + if (!$result) { //if native touch fails, we emulate it by changing the mtime in the cache + $this->putFileInfo($path, array('mtime' => $mtime)); + } + return true; } public function file_get_contents($path) { @@ -917,11 +921,11 @@ class View { } /** - * Get the owner for a file or folder - * - * @param string $path - * @return string - */ + * Get the owner for a file or folder + * + * @param string $path + * @return string + */ public function getOwner($path) { return $this->basicOperation('getOwner', $path); } diff --git a/tests/lib/files/view.php b/tests/lib/files/view.php index c9a8a0fb08..af97761bbb 100644 --- a/tests/lib/files/view.php +++ b/tests/lib/files/view.php @@ -7,6 +7,12 @@ namespace Test\Files; +class TemporaryNoTouch extends \OC\Files\Storage\Temporary { + public function touch($path, $mtime = null) { + return false; + } +} + class View extends \PHPUnit_Framework_TestCase { /** * @var \OC\Files\Storage\Storage[] $storages; @@ -228,12 +234,36 @@ class View extends \PHPUnit_Framework_TestCase { $this->assertEquals(3, $cachedData['size']); } + function testTouch() { + $storage = $this->getTestStorage(true, '\Test\Files\TemporaryNoTouch'); + + \OC\Files\Filesystem::mount($storage, array(), '/'); + + $rootView = new \OC\Files\View(''); + $oldCachedData = $rootView->getFileInfo('foo.txt'); + + $rootView->touch('foo.txt', 500); + + $cachedData = $rootView->getFileInfo('foo.txt'); + $this->assertEquals(500, $cachedData['mtime']); + $this->assertEquals($oldCachedData['storage_mtime'], $cachedData['storage_mtime']); + + $rootView->putFileInfo('foo.txt', array('storage_mtime' => 1000)); //make sure the watcher detects the change + $rootView->file_put_contents('foo.txt', 'asd'); + $cachedData = $rootView->getFileInfo('foo.txt'); + $this->assertGreaterThanOrEqual($cachedData['mtime'], $oldCachedData['mtime']); + $this->assertEquals($cachedData['storage_mtime'], $cachedData['mtime']); + } + /** * @param bool $scan * @return \OC\Files\Storage\Storage */ - private function getTestStorage($scan = true) { - $storage = new \OC\Files\Storage\Temporary(array()); + private function getTestStorage($scan = true, $class = '\OC\Files\Storage\Temporary') { + /** + * @var \OC\Files\Storage\Storage $storage + */ + $storage = new $class(array()); $textData = "dummy file data\n"; $imgData = file_get_contents(\OC::$SERVERROOT . '/core/img/logo.png'); $storage->mkdir('folder'); From b8a421a86db09c7ed106c2f9aee15aa337761e4a Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 7 May 2013 16:34:09 +0200 Subject: [PATCH 04/33] New hook system --- lib/hooks/basicemitter.php | 89 ++++++++++ lib/hooks/emitter.php | 32 ++++ lib/hooks/legacyemitter.php | 16 ++ tests/lib/hooks/basicemitter.php | 261 ++++++++++++++++++++++++++++++ tests/lib/hooks/legacyemitter.php | 55 +++++++ 5 files changed, 453 insertions(+) create mode 100644 lib/hooks/basicemitter.php create mode 100644 lib/hooks/emitter.php create mode 100644 lib/hooks/legacyemitter.php create mode 100644 tests/lib/hooks/basicemitter.php create mode 100644 tests/lib/hooks/legacyemitter.php diff --git a/lib/hooks/basicemitter.php b/lib/hooks/basicemitter.php new file mode 100644 index 0000000000..bd24539a40 --- /dev/null +++ b/lib/hooks/basicemitter.php @@ -0,0 +1,89 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Hooks; + +abstract class BasicEmitter implements Emitter { + + /** + * @var (callable[])[] $listeners + */ + private $listeners = array(); + + /** + * @param string $scope + * @param string $method + * @param callable $callback + */ + public function listen($scope, $method, $callback) { + $eventName = $scope . '::' . $method; + if (!isset($this->listeners[$eventName])) { + $this->listeners[$eventName] = array(); + } + if (array_search($callback, $this->listeners[$eventName]) === false) { + $this->listeners[$eventName][] = $callback; + } + } + + /** + * @param string $scope optional + * @param string $method optional + * @param callable $callback optional + */ + public function remoteListener($scope = null, $method = null, $callback = null) { + $names = array(); + $allNames = array_keys($this->listeners); + if ($scope and $method) { + $name = $scope . '::' . $method; + if (isset($this->listeners[$name])) { + $names[] = $name; + } + } elseif ($scope) { + foreach ($allNames as $name) { + $parts = explode('::', $name, 2); + if ($parts[0] == $scope) { + $names[] = $name; + } + } + } elseif ($method) { + foreach ($allNames as $name) { + $parts = explode('::', $name, 2); + if ($parts[1] == $method) { + $names[] = $name; + } + } + } else { + $names = $allNames; + } + + foreach ($names as $name) { + if ($callback) { + $index = array_search($callback, $this->listeners[$name]); + if ($index !== false) { + unset($this->listeners[$name][$index]); + } + } else { + $this->listeners[$name] = array(); + } + } + } + + /** + * @param string $scope + * @param string $method + * @param array $arguments optional + */ + protected function emit($scope, $method, $arguments = array()) { + $eventName = $scope . '::' . $method; + if (isset($this->listeners[$eventName])) { + foreach ($this->listeners[$eventName] as $callback) { + call_user_func_array($callback, $arguments); + } + } + } +} diff --git a/lib/hooks/emitter.php b/lib/hooks/emitter.php new file mode 100644 index 0000000000..4219b6f354 --- /dev/null +++ b/lib/hooks/emitter.php @@ -0,0 +1,32 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Hooks; + +/** + * Class Emitter + * + * interface for all classes that are able to emit events + * + * @package OC\Hooks + */ +interface Emitter { + /** + * @param string $scope + * @param string $method + * @param callable $callback + */ + public function listen($scope, $method, $callback); + + /** + * @param string $scope optional + * @param string $method optional + * @param callable $callback optional + */ + public function remoteListener($scope = null, $method = null, $callback = null); +} diff --git a/lib/hooks/legacyemitter.php b/lib/hooks/legacyemitter.php new file mode 100644 index 0000000000..a2d16ace9a --- /dev/null +++ b/lib/hooks/legacyemitter.php @@ -0,0 +1,16 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Hooks; + +abstract class LegacyEmitter extends BasicEmitter { + protected function emit($scope, $method, $arguments = array()) { + \OC_Hook::emit($scope, $method, $arguments); + parent::emit($scope, $method, $arguments); + } +} diff --git a/tests/lib/hooks/basicemitter.php b/tests/lib/hooks/basicemitter.php new file mode 100644 index 0000000000..53de996c5c --- /dev/null +++ b/tests/lib/hooks/basicemitter.php @@ -0,0 +1,261 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace Test\Hooks; + +/** + * Class DummyEmitter + * + * class to make BasicEmitter::emit publicly available + * + * @package Test\Hooks + */ +class DummyEmitter extends \OC\Hooks\BasicEmitter { + public function emitEvent($scope, $method, $arguments = array()) { + $this->emit($scope, $method, $arguments); + } +} + +/** + * Class EmittedException + * + * a dummy exception so we can check if an event is emitted + * + * @package Test\Hooks + */ +class EmittedException extends \Exception { +} + +class BasicEmitter extends \PHPUnit_Framework_TestCase { + /** + * @var \OC\Hooks\Emitter $emitter + */ + protected $emitter; + + public function setUp() { + $this->emitter = new DummyEmitter(); + } + + public function nonStaticCallBack() { + throw new EmittedException; + } + + public static function staticCallBack() { + throw new EmittedException; + } + + /** + * @expectedException \Test\Hooks\EmittedException + */ + public function testAnonymousFunction() { + $this->emitter->listen('Test', 'test', function () { + throw new EmittedException; + }); + $this->emitter->emitEvent('Test', 'test'); + } + + /** + * @expectedException \Test\Hooks\EmittedException + */ + public function testStaticCallback() { + $this->emitter->listen('Test', 'test', array('\Test\Hooks\BasicEmitter', 'staticCallBack')); + $this->emitter->emitEvent('Test', 'test'); + } + + /** + * @expectedException \Test\Hooks\EmittedException + */ + public function testNonStaticCallback() { + $this->emitter->listen('Test', 'test', array($this, 'nonStaticCallBack')); + $this->emitter->emitEvent('Test', 'test'); + } + + public function testOnlyCallOnce() { + $count = 0; + $listener = function () use (&$count) { + $count++; + }; + $this->emitter->listen('Test', 'test', $listener); + $this->emitter->listen('Test', 'test', $listener); + $this->emitter->emitEvent('Test', 'test'); + $this->assertEquals(1, $count, 'Listener called an invalid number of times (' . $count . ') expected 1'); + } + + public function testDifferentMethods() { + $count = 0; + $listener = function () use (&$count) { + $count++; + }; + $this->emitter->listen('Test', 'test', $listener); + $this->emitter->listen('Test', 'foo', $listener); + $this->emitter->emitEvent('Test', 'test'); + $this->emitter->emitEvent('Test', 'foo'); + $this->assertEquals(2, $count, 'Listener called an invalid number of times (' . $count . ') expected 2'); + } + + public function testDifferentScopes() { + $count = 0; + $listener = function () use (&$count) { + $count++; + }; + $this->emitter->listen('Test', 'test', $listener); + $this->emitter->listen('Bar', 'test', $listener); + $this->emitter->emitEvent('Test', 'test'); + $this->emitter->emitEvent('Bar', 'test'); + $this->assertEquals(2, $count, 'Listener called an invalid number of times (' . $count . ') expected 2'); + } + + public function testDifferentCallbacks() { + $count = 0; + $listener1 = function () use (&$count) { + $count++; + }; + $listener2 = function () use (&$count) { + $count++; + }; + $this->emitter->listen('Test', 'test', $listener1); + $this->emitter->listen('Test', 'test', $listener2); + $this->emitter->emitEvent('Test', 'test'); + $this->assertEquals(2, $count, 'Listener called an invalid number of times (' . $count . ') expected 2'); + } + + /** + * @expectedException \Test\Hooks\EmittedException + */ + public function testArguments() { + $this->emitter->listen('Test', 'test', function ($foo, $bar) { + if ($foo == 'foo' and $bar == 'bar') { + throw new EmittedException; + } + }); + $this->emitter->emitEvent('Test', 'test', array('foo', 'bar')); + } + + /** + * @expectedException \Test\Hooks\EmittedException + */ + public function testNamedArguments() { + $this->emitter->listen('Test', 'test', function ($foo, $bar) { + if ($foo == 'foo' and $bar == 'bar') { + throw new EmittedException; + } + }); + $this->emitter->emitEvent('Test', 'test', array('foo' => 'foo', 'bar' => 'bar')); + } + + public function testRemoveAllSpecified() { + $listener = function () { + throw new EmittedException; + }; + $this->emitter->listen('Test', 'test', $listener); + $this->emitter->remoteListener('Test', 'test', $listener); + $this->emitter->emitEvent('Test', 'test'); + } + + public function testRemoveWildcardListener() { + $listener1 = function () { + throw new EmittedException; + }; + $listener2 = function () { + throw new EmittedException; + }; + $this->emitter->listen('Test', 'test', $listener1); + $this->emitter->listen('Test', 'test', $listener2); + $this->emitter->remoteListener('Test', 'test'); + $this->emitter->emitEvent('Test', 'test'); + } + + public function testRemoveWildcardMethod() { + $listener = function () { + throw new EmittedException; + }; + $this->emitter->listen('Test', 'test', $listener); + $this->emitter->listen('Test', 'foo', $listener); + $this->emitter->remoteListener('Test', null, $listener); + $this->emitter->emitEvent('Test', 'test'); + $this->emitter->emitEvent('Test', 'foo'); + } + + public function testRemoveWildcardScope() { + $listener = function () { + throw new EmittedException; + }; + $this->emitter->listen('Test', 'test', $listener); + $this->emitter->listen('Bar', 'test', $listener); + $this->emitter->remoteListener(null, 'test', $listener); + $this->emitter->emitEvent('Test', 'test'); + $this->emitter->emitEvent('Bar', 'test'); + } + + public function testRemoveWildcardScopeAndMethod() { + $listener = function () { + throw new EmittedException; + }; + $this->emitter->listen('Test', 'test', $listener); + $this->emitter->listen('Test', 'foo', $listener); + $this->emitter->listen('Bar', 'foo', $listener); + $this->emitter->remoteListener(null, null, $listener); + $this->emitter->emitEvent('Test', 'test'); + $this->emitter->emitEvent('Test', 'foo'); + $this->emitter->emitEvent('Bar', 'foo'); + } + + /** + * @expectedException \Test\Hooks\EmittedException + */ + public function testRemoveKeepOtherCallback() { + $listener1 = function () { + throw new EmittedException; + }; + $listener2 = function () { + throw new EmittedException; + }; + $this->emitter->listen('Test', 'test', $listener1); + $this->emitter->listen('Test', 'test', $listener2); + $this->emitter->remoteListener('Test', 'test', $listener1); + $this->emitter->emitEvent('Test', 'test'); + } + + /** + * @expectedException \Test\Hooks\EmittedException + */ + public function testRemoveKeepOtherMethod() { + $listener = function () { + throw new EmittedException; + }; + $this->emitter->listen('Test', 'test', $listener); + $this->emitter->listen('Test', 'foo', $listener); + $this->emitter->remoteListener('Test', 'foo', $listener); + $this->emitter->emitEvent('Test', 'test'); + } + + /** + * @expectedException \Test\Hooks\EmittedException + */ + public function testRemoveKeepOtherScope() { + $listener = function () { + throw new EmittedException; + }; + $this->emitter->listen('Test', 'test', $listener); + $this->emitter->listen('Bar', 'test', $listener); + $this->emitter->remoteListener('Bar', 'test', $listener); + $this->emitter->emitEvent('Test', 'test'); + } + + /** + * @expectedException \Test\Hooks\EmittedException + */ + public function testRemoveNonExistingName() { + $listener = function () { + throw new EmittedException; + }; + $this->emitter->listen('Test', 'test', $listener); + $this->emitter->remoteListener('Bar', 'test', $listener); + $this->emitter->emitEvent('Test', 'test'); + } +} diff --git a/tests/lib/hooks/legacyemitter.php b/tests/lib/hooks/legacyemitter.php new file mode 100644 index 0000000000..a7bed879a7 --- /dev/null +++ b/tests/lib/hooks/legacyemitter.php @@ -0,0 +1,55 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace Test\Hooks; + +/** + * Class DummyLegacyEmitter + * + * class to make LegacyEmitter::emit publicly available + * + * @package Test\Hooks + */ +class DummyLegacyEmitter extends \OC\Hooks\LegacyEmitter { + public function emitEvent($scope, $method, $arguments = array()) { + $this->emit($scope, $method, $arguments); + } +} + +class LegacyEmitter extends BasicEmitter { + + //we can't use exceptions here since OC_Hooks catches all exceptions + private static $emitted = false; + + public function setUp() { + $this->emitter = new DummyLegacyEmitter(); + self::$emitted = false; + \OC_Hook::clear('Test','test'); + } + + public static function staticLegacyCallBack() { + self::$emitted = true; + } + + public static function staticLegacyArgumentsCallBack($arguments) { + if ($arguments['foo'] == 'foo' and $arguments['bar'] == 'bar') + self::$emitted = true; + } + + public function testLegacyHook() { + \OC_Hook::connect('Test', 'test', '\Test\Hooks\LegacyEmitter', 'staticLegacyCallBack'); + $this->emitter->emitEvent('Test', 'test'); + $this->assertEquals(true, self::$emitted); + } + + public function testLegacyArguments() { + \OC_Hook::connect('Test', 'test', '\Test\Hooks\LegacyEmitter', 'staticLegacyArgumentsCallBack'); + $this->emitter->emitEvent('Test', 'test', array('foo' => 'foo', 'bar' => 'bar')); + $this->assertEquals(true, self::$emitted); + } +} From 990f23c0249682043c9e0dd42f33c478e2aa9131 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 9 May 2013 22:52:44 +0200 Subject: [PATCH 05/33] fix typo --- lib/hooks/basicemitter.php | 2 +- lib/hooks/emitter.php | 2 +- tests/lib/hooks/basicemitter.php | 18 +++++++++--------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/hooks/basicemitter.php b/lib/hooks/basicemitter.php index bd24539a40..e615a58cfe 100644 --- a/lib/hooks/basicemitter.php +++ b/lib/hooks/basicemitter.php @@ -35,7 +35,7 @@ abstract class BasicEmitter implements Emitter { * @param string $method optional * @param callable $callback optional */ - public function remoteListener($scope = null, $method = null, $callback = null) { + public function removeListener($scope = null, $method = null, $callback = null) { $names = array(); $allNames = array_keys($this->listeners); if ($scope and $method) { diff --git a/lib/hooks/emitter.php b/lib/hooks/emitter.php index 4219b6f354..8e9074bad6 100644 --- a/lib/hooks/emitter.php +++ b/lib/hooks/emitter.php @@ -28,5 +28,5 @@ interface Emitter { * @param string $method optional * @param callable $callback optional */ - public function remoteListener($scope = null, $method = null, $callback = null); + public function removeListener($scope = null, $method = null, $callback = null); } diff --git a/tests/lib/hooks/basicemitter.php b/tests/lib/hooks/basicemitter.php index 53de996c5c..f48dc53c56 100644 --- a/tests/lib/hooks/basicemitter.php +++ b/tests/lib/hooks/basicemitter.php @@ -153,7 +153,7 @@ class BasicEmitter extends \PHPUnit_Framework_TestCase { throw new EmittedException; }; $this->emitter->listen('Test', 'test', $listener); - $this->emitter->remoteListener('Test', 'test', $listener); + $this->emitter->removeListener('Test', 'test', $listener); $this->emitter->emitEvent('Test', 'test'); } @@ -166,7 +166,7 @@ class BasicEmitter extends \PHPUnit_Framework_TestCase { }; $this->emitter->listen('Test', 'test', $listener1); $this->emitter->listen('Test', 'test', $listener2); - $this->emitter->remoteListener('Test', 'test'); + $this->emitter->removeListener('Test', 'test'); $this->emitter->emitEvent('Test', 'test'); } @@ -176,7 +176,7 @@ class BasicEmitter extends \PHPUnit_Framework_TestCase { }; $this->emitter->listen('Test', 'test', $listener); $this->emitter->listen('Test', 'foo', $listener); - $this->emitter->remoteListener('Test', null, $listener); + $this->emitter->removeListener('Test', null, $listener); $this->emitter->emitEvent('Test', 'test'); $this->emitter->emitEvent('Test', 'foo'); } @@ -187,7 +187,7 @@ class BasicEmitter extends \PHPUnit_Framework_TestCase { }; $this->emitter->listen('Test', 'test', $listener); $this->emitter->listen('Bar', 'test', $listener); - $this->emitter->remoteListener(null, 'test', $listener); + $this->emitter->removeListener(null, 'test', $listener); $this->emitter->emitEvent('Test', 'test'); $this->emitter->emitEvent('Bar', 'test'); } @@ -199,7 +199,7 @@ class BasicEmitter extends \PHPUnit_Framework_TestCase { $this->emitter->listen('Test', 'test', $listener); $this->emitter->listen('Test', 'foo', $listener); $this->emitter->listen('Bar', 'foo', $listener); - $this->emitter->remoteListener(null, null, $listener); + $this->emitter->removeListener(null, null, $listener); $this->emitter->emitEvent('Test', 'test'); $this->emitter->emitEvent('Test', 'foo'); $this->emitter->emitEvent('Bar', 'foo'); @@ -217,7 +217,7 @@ class BasicEmitter extends \PHPUnit_Framework_TestCase { }; $this->emitter->listen('Test', 'test', $listener1); $this->emitter->listen('Test', 'test', $listener2); - $this->emitter->remoteListener('Test', 'test', $listener1); + $this->emitter->removeListener('Test', 'test', $listener1); $this->emitter->emitEvent('Test', 'test'); } @@ -230,7 +230,7 @@ class BasicEmitter extends \PHPUnit_Framework_TestCase { }; $this->emitter->listen('Test', 'test', $listener); $this->emitter->listen('Test', 'foo', $listener); - $this->emitter->remoteListener('Test', 'foo', $listener); + $this->emitter->removeListener('Test', 'foo', $listener); $this->emitter->emitEvent('Test', 'test'); } @@ -243,7 +243,7 @@ class BasicEmitter extends \PHPUnit_Framework_TestCase { }; $this->emitter->listen('Test', 'test', $listener); $this->emitter->listen('Bar', 'test', $listener); - $this->emitter->remoteListener('Bar', 'test', $listener); + $this->emitter->removeListener('Bar', 'test', $listener); $this->emitter->emitEvent('Test', 'test'); } @@ -255,7 +255,7 @@ class BasicEmitter extends \PHPUnit_Framework_TestCase { throw new EmittedException; }; $this->emitter->listen('Test', 'test', $listener); - $this->emitter->remoteListener('Bar', 'test', $listener); + $this->emitter->removeListener('Bar', 'test', $listener); $this->emitter->emitEvent('Test', 'test'); } } From 71eed76dbef92363d5f6eeb423496c6b8dac0579 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Mon, 13 May 2013 11:17:08 -0400 Subject: [PATCH 06/33] Prevent backgroundScan() from looping if opendir() is failing for the same path --- lib/files/cache/scanner.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/files/cache/scanner.php b/lib/files/cache/scanner.php index 661bc48633..b99dea23cb 100644 --- a/lib/files/cache/scanner.php +++ b/lib/files/cache/scanner.php @@ -179,9 +179,11 @@ class Scanner { * walk over any folders that are not fully scanned yet and scan them */ public function backgroundScan() { - while (($path = $this->cache->getIncomplete()) !== false) { + $lastPath = null; + while (($path = $this->cache->getIncomplete()) !== false && $path !== $lastPath) { $this->scan($path); $this->cache->correctFolderSize($path); + $lastPath = $path; } } } From f355d797ff1dc5f92d09acbee48f3d6ff9fb4ecc Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Thu, 16 May 2013 00:23:05 +0200 Subject: [PATCH 07/33] Port OC.dialogs to use octemplate except for prompt() and form(). Also load octemplate per default. --- core/css/styles.css | 3 + core/js/oc-dialogs.js | 340 +++++++++++++++++++-------------- core/templates/filepicker.html | 11 ++ core/templates/message.html | 3 + lib/base.php | 1 + 5 files changed, 217 insertions(+), 141 deletions(-) create mode 100644 core/templates/filepicker.html create mode 100644 core/templates/message.html diff --git a/core/css/styles.css b/core/css/styles.css index 93f2cecbfe..ee00196d66 100644 --- a/core/css/styles.css +++ b/core/css/styles.css @@ -385,10 +385,13 @@ a.bookmarklet { background-color:#ddd; border:1px solid #ccc; padding:5px;paddin #dirup {width:4%;} #dirtree {width:92%;} #filelist {height:270px; overflow-y:auto; background-color:white; width:100%;} +#filelist img { margin: 2px 1em 0 4px; } +#filelist .date { float:right;margin-right:1em; } .filepicker_element_selected { background-color:lightblue;} .filepicker_loader {height:170px; width:100%; background-color:#333; -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=30)"; filter:alpha(opacity=30); opacity:.3; visibility:visible; position:absolute; top:0; left:0; text-align:center; padding-top:150px;} .ui-dialog {position:fixed !important;} span.ui-icon {float: left; margin: 3px 7px 30px 0;} +.loading { background: url('../img/loading.gif') no-repeat center; cursor: wait; } /* ---- CATEGORIES ---- */ #categoryform .scrollarea { position:absolute; left:10px; top:10px; right:10px; bottom:50px; overflow:auto; border:1px solid #ddd; background:#f8f8f8; } diff --git a/core/js/oc-dialogs.js b/core/js/oc-dialogs.js index 990c3f8bf3..c0c035bafd 100644 --- a/core/js/oc-dialogs.js +++ b/core/js/oc-dialogs.js @@ -1,7 +1,7 @@ /** * ownCloud * - * @author Bartek Przybylski + * @author Bartek Przybylski,Christopher Schäpers, Thomas Tanghus * @copyright 2012 Bartek Przybylski bartek@alefzero.eu * * This library is free software; you can redistribute it and/or @@ -31,8 +31,7 @@ var OCdialogs = { * @param modal make the dialog modal */ alert:function(text, title, callback, modal) { - var content = '

' + escapeHTML(text) + '

'; - OCdialogs.message(content, title, OCdialogs.ALERT_DIALOG, OCdialogs.OK_BUTTON, callback, modal); + OCdialogs.message(text, title, 'alert', OCdialogs.OK_BUTTON, callback, modal); }, /** * displays info dialog @@ -42,8 +41,7 @@ var OCdialogs = { * @param modal make the dialog modal */ info:function(text, title, callback, modal) { - var content = '

' + escapeHTML(text) + '

'; - OCdialogs.message(content, title, OCdialogs.ALERT_DIALOG, OCdialogs.OK_BUTTON, callback, modal); + OCdialogs.message(text, title, 'info', OCdialogs.OK_BUTTON, callback, modal); }, /** * displays confirmation dialog @@ -53,8 +51,7 @@ var OCdialogs = { * @param modal make the dialog modal */ confirm:function(text, title, callback, modal) { - var content = '

' + escapeHTML(text) + '

'; - OCdialogs.message(content, title, OCdialogs.ALERT_DIALOG, OCdialogs.YES_NO_BUTTONS, callback, modal); + OCdialogs.message(text, title, 'notice', OCdialogs.YES_NO_BUTTONS, callback, modal); }, /** * prompt for user input @@ -66,7 +63,7 @@ var OCdialogs = { prompt:function(text, title, default_value, callback, modal) { var input = ''; var content = '

' + escapeHTML(text) + ':
' + input + '

'; - OCdialogs.message(content, title, OCdialogs.PROMPT_DIALOG, OCdialogs.OK_BUTTON, callback, modal); + OCdialogs.message(content, title, 'prompt', OCdialogs.OK_BUTTON, callback, modal); }, /** * prompt user for input with custom form @@ -130,6 +127,39 @@ var OCdialogs = { }); OCdialogs.dialogs_counter++; }, + _getFilePickerTemplate: function() { + var defer = $.Deferred(); + if(!this.$filePickerTemplate) { + var self = this; + $.get(OC.filePath('core', 'templates', 'filepicker.html'), function(tmpl) { + self.$filePickerTemplate = $(tmpl); + self.$listTmpl = self.$filePickerTemplate.find('#filelist li:first-child').detach(); + defer.resolve(self.$filePickerTemplate); + }) + .fail(function() { + defer.reject(); + }); + } else { + defer.resolve(this.$filePickerTemplate); + } + return defer.promise(); + }, + _getMessageTemplate: function() { + var defer = $.Deferred(); + if(!this.$messageTemplate) { + var self = this; + $.get(OC.filePath('core', 'templates', 'message.html'), function(tmpl) { + self.$messageTemplate = $(tmpl); + defer.resolve(self.$messageTemplate); + }) + .fail(function() { + defer.reject(); + }); + } else { + defer.resolve(this.$messageTemplate); + } + return defer.promise(); + }, /** * show a file picker to pick a file from * @param title dialog title @@ -139,132 +169,146 @@ var OCdialogs = { * @param modal make the dialog modal */ filepicker:function(title, callback, multiselect, mimetype_filter, modal) { - var dialog_name = 'oc-dialog-' + OCdialogs.dialogs_counter + '-content'; - var dialog_id = '#' + dialog_name; - var dialog_content = '
'; - var dialog_loader = '
'; - var dialog_div = '
' + dialog_content + dialog_loader + '
'; - if (modal === undefined) { modal = false }; - if (multiselect === undefined) { multiselect = false }; - if (mimetype_filter === undefined) { mimetype_filter = '' }; + $.when(this._getFilePickerTemplate()).then(function($tmpl) { + var dialog_name = 'oc-dialog-' + OCdialogs.dialogs_counter + '-content'; + var dialog_id = '#' + dialog_name; + var $dlg = $tmpl.octemplate({ + dialog_name: dialog_name, + title: title + }).data('path', '/'); - $('body').append(dialog_div); + if (modal === undefined) { modal = false }; + if (multiselect === undefined) { multiselect = false }; + if (mimetype_filter === undefined) { mimetype_filter = '' }; - $(dialog_id).data('path', '/'); + $('body').append($dlg); - $(dialog_id + ' #dirtree').focus().change( {dcid: dialog_id}, OCdialogs.handleTreeListSelect ); - $(dialog_id + ' #dirup').click( {dcid: dialog_id}, OCdialogs.filepickerDirUp ); + $dlg.find('#dirtree').focus().change( {dcid: dialog_id}, OCdialogs.handleTreeListSelect ); + $dlg.find('#dirup').click( {dcid: dialog_id}, OCdialogs.filepickerDirUp ); - $(dialog_id).ready(function(){ - $.getJSON(OC.filePath('files', 'ajax', 'rawlist.php'), { mimetype: mimetype_filter } ,function(request) { - OCdialogs.fillFilePicker(request, dialog_id); - }); - $.getJSON(OC.filePath('files', 'ajax', 'rawlist.php'), { mimetype: "httpd/unix-directory" }, function(request) { - OCdialogs.fillTreeList(request, dialog_id); - }); - }).data('multiselect', multiselect).data('mimetype',mimetype_filter); + $dlg.find('#filelist').empty().addClass('loading'); + $dlg.ready(function(){ + $.getJSON(OC.filePath('files', 'ajax', 'rawlist.php'), + {mimetype: mimetype_filter}, + function(response) { + OCdialogs.fillFilePicker(response, dialog_id); + }); + $.getJSON(OC.filePath('files', 'ajax', 'rawlist.php'), + {mimetype: 'httpd/unix-directory'}, + function(response) { + OCdialogs.fillTreeList(response, dialog_id); + }); + }).data('multiselect', multiselect).data('mimetype',mimetype_filter); - // build buttons - var functionToCall = function() { - if (callback !== undefined) { - var datapath; - if (multiselect === true) { - datapath = []; - $(dialog_id + ' .filepicker_element_selected .filename').each(function(index, element) { - datapath.push( $(dialog_id).data('path') + $(element).text() ); - }); - } else { - var datapath = $(dialog_id).data('path'); - datapath += $(dialog_id+' .filepicker_element_selected .filename').text(); + // build buttons + var functionToCall = function() { + if (callback !== undefined) { + var datapath; + if (multiselect === true) { + datapath = []; + $(dialog_id + ' .filepicker_element_selected .filename').each(function(index, element) { + datapath.push( $(dialog_id).data('path') + $(element).text() ); + }); + } else { + var datapath = $(dialog_id).data('path'); + datapath += $(dialog_id+' .filepicker_element_selected .filename').text(); + } + callback(datapath); + $(dialog_id).dialog('close'); } - callback(datapath); - $(dialog_id).dialog('close'); - } - }; - var buttonlist = [{ - text: t('core', 'Choose'), - click: functionToCall - }, - { - text: t('core', 'Cancel'), - click: function(){$(dialog_id).dialog('close'); } - }]; + }; + var buttonlist = [{ + text: t('core', 'Choose'), + click: functionToCall + }, + { + text: t('core', 'Cancel'), + click: function(){$(dialog_id).dialog('close'); } + }]; - $(dialog_id).dialog({ - width: (4/9)*$(document).width(), - height: 420, - modal: modal, - buttons: buttonlist + $(dialog_id).dialog({ + width: (4/9)*$(document).width(), + height: 420, + modal: modal, + buttons: buttonlist + }); + OCdialogs.dialogs_counter++; + }) + .fail(function() { + alert(t('core', 'Error loading file picker template')); }); - OCdialogs.dialogs_counter++; }, /** * Displays raw dialog * You better use a wrapper instead ... */ message:function(content, title, dialog_type, buttons, callback, modal) { - var dialog_name = 'oc-dialog-' + OCdialogs.dialogs_counter + '-content'; - var dialog_id = '#' + dialog_name; - var dialog_div = '
' + content + '
'; - if (modal === undefined) { modal = false }; - $('body').append(dialog_div); - var buttonlist = []; - switch (buttons) { - case OCdialogs.YES_NO_BUTTONS: - buttonlist = [{ - text: t('core', 'Yes'), - click: function(){ - if (callback !== undefined) { callback(true) }; - $(dialog_id).dialog('close'); - } - }, - { - text: t('core', 'No'), - click: function(){ - if (callback !== undefined) { callback(false) }; - $(dialog_id).dialog('close'); - } - }]; - break; - case OCdialogs.OK_BUTTON: - var functionToCall; - switch(dialog_type) { - case OCdialogs.ALERT_DIALOG: - functionToCall = function() { + $.when(this._getMessageTemplate()).then(function($tmpl) { + var dialog_name = 'oc-dialog-' + OCdialogs.dialogs_counter + '-content'; + var dialog_id = '#' + dialog_name; + var $dlg = $tmpl.octemplate({ + dialog_name: dialog_name, + title: title, + message: content, + type: dialog_type + }); + if (modal === undefined) { modal = false }; + $('body').append($dlg); + var buttonlist = []; + switch (buttons) { + case OCdialogs.YES_NO_BUTTONS: + buttonlist = [{ + text: t('core', 'Yes'), + click: function(){ + if (callback !== undefined) { callback(true) }; $(dialog_id).dialog('close'); - if(callback !== undefined) { callback() }; - }; - break; - case OCdialogs.PROMPT_DIALOG: - buttonlist[1] = { - text: t('core', 'Cancel'), - click: function() { $(dialog_id).dialog('close'); } - }; - functionToCall = function() { OCdialogs.prompt_ok_handler(callback, dialog_id); }; - break; - } - buttonlist[0] = { - text: t('core', 'Ok'), - click: functionToCall - }; - break; - }; + } + }, + { + text: t('core', 'No'), + click: function(){ + if (callback !== undefined) { callback(false) }; + $(dialog_id).dialog('close'); + } + }]; + break; + case OCdialogs.OK_BUTTON: + var functionToCall; + switch(dialog_type) { + case 'prompt': + buttonlist[1] = { + text: t('core', 'Cancel'), + click: function() { $(dialog_id).dialog('close'); } + }; + functionToCall = function() { OCdialogs.prompt_ok_handler(callback, dialog_id); }; + break; + default: + functionToCall = function() { + $(dialog_id).dialog('close'); + if(callback !== undefined) { callback() }; + }; + break; + } + buttonlist[0] = { + text: t('core', 'Ok'), + click: functionToCall + }; + break; + }; - $(dialog_id).dialog({ - width: (4/9) * $(document).width(), - height: 180, - modal: modal, - buttons: buttonlist + $(dialog_id).dialog({ + modal: modal, + buttons: buttonlist + }); + OCdialogs.dialogs_counter++; + }) + .fail(function() { + alert(t('core', 'Error loading file picker template')); }); - OCdialogs.dialogs_counter++; }, // dialog button types YES_NO_BUTTONS: 70, OK_BUTTONS: 71, - // dialogs types - ALERT_DIALOG: 80, - INFO_DIALOG: 81, - FORM_DIALOG: 82, // used to name each dialog dialogs_counter: 0, @@ -278,7 +322,7 @@ var OCdialogs = { prompt_ok_handler: function(callback, dialog_id) { $(dialog_id).dialog('close'); - if (callback !== undefined) { callback($(dialog_id + " input#oc-dialog-prompt-input").val()) }; + if (callback !== undefined) { callback($(dialog_id + ' input#oc-dialog-prompt-input').val()) }; }, form_ok_handler: function(callback, dialog_id) { @@ -297,8 +341,7 @@ var OCdialogs = { * fills the filepicker with files */ fillFilePicker:function(request, dialog_content_id) { - var template_content = '*NAME*
*LASTMODDATE*
'; - var template = '
*CONTENT*
'; + var $filelist = $(dialog_content_id + ' #filelist').empty(); var files = ''; var dirs = []; var others = []; @@ -309,14 +352,24 @@ var OCdialogs = { others.push(file); } }); - var sorted = dirs.concat(others); - for (var i = 0; i < sorted.length; i++) { - files_content = template_content.replace('*LASTMODDATE*', OC.mtime2date(sorted[i].mtime)).replace('*NAME*', escapeHTML(sorted[i].name)).replace('*MIMETYPEICON*', sorted[i].mimetype_icon); - files += template.replace('*ENTRYNAME*', escapeHTML(sorted[i].name)).replace('*ENTRYTYPE*', escapeHTML(sorted[i].type)).replace('*CONTENT*', files_content); - } - $(dialog_content_id + ' #filelist').html(files); - $('#filelist div').click(function() { + var sorted = dirs.concat(others); + + var self = this; + $.each(sorted, function(idx, entry) { + $li = self.$listTmpl.octemplate({ + type: entry.type, + dcid: dialog_content_id, + imgsrc: entry.mimetype_icon, + filename: entry.name, + date: OC.mtime2date(entry.mtime) + }); + $filelist.append($li); + }); + + $filelist.removeClass('loading'); + + $filelist.on('click', 'li', function() { OCdialogs.handlePickerClick($(this), $(this).data('entryname'), dialog_content_id); }); @@ -326,24 +379,29 @@ var OCdialogs = { * fills the tree list with directories */ fillTreeList: function(request, dialog_id) { - var template = ''; - var paths = ''; + var $dirtree = $(dialog_id + ' #dirtree').empty(); + var $template = $(''); + $dirtree.append($template.octemplate({ + count: 0, + name: $(dialog_id).data('path') + })); $.each(request.data, function(index, file) { - paths += template.replace('*COUNT*', index).replace('*NAME*', escapeHTML(file.name)); + $dirtree.append($template.octemplate({ + count: index, + name: file.name + })); }); - - $(dialog_id + ' #dirtree').html(paths); }, /** * handle selection made in the tree list */ handleTreeListSelect:function(event) { - if ($("option:selected", this).html().indexOf('/') !== -1) { // if there's a slash in the selected path, don't append it - $(event.data.dcid).data('path', $("option:selected", this).html()); + if ($('option:selected', this).html().indexOf('/') !== -1) { // if there's a slash in the selected path, don't append it + $(event.data.dcid).data('path', $('option:selected', this).html()); } else { - $(event.data.dcid).data('path', $(event.data.dcid).data('path') + $("option:selected", this).html() + '/'); + $(event.data.dcid).data('path', $(event.data.dcid).data('path') + $('option:selected', this).html() + '/'); } - $(event.data.dcid + ' .filepicker_loader').css('visibility', 'visible'); + $(event.data.dcid).find('#filelist').addClass('loading'); $.getJSON( OC.filePath('files', 'ajax', 'rawlist.php'), { @@ -356,7 +414,7 @@ var OCdialogs = { OC.filePath('files', 'ajax', 'rawlist.php'), { dir: $(event.data.dcid).data('path'), - mimetype: "httpd/unix-directory" + mimetype: 'httpd/unix-directory' }, function(request) { OCdialogs.fillTreeList(request, event.data.dcid) } ); @@ -366,13 +424,13 @@ var OCdialogs = { */ filepickerDirUp:function(event) { var old_path = $(event.data.dcid).data('path'); - if ( old_path !== "/") { + if ( old_path !== '/') { var splitted_path = old_path.split("/"); var new_path = "" for (var i = 0; i < splitted_path.length - 2; i++) { - new_path += splitted_path[i] + "/" + new_path += splitted_path[i] + '/' } - $(event.data.dcid).data('path', new_path); + $(event.data.dcid).data('path', new_path).find('#filelist').empty().addClass('loading');; $.getJSON( OC.filePath('files', 'ajax', 'rawlist.php'), { @@ -385,7 +443,7 @@ var OCdialogs = { OC.filePath('files', 'ajax', 'rawlist.php'), { dir: $(event.data.dcid).data('path'), - mimetype: "httpd/unix-directory" + mimetype: 'httpd/unix-directory' }, function(request) { OCdialogs.fillTreeList(request, event.data.dcid) } ); @@ -395,16 +453,16 @@ var OCdialogs = { * handle clicks made in the filepicker */ handlePickerClick:function(element, name, dialog_content_id) { - if ( $(element).attr('data') === 'file' ){ + if ( $(element).data('type') === 'file' ){ if ( $(dialog_content_id).data('multiselect') !== true) { $(dialog_content_id + ' .filepicker_element_selected').removeClass('filepicker_element_selected'); } $(element).toggleClass('filepicker_element_selected'); return; - } else if ( $(element).attr('data') === 'dir' ) { + } else if ( $(element).data('type') === 'dir' ) { var datapath = escapeHTML( $(dialog_content_id).data('path') + name + '/' ); $(dialog_content_id).data('path', datapath); - $(dialog_content_id + ' .filepicker_loader').css('visibility', 'visible'); + $(dialog_content_id).find('#filelist').empty().addClass('loading'); $.getJSON( OC.filePath('files', 'ajax', 'rawlist.php'), { @@ -417,7 +475,7 @@ var OCdialogs = { OC.filePath('files', 'ajax', 'rawlist.php'), { dir: datapath, - mimetype: "httpd/unix-directory" + mimetype: 'httpd/unix-directory' }, function(request) { OCdialogs.fillTreeList(request, dialog_content_id) } ); diff --git a/core/templates/filepicker.html b/core/templates/filepicker.html new file mode 100644 index 0000000000..f29de390ef --- /dev/null +++ b/core/templates/filepicker.html @@ -0,0 +1,11 @@ +
+ + +
    +
  • + + {filename} + {date} +
  • +
+
diff --git a/core/templates/message.html b/core/templates/message.html new file mode 100644 index 0000000000..59048100f3 --- /dev/null +++ b/core/templates/message.html @@ -0,0 +1,3 @@ +
+

{message}

+
diff --git a/lib/base.php b/lib/base.php index 667202d3ae..2e2cee97eb 100644 --- a/lib/base.php +++ b/lib/base.php @@ -260,6 +260,7 @@ class OC { OC_Util::addScript("jquery-tipsy"); OC_Util::addScript("compatibility"); OC_Util::addScript("oc-dialogs"); + OC_Util::addScript("octemplate"); OC_Util::addScript("js"); OC_Util::addScript("eventsource"); OC_Util::addScript("config"); From c8bbf90feb5a874b9988198747b79c4f43708740 Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Thu, 16 May 2013 00:23:05 +0200 Subject: [PATCH 08/33] Port OC.dialogs to use octemplate except for prompt() and form(). Also load octemplate per default. --- core/css/styles.css | 3 + core/js/oc-dialogs.js | 340 +++++++++++++++++++-------------- core/templates/filepicker.html | 11 ++ core/templates/message.html | 3 + lib/base.php | 1 + 5 files changed, 217 insertions(+), 141 deletions(-) create mode 100644 core/templates/filepicker.html create mode 100644 core/templates/message.html diff --git a/core/css/styles.css b/core/css/styles.css index 93f2cecbfe..ee00196d66 100644 --- a/core/css/styles.css +++ b/core/css/styles.css @@ -385,10 +385,13 @@ a.bookmarklet { background-color:#ddd; border:1px solid #ccc; padding:5px;paddin #dirup {width:4%;} #dirtree {width:92%;} #filelist {height:270px; overflow-y:auto; background-color:white; width:100%;} +#filelist img { margin: 2px 1em 0 4px; } +#filelist .date { float:right;margin-right:1em; } .filepicker_element_selected { background-color:lightblue;} .filepicker_loader {height:170px; width:100%; background-color:#333; -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=30)"; filter:alpha(opacity=30); opacity:.3; visibility:visible; position:absolute; top:0; left:0; text-align:center; padding-top:150px;} .ui-dialog {position:fixed !important;} span.ui-icon {float: left; margin: 3px 7px 30px 0;} +.loading { background: url('../img/loading.gif') no-repeat center; cursor: wait; } /* ---- CATEGORIES ---- */ #categoryform .scrollarea { position:absolute; left:10px; top:10px; right:10px; bottom:50px; overflow:auto; border:1px solid #ddd; background:#f8f8f8; } diff --git a/core/js/oc-dialogs.js b/core/js/oc-dialogs.js index 990c3f8bf3..c0c035bafd 100644 --- a/core/js/oc-dialogs.js +++ b/core/js/oc-dialogs.js @@ -1,7 +1,7 @@ /** * ownCloud * - * @author Bartek Przybylski + * @author Bartek Przybylski,Christopher Schäpers, Thomas Tanghus * @copyright 2012 Bartek Przybylski bartek@alefzero.eu * * This library is free software; you can redistribute it and/or @@ -31,8 +31,7 @@ var OCdialogs = { * @param modal make the dialog modal */ alert:function(text, title, callback, modal) { - var content = '

' + escapeHTML(text) + '

'; - OCdialogs.message(content, title, OCdialogs.ALERT_DIALOG, OCdialogs.OK_BUTTON, callback, modal); + OCdialogs.message(text, title, 'alert', OCdialogs.OK_BUTTON, callback, modal); }, /** * displays info dialog @@ -42,8 +41,7 @@ var OCdialogs = { * @param modal make the dialog modal */ info:function(text, title, callback, modal) { - var content = '

' + escapeHTML(text) + '

'; - OCdialogs.message(content, title, OCdialogs.ALERT_DIALOG, OCdialogs.OK_BUTTON, callback, modal); + OCdialogs.message(text, title, 'info', OCdialogs.OK_BUTTON, callback, modal); }, /** * displays confirmation dialog @@ -53,8 +51,7 @@ var OCdialogs = { * @param modal make the dialog modal */ confirm:function(text, title, callback, modal) { - var content = '

' + escapeHTML(text) + '

'; - OCdialogs.message(content, title, OCdialogs.ALERT_DIALOG, OCdialogs.YES_NO_BUTTONS, callback, modal); + OCdialogs.message(text, title, 'notice', OCdialogs.YES_NO_BUTTONS, callback, modal); }, /** * prompt for user input @@ -66,7 +63,7 @@ var OCdialogs = { prompt:function(text, title, default_value, callback, modal) { var input = ''; var content = '

' + escapeHTML(text) + ':
' + input + '

'; - OCdialogs.message(content, title, OCdialogs.PROMPT_DIALOG, OCdialogs.OK_BUTTON, callback, modal); + OCdialogs.message(content, title, 'prompt', OCdialogs.OK_BUTTON, callback, modal); }, /** * prompt user for input with custom form @@ -130,6 +127,39 @@ var OCdialogs = { }); OCdialogs.dialogs_counter++; }, + _getFilePickerTemplate: function() { + var defer = $.Deferred(); + if(!this.$filePickerTemplate) { + var self = this; + $.get(OC.filePath('core', 'templates', 'filepicker.html'), function(tmpl) { + self.$filePickerTemplate = $(tmpl); + self.$listTmpl = self.$filePickerTemplate.find('#filelist li:first-child').detach(); + defer.resolve(self.$filePickerTemplate); + }) + .fail(function() { + defer.reject(); + }); + } else { + defer.resolve(this.$filePickerTemplate); + } + return defer.promise(); + }, + _getMessageTemplate: function() { + var defer = $.Deferred(); + if(!this.$messageTemplate) { + var self = this; + $.get(OC.filePath('core', 'templates', 'message.html'), function(tmpl) { + self.$messageTemplate = $(tmpl); + defer.resolve(self.$messageTemplate); + }) + .fail(function() { + defer.reject(); + }); + } else { + defer.resolve(this.$messageTemplate); + } + return defer.promise(); + }, /** * show a file picker to pick a file from * @param title dialog title @@ -139,132 +169,146 @@ var OCdialogs = { * @param modal make the dialog modal */ filepicker:function(title, callback, multiselect, mimetype_filter, modal) { - var dialog_name = 'oc-dialog-' + OCdialogs.dialogs_counter + '-content'; - var dialog_id = '#' + dialog_name; - var dialog_content = '
'; - var dialog_loader = '
'; - var dialog_div = '
' + dialog_content + dialog_loader + '
'; - if (modal === undefined) { modal = false }; - if (multiselect === undefined) { multiselect = false }; - if (mimetype_filter === undefined) { mimetype_filter = '' }; + $.when(this._getFilePickerTemplate()).then(function($tmpl) { + var dialog_name = 'oc-dialog-' + OCdialogs.dialogs_counter + '-content'; + var dialog_id = '#' + dialog_name; + var $dlg = $tmpl.octemplate({ + dialog_name: dialog_name, + title: title + }).data('path', '/'); - $('body').append(dialog_div); + if (modal === undefined) { modal = false }; + if (multiselect === undefined) { multiselect = false }; + if (mimetype_filter === undefined) { mimetype_filter = '' }; - $(dialog_id).data('path', '/'); + $('body').append($dlg); - $(dialog_id + ' #dirtree').focus().change( {dcid: dialog_id}, OCdialogs.handleTreeListSelect ); - $(dialog_id + ' #dirup').click( {dcid: dialog_id}, OCdialogs.filepickerDirUp ); + $dlg.find('#dirtree').focus().change( {dcid: dialog_id}, OCdialogs.handleTreeListSelect ); + $dlg.find('#dirup').click( {dcid: dialog_id}, OCdialogs.filepickerDirUp ); - $(dialog_id).ready(function(){ - $.getJSON(OC.filePath('files', 'ajax', 'rawlist.php'), { mimetype: mimetype_filter } ,function(request) { - OCdialogs.fillFilePicker(request, dialog_id); - }); - $.getJSON(OC.filePath('files', 'ajax', 'rawlist.php'), { mimetype: "httpd/unix-directory" }, function(request) { - OCdialogs.fillTreeList(request, dialog_id); - }); - }).data('multiselect', multiselect).data('mimetype',mimetype_filter); + $dlg.find('#filelist').empty().addClass('loading'); + $dlg.ready(function(){ + $.getJSON(OC.filePath('files', 'ajax', 'rawlist.php'), + {mimetype: mimetype_filter}, + function(response) { + OCdialogs.fillFilePicker(response, dialog_id); + }); + $.getJSON(OC.filePath('files', 'ajax', 'rawlist.php'), + {mimetype: 'httpd/unix-directory'}, + function(response) { + OCdialogs.fillTreeList(response, dialog_id); + }); + }).data('multiselect', multiselect).data('mimetype',mimetype_filter); - // build buttons - var functionToCall = function() { - if (callback !== undefined) { - var datapath; - if (multiselect === true) { - datapath = []; - $(dialog_id + ' .filepicker_element_selected .filename').each(function(index, element) { - datapath.push( $(dialog_id).data('path') + $(element).text() ); - }); - } else { - var datapath = $(dialog_id).data('path'); - datapath += $(dialog_id+' .filepicker_element_selected .filename').text(); + // build buttons + var functionToCall = function() { + if (callback !== undefined) { + var datapath; + if (multiselect === true) { + datapath = []; + $(dialog_id + ' .filepicker_element_selected .filename').each(function(index, element) { + datapath.push( $(dialog_id).data('path') + $(element).text() ); + }); + } else { + var datapath = $(dialog_id).data('path'); + datapath += $(dialog_id+' .filepicker_element_selected .filename').text(); + } + callback(datapath); + $(dialog_id).dialog('close'); } - callback(datapath); - $(dialog_id).dialog('close'); - } - }; - var buttonlist = [{ - text: t('core', 'Choose'), - click: functionToCall - }, - { - text: t('core', 'Cancel'), - click: function(){$(dialog_id).dialog('close'); } - }]; + }; + var buttonlist = [{ + text: t('core', 'Choose'), + click: functionToCall + }, + { + text: t('core', 'Cancel'), + click: function(){$(dialog_id).dialog('close'); } + }]; - $(dialog_id).dialog({ - width: (4/9)*$(document).width(), - height: 420, - modal: modal, - buttons: buttonlist + $(dialog_id).dialog({ + width: (4/9)*$(document).width(), + height: 420, + modal: modal, + buttons: buttonlist + }); + OCdialogs.dialogs_counter++; + }) + .fail(function() { + alert(t('core', 'Error loading file picker template')); }); - OCdialogs.dialogs_counter++; }, /** * Displays raw dialog * You better use a wrapper instead ... */ message:function(content, title, dialog_type, buttons, callback, modal) { - var dialog_name = 'oc-dialog-' + OCdialogs.dialogs_counter + '-content'; - var dialog_id = '#' + dialog_name; - var dialog_div = '
' + content + '
'; - if (modal === undefined) { modal = false }; - $('body').append(dialog_div); - var buttonlist = []; - switch (buttons) { - case OCdialogs.YES_NO_BUTTONS: - buttonlist = [{ - text: t('core', 'Yes'), - click: function(){ - if (callback !== undefined) { callback(true) }; - $(dialog_id).dialog('close'); - } - }, - { - text: t('core', 'No'), - click: function(){ - if (callback !== undefined) { callback(false) }; - $(dialog_id).dialog('close'); - } - }]; - break; - case OCdialogs.OK_BUTTON: - var functionToCall; - switch(dialog_type) { - case OCdialogs.ALERT_DIALOG: - functionToCall = function() { + $.when(this._getMessageTemplate()).then(function($tmpl) { + var dialog_name = 'oc-dialog-' + OCdialogs.dialogs_counter + '-content'; + var dialog_id = '#' + dialog_name; + var $dlg = $tmpl.octemplate({ + dialog_name: dialog_name, + title: title, + message: content, + type: dialog_type + }); + if (modal === undefined) { modal = false }; + $('body').append($dlg); + var buttonlist = []; + switch (buttons) { + case OCdialogs.YES_NO_BUTTONS: + buttonlist = [{ + text: t('core', 'Yes'), + click: function(){ + if (callback !== undefined) { callback(true) }; $(dialog_id).dialog('close'); - if(callback !== undefined) { callback() }; - }; - break; - case OCdialogs.PROMPT_DIALOG: - buttonlist[1] = { - text: t('core', 'Cancel'), - click: function() { $(dialog_id).dialog('close'); } - }; - functionToCall = function() { OCdialogs.prompt_ok_handler(callback, dialog_id); }; - break; - } - buttonlist[0] = { - text: t('core', 'Ok'), - click: functionToCall - }; - break; - }; + } + }, + { + text: t('core', 'No'), + click: function(){ + if (callback !== undefined) { callback(false) }; + $(dialog_id).dialog('close'); + } + }]; + break; + case OCdialogs.OK_BUTTON: + var functionToCall; + switch(dialog_type) { + case 'prompt': + buttonlist[1] = { + text: t('core', 'Cancel'), + click: function() { $(dialog_id).dialog('close'); } + }; + functionToCall = function() { OCdialogs.prompt_ok_handler(callback, dialog_id); }; + break; + default: + functionToCall = function() { + $(dialog_id).dialog('close'); + if(callback !== undefined) { callback() }; + }; + break; + } + buttonlist[0] = { + text: t('core', 'Ok'), + click: functionToCall + }; + break; + }; - $(dialog_id).dialog({ - width: (4/9) * $(document).width(), - height: 180, - modal: modal, - buttons: buttonlist + $(dialog_id).dialog({ + modal: modal, + buttons: buttonlist + }); + OCdialogs.dialogs_counter++; + }) + .fail(function() { + alert(t('core', 'Error loading file picker template')); }); - OCdialogs.dialogs_counter++; }, // dialog button types YES_NO_BUTTONS: 70, OK_BUTTONS: 71, - // dialogs types - ALERT_DIALOG: 80, - INFO_DIALOG: 81, - FORM_DIALOG: 82, // used to name each dialog dialogs_counter: 0, @@ -278,7 +322,7 @@ var OCdialogs = { prompt_ok_handler: function(callback, dialog_id) { $(dialog_id).dialog('close'); - if (callback !== undefined) { callback($(dialog_id + " input#oc-dialog-prompt-input").val()) }; + if (callback !== undefined) { callback($(dialog_id + ' input#oc-dialog-prompt-input').val()) }; }, form_ok_handler: function(callback, dialog_id) { @@ -297,8 +341,7 @@ var OCdialogs = { * fills the filepicker with files */ fillFilePicker:function(request, dialog_content_id) { - var template_content = '*NAME*
*LASTMODDATE*
'; - var template = '
*CONTENT*
'; + var $filelist = $(dialog_content_id + ' #filelist').empty(); var files = ''; var dirs = []; var others = []; @@ -309,14 +352,24 @@ var OCdialogs = { others.push(file); } }); - var sorted = dirs.concat(others); - for (var i = 0; i < sorted.length; i++) { - files_content = template_content.replace('*LASTMODDATE*', OC.mtime2date(sorted[i].mtime)).replace('*NAME*', escapeHTML(sorted[i].name)).replace('*MIMETYPEICON*', sorted[i].mimetype_icon); - files += template.replace('*ENTRYNAME*', escapeHTML(sorted[i].name)).replace('*ENTRYTYPE*', escapeHTML(sorted[i].type)).replace('*CONTENT*', files_content); - } - $(dialog_content_id + ' #filelist').html(files); - $('#filelist div').click(function() { + var sorted = dirs.concat(others); + + var self = this; + $.each(sorted, function(idx, entry) { + $li = self.$listTmpl.octemplate({ + type: entry.type, + dcid: dialog_content_id, + imgsrc: entry.mimetype_icon, + filename: entry.name, + date: OC.mtime2date(entry.mtime) + }); + $filelist.append($li); + }); + + $filelist.removeClass('loading'); + + $filelist.on('click', 'li', function() { OCdialogs.handlePickerClick($(this), $(this).data('entryname'), dialog_content_id); }); @@ -326,24 +379,29 @@ var OCdialogs = { * fills the tree list with directories */ fillTreeList: function(request, dialog_id) { - var template = ''; - var paths = ''; + var $dirtree = $(dialog_id + ' #dirtree').empty(); + var $template = $(''); + $dirtree.append($template.octemplate({ + count: 0, + name: $(dialog_id).data('path') + })); $.each(request.data, function(index, file) { - paths += template.replace('*COUNT*', index).replace('*NAME*', escapeHTML(file.name)); + $dirtree.append($template.octemplate({ + count: index, + name: file.name + })); }); - - $(dialog_id + ' #dirtree').html(paths); }, /** * handle selection made in the tree list */ handleTreeListSelect:function(event) { - if ($("option:selected", this).html().indexOf('/') !== -1) { // if there's a slash in the selected path, don't append it - $(event.data.dcid).data('path', $("option:selected", this).html()); + if ($('option:selected', this).html().indexOf('/') !== -1) { // if there's a slash in the selected path, don't append it + $(event.data.dcid).data('path', $('option:selected', this).html()); } else { - $(event.data.dcid).data('path', $(event.data.dcid).data('path') + $("option:selected", this).html() + '/'); + $(event.data.dcid).data('path', $(event.data.dcid).data('path') + $('option:selected', this).html() + '/'); } - $(event.data.dcid + ' .filepicker_loader').css('visibility', 'visible'); + $(event.data.dcid).find('#filelist').addClass('loading'); $.getJSON( OC.filePath('files', 'ajax', 'rawlist.php'), { @@ -356,7 +414,7 @@ var OCdialogs = { OC.filePath('files', 'ajax', 'rawlist.php'), { dir: $(event.data.dcid).data('path'), - mimetype: "httpd/unix-directory" + mimetype: 'httpd/unix-directory' }, function(request) { OCdialogs.fillTreeList(request, event.data.dcid) } ); @@ -366,13 +424,13 @@ var OCdialogs = { */ filepickerDirUp:function(event) { var old_path = $(event.data.dcid).data('path'); - if ( old_path !== "/") { + if ( old_path !== '/') { var splitted_path = old_path.split("/"); var new_path = "" for (var i = 0; i < splitted_path.length - 2; i++) { - new_path += splitted_path[i] + "/" + new_path += splitted_path[i] + '/' } - $(event.data.dcid).data('path', new_path); + $(event.data.dcid).data('path', new_path).find('#filelist').empty().addClass('loading');; $.getJSON( OC.filePath('files', 'ajax', 'rawlist.php'), { @@ -385,7 +443,7 @@ var OCdialogs = { OC.filePath('files', 'ajax', 'rawlist.php'), { dir: $(event.data.dcid).data('path'), - mimetype: "httpd/unix-directory" + mimetype: 'httpd/unix-directory' }, function(request) { OCdialogs.fillTreeList(request, event.data.dcid) } ); @@ -395,16 +453,16 @@ var OCdialogs = { * handle clicks made in the filepicker */ handlePickerClick:function(element, name, dialog_content_id) { - if ( $(element).attr('data') === 'file' ){ + if ( $(element).data('type') === 'file' ){ if ( $(dialog_content_id).data('multiselect') !== true) { $(dialog_content_id + ' .filepicker_element_selected').removeClass('filepicker_element_selected'); } $(element).toggleClass('filepicker_element_selected'); return; - } else if ( $(element).attr('data') === 'dir' ) { + } else if ( $(element).data('type') === 'dir' ) { var datapath = escapeHTML( $(dialog_content_id).data('path') + name + '/' ); $(dialog_content_id).data('path', datapath); - $(dialog_content_id + ' .filepicker_loader').css('visibility', 'visible'); + $(dialog_content_id).find('#filelist').empty().addClass('loading'); $.getJSON( OC.filePath('files', 'ajax', 'rawlist.php'), { @@ -417,7 +475,7 @@ var OCdialogs = { OC.filePath('files', 'ajax', 'rawlist.php'), { dir: datapath, - mimetype: "httpd/unix-directory" + mimetype: 'httpd/unix-directory' }, function(request) { OCdialogs.fillTreeList(request, dialog_content_id) } ); diff --git a/core/templates/filepicker.html b/core/templates/filepicker.html new file mode 100644 index 0000000000..f29de390ef --- /dev/null +++ b/core/templates/filepicker.html @@ -0,0 +1,11 @@ +
+ + +
    +
  • + + {filename} + {date} +
  • +
+
diff --git a/core/templates/message.html b/core/templates/message.html new file mode 100644 index 0000000000..59048100f3 --- /dev/null +++ b/core/templates/message.html @@ -0,0 +1,3 @@ +
+

{message}

+
diff --git a/lib/base.php b/lib/base.php index 667202d3ae..2e2cee97eb 100644 --- a/lib/base.php +++ b/lib/base.php @@ -260,6 +260,7 @@ class OC { OC_Util::addScript("jquery-tipsy"); OC_Util::addScript("compatibility"); OC_Util::addScript("oc-dialogs"); + OC_Util::addScript("octemplate"); OC_Util::addScript("js"); OC_Util::addScript("eventsource"); OC_Util::addScript("config"); From 913941d894579ed332169a7573654fd6b0ca9eca Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Thu, 16 May 2013 18:19:28 +0200 Subject: [PATCH 09/33] Line length etc. --- core/js/oc-dialogs.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/js/oc-dialogs.js b/core/js/oc-dialogs.js index c0c035bafd..8989738287 100644 --- a/core/js/oc-dialogs.js +++ b/core/js/oc-dialogs.js @@ -1,7 +1,7 @@ /** * ownCloud * - * @author Bartek Przybylski,Christopher Schäpers, Thomas Tanghus + * @author Bartek Przybylski, Christopher Schäpers, Thomas Tanghus * @copyright 2012 Bartek Przybylski bartek@alefzero.eu * * This library is free software; you can redistribute it and/or @@ -396,7 +396,8 @@ var OCdialogs = { * handle selection made in the tree list */ handleTreeListSelect:function(event) { - if ($('option:selected', this).html().indexOf('/') !== -1) { // if there's a slash in the selected path, don't append it + // if there's a slash in the selected path, don't append it + if ($('option:selected', this).html().indexOf('/') !== -1) { $(event.data.dcid).data('path', $('option:selected', this).html()); } else { $(event.data.dcid).data('path', $(event.data.dcid).data('path') + $('option:selected', this).html() + '/'); From 152e275c8a780c220c5022ef059dd7b12adc7cf1 Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Fri, 17 May 2013 04:54:08 +0200 Subject: [PATCH 10/33] Various cleanups in OC.dialogs --- apps/files/ajax/rawlist.php | 8 ++ core/css/styles.css | 15 ++- core/js/oc-dialogs.js | 239 ++++++++++++++------------------- core/templates/filepicker.html | 8 +- 4 files changed, 120 insertions(+), 150 deletions(-) diff --git a/apps/files/ajax/rawlist.php b/apps/files/ajax/rawlist.php index 1cd2944483..f568afad4d 100644 --- a/apps/files/ajax/rawlist.php +++ b/apps/files/ajax/rawlist.php @@ -15,6 +15,14 @@ $mimetype = isset($_GET['mimetype']) ? $_GET['mimetype'] : ''; // make filelist $files = array(); +// If a type other than directory is requested first load them. +if($mimetype && strpos($mimetype, 'httpd/unix-directory') === false) { + foreach( \OC\Files\Filesystem::getDirectoryContent( $dir, 'httpd/unix-directory' ) as $i ) { + $i["date"] = OCP\Util::formatDate($i["mtime"] ); + $i['mimetype_icon'] = $i['type'] == 'dir' ? \mimetype_icon('dir'): \mimetype_icon($i['mimetype']); + $files[] = $i; + } +} foreach( \OC\Files\Filesystem::getDirectoryContent( $dir, $mimetype ) as $i ) { $i["date"] = OCP\Util::formatDate($i["mtime"] ); $i['mimetype_icon'] = $i['type'] == 'dir' ? \mimetype_icon('dir'): \mimetype_icon($i['mimetype']); diff --git a/core/css/styles.css b/core/css/styles.css index ee00196d66..2038417685 100644 --- a/core/css/styles.css +++ b/core/css/styles.css @@ -382,13 +382,14 @@ a.bookmarklet { background-color:#ddd; border:1px solid #ccc; padding:5px;paddin .ui-datepicker-prev,.ui-datepicker-next{ border:1px solid #ddd; background:#fff; } /* ---- DIALOGS ---- */ -#dirup {width:4%;} -#dirtree {width:92%;} -#filelist {height:270px; overflow-y:auto; background-color:white; width:100%;} -#filelist img { margin: 2px 1em 0 4px; } -#filelist .date { float:right;margin-right:1em; } -.filepicker_element_selected { background-color:lightblue;} -.filepicker_loader {height:170px; width:100%; background-color:#333; -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=30)"; filter:alpha(opacity=30); opacity:.3; visibility:visible; position:absolute; top:0; left:0; text-align:center; padding-top:150px;} +#oc-dialog-filepicker-content .dirup {width:4%; font-weight: bold;} +#oc-dialog-filepicker-content .dirtree {width:92%; overflow:hidden; font-weight: bold; } +#oc-dialog-filepicker-content .dirtree span { cursor: pointer; } +#oc-dialog-filepicker-content .dirtree span:not(last-child)::after { content: '>'; padding: 3px;} +#oc-dialog-filepicker-content .filelist {height:270px; overflow-y:auto; background-color:white; width:100%;} +#oc-dialog-filepicker-content .filelist img { margin: 2px 1em 0 4px; } +#oc-dialog-filepicker-content .filelist .date { float:right;margin-right:1em; } +#oc-dialog-filepicker-content .filepicker_element_selected { background-color:lightblue;} .ui-dialog {position:fixed !important;} span.ui-icon {float: left; margin: 3px 7px 30px 0;} .loading { background: url('../img/loading.gif') no-repeat center; cursor: wait; } diff --git a/core/js/oc-dialogs.js b/core/js/oc-dialogs.js index 8989738287..4da9623c0a 100644 --- a/core/js/oc-dialogs.js +++ b/core/js/oc-dialogs.js @@ -133,7 +133,7 @@ var OCdialogs = { var self = this; $.get(OC.filePath('core', 'templates', 'filepicker.html'), function(tmpl) { self.$filePickerTemplate = $(tmpl); - self.$listTmpl = self.$filePickerTemplate.find('#filelist li:first-child').detach(); + self.$listTmpl = self.$filePickerTemplate.find('.filelist li:first-child').detach(); defer.resolve(self.$filePickerTemplate); }) .fail(function() { @@ -160,6 +160,12 @@ var OCdialogs = { } return defer.promise(); }, + _getFileList: function(dir, mimeType) { + return $.getJSON( + OC.filePath('files', 'ajax', 'rawlist.php'), + {dir: dir, mimetype: mimeType} + ); + }, /** * show a file picker to pick a file from * @param title dialog title @@ -169,35 +175,35 @@ var OCdialogs = { * @param modal make the dialog modal */ filepicker:function(title, callback, multiselect, mimetype_filter, modal) { + var self = this; $.when(this._getFilePickerTemplate()).then(function($tmpl) { - var dialog_name = 'oc-dialog-' + OCdialogs.dialogs_counter + '-content'; + var dialog_name = 'oc-dialog-filepicker-content'; var dialog_id = '#' + dialog_name; - var $dlg = $tmpl.octemplate({ + if(self.$filePicker) { + self.$filePicker.dialog('close'); + } + self.$filePicker = $tmpl.octemplate({ dialog_name: dialog_name, title: title - }).data('path', '/'); + }).data('path', ''); if (modal === undefined) { modal = false }; if (multiselect === undefined) { multiselect = false }; if (mimetype_filter === undefined) { mimetype_filter = '' }; - $('body').append($dlg); + $('body').append(self.$filePicker); - $dlg.find('#dirtree').focus().change( {dcid: dialog_id}, OCdialogs.handleTreeListSelect ); - $dlg.find('#dirup').click( {dcid: dialog_id}, OCdialogs.filepickerDirUp ); - $dlg.find('#filelist').empty().addClass('loading'); - $dlg.ready(function(){ - $.getJSON(OC.filePath('files', 'ajax', 'rawlist.php'), - {mimetype: mimetype_filter}, - function(response) { - OCdialogs.fillFilePicker(response, dialog_id); - }); - $.getJSON(OC.filePath('files', 'ajax', 'rawlist.php'), - {mimetype: 'httpd/unix-directory'}, - function(response) { - OCdialogs.fillTreeList(response, dialog_id); + self.$filePicker.ready(function() { + self.$filelist = self.$filePicker.find('.filelist'); + self.$dirUp = self.$filePicker.find('.dirup'); + self.$dirTree = self.$filePicker.find('.dirtree'); + self.$dirTree.on('click', 'span', self, self.handleTreeListSelect); + self.$dirUp.click(self, self.filepickerDirUp); + self.$filelist.on('click', 'li', function(event) { + self.handlePickerClick(event, $(this)); }); + self.fillFilePicker(''); }).data('multiselect', multiselect).data('mimetype',mimetype_filter); // build buttons @@ -206,15 +212,15 @@ var OCdialogs = { var datapath; if (multiselect === true) { datapath = []; - $(dialog_id + ' .filepicker_element_selected .filename').each(function(index, element) { - datapath.push( $(dialog_id).data('path') + $(element).text() ); + self.$filelist.find('.filepicker_element_selected .filename').each(function(index, element) { + datapath.push(self.$filePicker.data('path') + '/' + $(element).text()); }); } else { - var datapath = $(dialog_id).data('path'); - datapath += $(dialog_id+' .filepicker_element_selected .filename').text(); + var datapath = self.$filePicker.data('path'); + datapath += '/' + self.$filelist.find('.filepicker_element_selected .filename').text(); } callback(datapath); - $(dialog_id).dialog('close'); + self.$filePicker.dialog('close'); } }; var buttonlist = [{ @@ -223,16 +229,19 @@ var OCdialogs = { }, { text: t('core', 'Cancel'), - click: function(){$(dialog_id).dialog('close'); } + click: function(){self.$filePicker.dialog('close'); } }]; - $(dialog_id).dialog({ + self.$filePicker.dialog({ width: (4/9)*$(document).width(), height: 420, modal: modal, - buttons: buttonlist + buttons: buttonlist, + close: function(event, ui) { + self.$filePicker.dialog('destroy').remove(); + self.$filePicker = null; + } }); - OCdialogs.dialogs_counter++; }) .fail(function() { alert(t('core', 'Error loading file picker template')); @@ -340,146 +349,98 @@ var OCdialogs = { /** * fills the filepicker with files */ - fillFilePicker:function(request, dialog_content_id) { - var $filelist = $(dialog_content_id + ' #filelist').empty(); - var files = ''; + fillFilePicker:function(dir) { var dirs = []; var others = []; - $.each(request.data, function(index, file) { - if (file.type === 'dir') { - dirs.push(file); - } else { - others.push(file); - } - }); - - var sorted = dirs.concat(others); - var self = this; - $.each(sorted, function(idx, entry) { - $li = self.$listTmpl.octemplate({ - type: entry.type, - dcid: dialog_content_id, - imgsrc: entry.mimetype_icon, - filename: entry.name, - date: OC.mtime2date(entry.mtime) + this.$filelist.empty().addClass('loading'); + this.$filePicker.data('path', dir); + $.when(this._getFileList(dir, this.$filePicker.data('mimetype'))).then(function(response) { + $.each(response.data, function(index, file) { + if (file.type === 'dir') { + dirs.push(file); + } else { + others.push(file); + } }); - $filelist.append($li); + + self.fillTreeList(); + var sorted = dirs.concat(others); + + $.each(sorted, function(idx, entry) { + $li = self.$listTmpl.octemplate({ + type: entry.type, + dir: dir, + imgsrc: entry.mimetype_icon, + filename: entry.name, + date: OC.mtime2date(entry.mtime) + }); + self.$filelist.append($li); + }); + + self.$filelist.removeClass('loading'); }); - - $filelist.removeClass('loading'); - - $filelist.on('click', 'li', function() { - OCdialogs.handlePickerClick($(this), $(this).data('entryname'), dialog_content_id); - }); - - $(dialog_content_id + ' .filepicker_loader').css('visibility', 'hidden'); }, /** * fills the tree list with directories */ - fillTreeList: function(request, dialog_id) { - var $dirtree = $(dialog_id + ' #dirtree').empty(); - var $template = $(''); - $dirtree.append($template.octemplate({ - count: 0, - name: $(dialog_id).data('path') - })); - $.each(request.data, function(index, file) { - $dirtree.append($template.octemplate({ - count: index, - name: file.name + fillTreeList: function() { + this.$dirTree.empty(); + var self = this + var path = this.$filePicker.data('path'); + if(!path) { + return; + } + var $template = $('{name}'); + var paths = path.split('/'); + paths.pop(); + $.each(paths, function(index, dir) { + var dir = paths.pop(); + if(dir === '') { + return false; + } + self.$dirTree.prepend($template.octemplate({ + dir: paths.join('/') + '/' + dir, + name: dir })); }); + self.$dirTree.prepend($template.octemplate({ + dir: '', + name: '/' + })); }, /** * handle selection made in the tree list */ handleTreeListSelect:function(event) { - // if there's a slash in the selected path, don't append it - if ($('option:selected', this).html().indexOf('/') !== -1) { - $(event.data.dcid).data('path', $('option:selected', this).html()); - } else { - $(event.data.dcid).data('path', $(event.data.dcid).data('path') + $('option:selected', this).html() + '/'); - } - $(event.data.dcid).find('#filelist').addClass('loading'); - $.getJSON( - OC.filePath('files', 'ajax', 'rawlist.php'), - { - dir: $(event.data.dcid).data('path'), - mimetype: $(event.data.dcid).data('mimetype') - }, - function(request) { OCdialogs.fillFilePicker(request, event.data.dcid) } - ); - $.getJSON( - OC.filePath('files', 'ajax', 'rawlist.php'), - { - dir: $(event.data.dcid).data('path'), - mimetype: 'httpd/unix-directory' - }, - function(request) { OCdialogs.fillTreeList(request, event.data.dcid) } - ); + var self = event.data; + var dir = $(event.target).data('dir'); + self.fillFilePicker(dir); }, /** * go one directory up */ filepickerDirUp:function(event) { - var old_path = $(event.data.dcid).data('path'); - if ( old_path !== '/') { - var splitted_path = old_path.split("/"); - var new_path = "" - for (var i = 0; i < splitted_path.length - 2; i++) { - new_path += splitted_path[i] + '/' - } - $(event.data.dcid).data('path', new_path).find('#filelist').empty().addClass('loading');; - $.getJSON( - OC.filePath('files', 'ajax', 'rawlist.php'), - { - dir: $(event.data.dcid).data('path'), - mimetype: $(event.data.dcid).data('mimetype') - }, - function(request) { OCdialogs.fillFilePicker(request, event.data.dcid) } - ); - $.getJSON( - OC.filePath('files', 'ajax', 'rawlist.php'), - { - dir: $(event.data.dcid).data('path'), - mimetype: 'httpd/unix-directory' - }, - function(request) { OCdialogs.fillTreeList(request, event.data.dcid) } - ); + var self = event.data; + var old_path = self.$filePicker.data('path'); + if (old_path !== '') { + var splitted_path = old_path.split('/'); + splitted_path.pop(); + self.fillFilePicker(splitted_path.join('/')); } }, /** * handle clicks made in the filepicker */ - handlePickerClick:function(element, name, dialog_content_id) { - if ( $(element).data('type') === 'file' ){ - if ( $(dialog_content_id).data('multiselect') !== true) { - $(dialog_content_id + ' .filepicker_element_selected').removeClass('filepicker_element_selected'); + handlePickerClick:function(event, $element) { + if ($element.data('type') === 'file') { + if (this.$filePicker.data('multiselect') !== true || !event.ctrlKey) { + this.$filelist.find('.filepicker_element_selected').removeClass('filepicker_element_selected'); } - $(element).toggleClass('filepicker_element_selected'); + $element.toggleClass('filepicker_element_selected'); return; - } else if ( $(element).data('type') === 'dir' ) { - var datapath = escapeHTML( $(dialog_content_id).data('path') + name + '/' ); - $(dialog_content_id).data('path', datapath); - $(dialog_content_id).find('#filelist').empty().addClass('loading'); - $.getJSON( - OC.filePath('files', 'ajax', 'rawlist.php'), - { - dir: datapath, - mimetype: $(dialog_content_id).data('mimetype') - }, - function(request){ OCdialogs.fillFilePicker(request, dialog_content_id) } - ); - $.getJSON( - OC.filePath('files', 'ajax', 'rawlist.php'), - { - dir: datapath, - mimetype: 'httpd/unix-directory' - }, - function(request) { OCdialogs.fillTreeList(request, dialog_content_id) } - ); + } else if ( $element.data('type') === 'dir' ) { + this.fillFilePicker(this.$filePicker.data('path') + '/' + $element.data('entryname')) } } }; diff --git a/core/templates/filepicker.html b/core/templates/filepicker.html index f29de390ef..e7aa77a732 100644 --- a/core/templates/filepicker.html +++ b/core/templates/filepicker.html @@ -1,8 +1,8 @@
- - -