diff --git a/apps/files/admin.php b/apps/files/admin.php index f747f8645f..02c3147dba 100644 --- a/apps/files/admin.php +++ b/apps/files/admin.php @@ -21,10 +21,6 @@ * */ - -// Init owncloud - - OCP\User::checkAdminUser(); $htaccessWorking=(getenv('htaccessWorking')=='true'); diff --git a/apps/files/ajax/autocomplete.php b/apps/files/ajax/autocomplete.php deleted file mode 100644 index b32ba7c3d5..0000000000 --- a/apps/files/ajax/autocomplete.php +++ /dev/null @@ -1,54 +0,0 @@ -$item, 'label'=>$item, 'name'=>$item); - } - } - } - } - } -} -OCP\JSON::encodedPrint($files); diff --git a/apps/files/ajax/delete.php b/apps/files/ajax/delete.php index 6532b76df2..da7e9d6b2a 100644 --- a/apps/files/ajax/delete.php +++ b/apps/files/ajax/delete.php @@ -12,17 +12,22 @@ $files = isset($_POST["file"]) ? stripslashes($_POST["file"]) : stripslashes($_P $files = json_decode($files); $filesWithError = ''; + $success = true; + //Now delete -foreach($files as $file) { - if( !OC_Files::delete( $dir, $file )) { +foreach ($files as $file) { + if (($dir === '' && $file === 'Shared') || !\OC\Files\Filesystem::unlink($dir . '/' . $file)) { $filesWithError .= $file . "\n"; $success = false; } } -if($success) { - OCP\JSON::success(array("data" => array( "dir" => $dir, "files" => $files ))); +// get array with updated storage stats (e.g. max file size) after upload +$storageStats = \OCA\files\lib\Helper::buildFileStorageStatistics($dir); + +if ($success) { + OCP\JSON::success(array("data" => array_merge(array("dir" => $dir, "files" => $files), $storageStats))); } else { - OCP\JSON::error(array("data" => array( "message" => "Could not delete:\n" . $filesWithError ))); + OCP\JSON::error(array("data" => array_merge(array("message" => "Could not delete:\n" . $filesWithError), $storageStats))); } diff --git a/apps/files/ajax/getstoragestats.php b/apps/files/ajax/getstoragestats.php new file mode 100644 index 0000000000..7a2b642a9b --- /dev/null +++ b/apps/files/ajax/getstoragestats.php @@ -0,0 +1,9 @@ + \OCA\files\lib\Helper::buildFileStorageStatistics('/'))); diff --git a/apps/files/ajax/list.php b/apps/files/ajax/list.php index cade7e872b..878e4cb215 100644 --- a/apps/files/ajax/list.php +++ b/apps/files/ajax/list.php @@ -32,7 +32,7 @@ if($doBreadcrumb) { // make filelist $files = array(); -foreach( OC_Files::getdirectorycontent( $dir ) as $i ) { +foreach( \OC\Files\Filesystem::getDirectoryContent( $dir ) as $i ) { $i["date"] = OCP\Util::formatDate($i["mtime"] ); $files[] = $i; } diff --git a/apps/files/ajax/move.php b/apps/files/ajax/move.php index 4ebc3f42d9..78ed218c13 100644 --- a/apps/files/ajax/move.php +++ b/apps/files/ajax/move.php @@ -7,19 +7,23 @@ OCP\JSON::checkLoggedIn(); OCP\JSON::callCheck(); // Get data -$dir = stripslashes($_GET["dir"]); -$file = stripslashes($_GET["file"]); -$target = stripslashes(rawurldecode($_GET["target"])); +$dir = stripslashes($_POST["dir"]); +$file = stripslashes($_POST["file"]); +$target = stripslashes(rawurldecode($_POST["target"])); -$l=OC_L10N::get('files'); - -if(OC_Filesystem::file_exists($target . '/' . $file)) { - OCP\JSON::error(array("data" => array( "message" => $l->t("Could not move %s - File with this name already exists", array($file)) ))); +if(\OC\Files\Filesystem::file_exists($target . '/' . $file)) { + OCP\JSON::error(array("data" => array( "message" => "Could not move $file - File with this name already exists" ))); exit; } -if(OC_Files::move($dir, $file, $target, $file)) { - OCP\JSON::success(array("data" => array( "dir" => $dir, "files" => $file ))); -} else { - OCP\JSON::error(array("data" => array( "message" => $l->t("Could not move %s", array($file)) ))); +if ($dir != '' || $file != 'Shared') { + $targetFile = \OC\Files\Filesystem::normalizePath($target . '/' . $file); + $sourceFile = \OC\Files\Filesystem::normalizePath($dir . '/' . $file); + if(\OC\Files\Filesystem::rename($sourceFile, $targetFile)) { + OCP\JSON::success(array("data" => array( "dir" => $dir, "files" => $file ))); + } else { + OCP\JSON::error(array("data" => array( "message" => "Could not move $file" ))); + } +}else{ + OCP\JSON::error(array("data" => array( "message" => "Could not move $file" ))); } diff --git a/apps/files/ajax/newfile.php b/apps/files/ajax/newfile.php index 2bac9bb20b..38714f34a6 100644 --- a/apps/files/ajax/newfile.php +++ b/apps/files/ajax/newfile.php @@ -63,13 +63,12 @@ if($source) { $ctx = stream_context_create(null, array('notification' =>'progress')); $sourceStream=fopen($source, 'rb', false, $ctx); $target=$dir.'/'.$filename; - $result=OC_Filesystem::file_put_contents($target, $sourceStream); + $result=\OC\Files\Filesystem::file_put_contents($target, $sourceStream); if($result) { - $target = OC_Filesystem::normalizePath($target); - $meta = OC_FileCache::get($target); + $meta = \OC\Files\Filesystem::getFileInfo($target); $mime=$meta['mimetype']; - $id = OC_FileCache::getId($target); - $eventSource->send('success', array('mime'=>$mime, 'size'=>OC_Filesystem::filesize($target), 'id' => $id)); + $id = $meta['fileid']; + $eventSource->send('success', array('mime'=>$mime, 'size'=>\OC\Files\Filesystem::filesize($target), 'id' => $id)); } else { $eventSource->send('error', "Error while downloading ".$source. ' to '.$target); } @@ -77,15 +76,15 @@ if($source) { exit(); } else { if($content) { - if(OC_Filesystem::file_put_contents($dir.'/'.$filename, $content)) { - $meta = OC_FileCache::get($dir.'/'.$filename); - $id = OC_FileCache::getId($dir.'/'.$filename); + if(\OC\Files\Filesystem::file_put_contents($dir.'/'.$filename, $content)) { + $meta = \OC\Files\Filesystem::getFileInfo($dir.'/'.$filename); + $id = $meta['fileid']; OCP\JSON::success(array("data" => array('content'=>$content, 'id' => $id))); exit(); } - }elseif(OC_Files::newFile($dir, $filename, 'file')) { - $meta = OC_FileCache::get($dir.'/'.$filename); - $id = OC_FileCache::getId($dir.'/'.$filename); + }elseif(\OC\Files\Filesystem::touch($dir . '/' . $filename)) { + $meta = \OC\Files\Filesystem::getFileInfo($dir.'/'.$filename); + $id = $meta['fileid']; OCP\JSON::success(array("data" => array('content'=>$content, 'id' => $id))); exit(); } diff --git a/apps/files/ajax/newfolder.php b/apps/files/ajax/newfolder.php index 0f1f2f14eb..e26e1238bc 100644 --- a/apps/files/ajax/newfolder.php +++ b/apps/files/ajax/newfolder.php @@ -19,13 +19,14 @@ if(strpos($foldername, '/')!==false) { exit(); } -if(OC_Files::newFile($dir, stripslashes($foldername), 'dir')) { +if(\OC\Files\Filesystem::mkdir($dir . '/' . stripslashes($foldername))) { if ( $dir != '/') { $path = $dir.'/'.$foldername; } else { $path = '/'.$foldername; } - $id = OC_FileCache::getId($path); + $meta = \OC\Files\Filesystem::getFileInfo($path); + $id = $meta['fileid']; OCP\JSON::success(array("data" => array('id'=>$id))); exit(); } diff --git a/apps/files/ajax/rawlist.php b/apps/files/ajax/rawlist.php index e0aa0bdac5..1cd2944483 100644 --- a/apps/files/ajax/rawlist.php +++ b/apps/files/ajax/rawlist.php @@ -15,7 +15,7 @@ $mimetype = isset($_GET['mimetype']) ? $_GET['mimetype'] : ''; // make filelist $files = array(); -foreach( OC_Files::getdirectorycontent( $dir, $mimetype ) as $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']); $files[] = $i; diff --git a/apps/files/ajax/rename.php b/apps/files/ajax/rename.php index 89b4d4bba7..970aaa638d 100644 --- a/apps/files/ajax/rename.php +++ b/apps/files/ajax/rename.php @@ -11,10 +11,14 @@ $dir = stripslashes($_GET["dir"]); $file = stripslashes($_GET["file"]); $newname = stripslashes($_GET["newname"]); -// Delete -if( $newname !== '.' and OC_Files::move( $dir, $file, $dir, $newname )) { - OCP\JSON::success(array("data" => array( "dir" => $dir, "file" => $file, "newname" => $newname ))); -} else { - $l=OC_L10N::get('files'); - OCP\JSON::error(array("data" => array( "message" => $l->t("Unable to rename file") ))); +if ( $newname !== '.' and ($dir != '' || $file != 'Shared') and $newname !== '.') { + $targetFile = \OC\Files\Filesystem::normalizePath($dir . '/' . $newname); + $sourceFile = \OC\Files\Filesystem::normalizePath($dir . '/' . $file); + if(\OC\Files\Filesystem::rename($sourceFile, $targetFile)) { + OCP\JSON::success(array("data" => array( "dir" => $dir, "file" => $file, "newname" => $newname ))); + } else { + OCP\JSON::error(array("data" => array( "message" => "Unable to rename file" ))); + } +}else{ + OCP\JSON::error(array("data" => array( "message" => "Unable to rename file" ))); } diff --git a/apps/files/ajax/scan.php b/apps/files/ajax/scan.php index a819578e30..391b98608b 100644 --- a/apps/files/ajax/scan.php +++ b/apps/files/ajax/scan.php @@ -1,44 +1,71 @@ getAbsolutePath($dir); + +$mountPoints = \OC\Files\Filesystem::getMountPoints($absolutePath); +$mountPoints[] = \OC\Files\Filesystem::getMountPoint($absolutePath); +$mountPoints = array_reverse($mountPoints); //start with the mount point of $dir + +foreach ($mountPoints as $mountPoint) { + $storage = \OC\Files\Filesystem::getStorage($mountPoint); + if ($storage) { + ScanListener::$mountPoints[$storage->getId()] = $mountPoint; + $scanner = $storage->getScanner(); + if ($force) { + $scanner->scan(''); + } else { + $scanner->backgroundScan(); } - - OC_FileCache::scan($dir, $eventSource); - OC_FileCache::clean(); - OCP\DB::commit(); - $eventSource->send('success', true); - } else { - OCP\JSON::success(array('data'=>array('done'=>true))); - exit; - } -} else { - if($checkOnly) { - OCP\JSON::success(array('data'=>array('done'=>false))); - exit; - } - if(isset($eventSource)) { - $eventSource->send('success', false); - } else { - exit; } } + +$eventSource->send('done', ScanListener::$fileCount); $eventSource->close(); + +class ScanListener { + + static public $fileCount = 0; + static public $lastCount = 0; + + /** + * @var \OC\Files\View $view + */ + static public $view; + + /** + * @var array $mountPoints map storage ids to mountpoints + */ + static public $mountPoints = array(); + + /** + * @var \OC_EventSource event source to pass events to + */ + static public $eventSource; + + static function folder($params) { + $internalPath = $params['path']; + $mountPoint = self::$mountPoints[$params['storage']]; + $path = self::$view->getRelativePath($mountPoint . $internalPath); + self::$eventSource->send('folder', $path); + } + + static function file() { + self::$fileCount++; + if (self::$fileCount > self::$lastCount + 20) { //send a count update every 20 files + self::$lastCount = self::$fileCount; + self::$eventSource->send('count', self::$fileCount); + } + } +} diff --git a/apps/files/ajax/upgrade.php b/apps/files/ajax/upgrade.php new file mode 100644 index 0000000000..7237b02c0b --- /dev/null +++ b/apps/files/ajax/upgrade.php @@ -0,0 +1,44 @@ +hasItems()) { + OC_Hook::connect('\OC\Files\Cache\Upgrade', 'migrate_path', $listener, 'upgradePath'); + + OC_DB::beginTransaction(); + $upgrade = new \OC\Files\Cache\Upgrade($legacy); + $count = $legacy->getCount(); + $eventSource->send('total', $count); + $upgrade->upgradePath('/' . $user . '/files'); + OC_DB::commit(); +} +\OC\Files\Cache\Upgrade::upgradeDone($user); +$eventSource->send('done', true); +$eventSource->close(); + +class UpgradeListener { + /** + * @var OC_EventSource $eventSource + */ + private $eventSource; + + private $count = 0; + private $lastSend = 0; + + public function __construct($eventSource) { + $this->eventSource = $eventSource; + } + + public function upgradePath($path) { + $this->count++; + if ($this->count > ($this->lastSend + 5)) { + $this->lastSend = $this->count; + $this->eventSource->send('count', $this->count); + } + } +} diff --git a/apps/files/ajax/upload.php b/apps/files/ajax/upload.php index 2a2d935da6..676612c0e4 100644 --- a/apps/files/ajax/upload.php +++ b/apps/files/ajax/upload.php @@ -8,65 +8,79 @@ OCP\JSON::setContentTypeHeader('text/plain'); OCP\JSON::checkLoggedIn(); OCP\JSON::callCheck(); -$l=OC_L10N::get('files'); +$l = OC_L10N::get('files'); + + +$dir = $_POST['dir']; +// get array with current storage stats (e.g. max file size) +$storageStats = \OCA\files\lib\Helper::buildFileStorageStatistics($dir); if (!isset($_FILES['files'])) { - OCP\JSON::error(array('data' => array( 'message' => $l->t( 'No file was uploaded. Unknown error' )))); + OCP\JSON::error(array('data' => array_merge(array('message' => $l->t('No file was uploaded. Unknown error')), $storageStats))); exit(); } foreach ($_FILES['files']['error'] as $error) { if ($error != 0) { $errors = array( - UPLOAD_ERR_OK=>$l->t('There is no error, the file uploaded with success'), - UPLOAD_ERR_INI_SIZE=>$l->t('The uploaded file exceeds the upload_max_filesize directive in php.ini: ') - .ini_get('upload_max_filesize'), - UPLOAD_ERR_FORM_SIZE=>$l->t('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified' - .' in the HTML form'), - UPLOAD_ERR_PARTIAL=>$l->t('The uploaded file was only partially uploaded'), - UPLOAD_ERR_NO_FILE=>$l->t('No file was uploaded'), - UPLOAD_ERR_NO_TMP_DIR=>$l->t('Missing a temporary folder'), - UPLOAD_ERR_CANT_WRITE=>$l->t('Failed to write to disk'), + UPLOAD_ERR_OK => $l->t('There is no error, the file uploaded with success'), + UPLOAD_ERR_INI_SIZE => $l->t('The uploaded file exceeds the upload_max_filesize directive in php.ini: ') + . ini_get('upload_max_filesize'), + UPLOAD_ERR_FORM_SIZE => $l->t('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified' + . ' in the HTML form'), + UPLOAD_ERR_PARTIAL => $l->t('The uploaded file was only partially uploaded'), + UPLOAD_ERR_NO_FILE => $l->t('No file was uploaded'), + UPLOAD_ERR_NO_TMP_DIR => $l->t('Missing a temporary folder'), + UPLOAD_ERR_CANT_WRITE => $l->t('Failed to write to disk'), ); - OCP\JSON::error(array('data' => array( 'message' => $errors[$error] ))); + OCP\JSON::error(array('data' => array_merge(array('message' => $errors[$error]), $storageStats))); exit(); } } -$files=$_FILES['files']; +$files = $_FILES['files']; -$dir = $_POST['dir']; -$error=''; +$error = ''; -$totalSize=0; -foreach($files['size'] as $size) { - $totalSize+=$size; +$maxUploadFilesize = OCP\Util::maxUploadFilesize($dir); +$maxHumanFilesize = OCP\Util::humanFileSize($maxUploadFilesize); + +$totalSize = 0; +foreach ($files['size'] as $size) { + $totalSize += $size; } -if($totalSize>OC_Filesystem::free_space($dir)) { - OCP\JSON::error(array('data' => array( 'message' => $l->t( 'Not enough space available' )))); +if ($totalSize > \OC\Files\Filesystem::free_space($dir)) { + OCP\JSON::error(array('data' => array('message' => $l->t('Not enough space available'), + 'uploadMaxFilesize' => $maxUploadFilesize, + 'maxHumanFilesize' => $maxHumanFilesize))); exit(); } -$result=array(); -if(strpos($dir, '..') === false) { - $fileCount=count($files['name']); - for($i=0;$i<$fileCount;$i++) { +$result = array(); +if (strpos($dir, '..') === false) { + $fileCount = count($files['name']); + for ($i = 0; $i < $fileCount; $i++) { $target = OCP\Files::buildNotExistingFileName(stripslashes($dir), $files['name'][$i]); // $path needs to be normalized - this failed within drag'n'drop upload to a sub-folder - $target = OC_Filesystem::normalizePath($target); - if(is_uploaded_file($files['tmp_name'][$i]) and OC_Filesystem::fromTmpFile($files['tmp_name'][$i], $target)) { - $meta = OC_FileCache::get($target); - $id = OC_FileCache::getId($target); - $result[]=array( 'status' => 'success', - 'mime'=>$meta['mimetype'], - 'size'=>$meta['size'], - 'id'=>$id, - 'name'=>basename($target)); + $target = \OC\Files\Filesystem::normalizePath($target); + if (is_uploaded_file($files['tmp_name'][$i]) and \OC\Files\Filesystem::fromTmpFile($files['tmp_name'][$i], $target)) { + $meta = \OC\Files\Filesystem::getFileInfo($target); + // updated max file size after upload + $storageStats = \OCA\files\lib\Helper::buildFileStorageStatistics($dir); + + $result[] = array('status' => 'success', + 'mime' => $meta['mimetype'], + 'size' => $meta['size'], + 'id' => $meta['fileid'], + 'name' => basename($target), + 'uploadMaxFilesize' => $maxUploadFilesize, + 'maxHumanFilesize' => $maxHumanFilesize + ); } } OCP\JSON::encodedPrint($result); exit(); } else { - $error=$l->t( 'Invalid directory.' ); + $error = $l->t('Invalid directory.'); } -OCP\JSON::error(array('data' => array('message' => $error ))); +OCP\JSON::error(array('data' => array_merge(array('message' => $error), $storageStats))); diff --git a/apps/files/appinfo/app.php b/apps/files/appinfo/app.php index 108f02930e..da17a7f2cc 100644 --- a/apps/files/appinfo/app.php +++ b/apps/files/appinfo/app.php @@ -1,12 +1,12 @@ "files_index", "order" => 0, "href" => OCP\Util::linkTo( "files", "index.php" ), - "icon" => OCP\Util::imagePath( "core", "places/home.svg" ), + "icon" => OCP\Util::imagePath( "core", "places/files.svg" ), "name" => $l->t("Files") )); OC_Search::registerProvider('OC_Search_Provider_File'); diff --git a/apps/files/appinfo/filesync.php b/apps/files/appinfo/filesync.php index cbed56a6de..47884a4f15 100644 --- a/apps/files/appinfo/filesync.php +++ b/apps/files/appinfo/filesync.php @@ -43,7 +43,7 @@ if ($type != 'oc_chunked') { die; } -if (!OC_Filesystem::is_file($file)) { +if (!\OC\Files\Filesystem::is_file($file)) { OC_Response::setStatus(OC_Response::STATUS_NOT_FOUND); die; } @@ -51,7 +51,7 @@ if (!OC_Filesystem::is_file($file)) { switch($_SERVER['REQUEST_METHOD']) { case 'PUT': $input = fopen("php://input", "r"); - $org_file = OC_Filesystem::fopen($file, 'rb'); + $org_file = \OC\Files\Filesystem::fopen($file, 'rb'); $info = array( 'name' => basename($file), ); diff --git a/apps/files/appinfo/info.xml b/apps/files/appinfo/info.xml index 0a1b196b06..7c82c839da 100644 --- a/apps/files/appinfo/info.xml +++ b/apps/files/appinfo/info.xml @@ -5,7 +5,7 @@ File Management AGPL Robin Appelman - 4.9 + 4.91 true diff --git a/apps/files/appinfo/remote.php b/apps/files/appinfo/remote.php index 6a78a1e0d7..6c92cc80b6 100644 --- a/apps/files/appinfo/remote.php +++ b/apps/files/appinfo/remote.php @@ -32,12 +32,14 @@ OC_Util::obEnd(); // Backends $authBackend = new OC_Connector_Sabre_Auth(); $lockBackend = new OC_Connector_Sabre_Locks(); +$requestBackend = new OC_Connector_Sabre_Request(); // Create ownCloud Dir $publicDir = new OC_Connector_Sabre_Directory(''); // Fire up server $server = new Sabre_DAV_Server($publicDir); +$server->httpRequest = $requestBackend; $server->setBaseUri($baseuri); // Load plugins diff --git a/apps/files/appinfo/version b/apps/files/appinfo/version index 0664a8fd29..2bf1ca5f54 100644 --- a/apps/files/appinfo/version +++ b/apps/files/appinfo/version @@ -1 +1 @@ -1.1.6 +1.1.7 diff --git a/apps/files/css/files.css b/apps/files/css/files.css index 0c97b009b8..661a2e827a 100644 --- a/apps/files/css/files.css +++ b/apps/files/css/files.css @@ -23,7 +23,9 @@ #new>ul>li>p { cursor:pointer; } #new>ul>li>form>input { padding:0.3em; margin:-0.3em; } -#upload { +#trash { height:17px; margin:0 0 0 1em; z-index:1010; position:absolute; right:13.5em; } + +#upload { height:27px; padding:0; margin-left:0.2em; overflow:hidden; } #upload a { @@ -104,21 +106,38 @@ table td.filename form { font-size:.85em; margin-left:3em; margin-right:3em; } #fileList tr:hover .fileactions { /* background to distinguish when overlaying with file names */ background:rgba(248,248,248,.9); box-shadow:-5px 0 7px rgba(248,248,248,.9); } -#fileList tr.selected:hover .fileactions { /* slightly darker color for selected rows */ +#fileList tr.selected:hover .fileactions, #fileList tr.mouseOver .fileactions { /* slightly darker color for selected rows */ background:rgba(238,238,238,.9); box-shadow:-5px 0 7px rgba(238,238,238,.9); } #fileList .fileactions a.action img { position:relative; top:.2em; } #fileList a.action { display:inline; margin:-.5em 0; padding:1em .5em 1em .5em !important; } +#fileList img.move2trash { display:inline; margin:-.5em 0; padding:1em .5em 1em .5em !important; float:right; } a.action.delete { float:right; } a.action>img { max-height:16px; max-width:16px; vertical-align:text-bottom; } .selectedActions { display:none; float:right; } .selectedActions a { display:inline; margin:-.5em 0; padding:.5em !important; } .selectedActions a img { position:relative; top:.3em; } -/* add breadcrumb divider to the File item in navigation panel */ -#navigation>ul>li:first-child { background:url('%webroot%/core/img/breadcrumb-start.svg') no-repeat 12.5em 0px; width:12.5em; padding-right:1em; position:fixed; } -#navigation>ul>li:first-child+li { padding-top:2.9em; } #scanning-message{ top:40%; left:40%; position:absolute; display:none; } div.crumb a{ padding:0.9em 0 0.7em 0; } + +table.dragshadow { + width:auto; +} +table.dragshadow td.filename { + padding-left:36px; + padding-right:16px; +} +table.dragshadow td.size { + padding-right:8px; +} +#upgrade { + width: 400px; + position: absolute; + top: 200px; + left: 50%; + text-align: center; + margin-left: -200px; +} diff --git a/apps/files/download.php b/apps/files/download.php index e2149cd413..e3fe24e45d 100644 --- a/apps/files/download.php +++ b/apps/files/download.php @@ -21,15 +21,12 @@ * */ -// Init owncloud - - // Check if we are a user OCP\User::checkLoggedIn(); $filename = $_GET["file"]; -if(!OC_Filesystem::file_exists($filename)) { +if(!\OC\Files\Filesystem::file_exists($filename)) { header("HTTP/1.0 404 Not Found"); $tmpl = new OCP\Template( '', '404', 'guest' ); $tmpl->assign('file', $filename); @@ -37,7 +34,7 @@ if(!OC_Filesystem::file_exists($filename)) { exit; } -$ftype=OC_Filesystem::getMimeType( $filename ); +$ftype=\OC\Files\Filesystem::getMimeType( $filename ); header('Content-Type:'.$ftype); if ( preg_match( "/MSIE/", $_SERVER["HTTP_USER_AGENT"] ) ) { @@ -47,7 +44,7 @@ if ( preg_match( "/MSIE/", $_SERVER["HTTP_USER_AGENT"] ) ) { . '; filename="' . rawurlencode( basename($filename) ) . '"' ); } OCP\Response::disableCaching(); -header('Content-Length: '.OC_Filesystem::filesize($filename)); +header('Content-Length: '.\OC\Files\Filesystem::filesize($filename)); OC_Util::obEnd(); -OC_Filesystem::readfile( $filename ); +\OC\Files\Filesystem::readfile( $filename ); diff --git a/apps/files/index.php b/apps/files/index.php index 08193eaee7..104cf1a55d 100644 --- a/apps/files/index.php +++ b/apps/files/index.php @@ -28,22 +28,40 @@ OCP\User::checkLoggedIn(); OCP\Util::addStyle('files', 'files'); OCP\Util::addscript('files', 'jquery.iframe-transport'); OCP\Util::addscript('files', 'jquery.fileupload'); -OCP\Util::addscript('files', 'files'); +OCP\Util::addscript('files', 'jquery-visibility'); OCP\Util::addscript('files', 'filelist'); -OCP\Util::addscript('files', 'fileactions'); -OCP\Util::addscript('files', 'keyboardshortcuts'); OCP\App::setActiveNavigationEntry('files_index'); // Load the files $dir = isset($_GET['dir']) ? stripslashes($_GET['dir']) : ''; // Redirect if directory does not exist -if (!OC_Filesystem::is_dir($dir . '/')) { - header('Location: ' . $_SERVER['SCRIPT_NAME'] . ''); +if (!\OC\Files\Filesystem::is_dir($dir . '/')) { + header('Location: ' . OCP\Util::getScriptName() . ''); exit(); } +function fileCmp($a, $b) { + if ($a['type'] == 'dir' and $b['type'] != 'dir') { + return -1; + } elseif ($a['type'] != 'dir' and $b['type'] == 'dir') { + return 1; + } else { + return strnatcasecmp($a['name'], $b['name']); + } +} + $files = array(); -foreach (OC_Files::getdirectorycontent($dir) as $i) { +$user = OC_User::getUser(); +if (\OC\Files\Cache\Upgrade::needUpgrade($user)) { //dont load anything if we need to upgrade the cache + $content = array(); + $needUpgrade = true; + $freeSpace = 0; +} else { + $content = \OC\Files\Filesystem::getDirectoryContent($dir); + $freeSpace = \OC\Files\Filesystem::free_space($dir); + $needUpgrade = false; +} +foreach ($content as $i) { $i['date'] = OCP\Util::formatDate($i['mtime']); if ($i['type'] == 'file') { $fileinfo = pathinfo($i['name']); @@ -54,12 +72,12 @@ foreach (OC_Files::getdirectorycontent($dir) as $i) { $i['extension'] = ''; } } - if ($i['directory'] == '/') { - $i['directory'] = ''; - } + $i['directory'] = $dir; $files[] = $i; } +usort($files, "fileCmp"); + // Make breadcrumb $breadcrumb = array(); $pathtohere = ''; @@ -75,37 +93,48 @@ $list = new OCP\Template('files', 'part.list', ''); $list->assign('files', $files, false); $list->assign('baseURL', OCP\Util::linkTo('files', 'index.php') . '?dir=', false); $list->assign('downloadURL', OCP\Util::linkTo('files', 'download.php') . '?file=', false); +$list->assign('disableSharing', false); $breadcrumbNav = new OCP\Template('files', 'part.breadcrumb', ''); $breadcrumbNav->assign('breadcrumb', $breadcrumb, false); $breadcrumbNav->assign('baseURL', OCP\Util::linkTo('files', 'index.php') . '?dir=', false); -$upload_max_filesize = OCP\Util::computerFileSize(ini_get('upload_max_filesize')); -$post_max_size = OCP\Util::computerFileSize(ini_get('post_max_size')); -$maxUploadFilesize = min($upload_max_filesize, $post_max_size); - -$freeSpace = OC_Filesystem::free_space($dir); -$freeSpace = max($freeSpace, 0); -$maxUploadFilesize = min($maxUploadFilesize, $freeSpace); - $permissions = OCP\PERMISSION_READ; -if (OC_Filesystem::isUpdatable($dir . '/')) { +if (\OC\Files\Filesystem::isCreatable($dir . '/')) { + $permissions |= OCP\PERMISSION_CREATE; +} +if (\OC\Files\Filesystem::isUpdatable($dir . '/')) { $permissions |= OCP\PERMISSION_UPDATE; } -if (OC_Filesystem::isDeletable($dir . '/')) { +if (\OC\Files\Filesystem::isDeletable($dir . '/')) { $permissions |= OCP\PERMISSION_DELETE; } -if (OC_Filesystem::isSharable($dir . '/')) { +if (\OC\Files\Filesystem::isSharable($dir . '/')) { $permissions |= OCP\PERMISSION_SHARE; } -$tmpl = new OCP\Template('files', 'index', 'user'); -$tmpl->assign('fileList', $list->fetchPage(), false); -$tmpl->assign('breadcrumb', $breadcrumbNav->fetchPage(), false); -$tmpl->assign('dir', OC_Filesystem::normalizePath($dir)); -$tmpl->assign('isCreatable', OC_Filesystem::isCreatable($dir . '/')); -$tmpl->assign('permissions', $permissions); -$tmpl->assign('files', $files); -$tmpl->assign('uploadMaxFilesize', $maxUploadFilesize); -$tmpl->assign('uploadMaxHumanFilesize', OCP\Util::humanFileSize($maxUploadFilesize)); -$tmpl->assign('allowZipDownload', intval(OCP\Config::getSystemValue('allowZipDownload', true))); -$tmpl->printPage(); +if ($needUpgrade) { + OCP\Util::addscript('files', 'upgrade'); + $tmpl = new OCP\Template('files', 'upgrade', 'user'); + $tmpl->printPage(); +} else { + // information about storage capacities + $storageInfo=OC_Helper::getStorageInfo(); + $maxUploadFilesize=OCP\Util::maxUploadFilesize($dir); + + OCP\Util::addscript('files', 'fileactions'); + OCP\Util::addscript('files', 'files'); + OCP\Util::addscript('files', 'keyboardshortcuts'); + $tmpl = new OCP\Template('files', 'index', 'user'); + $tmpl->assign('fileList', $list->fetchPage(), false); + $tmpl->assign('breadcrumb', $breadcrumbNav->fetchPage(), false); + $tmpl->assign('dir', \OC\Files\Filesystem::normalizePath($dir)); + $tmpl->assign('isCreatable', \OC\Files\Filesystem::isCreatable($dir . '/')); + $tmpl->assign('permissions', $permissions); + $tmpl->assign('files', $files); + $tmpl->assign('trash', \OCP\App::isEnabled('files_trashbin')); + $tmpl->assign('uploadMaxFilesize', $maxUploadFilesize); + $tmpl->assign('uploadMaxHumanFilesize', OCP\Util::humanFileSize($maxUploadFilesize)); + $tmpl->assign('allowZipDownload', intval(OCP\Config::getSystemValue('allowZipDownload', true))); + $tmpl->assign('usedSpacePercent', (int)$storageInfo['relative']); + $tmpl->printPage(); +} \ No newline at end of file diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js index f5ee363a4c..c30f1bcddd 100644 --- a/apps/files/js/fileactions.js +++ b/apps/files/js/fileactions.js @@ -147,15 +147,19 @@ $(document).ready(function () { } else { var downloadScope = 'file'; } - FileActions.register(downloadScope, 'Download', OC.PERMISSION_READ, function () { - return OC.imagePath('core', 'actions/download'); - }, function (filename) { - window.location = OC.filePath('files', 'ajax', 'download.php') + '?files=' + encodeURIComponent(filename) + '&dir=' + encodeURIComponent($('#dir').val()); - }); - + + if (typeof disableDownloadActions == 'undefined' || !disableDownloadActions) { + FileActions.register(downloadScope, 'Download', OC.PERMISSION_READ, function () { + return OC.imagePath('core', 'actions/download'); + }, function (filename) { + window.location = OC.filePath('files', 'ajax', 'download.php') + '?files=' + encodeURIComponent(filename) + '&dir=' + encodeURIComponent($('#dir').val()); + }); + } + $('#fileList tr').each(function(){ FileActions.display($(this).children('td.filename')); }); + }); FileActions.register('all', 'Delete', OC.PERMISSION_DELETE, function () { @@ -185,6 +189,7 @@ FileActions.register('all', 'Rename', OC.PERMISSION_UPDATE, function () { FileList.rename(filename); }); + FileActions.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename) { window.location = OC.linkTo('files', 'index.php') + '?dir=' + encodeURIComponent($('#dir').val()).replace(/%2F/g, '/') + '/' + encodeURIComponent(filename); }); diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 66697bbbf5..72b353b48c 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -201,15 +201,14 @@ var FileList={ }, checkName:function(oldName, newName, isNewFile) { if (isNewFile || $('tr').filterAttr('data-file', newName).length > 0) { - if (isNewFile) { - $('#notification').html(t('files', '{new_name} already exists', {new_name: escapeHTML(newName)})+''+t('files', 'replace')+''+t('files', 'suggest name')+''+t('files', 'cancel')+''); - } else { - $('#notification').html(t('files', '{new_name} already exists', {new_name: escapeHTML(newName)})+''+t('files', 'replace')+''+t('files', 'cancel')+''); - } $('#notification').data('oldName', oldName); $('#notification').data('newName', newName); $('#notification').data('isNewFile', isNewFile); - $('#notification').fadeIn(); + if (isNewFile) { + OC.Notification.showHtml(t('files', '{new_name} already exists', {new_name: escapeHTML(newName)})+''+t('files', 'replace')+''+t('files', 'suggest name')+''+t('files', 'cancel')+''); + } else { + OC.Notification.showHtml(t('files', '{new_name} already exists', {new_name: escapeHTML(newName)})+''+t('files', 'replace')+''+t('files', 'cancel')+''); + } return true; } else { return false; @@ -251,11 +250,10 @@ var FileList={ FileList.finishReplace(); }; if (isNewFile) { - $('#notification').html(t('files', 'replaced {new_name}', {new_name: newName})+''+t('files', 'undo')+''); + OC.Notification.showHtml(t('files', 'replaced {new_name}', {new_name: newName})+''+t('files', 'undo')+''); } else { - $('#notification').html(t('files', 'replaced {new_name} with {old_name}', {new_name: newName}, {old_name: oldName})+''+t('files', 'undo')+''); + OC.Notification.showHtml(t('files', 'replaced {new_name} with {old_name}', {new_name: newName}, {old_name: oldName})+''+t('files', 'undo')+''); } - $('#notification').fadeIn(); }, finishReplace:function() { if (!FileList.replaceCanceled && FileList.replaceOldName && FileList.replaceNewName) { @@ -273,72 +271,45 @@ var FileList={ } }, do_delete:function(files){ + if(files.substr){ + files=[files]; + } + for (var i in files) { + var deleteAction = $('tr').filterAttr('data-file',files[i]).children("td.date").children(".action.delete"); + var oldHTML = deleteAction[0].outerHTML; + var newHTML = ''; + deleteAction[0].outerHTML = newHTML; + } // Finish any existing actions if (FileList.lastAction) { FileList.lastAction(); } - FileList.prepareDeletion(files); - - if (!FileList.useUndo) { - FileList.lastAction(); - } else { - // NOTE: Temporary fix to change the text to unshared for files in root of Shared folder - if ($('#dir').val() == '/Shared') { - $('#notification').html(t('files', 'unshared {files}', {'files': escapeHTML(files)})+''+t('files', 'undo')+''); - } else { - $('#notification').html(t('files', 'deleted {files}', {'files': escapeHTML(files)})+''+t('files', 'undo')+''); - } - $('#notification').fadeIn(); - } - }, - finishDelete:function(ready,sync){ - if(!FileList.deleteCanceled && FileList.deleteFiles){ - var fileNames=JSON.stringify(FileList.deleteFiles); - $.ajax({ - url: OC.filePath('files', 'ajax', 'delete.php'), - async:!sync, - type:'post', - data: {dir:$('#dir').val(),files:fileNames}, - complete: function(data){ - boolOperationFinished(data, function(){ - $('#notification').fadeOut('400'); - $.each(FileList.deleteFiles,function(index,file){ - FileList.remove(file); + var fileNames = JSON.stringify(files); + $.post(OC.filePath('files', 'ajax', 'delete.php'), + {dir:$('#dir').val(),files:fileNames}, + function(result){ + if (result.status == 'success') { + $.each(files,function(index,file){ + var files = $('tr').filterAttr('data-file',file); + files.hide(); + files.find('input[type="checkbox"]').removeAttr('checked'); + files.removeClass('selected'); }); - FileList.deleteCanceled=true; - FileList.deleteFiles=null; - FileList.lastAction = null; - if(ready){ - ready(); - } - }); - } - }); - } - }, - prepareDeletion:function(files){ - if(files.substr){ - files=[files]; - } - $.each(files,function(index,file){ - var files = $('tr').filterAttr('data-file',file); - files.hide(); - files.find('input[type="checkbox"]').removeAttr('checked'); - files.removeClass('selected'); - }); - procesSelection(); - FileList.deleteCanceled=false; - FileList.deleteFiles=files; - FileList.lastAction = function() { - FileList.finishDelete(null, true); - }; + procesSelection(); + } else { + $.each(files,function(index,file) { + var deleteAction = $('tr').filterAttr('data-file',file).children("td.date").children(".move2trash"); + deleteAction[0].outerHTML = oldHTML; + }); + } + }); } }; $(document).ready(function(){ $('#notification').hide(); - $('#notification .undo').live('click', function(){ + $('#notification').on('click', '.undo', function(){ if (FileList.deleteFiles) { $.each(FileList.deleteFiles,function(index,file){ $('tr').filterAttr('data-file',file).show(); @@ -362,18 +333,18 @@ $(document).ready(function(){ FileList.replaceIsNewFile = null; } FileList.lastAction = null; - $('#notification').fadeOut('400'); + OC.Notification.hide(); }); - $('#notification .replace').live('click', function() { - $('#notification').fadeOut('400', function() { - FileList.replace($('#notification').data('oldName'), $('#notification').data('newName'), $('#notification').data('isNewFile')); - }); + $('#notification').on('click', '.replace', function() { + OC.Notification.hide(function() { + FileList.replace($('#notification').data('oldName'), $('#notification').data('newName'), $('#notification').data('isNewFile')); + }); }); - $('#notification .suggest').live('click', function() { + $('#notification').on('click', '.suggest', function() { $('tr').filterAttr('data-file', $('#notification').data('oldName')).show(); - $('#notification').fadeOut('400'); + OC.Notification.hide(); }); - $('#notification .cancel').live('click', function() { + $('#notification').on('click', '.cancel', function() { if ($('#notification').data('isNewFile')) { FileList.deleteCanceled = false; FileList.deleteFiles = [$('#notification').data('oldName')]; diff --git a/apps/files/js/files.js b/apps/files/js/files.js index 3a4af6416e..7c377afc62 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -26,15 +26,34 @@ Files={ }); procesSelection(); }, + updateMaxUploadFilesize:function(response) { + if(response == undefined) { + return; + } + if(response.data !== undefined && response.data.uploadMaxFilesize !== undefined) { + $('#max_upload').val(response.data.uploadMaxFilesize); + $('#upload.button').attr('original-title', response.data.maxHumanFilesize); + $('#usedSpacePercent').val(response.data.usedSpacePercent); + Files.displayStorageWarnings(); + } + if(response[0] == undefined) { + return; + } + if(response[0].uploadMaxFilesize !== undefined) { + $('#max_upload').val(response[0].uploadMaxFilesize); + $('#upload.button').attr('original-title', response[0].maxHumanFilesize); + $('#usedSpacePercent').val(response[0].usedSpacePercent); + Files.displayStorageWarnings(); + } + + }, isFileNameValid:function (name) { if (name === '.') { - $('#notification').text(t('files', '\'.\' is an invalid file name.')); - $('#notification').fadeIn(); + OC.Notification.show(t('files', '\'.\' is an invalid file name.')); return false; } if (name.length == 0) { - $('#notification').text(t('files', 'File name cannot be empty.')); - $('#notification').fadeIn(); + OC.Notification.show(t('files', 'File name cannot be empty.')); return false; } @@ -42,13 +61,26 @@ Files={ var invalid_characters = ['\\', '/', '<', '>', ':', '"', '|', '?', '*']; for (var i = 0; i < invalid_characters.length; i++) { if (name.indexOf(invalid_characters[i]) != -1) { - $('#notification').text(t('files', "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed.")); - $('#notification').fadeIn(); + OC.Notification.show(t('files', "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed.")); return false; } } - $('#notification').fadeOut(); + OC.Notification.hide(); return true; + }, + displayStorageWarnings: function() { + if (!OC.Notification.isHidden()) { + return; + } + + var usedSpacePercent = $('#usedSpacePercent').val(); + if (usedSpacePercent > 98) { + OC.Notification.show(t('files', 'Your storage is full, files can not be updated or synced anymore!')); + return; + } + if (usedSpacePercent > 90) { + OC.Notification.show(t('files', 'Your storage is almost full ({usedSpacePercent}%)', {usedSpacePercent: usedSpacePercent})); + } } }; $(document).ready(function() { @@ -78,15 +110,20 @@ $(document).ready(function() { } // Triggers invisible file input - $('#upload a').live('click', function() { + $('#upload a').on('click', function() { $(this).parent().children('#file_upload_start').trigger('click'); return false; }); + + // Show trash bin + $('#trash a').live('click', function() { + window.location=OC.filePath('files_trashbin', '', 'index.php'); + }); var lastChecked; // Sets the file link behaviour : - $('td.filename a').live('click',function(event) { + $('#fileList').on('click','td.filename a',function(event) { if (event.ctrlKey || event.shiftKey) { event.preventDefault(); if (event.shiftKey) { @@ -152,7 +189,7 @@ $(document).ready(function() { procesSelection(); }); - $('td.filename input:checkbox').live('change',function(event) { + $('#fileList').on('change', 'td.filename input:checkbox',function(event) { if (event.shiftKey) { var last = $(lastChecked).parent().parent().prevAll().length; var first = $(this).parent().parent().prevAll().length; @@ -184,8 +221,7 @@ $(document).ready(function() { $('.download').click('click',function(event) { var files=getSelectedFiles('name').join(';'); var dir=$('#dir').val()||'/'; - $('#notification').text(t('files','generating ZIP-file, it may take some time.')); - $('#notification').fadeIn(); + OC.Notification.show(t('files','Your download is being prepared. This might take some time if the files are big.')); // use special download URL if provided, e.g. for public shared files if ( (downloadURL = document.getElementById("downloadURL")) ) { window.location=downloadURL.value+"&download&files="+files; @@ -314,9 +350,9 @@ $(document).ready(function() { var response; response=jQuery.parseJSON(result); if(response[0] == undefined || response[0].status != 'success') { - $('#notification').text(t('files', response.data.message)); - $('#notification').fadeIn(); + OC.Notification.show(t('files', response.data.message)); } + Files.updateMaxUploadFilesize(response); var file=response[0]; // TODO: this doesn't work if the file name has been changed server side delete uploadingFiles[dirName][file.name]; @@ -354,9 +390,7 @@ $(document).ready(function() { uploadtext.text(t('files', '{count} files uploading', {count: currentUploads})); } delete uploadingFiles[dirName][fileName]; - $('#notification').hide(); - $('#notification').text(t('files', 'Upload cancelled.')); - $('#notification').fadeIn(); + OC.Notification.show(t('files', 'Upload cancelled.')); } }); //TODO test with filenames containing slashes @@ -369,6 +403,8 @@ $(document).ready(function() { .success(function(result, textStatus, jqXHR) { var response; response=jQuery.parseJSON(result); + Files.updateMaxUploadFilesize(response); + if(response[0] != undefined && response[0].status == 'success') { var file=response[0]; delete uploadingFiles[file.name]; @@ -381,20 +417,17 @@ $(document).ready(function() { FileList.loadingDone(file.name, file.id); } else { Files.cancelUpload(this.files[0].name); - $('#notification').text(t('files', response.data.message)); - $('#notification').fadeIn(); + OC.Notification.show(t('files', response.data.message)); $('#fileList > tr').not('[data-mime]').fadeOut(); $('#fileList > tr').not('[data-mime]').remove(); } - }) - .error(function(jqXHR, textStatus, errorThrown) { - if(errorThrown === 'abort') { - Files.cancelUpload(this.files[0].name); - $('#notification').hide(); - $('#notification').text(t('files', 'Upload cancelled.')); - $('#notification').fadeIn(); - } - }); + }) + .error(function(jqXHR, textStatus, errorThrown) { + if(errorThrown === 'abort') { + Files.cancelUpload(this.files[0].name); + OC.Notification.show(t('files', 'Upload cancelled.')); + } + }); uploadingFiles[uniqueName] = jqXHR; } } @@ -402,6 +435,7 @@ $(document).ready(function() { data.submit().success(function(data, status) { // in safari data is a string response = jQuery.parseJSON(typeof data === 'string' ? data : data[0].body.innerText); + Files.updateMaxUploadFilesize(response); if(response[0] != undefined && response[0].status == 'success') { var file=response[0]; delete uploadingFiles[file.name]; @@ -414,8 +448,7 @@ $(document).ready(function() { FileList.loadingDone(file.name, file.id); } else { //TODO Files.cancelUpload(/*where do we get the filename*/); - $('#notification').text(t('files', response.data.message)); - $('#notification').fadeIn(); + OC.Notification.show(t('files', response.data.message)); $('#fileList > tr').not('[data-mime]').fadeOut(); $('#fileList > tr').not('[data-mime]').remove(); } @@ -434,6 +467,10 @@ $(document).ready(function() { $('#uploadprogressbar').progressbar('value',progress); }, start: function(e, data) { + //IE < 10 does not fire the necessary events for the progress bar. + if($.browser.msie && parseInt($.browser.version) < 10) { + return; + } $('#uploadprogressbar').progressbar({value:0}); $('#uploadprogressbar').fadeIn(); if(data.dataType != 'iframe ') { @@ -535,14 +572,12 @@ $(document).ready(function() { event.preventDefault(); var newname=input.val(); if(type == 'web' && newname.length == 0) { - $('#notification').text(t('files', 'URL cannot be empty.')); - $('#notification').fadeIn(); + OC.Notification.show(t('files', 'URL cannot be empty.')); return false; } else if (type != 'web' && !Files.isFileNameValid(newname)) { return false; } else if( type == 'folder' && $('#dir').val() == '/' && newname == 'Shared') { - $('#notification').text(t('files','Invalid folder name. Usage of \'Shared\' is reserved by Owncloud')); - $('#notification').fadeIn(); + OC.Notification.show(t('files','Invalid folder name. Usage of \'Shared\' is reserved by Owncloud')); return false; } if (FileList.lastAction) { @@ -640,12 +675,8 @@ $(document).ready(function() { }); }); - //check if we need to scan the filesystem - $.get(OC.filePath('files','ajax','scan.php'),{checkonly:'true'}, function(response) { - if(response.data.done){ - scanFiles(); - } - }, "json"); + //do a background scan if needed + scanFiles(); var lastWidth = 0; var breadcrumbs = []; @@ -712,35 +743,66 @@ $(document).ready(function() { }); resizeBreadcrumbs(true); + + // display storage warnings + setTimeout ( "Files.displayStorageWarnings()", 100 ); + OC.Notification.setDefault(Files.displayStorageWarnings); + + // file space size sync + function update_storage_statistics() { + $.getJSON(OC.filePath('files','ajax','getstoragestats.php'),function(response) { + Files.updateMaxUploadFilesize(response); + }); + } + + // start on load - we ask the server every 5 minutes + var update_storage_statistics_interval = 5*60*1000; + var update_storage_statistics_interval_id = setInterval(update_storage_statistics, update_storage_statistics_interval); + + // Use jquery-visibility to de-/re-activate file stats sync + if ($.support.pageVisibility) { + $(document).on({ + 'show.visibility': function() { + if (!update_storage_statistics_interval_id) { + update_storage_statistics_interval_id = setInterval(update_storage_statistics, update_storage_statistics_interval); + } + }, + 'hide.visibility': function() { + clearInterval(update_storage_statistics_interval_id); + update_storage_statistics_interval_id = 0; + } + }); + } }); -function scanFiles(force,dir){ - if(!dir){ - dir=''; +function scanFiles(force, dir){ + if (!OC.currentUser) { + return; } - force=!!force; //cast to bool - scanFiles.scanning=true; - $('#scanning-message').show(); - $('#fileList').remove(); - var scannerEventSource=new OC.EventSource(OC.filePath('files','ajax','scan.php'),{force:force,dir:dir}); - scanFiles.cancel=scannerEventSource.close.bind(scannerEventSource); - scannerEventSource.listen('scanning',function(data){ - $('#scan-count').text(t('files', '{count} files scanned', {count: data.count})); - $('#scan-current').text(data.file+'/'); + + if(!dir){ + dir = ''; + } + force = !!force; //cast to bool + scanFiles.scanning = true; + var scannerEventSource = new OC.EventSource(OC.filePath('files','ajax','scan.php'),{force:force,dir:dir}); + scanFiles.cancel = scannerEventSource.close.bind(scannerEventSource); + scannerEventSource.listen('count',function(count){ + console.log(count + 'files scanned') }); - scannerEventSource.listen('success',function(success){ + scannerEventSource.listen('folder',function(path){ + console.log('now scanning ' + path) + }); + scannerEventSource.listen('done',function(count){ scanFiles.scanning=false; - if(success){ - window.location.reload(); - }else{ - alert(t('files', 'error while scanning')); - } + console.log('done after ' + count + 'files'); }); } scanFiles.scanning=false; function boolOperationFinished(data, callback) { result = jQuery.parseJSON(data.responseText); + Files.updateMaxUploadFilesize(result); if(result.status == 'success'){ callback.call(); } else { @@ -752,32 +814,101 @@ function updateBreadcrumb(breadcrumbHtml) { $('p.nav').empty().html(breadcrumbHtml); } -//options for file drag/dropp +var createDragShadow = function(event){ + //select dragged file + var isDragSelected = $(event.target).parents('tr').find('td input:first').prop('checked'); + if (!isDragSelected) { + //select dragged file + $(event.target).parents('tr').find('td input:first').prop('checked',true); + } + + var selectedFiles = getSelectedFiles(); + + if (!isDragSelected && selectedFiles.length == 1) { + //revert the selection + $(event.target).parents('tr').find('td input:first').prop('checked',false); + } + + //also update class when we dragged more than one file + if (selectedFiles.length > 1) { + $(event.target).parents('tr').addClass('selected'); + } + + // build dragshadow + var dragshadow = $('
'); + var tbody = $(''); + dragshadow.append(tbody); + + var dir=$('#dir').val(); + + $(selectedFiles).each(function(i,elem){ + var newtr = $('' + +''+elem.name+''+humanFileSize(elem.size)+'' + +''); + tbody.append(newtr); + if (elem.type === 'dir') { + newtr.find('td.filename').attr('style','background-image:url('+OC.imagePath('core', 'filetypes/folder.png')+')'); + } else { + getMimeIcon(elem.mime,function(path){ + newtr.find('td.filename').attr('style','background-image:url('+path+')'); + }); + } + }); + + return dragshadow; +} + +//options for file drag/drop var dragOptions={ - distance: 20, revert: 'invalid', opacity: 0.7, helper: 'clone', + revert: 'invalid', revertDuration: 300, + opacity: 0.7, zIndex: 100, appendTo: 'body', cursorAt: { left: -5, top: -5 }, + helper: createDragShadow, cursor: 'move', stop: function(event, ui) { $('#fileList tr td.filename').addClass('ui-draggable'); } -}; +} + var folderDropOptions={ drop: function( event, ui ) { - var file=ui.draggable.parent().data('file'); - var target=$(this).find('.nametext').text().trim(); - var dir=$('#dir').val(); - $.ajax({ - url: OC.filePath('files', 'ajax', 'move.php'), - data: "dir="+encodeURIComponent(dir)+"&file="+encodeURIComponent(file)+'&target='+encodeURIComponent(dir)+'/'+encodeURIComponent(target), - complete: function(data){boolOperationFinished(data, function(){ - var el = $('#fileList tr').filterAttr('data-file',file).find('td.filename'); - el.draggable('destroy'); - FileList.remove(file); - });} + //don't allow moving a file into a selected folder + if ($(event.target).parents('tr').find('td input:first').prop('checked') === true) { + return false; + } + + var target=$.trim($(this).find('.nametext').text()); + + var files = ui.helper.find('tr'); + $(files).each(function(i,row){ + var dir = $(row).data('dir'); + var file = $(row).data('filename'); + $.post(OC.filePath('files', 'ajax', 'move.php'), { dir: dir, file: file, target: dir+'/'+target }, function(result) { + if (result) { + if (result.status === 'success') { + //recalculate folder size + var oldSize = $('#fileList tr').filterAttr('data-file',target).data('size'); + var newSize = oldSize + $('#fileList tr').filterAttr('data-file',file).data('size'); + $('#fileList tr').filterAttr('data-file',target).data('size', newSize); + $('#fileList tr').filterAttr('data-file',target).find('td.filesize').text(humanFileSize(newSize)); + + FileList.remove(file); + procesSelection(); + $('#notification').hide(); + } else { + $('#notification').hide(); + $('#notification').text(result.data.message); + $('#notification').fadeIn(); + } + } else { + OC.dialogs.alert(t('Error moving file')); + } + }); }); - } + }, + tolerance: 'pointer' } + var crumbDropOptions={ drop: function( event, ui ) { - var file=ui.draggable.parent().data('file'); var target=$(this).data('dir'); var dir=$('#dir').val(); while(dir.substr(0,1)=='/'){//remove extra leading /'s @@ -790,12 +921,25 @@ var crumbDropOptions={ if(target==dir || target+'/'==dir){ return; } - $.ajax({ - url: OC.filePath('files', 'ajax', 'move.php'), - data: "dir="+encodeURIComponent(dir)+"&file="+encodeURIComponent(file)+'&target='+encodeURIComponent(target), - complete: function(data){boolOperationFinished(data, function(){ - FileList.remove(file); - });} + var files = ui.helper.find('tr'); + $(files).each(function(i,row){ + var dir = $(row).data('dir'); + var file = $(row).data('filename'); + $.post(OC.filePath('files', 'ajax', 'move.php'), { dir: dir, file: file, target: target }, function(result) { + if (result) { + if (result.status === 'success') { + FileList.remove(file); + procesSelection(); + $('#notification').hide(); + } else { + $('#notification').hide(); + $('#notification').text(result.data.message); + $('#notification').fadeIn(); + } + } else { + OC.dialogs.alert(t('Error moving file')); + } + }); }); }, tolerance: 'pointer' @@ -902,7 +1046,7 @@ function getUniqueName(name){ num=parseInt(numMatch[numMatch.length-1])+1; base=base.split('(') base.pop(); - base=base.join('(').trim(); + base=$.trim(base.join('(')); } name=base+' ('+num+')'; if (extension) { diff --git a/apps/files/js/jquery-visibility.js b/apps/files/js/jquery-visibility.js new file mode 100644 index 0000000000..a824bf6873 --- /dev/null +++ b/apps/files/js/jquery-visibility.js @@ -0,0 +1,32 @@ +/*! http://mths.be/visibility v1.0.5 by @mathias */ +(function (window, document, $, undefined) { + + var prefix, + property, +// In Opera, `'onfocusin' in document == true`, hence the extra `hasFocus` check to detect IE-like behavior + eventName = 'onfocusin' in document && 'hasFocus' in document ? 'focusin focusout' : 'focus blur', + prefixes = ['', 'moz', 'ms', 'o', 'webkit'], + $support = $.support, + $event = $.event; + + while ((property = prefix = prefixes.pop()) != undefined) { + property = (prefix ? prefix + 'H' : 'h') + 'idden'; + if ($support.pageVisibility = typeof document[property] == 'boolean') { + eventName = prefix + 'visibilitychange'; + break; + } + } + + $(/blur$/.test(eventName) ? window : document).on(eventName, function (event) { + var type = event.type, + originalEvent = event.originalEvent, + toElement = originalEvent.toElement; +// If it’s a `{focusin,focusout}` event (IE), `fromElement` and `toElement` should both be `null` or `undefined`; +// else, the page visibility hasn’t changed, but the user just clicked somewhere in the doc. +// In IE9, we need to check the `relatedTarget` property instead. + if (!/^focus./.test(type) || (toElement == undefined && originalEvent.fromElement == undefined && originalEvent.relatedTarget == undefined)) { + $event.trigger((property && document[property] || /^(?:blur|focusout)$/.test(type) ? 'hide' : 'show') + '.visibility'); + } + }); + +}(this, document, jQuery)); diff --git a/apps/files/js/upgrade.js b/apps/files/js/upgrade.js new file mode 100644 index 0000000000..02d57fc9e6 --- /dev/null +++ b/apps/files/js/upgrade.js @@ -0,0 +1,17 @@ +$(document).ready(function () { + var eventSource, total, bar = $('#progressbar'); + console.log('start'); + bar.progressbar({value: 0}); + eventSource = new OC.EventSource(OC.filePath('files', 'ajax', 'upgrade.php')); + eventSource.listen('total', function (count) { + total = count; + console.log(count + ' files needed to be migrated'); + }); + eventSource.listen('count', function (count) { + bar.progressbar({value: (count / total) * 100}); + console.log(count); + }); + eventSource.listen('done', function () { + document.location.reload(); + }); +}); diff --git a/apps/files/js/upload.js b/apps/files/js/upload.js new file mode 100644 index 0000000000..9d9f61f600 --- /dev/null +++ b/apps/files/js/upload.js @@ -0,0 +1,8 @@ +function Upload(fileSelector) { + if ($.support.xhrFileUpload) { + return new XHRUpload(fileSelector.target.files); + } else { + return new FormUpload(fileSelector); + } +} +Upload.target = OC.filePath('files', 'ajax', 'upload.php'); diff --git a/apps/files/l10n/ar.php b/apps/files/l10n/ar.php index 5740d54f8b..b741815be4 100644 --- a/apps/files/l10n/ar.php +++ b/apps/files/l10n/ar.php @@ -11,12 +11,12 @@ "Name" => "الاسم", "Size" => "حجم", "Modified" => "معدل", +"Upload" => "إرفع", "Maximum upload size" => "الحد الأقصى لحجم الملفات التي يمكن رفعها", "Save" => "حفظ", "New" => "جديد", "Text file" => "ملف", "Folder" => "مجلد", -"Upload" => "إرفع", "Nothing in here. Upload something!" => "لا يوجد شيء هنا. إرفع بعض الملفات!", "Download" => "تحميل", "Upload too large" => "حجم الترفيع أعلى من المسموح", diff --git a/apps/files/l10n/bg_BG.php b/apps/files/l10n/bg_BG.php index bc10979611..ae49f51699 100644 --- a/apps/files/l10n/bg_BG.php +++ b/apps/files/l10n/bg_BG.php @@ -10,12 +10,12 @@ "Name" => "Име", "Size" => "Размер", "Modified" => "Променено", +"Upload" => "Качване", "Maximum upload size" => "Максимален размер за качване", "0 is unlimited" => "Ползвайте 0 за без ограничения", "Save" => "Запис", "New" => "Ново", "Folder" => "Папка", -"Upload" => "Качване", "Nothing in here. Upload something!" => "Няма нищо тук. Качете нещо.", "Download" => "Изтегляне", "Upload too large" => "Файлът който сте избрали за качване е прекалено голям" diff --git a/apps/files/l10n/bn_BD.php b/apps/files/l10n/bn_BD.php index e55c881139..3d676810c7 100644 --- a/apps/files/l10n/bn_BD.php +++ b/apps/files/l10n/bn_BD.php @@ -1,7 +1,4 @@ "%s কে স্থানান্তর করা সম্ভব হলো না - এই নামের ফাইল বিদ্যমান", -"Could not move %s" => "%s কে স্থানান্তর করা সম্ভব হলো না", -"Unable to rename file" => "ফাইলের নাম পরিবর্তন করা সম্ভব হলো না", "No file was uploaded. Unknown error" => "কোন ফাইল আপলোড করা হয় নি। সমস্যা অজ্ঞাত।", "There is no error, the file uploaded with success" => "কোন সমস্যা নেই, ফাইল আপলোড সুসম্পন্ন হয়েছে", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "আপলোড করা ফাইলটি php.ini তে বর্ণিত upload_max_filesize নির্দেশিত আয়তন অতিক্রম করছেঃ", @@ -23,12 +20,9 @@ "replaced {new_name}" => "{new_name} প্রতিস্থাপন করা হয়েছে", "undo" => "ক্রিয়া প্রত্যাহার", "replaced {new_name} with {old_name}" => "{new_name} কে {old_name} নামে প্রতিস্থাপন করা হয়েছে", -"unshared {files}" => "{files} ভাগাভাগি বাতিল কর", -"deleted {files}" => "{files} মুছে ফেলা হয়েছে", "'.' is an invalid file name." => "টি একটি অননুমোদিত নাম।", "File name cannot be empty." => "ফাইলের নামটি ফাঁকা রাখা যাবে না।", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "নামটি সঠিক নয়, '\\', '/', '<', '>', ':', '\"', '|', '?' এবং '*' অনুমোদিত নয়।", -"generating ZIP-file, it may take some time." => "ZIP- ফাইল তৈরী করা হচ্ছে, এজন্য কিছু সময় আবশ্যক।", "Unable to upload your file as it is a directory or has 0 bytes" => "আপনার ফাইলটি আপলোড করা সম্ভব হলো না, কেননা এটি হয় একটি ফোল্ডার কিংবা এর আকার ০ বাইট", "Upload Error" => "আপলোড করতে সমস্যা ", "Close" => "বন্ধ", @@ -39,8 +33,6 @@ "File upload is in progress. Leaving the page now will cancel the upload." => "ফাইল আপলোড চলমান। এই পৃষ্ঠা পরিত্যাগ করলে আপলোড বাতিল করা হবে।", "URL cannot be empty." => "URL ফাঁকা রাখা যাবে না।", "Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "ফোল্ডারের নামটি সঠিক নয়। 'ভাগাভাগি করা' শুধুমাত্র Owncloud এর জন্য সংরক্ষিত।", -"{count} files scanned" => "{count} টি ফাইল স্ক্যান করা হয়েছে", -"error while scanning" => "স্ক্যান করার সময় সমস্যা দেখা দিয়েছে", "Name" => "নাম", "Size" => "আকার", "Modified" => "পরিবর্তিত", @@ -48,6 +40,7 @@ "{count} folders" => "{count} টি ফোল্ডার", "1 file" => "১টি ফাইল", "{count} files" => "{count} টি ফাইল", +"Upload" => "আপলোড", "File handling" => "ফাইল হ্যার্ডলিং", "Maximum upload size" => "আপলোডের সর্বোচ্চ আকার", "max. possible: " => "অনুমোদিত সর্বোচ্চ আকার", @@ -60,7 +53,6 @@ "Text file" => "টেক্সট ফাইল", "Folder" => "ফোল্ডার", "From link" => " লিংক থেকে", -"Upload" => "আপলোড", "Cancel upload" => "আপলোড বাতিল কর", "Nothing in here. Upload something!" => "এখানে কিছুই নেই। কিছু আপলোড করুন !", "Download" => "ডাউনলোড", diff --git a/apps/files/l10n/ca.php b/apps/files/l10n/ca.php index f6ddbcd8e1..22b684fcfd 100644 --- a/apps/files/l10n/ca.php +++ b/apps/files/l10n/ca.php @@ -1,7 +1,4 @@ "No s'ha pogut moure %s - Ja hi ha un fitxer amb aquest nom", -"Could not move %s" => " No s'ha pogut moure %s", -"Unable to rename file" => "No es pot canviar el nom del fitxer", "No file was uploaded. Unknown error" => "No s'ha carregat cap fitxer. Error desconegut", "There is no error, the file uploaded with success" => "El fitxer s'ha pujat correctament", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "L’arxiu que voleu carregar supera el màxim definit en la directiva upload_max_filesize del php.ini:", @@ -23,12 +20,13 @@ "replaced {new_name}" => "s'ha substituït {new_name}", "undo" => "desfés", "replaced {new_name} with {old_name}" => "s'ha substituït {old_name} per {new_name}", -"unshared {files}" => "no compartits {files}", -"deleted {files}" => "eliminats {files}", +"perform delete operation" => "executa d'operació d'esborrar", "'.' is an invalid file name." => "'.' és un nom no vàlid per un fitxer.", "File name cannot be empty." => "El nom del fitxer no pot ser buit.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "El nóm no és vàlid, '\\', '/', '<', '>', ':', '\"', '|', '?' i '*' no estan permesos.", -"generating ZIP-file, it may take some time." => "s'estan generant fitxers ZIP, pot trigar una estona.", +"Your storage is full, files can not be updated or synced anymore!" => "El vostre espai d'emmagatzemament és ple, els fitxers ja no es poden actualitzar o sincronitzar!", +"Your storage is almost full ({usedSpacePercent}%)" => "El vostre espai d'emmagatzemament és gairebé ple ({usedSpacePercent}%)", +"Your download is being prepared. This might take some time if the files are big." => "S'està preparant la baixada. Pot trigar una estona si els fitxers són grans.", "Unable to upload your file as it is a directory or has 0 bytes" => "No es pot pujar el fitxer perquè és una carpeta o té 0 bytes", "Upload Error" => "Error en la pujada", "Close" => "Tanca", @@ -39,8 +37,6 @@ "File upload is in progress. Leaving the page now will cancel the upload." => "Hi ha una pujada en curs. Si abandoneu la pàgina la pujada es cancel·larà.", "URL cannot be empty." => "La URL no pot ser buida", "Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Nom de carpeta no vàlid. L'ús de 'Shared' està reservat per Owncloud", -"{count} files scanned" => "{count} fitxers escannejats", -"error while scanning" => "error durant l'escaneig", "Name" => "Nom", "Size" => "Mida", "Modified" => "Modificat", @@ -48,6 +44,7 @@ "{count} folders" => "{count} carpetes", "1 file" => "1 fitxer", "{count} files" => "{count} fitxers", +"Upload" => "Puja", "File handling" => "Gestió de fitxers", "Maximum upload size" => "Mida màxima de pujada", "max. possible: " => "màxim possible:", @@ -60,12 +57,13 @@ "Text file" => "Fitxer de text", "Folder" => "Carpeta", "From link" => "Des d'enllaç", -"Upload" => "Puja", +"Trash" => "Esborra", "Cancel upload" => "Cancel·la la pujada", "Nothing in here. Upload something!" => "Res per aquí. Pugeu alguna cosa!", "Download" => "Baixa", "Upload too large" => "La pujada és massa gran", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Els fitxers que esteu intentant pujar excedeixen la mida màxima de pujada del servidor", "Files are being scanned, please wait." => "S'estan escanejant els fitxers, espereu", -"Current scanning" => "Actualment escanejant" +"Current scanning" => "Actualment escanejant", +"Upgrading filesystem cache..." => "Actualitzant la memòria de cau del sistema de fitxers..." ); diff --git a/apps/files/l10n/cs_CZ.php b/apps/files/l10n/cs_CZ.php index 65ac4b0493..f0beda9f55 100644 --- a/apps/files/l10n/cs_CZ.php +++ b/apps/files/l10n/cs_CZ.php @@ -1,7 +1,4 @@ "Nelze přesunout %s - existuje soubor se stejným názvem", -"Could not move %s" => "Nelze přesunout %s", -"Unable to rename file" => "Nelze přejmenovat soubor", "No file was uploaded. Unknown error" => "Soubor nebyl odeslán. Neznámá chyba", "There is no error, the file uploaded with success" => "Soubor byl odeslán úspěšně", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Odesílaný soubor přesahuje velikost upload_max_filesize povolenou v php.ini:", @@ -23,12 +20,13 @@ "replaced {new_name}" => "nahrazeno {new_name}", "undo" => "zpět", "replaced {new_name} with {old_name}" => "nahrazeno {new_name} s {old_name}", -"unshared {files}" => "sdílení zrušeno pro {files}", -"deleted {files}" => "smazáno {files}", +"perform delete operation" => "provést smazání", "'.' is an invalid file name." => "'.' je neplatným názvem souboru.", "File name cannot be empty." => "Název souboru nemůže být prázdný řetězec.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Neplatný název, znaky '\\', '/', '<', '>', ':', '\"', '|', '?' a '*' nejsou povoleny.", -"generating ZIP-file, it may take some time." => "generuji ZIP soubor, může to nějakou dobu trvat.", +"Your storage is full, files can not be updated or synced anymore!" => "Vaše úložiště je plné, nelze aktualizovat ani synchronizovat soubory.", +"Your storage is almost full ({usedSpacePercent}%)" => "Vaše úložiště je téměř plné ({usedSpacePercent}%)", +"Your download is being prepared. This might take some time if the files are big." => "Vaše soubory ke stažení se připravují. Pokud jsou velké může to chvíli trvat.", "Unable to upload your file as it is a directory or has 0 bytes" => "Nelze odeslat Váš soubor, protože je to adresář nebo má velikost 0 bajtů", "Upload Error" => "Chyba odesílání", "Close" => "Zavřít", @@ -39,8 +37,6 @@ "File upload is in progress. Leaving the page now will cancel the upload." => "Probíhá odesílání souboru. Opuštění stránky vyústí ve zrušení nahrávání.", "URL cannot be empty." => "URL nemůže být prázdná", "Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Neplatný název složky. Použití 'Shared' je rezervováno pro vnitřní potřeby Owncloud", -"{count} files scanned" => "prozkoumáno {count} souborů", -"error while scanning" => "chyba při prohledávání", "Name" => "Název", "Size" => "Velikost", "Modified" => "Změněno", @@ -48,6 +44,7 @@ "{count} folders" => "{count} složky", "1 file" => "1 soubor", "{count} files" => "{count} soubory", +"Upload" => "Odeslat", "File handling" => "Zacházení se soubory", "Maximum upload size" => "Maximální velikost pro odesílání", "max. possible: " => "největší možná: ", @@ -60,12 +57,13 @@ "Text file" => "Textový soubor", "Folder" => "Složka", "From link" => "Z odkazu", -"Upload" => "Odeslat", +"Trash" => "Koš", "Cancel upload" => "Zrušit odesílání", "Nothing in here. Upload something!" => "Žádný obsah. Nahrajte něco.", "Download" => "Stáhnout", "Upload too large" => "Odeslaný soubor je příliš velký", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Soubory, které se snažíte odeslat, překračují limit velikosti odesílání na tomto serveru.", "Files are being scanned, please wait." => "Soubory se prohledávají, prosím čekejte.", -"Current scanning" => "Aktuální prohledávání" +"Current scanning" => "Aktuální prohledávání", +"Upgrading filesystem cache..." => "Aktualizuji mezipaměť souborového systému..." ); diff --git a/apps/files/l10n/da.php b/apps/files/l10n/da.php index 02c177a2f1..71a5a56de5 100644 --- a/apps/files/l10n/da.php +++ b/apps/files/l10n/da.php @@ -7,6 +7,7 @@ "No file was uploaded" => "Ingen fil blev uploadet", "Missing a temporary folder" => "Mangler en midlertidig mappe", "Failed to write to disk" => "Fejl ved skrivning til disk.", +"Invalid directory." => "Ugyldig mappe.", "Files" => "Filer", "Unshare" => "Fjern deling", "Delete" => "Slet", @@ -18,10 +19,12 @@ "replaced {new_name}" => "erstattede {new_name}", "undo" => "fortryd", "replaced {new_name} with {old_name}" => "erstattede {new_name} med {old_name}", -"unshared {files}" => "ikke delte {files}", -"deleted {files}" => "slettede {files}", +"'.' is an invalid file name." => "'.' er et ugyldigt filnavn.", +"File name cannot be empty." => "Filnavnet kan ikke stå tomt.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Ugyldigt navn, '\\', '/', '<', '>', ':' | '?', '\"', '', og '*' er ikke tilladt.", -"generating ZIP-file, it may take some time." => "genererer ZIP-fil, det kan tage lidt tid.", +"Your storage is full, files can not be updated or synced anymore!" => "Din opbevaringsplads er fyldt op, filer kan ikke opdateres eller synkroniseres længere!", +"Your storage is almost full ({usedSpacePercent}%)" => "Din opbevaringsplads er næsten fyldt op ({usedSpacePercent}%)", +"Your download is being prepared. This might take some time if the files are big." => "Dit download forberedes. Dette kan tage lidt tid ved større filer.", "Unable to upload your file as it is a directory or has 0 bytes" => "Kunne ikke uploade din fil, da det enten er en mappe eller er tom", "Upload Error" => "Fejl ved upload", "Close" => "Luk", @@ -31,8 +34,7 @@ "Upload cancelled." => "Upload afbrudt.", "File upload is in progress. Leaving the page now will cancel the upload." => "Fil upload kører. Hvis du forlader siden nu, vil uploadet blive annuleret.", "URL cannot be empty." => "URLen kan ikke være tom.", -"{count} files scanned" => "{count} filer skannet", -"error while scanning" => "fejl under scanning", +"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Ugyldigt mappenavn. Brug af \"Shared\" er forbeholdt Owncloud", "Name" => "Navn", "Size" => "Størrelse", "Modified" => "Ændret", @@ -40,6 +42,7 @@ "{count} folders" => "{count} mapper", "1 file" => "1 fil", "{count} files" => "{count} filer", +"Upload" => "Upload", "File handling" => "Filhåndtering", "Maximum upload size" => "Maksimal upload-størrelse", "max. possible: " => "max. mulige: ", @@ -52,7 +55,6 @@ "Text file" => "Tekstfil", "Folder" => "Mappe", "From link" => "Fra link", -"Upload" => "Upload", "Cancel upload" => "Fortryd upload", "Nothing in here. Upload something!" => "Her er tomt. Upload noget!", "Download" => "Download", diff --git a/apps/files/l10n/de.php b/apps/files/l10n/de.php index 089ce1c0a2..55ea24baa2 100644 --- a/apps/files/l10n/de.php +++ b/apps/files/l10n/de.php @@ -1,7 +1,4 @@ "Konnte %s nicht verschieben - Datei mit diesem Namen existiert bereits.", -"Could not move %s" => "Konnte %s nicht verschieben", -"Unable to rename file" => "Konnte Datei nicht umbenennen", "No file was uploaded. Unknown error" => "Keine Datei hochgeladen. Unbekannter Fehler", "There is no error, the file uploaded with success" => "Datei fehlerfrei hochgeladen.", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Die hochgeladene Datei überschreitet die upload_max_filesize Vorgabe in php.ini", @@ -11,7 +8,7 @@ "Missing a temporary folder" => "Temporärer Ordner fehlt.", "Failed to write to disk" => "Fehler beim Schreiben auf die Festplatte", "Not enough space available" => "Nicht genug Speicherplatz verfügbar", -"Invalid directory." => "Ungültiges Verzeichnis", +"Invalid directory." => "Ungültiges Verzeichnis.", "Files" => "Dateien", "Unshare" => "Nicht mehr freigeben", "Delete" => "Löschen", @@ -23,12 +20,13 @@ "replaced {new_name}" => "{new_name} wurde ersetzt", "undo" => "rückgängig machen", "replaced {new_name} with {old_name}" => "{old_name} ersetzt durch {new_name}", -"unshared {files}" => "Freigabe von {files} aufgehoben", -"deleted {files}" => "{files} gelöscht", -"'.' is an invalid file name." => "'.' ist kein gültiger Dateiname", -"File name cannot be empty." => "Der Dateiname darf nicht leer sein", +"perform delete operation" => "Löschvorgang ausführen", +"'.' is an invalid file name." => "'.' ist kein gültiger Dateiname.", +"File name cannot be empty." => "Der Dateiname darf nicht leer sein.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Ungültiger Name, '\\', '/', '<', '>', ':', '\"', '|', '?' und '*' sind nicht zulässig.", -"generating ZIP-file, it may take some time." => "Erstelle ZIP-Datei. Dies kann eine Weile dauern.", +"Your storage is full, files can not be updated or synced anymore!" => "Ihr Speicherplatz ist voll, Dateien können nicht mehr aktualisiert oder synchronisiert werden!", +"Your storage is almost full ({usedSpacePercent}%)" => "Ihr Speicherplatz ist fast aufgebraucht ({usedSpacePercent}%)", +"Your download is being prepared. This might take some time if the files are big." => "Dein Download wird vorbereitet. Dies kann bei größeren Dateien etwas dauern.", "Unable to upload your file as it is a directory or has 0 bytes" => "Deine Datei kann nicht hochgeladen werden, da sie entweder ein Verzeichnis oder 0 Bytes groß ist.", "Upload Error" => "Fehler beim Upload", "Close" => "Schließen", @@ -37,10 +35,8 @@ "{count} files uploading" => "{count} Dateien werden hochgeladen", "Upload cancelled." => "Upload abgebrochen.", "File upload is in progress. Leaving the page now will cancel the upload." => "Dateiupload läuft. Wenn Du die Seite jetzt verlässt, wird der Upload abgebrochen.", -"URL cannot be empty." => "Die URL darf nicht leer sein", +"URL cannot be empty." => "Die URL darf nicht leer sein.", "Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Ungültiger Verzeichnisname. Die Nutzung von \"Shared\" ist ownCloud vorbehalten.", -"{count} files scanned" => "{count} Dateien wurden gescannt", -"error while scanning" => "Fehler beim Scannen", "Name" => "Name", "Size" => "Größe", "Modified" => "Bearbeitet", @@ -48,6 +44,7 @@ "{count} folders" => "{count} Ordner", "1 file" => "1 Datei", "{count} files" => "{count} Dateien", +"Upload" => "Hochladen", "File handling" => "Dateibehandlung", "Maximum upload size" => "Maximale Upload-Größe", "max. possible: " => "maximal möglich:", @@ -60,12 +57,13 @@ "Text file" => "Textdatei", "Folder" => "Ordner", "From link" => "Von einem Link", -"Upload" => "Hochladen", +"Trash" => "Papierkorb", "Cancel upload" => "Upload abbrechen", "Nothing in here. Upload something!" => "Alles leer. Lade etwas hoch!", "Download" => "Herunterladen", "Upload too large" => "Upload zu groß", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Die Datei überschreitet die Maximalgröße für Uploads auf diesem Server.", "Files are being scanned, please wait." => "Dateien werden gescannt, bitte warten.", -"Current scanning" => "Scanne" +"Current scanning" => "Scanne", +"Upgrading filesystem cache..." => "Dateisystem-Cache wird aktualisiert ..." ); diff --git a/apps/files/l10n/de_DE.php b/apps/files/l10n/de_DE.php index 5cd4ef7042..18f3ee3802 100644 --- a/apps/files/l10n/de_DE.php +++ b/apps/files/l10n/de_DE.php @@ -1,7 +1,4 @@ "Konnte %s nicht verschieben - Datei mit diesem Namen existiert bereits", -"Could not move %s" => "Konnte %s nicht verschieben", -"Unable to rename file" => "Konnte Datei nicht umbenennen", "No file was uploaded. Unknown error" => "Keine Datei hochgeladen. Unbekannter Fehler", "There is no error, the file uploaded with success" => "Es sind keine Fehler aufgetreten. Die Datei wurde erfolgreich hochgeladen.", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Die hochgeladene Datei überschreitet die upload_max_filesize Vorgabe in php.ini", @@ -23,12 +20,13 @@ "replaced {new_name}" => "{new_name} wurde ersetzt", "undo" => "rückgängig machen", "replaced {new_name} with {old_name}" => "{old_name} wurde ersetzt durch {new_name}", -"unshared {files}" => "Freigabe für {files} beendet", -"deleted {files}" => "{files} gelöscht", +"perform delete operation" => "Führe das Löschen aus", "'.' is an invalid file name." => "'.' ist kein gültiger Dateiname.", "File name cannot be empty." => "Der Dateiname darf nicht leer sein.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Ungültiger Name, '\\', '/', '<', '>', ':', '\"', '|', '?' und '*' sind nicht zulässig.", -"generating ZIP-file, it may take some time." => "Erstelle ZIP-Datei. Dies kann eine Weile dauern.", +"Your storage is full, files can not be updated or synced anymore!" => "Ihr Speicher ist voll. Daher können keine Dateien mehr aktualisiert oder synchronisiert werden!", +"Your storage is almost full ({usedSpacePercent}%)" => "Ihr Speicher ist fast voll ({usedSpacePercent}%)", +"Your download is being prepared. This might take some time if the files are big." => "Ihr Download wird vorbereitet. Dies kann bei größeren Dateien einen Moment dauern.", "Unable to upload your file as it is a directory or has 0 bytes" => "Ihre Datei kann nicht hochgeladen werden, da sie entweder ein Verzeichnis oder 0 Bytes groß ist.", "Upload Error" => "Fehler beim Upload", "Close" => "Schließen", @@ -39,8 +37,6 @@ "File upload is in progress. Leaving the page now will cancel the upload." => "Der Dateiupload läuft. Wenn Sie die Seite jetzt verlassen, wird der Upload abgebrochen.", "URL cannot be empty." => "Die URL darf nicht leer sein.", "Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Ungültiger Verzeichnisname. Die Nutzung von \"Shared\" ist ownCloud vorbehalten", -"{count} files scanned" => "{count} Dateien wurden gescannt", -"error while scanning" => "Fehler beim Scannen", "Name" => "Name", "Size" => "Größe", "Modified" => "Bearbeitet", @@ -48,6 +44,7 @@ "{count} folders" => "{count} Ordner", "1 file" => "1 Datei", "{count} files" => "{count} Dateien", +"Upload" => "Hochladen", "File handling" => "Dateibehandlung", "Maximum upload size" => "Maximale Upload-Größe", "max. possible: " => "maximal möglich:", @@ -60,12 +57,13 @@ "Text file" => "Textdatei", "Folder" => "Ordner", "From link" => "Von einem Link", -"Upload" => "Hochladen", +"Trash" => "Abfall", "Cancel upload" => "Upload abbrechen", "Nothing in here. Upload something!" => "Alles leer. Bitte laden Sie etwas hoch!", "Download" => "Herunterladen", "Upload too large" => "Der Upload ist zu groß", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Die Datei überschreitet die Maximalgröße für Uploads auf diesem Server.", "Files are being scanned, please wait." => "Dateien werden gescannt, bitte warten.", -"Current scanning" => "Scanne" +"Current scanning" => "Scanne", +"Upgrading filesystem cache..." => "Aktualisiere den Dateisystem-Cache" ); diff --git a/apps/files/l10n/el.php b/apps/files/l10n/el.php index 3c1ac53809..7b458bf35d 100644 --- a/apps/files/l10n/el.php +++ b/apps/files/l10n/el.php @@ -7,6 +7,8 @@ "No file was uploaded" => "Κανένα αρχείο δεν στάλθηκε", "Missing a temporary folder" => "Λείπει ο προσωρινός φάκελος", "Failed to write to disk" => "Αποτυχία εγγραφής στο δίσκο", +"Not enough space available" => "Δεν υπάρχει αρκετός διαθέσιμος χώρος", +"Invalid directory." => "Μη έγκυρος φάκελος.", "Files" => "Αρχεία", "Unshare" => "Διακοπή κοινής χρήσης", "Delete" => "Διαγραφή", @@ -18,10 +20,12 @@ "replaced {new_name}" => "{new_name} αντικαταστάθηκε", "undo" => "αναίρεση", "replaced {new_name} with {old_name}" => "αντικαταστάθηκε το {new_name} με {old_name}", -"unshared {files}" => "μη διαμοιρασμένα {files}", -"deleted {files}" => "διαγραμμένα {files}", +"'.' is an invalid file name." => "'.' είναι μη έγκυρο όνομα αρχείου.", +"File name cannot be empty." => "Το όνομα αρχείου δεν πρέπει να είναι κενό.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Μη έγκυρο όνομα, '\\', '/', '<', '>', ':', '\"', '|', '?' και '*' δεν επιτρέπονται.", -"generating ZIP-file, it may take some time." => "παραγωγή αρχείου ZIP, ίσως διαρκέσει αρκετά.", +"Your storage is full, files can not be updated or synced anymore!" => "Ο αποθηκευτικός σας χώρος είναι γεμάτος, τα αρχεία δεν μπορούν να ενημερωθούν ή να συγχρονιστούν πια!", +"Your storage is almost full ({usedSpacePercent}%)" => "Ο αποθηκευτικός χώρος είναι σχεδόν γεμάτος ({usedSpacePercent}%)", +"Your download is being prepared. This might take some time if the files are big." => "Η λήψη προετοιμάζεται. Αυτό μπορεί να πάρει ώρα εάν τα αρχεία έχουν μεγάλο μέγεθος.", "Unable to upload your file as it is a directory or has 0 bytes" => "Αδυναμία στην αποστολή του αρχείου σας αφού είναι φάκελος ή έχει 0 bytes", "Upload Error" => "Σφάλμα Αποστολής", "Close" => "Κλείσιμο", @@ -31,8 +35,7 @@ "Upload cancelled." => "Η αποστολή ακυρώθηκε.", "File upload is in progress. Leaving the page now will cancel the upload." => "Η αποστολή του αρχείου βρίσκεται σε εξέλιξη. Το κλείσιμο της σελίδας θα ακυρώσει την αποστολή.", "URL cannot be empty." => "Η URL δεν πρέπει να είναι κενή.", -"{count} files scanned" => "{count} αρχεία ανιχνεύτηκαν", -"error while scanning" => "σφάλμα κατά την ανίχνευση", +"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Μη έγκυρο όνομα φακέλου. Η χρήση του 'Κοινόχρηστος' χρησιμοποιείται από ο Owncloud", "Name" => "Όνομα", "Size" => "Μέγεθος", "Modified" => "Τροποποιήθηκε", @@ -40,6 +43,7 @@ "{count} folders" => "{count} φάκελοι", "1 file" => "1 αρχείο", "{count} files" => "{count} αρχεία", +"Upload" => "Αποστολή", "File handling" => "Διαχείριση αρχείων", "Maximum upload size" => "Μέγιστο μέγεθος αποστολής", "max. possible: " => "μέγιστο δυνατό:", @@ -52,7 +56,6 @@ "Text file" => "Αρχείο κειμένου", "Folder" => "Φάκελος", "From link" => "Από σύνδεσμο", -"Upload" => "Αποστολή", "Cancel upload" => "Ακύρωση αποστολής", "Nothing in here. Upload something!" => "Δεν υπάρχει τίποτα εδώ. Ανέβασε κάτι!", "Download" => "Λήψη", diff --git a/apps/files/l10n/eo.php b/apps/files/l10n/eo.php index 92c03ee882..a510d47ad6 100644 --- a/apps/files/l10n/eo.php +++ b/apps/files/l10n/eo.php @@ -7,6 +7,8 @@ "No file was uploaded" => "Neniu dosiero estas alŝutita", "Missing a temporary folder" => "Mankas tempa dosierujo", "Failed to write to disk" => "Malsukcesis skribo al disko", +"Not enough space available" => "Ne haveblas sufiĉa spaco", +"Invalid directory." => "Nevalida dosierujo.", "Files" => "Dosieroj", "Unshare" => "Malkunhavigi", "Delete" => "Forigi", @@ -18,10 +20,10 @@ "replaced {new_name}" => "anstataŭiĝis {new_name}", "undo" => "malfari", "replaced {new_name} with {old_name}" => "anstataŭiĝis {new_name} per {old_name}", -"unshared {files}" => "malkunhaviĝis {files}", -"deleted {files}" => "foriĝis {files}", +"'.' is an invalid file name." => "'.' ne estas valida dosiernomo.", +"File name cannot be empty." => "Dosiernomo devas ne malpleni.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Nevalida nomo: “\\”, “/”, “<”, “>”, “:”, “\"”, “|”, “?” kaj “*” ne permesatas.", -"generating ZIP-file, it may take some time." => "generanta ZIP-dosiero, ĝi povas daŭri iom da tempo", +"Your download is being prepared. This might take some time if the files are big." => "Via elŝuto pretiĝatas. Ĉi tio povas daŭri iom da tempo se la dosieroj grandas.", "Unable to upload your file as it is a directory or has 0 bytes" => "Ne eblis alŝuti vian dosieron ĉar ĝi estas dosierujo aŭ havas 0 duumokojn", "Upload Error" => "Alŝuta eraro", "Close" => "Fermi", @@ -31,8 +33,7 @@ "Upload cancelled." => "La alŝuto nuliĝis.", "File upload is in progress. Leaving the page now will cancel the upload." => "Dosieralŝuto plenumiĝas. Lasi la paĝon nun nuligus la alŝuton.", "URL cannot be empty." => "URL ne povas esti malplena.", -"{count} files scanned" => "{count} dosieroj skaniĝis", -"error while scanning" => "eraro dum skano", +"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Nevalida dosierujnomo. Uzo de “Shared” rezervatas de Owncloud.", "Name" => "Nomo", "Size" => "Grando", "Modified" => "Modifita", @@ -40,6 +41,7 @@ "{count} folders" => "{count} dosierujoj", "1 file" => "1 dosiero", "{count} files" => "{count} dosierujoj", +"Upload" => "Alŝuti", "File handling" => "Dosieradministro", "Maximum upload size" => "Maksimuma alŝutogrando", "max. possible: " => "maks. ebla: ", @@ -52,7 +54,6 @@ "Text file" => "Tekstodosiero", "Folder" => "Dosierujo", "From link" => "El ligilo", -"Upload" => "Alŝuti", "Cancel upload" => "Nuligi alŝuton", "Nothing in here. Upload something!" => "Nenio estas ĉi tie. Alŝutu ion!", "Download" => "Elŝuti", diff --git a/apps/files/l10n/es.php b/apps/files/l10n/es.php index 885ed3770e..bc5046767c 100644 --- a/apps/files/l10n/es.php +++ b/apps/files/l10n/es.php @@ -1,7 +1,4 @@ "No se puede mover %s - Ya existe un archivo con ese nombre", -"Could not move %s" => "No se puede mover %s", -"Unable to rename file" => "No se puede renombrar el archivo", "No file was uploaded. Unknown error" => "Fallo no se subió el fichero", "There is no error, the file uploaded with success" => "No se ha producido ningún error, el archivo se ha subido con éxito", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "El archivo que intentas subir sobrepasa el tamaño definido por la variable upload_max_filesize en php.ini", @@ -23,12 +20,10 @@ "replaced {new_name}" => "reemplazado {new_name}", "undo" => "deshacer", "replaced {new_name} with {old_name}" => "reemplazado {new_name} con {old_name}", -"unshared {files}" => "{files} descompartidos", -"deleted {files}" => "{files} eliminados", "'.' is an invalid file name." => "'.' es un nombre de archivo inválido.", "File name cannot be empty." => "El nombre de archivo no puede estar vacío.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Nombre Invalido, \"\\\", \"/\", \"<\", \">\", \":\", \"\", \"|\" \"?\" y \"*\" no están permitidos ", -"generating ZIP-file, it may take some time." => "generando un fichero ZIP, puede llevar un tiempo.", +"Your download is being prepared. This might take some time if the files are big." => "Tu descarga esta siendo preparada. Esto puede tardar algun tiempo si los archivos son muy grandes.", "Unable to upload your file as it is a directory or has 0 bytes" => "No ha sido posible subir tu archivo porque es un directorio o tiene 0 bytes", "Upload Error" => "Error al subir el archivo", "Close" => "cerrrar", @@ -38,8 +33,7 @@ "Upload cancelled." => "Subida cancelada.", "File upload is in progress. Leaving the page now will cancel the upload." => "La subida del archivo está en proceso. Salir de la página ahora cancelará la subida.", "URL cannot be empty." => "La URL no puede estar vacía.", -"{count} files scanned" => "{count} archivos escaneados", -"error while scanning" => "error escaneando", +"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Nombre de carpeta invalido. El uso de \"Shared\" esta reservado para Owncloud", "Name" => "Nombre", "Size" => "Tamaño", "Modified" => "Modificado", @@ -47,6 +41,7 @@ "{count} folders" => "{count} carpetas", "1 file" => "1 archivo", "{count} files" => "{count} archivos", +"Upload" => "Subir", "File handling" => "Tratamiento de archivos", "Maximum upload size" => "Tamaño máximo de subida", "max. possible: " => "máx. posible:", @@ -59,7 +54,6 @@ "Text file" => "Archivo de texto", "Folder" => "Carpeta", "From link" => "Desde el enlace", -"Upload" => "Subir", "Cancel upload" => "Cancelar subida", "Nothing in here. Upload something!" => "Aquí no hay nada. ¡Sube algo!", "Download" => "Descargar", diff --git a/apps/files/l10n/es_AR.php b/apps/files/l10n/es_AR.php index 650a3149e4..ea8352e325 100644 --- a/apps/files/l10n/es_AR.php +++ b/apps/files/l10n/es_AR.php @@ -1,7 +1,4 @@ "No se pudo mover %s - Un archivo con este nombre ya existe", -"Could not move %s" => "No se pudo mover %s ", -"Unable to rename file" => "No fue posible cambiar el nombre al archivo", "No file was uploaded. Unknown error" => "El archivo no fue subido. Error desconocido", "There is no error, the file uploaded with success" => "No se han producido errores, el archivo se ha subido con éxito", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "El archivo que intentás subir excede el tamaño definido por upload_max_filesize en el php.ini:", @@ -23,12 +20,12 @@ "replaced {new_name}" => "reemplazado {new_name}", "undo" => "deshacer", "replaced {new_name} with {old_name}" => "reemplazado {new_name} con {old_name}", -"unshared {files}" => "{files} se dejaron de compartir", -"deleted {files}" => "{files} borrados", "'.' is an invalid file name." => "'.' es un nombre de archivo inválido.", "File name cannot be empty." => "El nombre del archivo no puede quedar vacío.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Nombre invalido, '\\', '/', '<', '>', ':', '\"', '|', '?' y '*' no están permitidos.", -"generating ZIP-file, it may take some time." => "generando un archivo ZIP, puede llevar un tiempo.", +"Your storage is full, files can not be updated or synced anymore!" => "El almacenamiento está lleno, los archivos no se pueden seguir actualizando ni sincronizando", +"Your storage is almost full ({usedSpacePercent}%)" => "El almacenamiento está casi lleno ({usedSpacePercent}%)", +"Your download is being prepared. This might take some time if the files are big." => "Tu descarga esta siendo preparada. Esto puede tardar algun tiempo si los archivos son muy grandes.", "Unable to upload your file as it is a directory or has 0 bytes" => "No fue posible subir el archivo porque es un directorio o porque su tamaño es 0 bytes", "Upload Error" => "Error al subir el archivo", "Close" => "Cerrar", @@ -39,8 +36,6 @@ "File upload is in progress. Leaving the page now will cancel the upload." => "La subida del archivo está en proceso. Si salís de la página ahora, la subida se cancelará.", "URL cannot be empty." => "La URL no puede estar vacía", "Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Nombre de carpeta inválido. El uso de 'Shared' está reservado por ownCloud", -"{count} files scanned" => "{count} archivos escaneados", -"error while scanning" => "error mientras se escaneaba", "Name" => "Nombre", "Size" => "Tamaño", "Modified" => "Modificado", @@ -48,6 +43,7 @@ "{count} folders" => "{count} directorios", "1 file" => "1 archivo", "{count} files" => "{count} archivos", +"Upload" => "Subir", "File handling" => "Tratamiento de archivos", "Maximum upload size" => "Tamaño máximo de subida", "max. possible: " => "máx. posible:", @@ -60,7 +56,6 @@ "Text file" => "Archivo de texto", "Folder" => "Carpeta", "From link" => "Desde enlace", -"Upload" => "Subir", "Cancel upload" => "Cancelar subida", "Nothing in here. Upload something!" => "No hay nada. ¡Subí contenido!", "Download" => "Descargar", diff --git a/apps/files/l10n/et_EE.php b/apps/files/l10n/et_EE.php index 6996b0a791..54dd7cfdc5 100644 --- a/apps/files/l10n/et_EE.php +++ b/apps/files/l10n/et_EE.php @@ -17,10 +17,7 @@ "replaced {new_name}" => "asendatud nimega {new_name}", "undo" => "tagasi", "replaced {new_name} with {old_name}" => "asendas nime {old_name} nimega {new_name}", -"unshared {files}" => "jagamata {files}", -"deleted {files}" => "kustutatud {files}", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Vigane nimi, '\\', '/', '<', '>', ':', '\"', '|', '?' ja '*' pole lubatud.", -"generating ZIP-file, it may take some time." => "ZIP-faili loomine, see võib veidi aega võtta.", "Unable to upload your file as it is a directory or has 0 bytes" => "Sinu faili üleslaadimine ebaõnnestus, kuna see on kaust või selle suurus on 0 baiti", "Upload Error" => "Üleslaadimise viga", "Close" => "Sulge", @@ -30,8 +27,6 @@ "Upload cancelled." => "Üleslaadimine tühistati.", "File upload is in progress. Leaving the page now will cancel the upload." => "Faili üleslaadimine on töös. Lehelt lahkumine katkestab selle üleslaadimise.", "URL cannot be empty." => "URL ei saa olla tühi.", -"{count} files scanned" => "{count} faili skännitud", -"error while scanning" => "viga skännimisel", "Name" => "Nimi", "Size" => "Suurus", "Modified" => "Muudetud", @@ -39,6 +34,7 @@ "{count} folders" => "{count} kausta", "1 file" => "1 fail", "{count} files" => "{count} faili", +"Upload" => "Lae üles", "File handling" => "Failide käsitlemine", "Maximum upload size" => "Maksimaalne üleslaadimise suurus", "max. possible: " => "maks. võimalik: ", @@ -51,7 +47,6 @@ "Text file" => "Tekstifail", "Folder" => "Kaust", "From link" => "Allikast", -"Upload" => "Lae üles", "Cancel upload" => "Tühista üleslaadimine", "Nothing in here. Upload something!" => "Siin pole midagi. Lae midagi üles!", "Download" => "Lae alla", diff --git a/apps/files/l10n/eu.php b/apps/files/l10n/eu.php index 96f59a668e..6f4c55f484 100644 --- a/apps/files/l10n/eu.php +++ b/apps/files/l10n/eu.php @@ -7,6 +7,8 @@ "No file was uploaded" => "Ez da fitxategirik igo", "Missing a temporary folder" => "Aldi baterako karpeta falta da", "Failed to write to disk" => "Errore bat izan da diskoan idazterakoan", +"Not enough space available" => "Ez dago leku nahikorik.", +"Invalid directory." => "Baliogabeko karpeta.", "Files" => "Fitxategiak", "Unshare" => "Ez elkarbanatu", "Delete" => "Ezabatu", @@ -18,10 +20,12 @@ "replaced {new_name}" => "ordezkatua {new_name}", "undo" => "desegin", "replaced {new_name} with {old_name}" => " {new_name}-k {old_name} ordezkatu du", -"unshared {files}" => "elkarbanaketa utzita {files}", -"deleted {files}" => "ezabatuta {files}", +"'.' is an invalid file name." => "'.' ez da fitxategi izen baliogarria.", +"File name cannot be empty." => "Fitxategi izena ezin da hutsa izan.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "IZen aliogabea, '\\', '/', '<', '>', ':', '\"', '|', '?' eta '*' ez daude baimenduta.", -"generating ZIP-file, it may take some time." => "ZIP-fitxategia sortzen ari da, denbora har dezake", +"Your storage is full, files can not be updated or synced anymore!" => "Zure biltegiratzea beterik dago, ezingo duzu aurrerantzean fitxategirik igo edo sinkronizatu!", +"Your storage is almost full ({usedSpacePercent}%)" => "Zure biltegiratzea nahiko beterik dago (%{usedSpacePercent})", +"Your download is being prepared. This might take some time if the files are big." => "Zure deskarga prestatu egin behar da. Denbora bat har lezake fitxategiak handiak badira. ", "Unable to upload your file as it is a directory or has 0 bytes" => "Ezin da zure fitxategia igo, karpeta bat da edo 0 byt ditu", "Upload Error" => "Igotzean errore bat suertatu da", "Close" => "Itxi", @@ -31,8 +35,7 @@ "Upload cancelled." => "Igoera ezeztatuta", "File upload is in progress. Leaving the page now will cancel the upload." => "Fitxategien igoera martxan da. Orria orain uzteak igoera ezeztatutko du.", "URL cannot be empty." => "URLa ezin da hutsik egon.", -"{count} files scanned" => "{count} fitxategi eskaneatuta", -"error while scanning" => "errore bat egon da eskaneatzen zen bitartean", +"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Baliogabeako karpeta izena. 'Shared' izena Owncloudek erreserbatzen du", "Name" => "Izena", "Size" => "Tamaina", "Modified" => "Aldatuta", @@ -40,6 +43,7 @@ "{count} folders" => "{count} karpeta", "1 file" => "fitxategi bat", "{count} files" => "{count} fitxategi", +"Upload" => "Igo", "File handling" => "Fitxategien kudeaketa", "Maximum upload size" => "Igo daitekeen gehienezko tamaina", "max. possible: " => "max, posiblea:", @@ -52,7 +56,6 @@ "Text file" => "Testu fitxategia", "Folder" => "Karpeta", "From link" => "Estekatik", -"Upload" => "Igo", "Cancel upload" => "Ezeztatu igoera", "Nothing in here. Upload something!" => "Ez dago ezer. Igo zerbait!", "Download" => "Deskargatu", diff --git a/apps/files/l10n/fa.php b/apps/files/l10n/fa.php index 062df6a56b..a4181c6ff5 100644 --- a/apps/files/l10n/fa.php +++ b/apps/files/l10n/fa.php @@ -1,26 +1,47 @@ "هیچ فایلی آپلود نشد.خطای ناشناس", "There is no error, the file uploaded with success" => "هیچ خطایی وجود ندارد فایل با موفقیت بار گذاری شد", +"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "پرونده آپلود شده بیش ازدستور ماکزیمم_حجم فایل_برای آپلود در php.ini استفاده کرده است.", "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "حداکثر حجم مجاز برای بارگذاری از طریق HTML \nMAX_FILE_SIZE", "The uploaded file was only partially uploaded" => "مقدار کمی از فایل بارگذاری شده", "No file was uploaded" => "هیچ فایلی بارگذاری نشده", "Missing a temporary folder" => "یک پوشه موقت گم شده است", "Failed to write to disk" => "نوشتن بر روی دیسک سخت ناموفق بود", +"Not enough space available" => "فضای کافی در دسترس نیست", +"Invalid directory." => "فهرست راهنما نامعتبر می باشد.", "Files" => "فایل ها", +"Unshare" => "لغو اشتراک", "Delete" => "پاک کردن", "Rename" => "تغییرنام", +"{new_name} already exists" => "{نام _جدید} در حال حاضر وجود دارد.", "replace" => "جایگزین", +"suggest name" => "پیشنهاد نام", "cancel" => "لغو", +"replaced {new_name}" => "{نام _جدید} جایگزین شد ", "undo" => "بازگشت", -"generating ZIP-file, it may take some time." => "در حال ساخت فایل فشرده ممکن است زمان زیادی به طول بیانجامد", +"replaced {new_name} with {old_name}" => "{نام_جدید} با { نام_قدیمی} جایگزین شد.", +"'.' is an invalid file name." => "'.' یک نام پرونده نامعتبر است.", +"File name cannot be empty." => "نام پرونده نمی تواند خالی باشد.", +"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "نام نامعتبر ، '\\', '/', '<', '>', ':', '\"', '|', '?' و '*' مجاز نمی باشند.", +"Your download is being prepared. This might take some time if the files are big." => "دانلود شما در حال آماده شدن است. در صورتیکه پرونده ها بزرگ باشند ممکن است مدتی طول بکشد.", "Unable to upload your file as it is a directory or has 0 bytes" => "ناتوان در بارگذاری یا فایل یک پوشه است یا 0بایت دارد", "Upload Error" => "خطا در بار گذاری", "Close" => "بستن", "Pending" => "در انتظار", +"1 file uploading" => "1 پرونده آپلود شد.", +"{count} files uploading" => "{ شمار } فایل های در حال آپلود", "Upload cancelled." => "بار گذاری لغو شد", +"File upload is in progress. Leaving the page now will cancel the upload." => "آپلودکردن پرونده در حال پیشرفت است. در صورت خروج از صفحه آپلود لغو میگردد. ", +"URL cannot be empty." => "URL نمی تواند خالی باشد.", +"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "نام پوشه نامعتبر است. استفاده از \" به اشتراک گذاشته شده \" متعلق به سایت Owncloud است.", "Name" => "نام", "Size" => "اندازه", "Modified" => "تغییر یافته", +"1 folder" => "1 پوشه", +"{count} folders" => "{ شمار} پوشه ها", +"1 file" => "1 پرونده", +"{count} files" => "{ شمار } فایل ها", +"Upload" => "بارگذاری", "File handling" => "اداره پرونده ها", "Maximum upload size" => "حداکثر اندازه بارگزاری", "max. possible: " => "حداکثرمقدارممکن:", @@ -32,7 +53,7 @@ "New" => "جدید", "Text file" => "فایل متنی", "Folder" => "پوشه", -"Upload" => "بارگذاری", +"From link" => "از پیوند", "Cancel upload" => "متوقف کردن بار گذاری", "Nothing in here. Upload something!" => "اینجا هیچ چیز نیست.", "Download" => "بارگیری", diff --git a/apps/files/l10n/fi_FI.php b/apps/files/l10n/fi_FI.php index e7e4b04437..809a5e5c55 100644 --- a/apps/files/l10n/fi_FI.php +++ b/apps/files/l10n/fi_FI.php @@ -1,7 +1,4 @@ "Kohteen %s siirto ei onnistunut - Tiedosto samalla nimellä on jo olemassa", -"Could not move %s" => "Kohteen %s siirto ei onnistunut", -"Unable to rename file" => "Tiedoston nimeäminen uudelleen ei onnistunut", "No file was uploaded. Unknown error" => "Tiedostoa ei lähetetty. Tuntematon virhe", "There is no error, the file uploaded with success" => "Ei virheitä, tiedosto lähetettiin onnistuneesti", "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Lähetetty tiedosto ylittää HTML-lomakkeessa määritetyn MAX_FILE_SIZE-arvon ylärajan", @@ -20,10 +17,13 @@ "suggest name" => "ehdota nimeä", "cancel" => "peru", "undo" => "kumoa", +"perform delete operation" => "suorita poistotoiminto", "'.' is an invalid file name." => "'.' on virheellinen nimi tiedostolle.", "File name cannot be empty." => "Tiedoston nimi ei voi olla tyhjä.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Virheellinen nimi, merkit '\\', '/', '<', '>', ':', '\"', '|', '?' ja '*' eivät ole sallittuja.", -"generating ZIP-file, it may take some time." => "luodaan ZIP-tiedostoa, tämä saattaa kestää hetken.", +"Your storage is full, files can not be updated or synced anymore!" => "Tallennustila on loppu, tiedostoja ei voi enää päivittää tai synkronoida!", +"Your storage is almost full ({usedSpacePercent}%)" => "Tallennustila on melkein loppu ({usedSpacePercent}%)", +"Your download is being prepared. This might take some time if the files are big." => "Lataustasi valmistellaan. Tämä saattaa kestää hetken, jos tiedostot ovat suuria kooltaan.", "Unable to upload your file as it is a directory or has 0 bytes" => "Tiedoston lähetys epäonnistui, koska sen koko on 0 tavua tai kyseessä on kansio", "Upload Error" => "Lähetysvirhe.", "Close" => "Sulje", @@ -38,6 +38,7 @@ "{count} folders" => "{count} kansiota", "1 file" => "1 tiedosto", "{count} files" => "{count} tiedostoa", +"Upload" => "Lähetä", "File handling" => "Tiedostonhallinta", "Maximum upload size" => "Lähetettävän tiedoston suurin sallittu koko", "max. possible: " => "suurin mahdollinen:", @@ -50,12 +51,13 @@ "Text file" => "Tekstitiedosto", "Folder" => "Kansio", "From link" => "Linkistä", -"Upload" => "Lähetä", +"Trash" => "Roskakori", "Cancel upload" => "Peru lähetys", "Nothing in here. Upload something!" => "Täällä ei ole mitään. Lähetä tänne jotakin!", "Download" => "Lataa", "Upload too large" => "Lähetettävä tiedosto on liian suuri", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Lähetettäväksi valitsemasi tiedostot ylittävät palvelimen salliman tiedostokoon rajan.", "Files are being scanned, please wait." => "Tiedostoja tarkistetaan, odota hetki.", -"Current scanning" => "Tämänhetkinen tutkinta" +"Current scanning" => "Tämänhetkinen tutkinta", +"Upgrading filesystem cache..." => "Päivitetään tiedostojärjestelmän välimuistia..." ); diff --git a/apps/files/l10n/fr.php b/apps/files/l10n/fr.php index f14759ff8f..4be699c001 100644 --- a/apps/files/l10n/fr.php +++ b/apps/files/l10n/fr.php @@ -1,7 +1,4 @@ "Impossible de déplacer %s - Un fichier possédant ce nom existe déjà", -"Could not move %s" => "Impossible de déplacer %s", -"Unable to rename file" => "Impossible de renommer le fichier", "No file was uploaded. Unknown error" => "Aucun fichier n'a été chargé. Erreur inconnue", "There is no error, the file uploaded with success" => "Aucune erreur, le fichier a été téléversé avec succès", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Le fichier envoyé dépasse la valeur upload_max_filesize située dans le fichier php.ini:", @@ -23,12 +20,13 @@ "replaced {new_name}" => "{new_name} a été remplacé", "undo" => "annuler", "replaced {new_name} with {old_name}" => "{new_name} a été remplacé par {old_name}", -"unshared {files}" => "Fichiers non partagés : {files}", -"deleted {files}" => "Fichiers supprimés : {files}", +"perform delete operation" => "effectuer l'opération de suppression", "'.' is an invalid file name." => "'.' n'est pas un nom de fichier valide.", "File name cannot be empty." => "Le nom de fichier ne peut être vide.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Nom invalide, les caractères '\\', '/', '<', '>', ':', '\"', '|', '?' et '*' ne sont pas autorisés.", -"generating ZIP-file, it may take some time." => "Fichier ZIP en cours d'assemblage ; cela peut prendre du temps.", +"Your storage is full, files can not be updated or synced anymore!" => "Votre espage de stockage est plein, les fichiers ne peuvent plus être téléversés ou synchronisés !", +"Your storage is almost full ({usedSpacePercent}%)" => "Votre espace de stockage est presque plein ({usedSpacePercent}%)", +"Your download is being prepared. This might take some time if the files are big." => "Votre téléchargement est cours de préparation. Ceci peut nécessiter un certain temps si les fichiers sont volumineux.", "Unable to upload your file as it is a directory or has 0 bytes" => "Impossible de charger vos fichiers car il s'agit d'un dossier ou le fichier fait 0 octet.", "Upload Error" => "Erreur de chargement", "Close" => "Fermer", @@ -39,8 +37,6 @@ "File upload is in progress. Leaving the page now will cancel the upload." => "L'envoi du fichier est en cours. Quitter cette page maintenant annulera l'envoi du fichier.", "URL cannot be empty." => "L'URL ne peut-être vide", "Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Nom de dossier invalide. L'utilisation du mot 'Shared' est réservée à Owncloud", -"{count} files scanned" => "{count} fichiers indexés", -"error while scanning" => "erreur lors de l'indexation", "Name" => "Nom", "Size" => "Taille", "Modified" => "Modifié", @@ -48,6 +44,7 @@ "{count} folders" => "{count} dossiers", "1 file" => "1 fichier", "{count} files" => "{count} fichiers", +"Upload" => "Envoyer", "File handling" => "Gestion des fichiers", "Maximum upload size" => "Taille max. d'envoi", "max. possible: " => "Max. possible :", @@ -60,12 +57,13 @@ "Text file" => "Fichier texte", "Folder" => "Dossier", "From link" => "Depuis le lien", -"Upload" => "Envoyer", +"Trash" => "Corbeille", "Cancel upload" => "Annuler l'envoi", "Nothing in here. Upload something!" => "Il n'y a rien ici ! Envoyez donc quelque chose :)", "Download" => "Télécharger", "Upload too large" => "Fichier trop volumineux", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Les fichiers que vous essayez d'envoyer dépassent la taille maximale permise par ce serveur.", "Files are being scanned, please wait." => "Les fichiers sont en cours d'analyse, veuillez patienter.", -"Current scanning" => "Analyse en cours" +"Current scanning" => "Analyse en cours", +"Upgrading filesystem cache..." => "Mise à niveau du cache du système de fichier" ); diff --git a/apps/files/l10n/gl.php b/apps/files/l10n/gl.php index c15066163c..a1c0f0a5dd 100644 --- a/apps/files/l10n/gl.php +++ b/apps/files/l10n/gl.php @@ -1,7 +1,4 @@ "Non se moveu %s - Xa existe un ficheiro con ese nome.", -"Could not move %s" => "Non se puido mover %s", -"Unable to rename file" => "Non se pode renomear o ficheiro", "No file was uploaded. Unknown error" => "Non se subiu ningún ficheiro. Erro descoñecido.", "There is no error, the file uploaded with success" => "Non hai erros. O ficheiro enviouse correctamente", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "O ficheiro subido excede a directiva indicada polo tamaño_máximo_de_subida de php.ini", @@ -23,12 +20,9 @@ "replaced {new_name}" => "substituír {new_name}", "undo" => "desfacer", "replaced {new_name} with {old_name}" => "substituír {new_name} polo {old_name}", -"unshared {files}" => "{files} sen compartir", -"deleted {files}" => "{files} eliminados", "'.' is an invalid file name." => "'.' é un nonme de ficheiro non válido", "File name cannot be empty." => "O nome de ficheiro non pode estar baldeiro", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Nome non válido, '\\', '/', '<', '>', ':', '\"', '|', '?' e '*' non se permiten.", -"generating ZIP-file, it may take some time." => "xerando un ficheiro ZIP, o que pode levar un anaco.", "Unable to upload your file as it is a directory or has 0 bytes" => "Non se puido subir o ficheiro pois ou é un directorio ou ten 0 bytes", "Upload Error" => "Erro na subida", "Close" => "Pechar", @@ -39,8 +33,6 @@ "File upload is in progress. Leaving the page now will cancel the upload." => "A subida do ficheiro está en curso. Saír agora da páxina cancelará a subida.", "URL cannot be empty." => "URL non pode quedar baleiro.", "Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Nome de cartafol non válido. O uso de 'Shared' está reservado por Owncloud", -"{count} files scanned" => "{count} ficheiros escaneados", -"error while scanning" => "erro mentres analizaba", "Name" => "Nome", "Size" => "Tamaño", "Modified" => "Modificado", @@ -48,6 +40,7 @@ "{count} folders" => "{count} cartafoles", "1 file" => "1 ficheiro", "{count} files" => "{count} ficheiros", +"Upload" => "Enviar", "File handling" => "Manexo de ficheiro", "Maximum upload size" => "Tamaño máximo de envío", "max. possible: " => "máx. posible: ", @@ -60,7 +53,6 @@ "Text file" => "Ficheiro de texto", "Folder" => "Cartafol", "From link" => "Dende a ligazón", -"Upload" => "Enviar", "Cancel upload" => "Cancelar a subida", "Nothing in here. Upload something!" => "Nada por aquí. Envía algo.", "Download" => "Descargar", diff --git a/apps/files/l10n/he.php b/apps/files/l10n/he.php index bac9a8a6a5..94cddca000 100644 --- a/apps/files/l10n/he.php +++ b/apps/files/l10n/he.php @@ -18,10 +18,7 @@ "replaced {new_name}" => "{new_name} הוחלף", "undo" => "ביטול", "replaced {new_name} with {old_name}" => "{new_name} הוחלף ב־{old_name}", -"unshared {files}" => "בוטל שיתופם של {files}", -"deleted {files}" => "{files} נמחקו", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "השם שגוי, אסור להשתמש בתווים '\\', '/', '<', '>', ':', '\"', '|', '?' ו־'*'.", -"generating ZIP-file, it may take some time." => "יוצר קובץ ZIP, אנא המתן.", "Unable to upload your file as it is a directory or has 0 bytes" => "לא יכול להעלות את הקובץ מכיוון שזו תקיה או שמשקל הקובץ 0 בתים", "Upload Error" => "שגיאת העלאה", "Close" => "סגירה", @@ -31,8 +28,6 @@ "Upload cancelled." => "ההעלאה בוטלה.", "File upload is in progress. Leaving the page now will cancel the upload." => "מתבצעת כעת העלאת קבצים. עזיבה של העמוד תבטל את ההעלאה.", "URL cannot be empty." => "קישור אינו יכול להיות ריק.", -"{count} files scanned" => "{count} קבצים נסרקו", -"error while scanning" => "אירעה שגיאה במהלך הסריקה", "Name" => "שם", "Size" => "גודל", "Modified" => "זמן שינוי", @@ -40,6 +35,7 @@ "{count} folders" => "{count} תיקיות", "1 file" => "קובץ אחד", "{count} files" => "{count} קבצים", +"Upload" => "העלאה", "File handling" => "טיפול בקבצים", "Maximum upload size" => "גודל העלאה מקסימלי", "max. possible: " => "המרבי האפשרי: ", @@ -52,7 +48,6 @@ "Text file" => "קובץ טקסט", "Folder" => "תיקייה", "From link" => "מקישור", -"Upload" => "העלאה", "Cancel upload" => "ביטול ההעלאה", "Nothing in here. Upload something!" => "אין כאן שום דבר. אולי ברצונך להעלות משהו?", "Download" => "הורדה", diff --git a/apps/files/l10n/hr.php b/apps/files/l10n/hr.php index 4db4ac3f3e..4f4546aaf0 100644 --- a/apps/files/l10n/hr.php +++ b/apps/files/l10n/hr.php @@ -13,7 +13,6 @@ "suggest name" => "predloži ime", "cancel" => "odustani", "undo" => "vrati", -"generating ZIP-file, it may take some time." => "generiranje ZIP datoteke, ovo može potrajati.", "Unable to upload your file as it is a directory or has 0 bytes" => "Nemoguće poslati datoteku jer je prazna ili je direktorij", "Upload Error" => "Pogreška pri slanju", "Close" => "Zatvori", @@ -21,10 +20,10 @@ "1 file uploading" => "1 datoteka se učitava", "Upload cancelled." => "Slanje poništeno.", "File upload is in progress. Leaving the page now will cancel the upload." => "Učitavanje datoteke. Napuštanjem stranice će prekinuti učitavanje.", -"error while scanning" => "grečka prilikom skeniranja", "Name" => "Naziv", "Size" => "Veličina", "Modified" => "Zadnja promjena", +"Upload" => "Pošalji", "File handling" => "datoteka za rukovanje", "Maximum upload size" => "Maksimalna veličina prijenosa", "max. possible: " => "maksimalna moguća: ", @@ -36,7 +35,6 @@ "New" => "novo", "Text file" => "tekstualna datoteka", "Folder" => "mapa", -"Upload" => "Pošalji", "Cancel upload" => "Prekini upload", "Nothing in here. Upload something!" => "Nema ničega u ovoj mapi. Pošalji nešto!", "Download" => "Preuzmi", diff --git a/apps/files/l10n/hu_HU.php b/apps/files/l10n/hu_HU.php index b0d46ee7a2..86fc0f223f 100644 --- a/apps/files/l10n/hu_HU.php +++ b/apps/files/l10n/hu_HU.php @@ -20,12 +20,12 @@ "replaced {new_name}" => "a(z) {new_name} állományt kicseréltük", "undo" => "visszavonás", "replaced {new_name} with {old_name}" => "{new_name} fájlt kicseréltük ezzel: {old_name}", -"unshared {files}" => "{files} fájl megosztása visszavonva", -"deleted {files}" => "{files} fájl törölve", "'.' is an invalid file name." => "'.' fájlnév érvénytelen.", "File name cannot be empty." => "A fájlnév nem lehet semmi.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Érvénytelen elnevezés. Ezek a karakterek nem használhatók: '\\', '/', '<', '>', ':', '\"', '|', '?' és '*'", -"generating ZIP-file, it may take some time." => "ZIP-fájl generálása, ez eltarthat egy ideig.", +"Your storage is full, files can not be updated or synced anymore!" => "A tároló tele van, a fájlok nem frissíthetőek vagy szinkronizálhatóak a jövőben.", +"Your storage is almost full ({usedSpacePercent}%)" => "A tároló majdnem tele van ({usedSpacePercent}%)", +"Your download is being prepared. This might take some time if the files are big." => "Készül a letöltendő állomány. Ez eltarthat egy ideig, ha nagyok a fájlok.", "Unable to upload your file as it is a directory or has 0 bytes" => "Nem tölthető fel, mert mappa volt, vagy 0 byte méretű", "Upload Error" => "Feltöltési hiba", "Close" => "Bezárás", @@ -35,8 +35,7 @@ "Upload cancelled." => "A feltöltést megszakítottuk.", "File upload is in progress. Leaving the page now will cancel the upload." => "Fájlfeltöltés van folyamatban. Az oldal elhagyása megszakítja a feltöltést.", "URL cannot be empty." => "Az URL nem lehet semmi.", -"{count} files scanned" => "{count} fájlt találtunk", -"error while scanning" => "Hiba a fájllista-ellenőrzés során", +"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Érvénytelen mappanév. A név használata csak a Owncloud számára lehetséges.", "Name" => "Név", "Size" => "Méret", "Modified" => "Módosítva", @@ -44,6 +43,7 @@ "{count} folders" => "{count} mappa", "1 file" => "1 fájl", "{count} files" => "{count} fájl", +"Upload" => "Feltöltés", "File handling" => "Fájlkezelés", "Maximum upload size" => "Maximális feltölthető fájlméret", "max. possible: " => "max. lehetséges: ", @@ -56,7 +56,6 @@ "Text file" => "Szövegfájl", "Folder" => "Mappa", "From link" => "Feltöltés linkről", -"Upload" => "Feltöltés", "Cancel upload" => "A feltöltés megszakítása", "Nothing in here. Upload something!" => "Itt nincs semmi. Töltsön fel valamit!", "Download" => "Letöltés", diff --git a/apps/files/l10n/ia.php b/apps/files/l10n/ia.php index ada64cd757..ae614c1bf5 100644 --- a/apps/files/l10n/ia.php +++ b/apps/files/l10n/ia.php @@ -8,12 +8,12 @@ "Name" => "Nomine", "Size" => "Dimension", "Modified" => "Modificate", +"Upload" => "Incargar", "Maximum upload size" => "Dimension maxime de incargamento", "Save" => "Salveguardar", "New" => "Nove", "Text file" => "File de texto", "Folder" => "Dossier", -"Upload" => "Incargar", "Nothing in here. Upload something!" => "Nihil hic. Incarga alcun cosa!", "Download" => "Discargar", "Upload too large" => "Incargamento troppo longe" diff --git a/apps/files/l10n/id.php b/apps/files/l10n/id.php index 5d934e97e7..3ebb998329 100644 --- a/apps/files/l10n/id.php +++ b/apps/files/l10n/id.php @@ -11,7 +11,6 @@ "replace" => "mengganti", "cancel" => "batalkan", "undo" => "batal dikerjakan", -"generating ZIP-file, it may take some time." => "membuat berkas ZIP, ini mungkin memakan waktu.", "Unable to upload your file as it is a directory or has 0 bytes" => "Gagal mengunggah berkas anda karena berupa direktori atau mempunyai ukuran 0 byte", "Upload Error" => "Terjadi Galat Pengunggahan", "Close" => "tutup", @@ -21,6 +20,7 @@ "Name" => "Nama", "Size" => "Ukuran", "Modified" => "Dimodifikasi", +"Upload" => "Unggah", "File handling" => "Penanganan berkas", "Maximum upload size" => "Ukuran unggah maksimum", "max. possible: " => "Kemungkinan maks:", @@ -32,7 +32,6 @@ "New" => "Baru", "Text file" => "Berkas teks", "Folder" => "Folder", -"Upload" => "Unggah", "Cancel upload" => "Batal mengunggah", "Nothing in here. Upload something!" => "Tidak ada apa-apa di sini. Unggah sesuatu!", "Download" => "Unduh", diff --git a/apps/files/l10n/is.php b/apps/files/l10n/is.php index 2eff686611..43c10ef236 100644 --- a/apps/files/l10n/is.php +++ b/apps/files/l10n/is.php @@ -1,7 +1,4 @@ "Gat ekki fært %s - Skrá með þessu nafni er þegar til", -"Could not move %s" => "Gat ekki fært %s", -"Unable to rename file" => "Gat ekki endurskýrt skrá", "No file was uploaded. Unknown error" => "Engin skrá var send inn. Óþekkt villa.", "There is no error, the file uploaded with success" => "Engin villa, innsending heppnaðist", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Innsend skrá er stærri en upload_max stillingin í php.ini:", @@ -23,12 +20,9 @@ "replaced {new_name}" => "endurskýrði {new_name}", "undo" => "afturkalla", "replaced {new_name} with {old_name}" => "yfirskrifaði {new_name} með {old_name}", -"unshared {files}" => "Hætti við deilingu á {files}", -"deleted {files}" => "eyddi {files}", "'.' is an invalid file name." => "'.' er ekki leyfilegt nafn.", "File name cannot be empty." => "Nafn skráar má ekki vera tómt", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Ógilt nafn, táknin '\\', '/', '<', '>', ':', '\"', '|', '?' og '*' eru ekki leyfð.", -"generating ZIP-file, it may take some time." => "bý til ZIP skrá, það gæti tekið smá stund.", "Unable to upload your file as it is a directory or has 0 bytes" => "Innsending á skrá mistókst, hugsanlega sendir þú möppu eða skráin er 0 bæti.", "Upload Error" => "Villa við innsendingu", "Close" => "Loka", @@ -39,8 +33,6 @@ "File upload is in progress. Leaving the page now will cancel the upload." => "Innsending í gangi. Ef þú ferð af þessari síðu mun innsending misheppnast.", "URL cannot be empty." => "Vefslóð má ekki vera tóm.", "Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Óleyfilegt nafn á möppu. Nafnið 'Shared' er frátekið fyrir Owncloud", -"{count} files scanned" => "{count} skrár skimaðar", -"error while scanning" => "villa við skimun", "Name" => "Nafn", "Size" => "Stærð", "Modified" => "Breytt", @@ -48,6 +40,7 @@ "{count} folders" => "{count} möppur", "1 file" => "1 skrá", "{count} files" => "{count} skrár", +"Upload" => "Senda inn", "File handling" => "Meðhöndlun skrár", "Maximum upload size" => "Hámarks stærð innsendingar", "max. possible: " => "hámark mögulegt: ", @@ -60,7 +53,6 @@ "Text file" => "Texta skrá", "Folder" => "Mappa", "From link" => "Af tengli", -"Upload" => "Senda inn", "Cancel upload" => "Hætta við innsendingu", "Nothing in here. Upload something!" => "Ekkert hér. Settu eitthvað inn!", "Download" => "Niðurhal", diff --git a/apps/files/l10n/it.php b/apps/files/l10n/it.php index a54e424694..bb3a5bdf05 100644 --- a/apps/files/l10n/it.php +++ b/apps/files/l10n/it.php @@ -1,7 +1,4 @@ "Impossibile spostare %s - un file con questo nome esiste già", -"Could not move %s" => "Impossibile spostare %s", -"Unable to rename file" => "Impossibile rinominare il file", "No file was uploaded. Unknown error" => "Nessun file è stato inviato. Errore sconosciuto", "There is no error, the file uploaded with success" => "Non ci sono errori, file caricato con successo", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Il file caricato supera la direttiva upload_max_filesize in php.ini:", @@ -23,12 +20,13 @@ "replaced {new_name}" => "sostituito {new_name}", "undo" => "annulla", "replaced {new_name} with {old_name}" => "sostituito {new_name} con {old_name}", -"unshared {files}" => "non condivisi {files}", -"deleted {files}" => "eliminati {files}", +"perform delete operation" => "esegui l'operazione di eliminazione", "'.' is an invalid file name." => "'.' non è un nome file valido.", "File name cannot be empty." => "Il nome del file non può essere vuoto.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Nome non valido, '\\', '/', '<', '>', ':', '\"', '|', '?' e '*' non sono consentiti.", -"generating ZIP-file, it may take some time." => "creazione file ZIP, potrebbe richiedere del tempo.", +"Your storage is full, files can not be updated or synced anymore!" => "Lo spazio di archiviazione è pieno, i file non possono essere più aggiornati o sincronizzati!", +"Your storage is almost full ({usedSpacePercent}%)" => "Lo spazio di archiviazione è quasi pieno ({usedSpacePercent}%)", +"Your download is being prepared. This might take some time if the files are big." => "Il tuo scaricamento è in fase di preparazione. Ciò potrebbe richiedere del tempo se i file sono grandi.", "Unable to upload your file as it is a directory or has 0 bytes" => "Impossibile inviare il file poiché è una cartella o ha dimensione 0 byte", "Upload Error" => "Errore di invio", "Close" => "Chiudi", @@ -39,8 +37,6 @@ "File upload is in progress. Leaving the page now will cancel the upload." => "Caricamento del file in corso. La chiusura della pagina annullerà il caricamento.", "URL cannot be empty." => "L'URL non può essere vuoto.", "Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Nome della cartella non valido. L'uso di 'Shared' è riservato da ownCloud", -"{count} files scanned" => "{count} file analizzati", -"error while scanning" => "errore durante la scansione", "Name" => "Nome", "Size" => "Dimensione", "Modified" => "Modificato", @@ -48,6 +44,7 @@ "{count} folders" => "{count} cartelle", "1 file" => "1 file", "{count} files" => "{count} file", +"Upload" => "Carica", "File handling" => "Gestione file", "Maximum upload size" => "Dimensione massima upload", "max. possible: " => "numero mass.: ", @@ -60,12 +57,13 @@ "Text file" => "File di testo", "Folder" => "Cartella", "From link" => "Da collegamento", -"Upload" => "Carica", +"Trash" => "Cestino", "Cancel upload" => "Annulla invio", "Nothing in here. Upload something!" => "Non c'è niente qui. Carica qualcosa!", "Download" => "Scarica", "Upload too large" => "Il file caricato è troppo grande", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "I file che stai provando a caricare superano la dimensione massima consentita su questo server.", "Files are being scanned, please wait." => "Scansione dei file in corso, attendi", -"Current scanning" => "Scansione corrente" +"Current scanning" => "Scansione corrente", +"Upgrading filesystem cache..." => "Aggiornamento della cache del filesystem in corso..." ); diff --git a/apps/files/l10n/ja_JP.php b/apps/files/l10n/ja_JP.php index 4621cc5d4e..c8b1054f30 100644 --- a/apps/files/l10n/ja_JP.php +++ b/apps/files/l10n/ja_JP.php @@ -1,7 +1,4 @@ "%s を移動できませんでした ― この名前のファイルはすでに存在します", -"Could not move %s" => "%s を移動できませんでした", -"Unable to rename file" => "ファイル名の変更ができません", "No file was uploaded. Unknown error" => "ファイルは何もアップロードされていません。不明なエラー", "There is no error, the file uploaded with success" => "エラーはありません。ファイルのアップロードは成功しました", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "アップロードされたファイルはphp.ini の upload_max_filesize に設定されたサイズを超えています:", @@ -23,12 +20,13 @@ "replaced {new_name}" => "{new_name} を置換", "undo" => "元に戻す", "replaced {new_name} with {old_name}" => "{old_name} を {new_name} に置換", -"unshared {files}" => "未共有 {files}", -"deleted {files}" => "削除 {files}", +"perform delete operation" => "削除を実行", "'.' is an invalid file name." => "'.' は無効なファイル名です。", "File name cannot be empty." => "ファイル名を空にすることはできません。", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "無効な名前、'\\', '/', '<', '>', ':', '\"', '|', '?', '*' は使用できません。", -"generating ZIP-file, it may take some time." => "ZIPファイルを生成中です、しばらくお待ちください。", +"Your storage is full, files can not be updated or synced anymore!" => "あなたのストレージは一杯です。ファイルの更新と同期はもうできません!", +"Your storage is almost full ({usedSpacePercent}%)" => "あなたのストレージはほぼ一杯です({usedSpacePercent}%)", +"Your download is being prepared. This might take some time if the files are big." => "ダウンロードの準備中です。ファイルサイズが大きい場合は少し時間がかかるかもしれません。", "Unable to upload your file as it is a directory or has 0 bytes" => "ディレクトリもしくは0バイトのファイルはアップロードできません", "Upload Error" => "アップロードエラー", "Close" => "閉じる", @@ -39,8 +37,6 @@ "File upload is in progress. Leaving the page now will cancel the upload." => "ファイル転送を実行中です。今このページから移動するとアップロードが中止されます。", "URL cannot be empty." => "URLは空にできません。", "Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "無効なフォルダ名です。'Shared' の利用は ownCloud が予約済みです。", -"{count} files scanned" => "{count} ファイルをスキャン", -"error while scanning" => "スキャン中のエラー", "Name" => "名前", "Size" => "サイズ", "Modified" => "更新日時", @@ -48,6 +44,7 @@ "{count} folders" => "{count} フォルダ", "1 file" => "1 ファイル", "{count} files" => "{count} ファイル", +"Upload" => "アップロード", "File handling" => "ファイル操作", "Maximum upload size" => "最大アップロードサイズ", "max. possible: " => "最大容量: ", @@ -60,12 +57,13 @@ "Text file" => "テキストファイル", "Folder" => "フォルダ", "From link" => "リンク", -"Upload" => "アップロード", +"Trash" => "ゴミ箱", "Cancel upload" => "アップロードをキャンセル", "Nothing in here. Upload something!" => "ここには何もありません。何かアップロードしてください。", "Download" => "ダウンロード", "Upload too large" => "ファイルサイズが大きすぎます", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "アップロードしようとしているファイルは、サーバで規定された最大サイズを超えています。", "Files are being scanned, please wait." => "ファイルをスキャンしています、しばらくお待ちください。", -"Current scanning" => "スキャン中" +"Current scanning" => "スキャン中", +"Upgrading filesystem cache..." => "ファイルシステムキャッシュを更新中..." ); diff --git a/apps/files/l10n/ka_GE.php b/apps/files/l10n/ka_GE.php index 9a73abfbe3..7ab6122c65 100644 --- a/apps/files/l10n/ka_GE.php +++ b/apps/files/l10n/ka_GE.php @@ -16,9 +16,6 @@ "replaced {new_name}" => "{new_name} შეცვლილია", "undo" => "დაბრუნება", "replaced {new_name} with {old_name}" => "{new_name} შეცვლილია {old_name}–ით", -"unshared {files}" => "გაზიარება მოხსნილი {files}", -"deleted {files}" => "წაშლილი {files}", -"generating ZIP-file, it may take some time." => "ZIP-ფაილის გენერირება, ამას ჭირდება გარკვეული დრო.", "Unable to upload your file as it is a directory or has 0 bytes" => "თქვენი ფაილის ატვირთვა ვერ მოხერხდა. ის არის საქაღალდე და შეიცავს 0 ბაიტს", "Upload Error" => "შეცდომა ატვირთვისას", "Close" => "დახურვა", @@ -27,8 +24,6 @@ "{count} files uploading" => "{count} ფაილი იტვირთება", "Upload cancelled." => "ატვირთვა შეჩერებულ იქნა.", "File upload is in progress. Leaving the page now will cancel the upload." => "მიმდინარეობს ფაილის ატვირთვა. სხვა გვერდზე გადასვლა გამოიწვევს ატვირთვის შეჩერებას", -"{count} files scanned" => "{count} ფაილი სკანირებულია", -"error while scanning" => "შეცდომა სკანირებისას", "Name" => "სახელი", "Size" => "ზომა", "Modified" => "შეცვლილია", @@ -36,6 +31,7 @@ "{count} folders" => "{count} საქაღალდე", "1 file" => "1 ფაილი", "{count} files" => "{count} ფაილი", +"Upload" => "ატვირთვა", "File handling" => "ფაილის დამუშავება", "Maximum upload size" => "მაქსიმუმ ატვირთის ზომა", "max. possible: " => "მაქს. შესაძლებელი:", @@ -47,7 +43,6 @@ "New" => "ახალი", "Text file" => "ტექსტური ფაილი", "Folder" => "საქაღალდე", -"Upload" => "ატვირთვა", "Cancel upload" => "ატვირთვის გაუქმება", "Nothing in here. Upload something!" => "აქ არაფერი არ არის. ატვირთე რამე!", "Download" => "ჩამოტვირთვა", diff --git a/apps/files/l10n/ko.php b/apps/files/l10n/ko.php index 928b7cbb7e..7774aeea31 100644 --- a/apps/files/l10n/ko.php +++ b/apps/files/l10n/ko.php @@ -1,7 +1,4 @@ "%s 항목을 이동시키지 못하였음 - 파일 이름이 이미 존재함", -"Could not move %s" => "%s 항목을 이딩시키지 못하였음", -"Unable to rename file" => "파일 이름바꾸기 할 수 없음", "No file was uploaded. Unknown error" => "파일이 업로드되지 않았습니다. 알 수 없는 오류입니다", "There is no error, the file uploaded with success" => "업로드에 성공하였습니다.", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "업로드한 파일이 php.ini의 upload_max_filesize보다 큽니다:", @@ -10,8 +7,8 @@ "No file was uploaded" => "업로드된 파일 없음", "Missing a temporary folder" => "임시 폴더가 사라짐", "Failed to write to disk" => "디스크에 쓰지 못했습니다", -"Not enough space available" => "여유공간이 부족합니다", -"Invalid directory." => "올바르지 않은 디렉토리입니다.", +"Not enough space available" => "여유 공간이 부족합니다", +"Invalid directory." => "올바르지 않은 디렉터리입니다.", "Files" => "파일", "Unshare" => "공유 해제", "Delete" => "삭제", @@ -23,12 +20,12 @@ "replaced {new_name}" => "{new_name}을(를) 대체함", "undo" => "실행 취소", "replaced {new_name} with {old_name}" => "{old_name}이(가) {new_name}(으)로 대체됨", -"unshared {files}" => "{files} 공유 해제됨", -"deleted {files}" => "{files} 삭제됨", "'.' is an invalid file name." => "'.' 는 올바르지 않은 파일 이름 입니다.", -"File name cannot be empty." => "파일이름은 공란이 될 수 없습니다.", +"File name cannot be empty." => "파일 이름이 비어 있을 수 없습니다.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "폴더 이름이 올바르지 않습니다. 이름에 문자 '\\', '/', '<', '>', ':', '\"', '|', '? ', '*'는 사용할 수 없습니다.", -"generating ZIP-file, it may take some time." => "ZIP 파일을 생성하고 있습니다. 시간이 걸릴 수도 있습니다.", +"Your storage is full, files can not be updated or synced anymore!" => "저장 공간이 가득 찼습니다. 파일을 업데이트하거나 동기화할 수 없습니다!", +"Your storage is almost full ({usedSpacePercent}%)" => "저장 공간이 거의 가득 찼습니다 ({usedSpacePercent}%)", +"Your download is being prepared. This might take some time if the files are big." => "다운로드가 준비 중입니다. 파일 크기가 크다면 시간이 오래 걸릴 수도 있습니다.", "Unable to upload your file as it is a directory or has 0 bytes" => "이 파일은 디렉터리이거나 비어 있기 때문에 업로드할 수 없습니다", "Upload Error" => "업로드 오류", "Close" => "닫기", @@ -39,8 +36,6 @@ "File upload is in progress. Leaving the page now will cancel the upload." => "파일 업로드가 진행 중입니다. 이 페이지를 벗어나면 업로드가 취소됩니다.", "URL cannot be empty." => "URL을 입력해야 합니다.", "Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "폴더 이름이 유효하지 않습니다. ", -"{count} files scanned" => "파일 {count}개 검색됨", -"error while scanning" => "검색 중 오류 발생", "Name" => "이름", "Size" => "크기", "Modified" => "수정됨", @@ -48,6 +43,7 @@ "{count} folders" => "폴더 {count}개", "1 file" => "파일 1개", "{count} files" => "파일 {count}개", +"Upload" => "업로드", "File handling" => "파일 처리", "Maximum upload size" => "최대 업로드 크기", "max. possible: " => "최대 가능:", @@ -60,12 +56,12 @@ "Text file" => "텍스트 파일", "Folder" => "폴더", "From link" => "링크에서", -"Upload" => "업로드", "Cancel upload" => "업로드 취소", "Nothing in here. Upload something!" => "내용이 없습니다. 업로드할 수 있습니다!", "Download" => "다운로드", "Upload too large" => "업로드 용량 초과", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "이 파일이 서버에서 허용하는 최대 업로드 가능 용량보다 큽니다.", "Files are being scanned, please wait." => "파일을 검색하고 있습니다. 기다려 주십시오.", -"Current scanning" => "현재 검색" +"Current scanning" => "현재 검색", +"Upgrading filesystem cache..." => "파일 시스템 캐시 업그레이드 중..." ); diff --git a/apps/files/l10n/ku_IQ.php b/apps/files/l10n/ku_IQ.php index d6cf645079..5c5a3d6bd8 100644 --- a/apps/files/l10n/ku_IQ.php +++ b/apps/files/l10n/ku_IQ.php @@ -2,8 +2,8 @@ "Close" => "داخستن", "URL cannot be empty." => "ناونیشانی به‌سته‌ر نابێت به‌تاڵ بێت.", "Name" => "ناو", +"Upload" => "بارکردن", "Save" => "پاشکه‌وتکردن", "Folder" => "بوخچه", -"Upload" => "بارکردن", "Download" => "داگرتن" ); diff --git a/apps/files/l10n/lb.php b/apps/files/l10n/lb.php index 229ec3f202..79ef4bc941 100644 --- a/apps/files/l10n/lb.php +++ b/apps/files/l10n/lb.php @@ -6,11 +6,11 @@ "Missing a temporary folder" => "Et feelt en temporären Dossier", "Failed to write to disk" => "Konnt net op den Disk schreiwen", "Files" => "Dateien", +"Unshare" => "Net méi deelen", "Delete" => "Läschen", "replace" => "ersetzen", "cancel" => "ofbriechen", "undo" => "réckgängeg man", -"generating ZIP-file, it may take some time." => "Et gëtt eng ZIP-File generéiert, dëst ka bëssen daueren.", "Unable to upload your file as it is a directory or has 0 bytes" => "Kann deng Datei net eroplueden well et en Dossier ass oder 0 byte grouss ass.", "Upload Error" => "Fehler beim eroplueden", "Close" => "Zoumaachen", @@ -19,6 +19,7 @@ "Name" => "Numm", "Size" => "Gréisst", "Modified" => "Geännert", +"Upload" => "Eroplueden", "File handling" => "Fichier handling", "Maximum upload size" => "Maximum Upload Gréisst ", "max. possible: " => "max. méiglech:", @@ -30,7 +31,6 @@ "New" => "Nei", "Text file" => "Text Fichier", "Folder" => "Dossier", -"Upload" => "Eroplueden", "Cancel upload" => "Upload ofbriechen", "Nothing in here. Upload something!" => "Hei ass näischt. Lued eppes rop!", "Download" => "Eroflueden", diff --git a/apps/files/l10n/lt_LT.php b/apps/files/l10n/lt_LT.php index fd9824e0c1..f4ad655f42 100644 --- a/apps/files/l10n/lt_LT.php +++ b/apps/files/l10n/lt_LT.php @@ -16,9 +16,6 @@ "replaced {new_name}" => "pakeiskite {new_name}", "undo" => "anuliuoti", "replaced {new_name} with {old_name}" => "pakeiskite {new_name} į {old_name}", -"unshared {files}" => "nebesidalinti {files}", -"deleted {files}" => "ištrinti {files}", -"generating ZIP-file, it may take some time." => "kuriamas ZIP archyvas, tai gali užtrukti šiek tiek laiko.", "Unable to upload your file as it is a directory or has 0 bytes" => "Neįmanoma įkelti failo - jo dydis gali būti 0 bitų arba tai katalogas", "Upload Error" => "Įkėlimo klaida", "Close" => "Užverti", @@ -27,8 +24,6 @@ "{count} files uploading" => "{count} įkeliami failai", "Upload cancelled." => "Įkėlimas atšauktas.", "File upload is in progress. Leaving the page now will cancel the upload." => "Failo įkėlimas pradėtas. Jei paliksite šį puslapį, įkėlimas nutrūks.", -"{count} files scanned" => "{count} praskanuoti failai", -"error while scanning" => "klaida skanuojant", "Name" => "Pavadinimas", "Size" => "Dydis", "Modified" => "Pakeista", @@ -36,6 +31,7 @@ "{count} folders" => "{count} aplankalai", "1 file" => "1 failas", "{count} files" => "{count} failai", +"Upload" => "Įkelti", "File handling" => "Failų tvarkymas", "Maximum upload size" => "Maksimalus įkeliamo failo dydis", "max. possible: " => "maks. galima:", @@ -47,7 +43,6 @@ "New" => "Naujas", "Text file" => "Teksto failas", "Folder" => "Katalogas", -"Upload" => "Įkelti", "Cancel upload" => "Atšaukti siuntimą", "Nothing in here. Upload something!" => "Čia tuščia. Įkelkite ką nors!", "Download" => "Atsisiųsti", diff --git a/apps/files/l10n/lv.php b/apps/files/l10n/lv.php index 3336798491..25c1da73be 100644 --- a/apps/files/l10n/lv.php +++ b/apps/files/l10n/lv.php @@ -1,41 +1,69 @@ "Viss kārtībā, augšupielāde veiksmīga", -"No file was uploaded" => "Neviens fails netika augšuplādēts", +"No file was uploaded. Unknown error" => "Netika augšupielādēta neviena datne. Nezināma kļūda", +"There is no error, the file uploaded with success" => "Augšupielāde pabeigta bez kļūdām", +"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Augšupielādētā datne pārsniedz upload_max_filesize norādījumu php.ini datnē:", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Augšupielādētā datne pārsniedz MAX_FILE_SIZE norādi, kas ir norādīta HTML formā", +"The uploaded file was only partially uploaded" => "Augšupielādētā datne ir tikai daļēji augšupielādēta", +"No file was uploaded" => "Neviena datne netika augšupielādēta", "Missing a temporary folder" => "Trūkst pagaidu mapes", -"Failed to write to disk" => "Nav iespējams saglabāt", -"Files" => "Faili", -"Unshare" => "Pārtraukt līdzdalīšanu", -"Delete" => "Izdzēst", -"Rename" => "Pārdēvēt", +"Failed to write to disk" => "Neizdevās saglabāt diskā", +"Not enough space available" => "Nepietiek brīvas vietas", +"Invalid directory." => "Nederīga direktorija.", +"Files" => "Datnes", +"Unshare" => "Pārtraukt dalīšanos", +"Delete" => "Dzēst", +"Rename" => "Pārsaukt", +"{new_name} already exists" => "{new_name} jau eksistē", "replace" => "aizvietot", -"suggest name" => "Ieteiktais nosaukums", +"suggest name" => "ieteiktais nosaukums", "cancel" => "atcelt", -"undo" => "vienu soli atpakaļ", -"generating ZIP-file, it may take some time." => "lai uzģenerētu ZIP failu, kāds brīdis ir jāpagaida", -"Unable to upload your file as it is a directory or has 0 bytes" => "Nav iespējams augšuplādēt jūsu failu, jo tāds jau eksistē vai arī failam nav izmēra (0 baiti)", -"Upload Error" => "Augšuplādēšanas laikā radās kļūda", +"replaced {new_name}" => "aizvietots {new_name}", +"undo" => "atsaukt", +"replaced {new_name} with {old_name}" => "aizvietoja {new_name} ar {old_name}", +"perform delete operation" => "veikt dzēšanas darbību", +"'.' is an invalid file name." => "'.' ir nederīgs datnes nosaukums.", +"File name cannot be empty." => "Datnes nosaukums nevar būt tukšs.", +"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Nederīgs nosaukums, nav atļauti '\\', '/', '<', '>', ':', '\"', '|', '?' un '*'.", +"Your storage is full, files can not be updated or synced anymore!" => "Jūsu krātuve ir pilna, datnes vairs nevar augšupielādēt vai sinhronizēt!", +"Your storage is almost full ({usedSpacePercent}%)" => "Jūsu krātuve ir gandrīz pilna ({usedSpacePercent}%)", +"Your download is being prepared. This might take some time if the files are big." => "Tiek sagatavota lejupielāde. Tas var aizņemt kādu laiciņu, ja datnes ir lielas.", +"Unable to upload your file as it is a directory or has 0 bytes" => "Nevar augšupielādēt jūsu datni, jo tā ir direktorija vai arī tās izmērs ir 0 baiti", +"Upload Error" => "Kļūda augšupielādējot", +"Close" => "Aizvērt", "Pending" => "Gaida savu kārtu", -"Upload cancelled." => "Augšuplāde ir atcelta", +"1 file uploading" => "Augšupielādē 1 datni", +"{count} files uploading" => "augšupielādē {count} datnes", +"Upload cancelled." => "Augšupielāde ir atcelta.", "File upload is in progress. Leaving the page now will cancel the upload." => "Notiek augšupielāde. Pametot lapu tagad, tiks atcelta augšupielāde.", +"URL cannot be empty." => "URL nevar būt tukšs.", +"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Nederīgs mapes nosaukums. “Koplietots” izmantojums ir rezervēts ownCloud servisam.", "Name" => "Nosaukums", "Size" => "Izmērs", -"Modified" => "Izmainīts", -"File handling" => "Failu pārvaldība", -"Maximum upload size" => "Maksimālais failu augšuplādes apjoms", -"max. possible: " => "maksīmālais iespējamais:", -"Needed for multi-file and folder downloads." => "Vajadzīgs vairāku failu un mapju lejuplādei", -"Enable ZIP-download" => "Iespējot ZIP lejuplādi", +"Modified" => "Mainīts", +"1 folder" => "1 mape", +"{count} folders" => "{count} mapes", +"1 file" => "1 datne", +"{count} files" => "{count} datnes", +"Upload" => "Augšupielādēt", +"File handling" => "Datņu pārvaldība", +"Maximum upload size" => "Maksimālais datņu augšupielādes apjoms", +"max. possible: " => "maksimālais iespējamais:", +"Needed for multi-file and folder downloads." => "Vajadzīgs vairāku datņu un mapju lejupielādēšanai.", +"Enable ZIP-download" => "Aktivēt ZIP lejupielādi", "0 is unlimited" => "0 ir neierobežots", +"Maximum input size for ZIP files" => "Maksimālais ievades izmērs ZIP datnēm", "Save" => "Saglabāt", -"New" => "Jauns", -"Text file" => "Teksta fails", +"New" => "Jauna", +"Text file" => "Teksta datne", "Folder" => "Mape", -"Upload" => "Augšuplādet", -"Cancel upload" => "Atcelt augšuplādi", -"Nothing in here. Upload something!" => "Te vēl nekas nav. Rīkojies, sāc augšuplādēt", -"Download" => "Lejuplādēt", -"Upload too large" => "Fails ir par lielu lai to augšuplādetu", -"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Jūsu augšuplādējamie faili pārsniedz servera pieļaujamo failu augšupielādes apjomu", -"Files are being scanned, please wait." => "Faili šobrīd tiek caurskatīti, nedaudz jāpagaida.", -"Current scanning" => "Šobrīd tiek pārbaudīti" +"From link" => "No saites", +"Trash" => "Miskaste", +"Cancel upload" => "Atcelt augšupielādi", +"Nothing in here. Upload something!" => "Te vēl nekas nav. Rīkojies, sāc augšupielādēt!", +"Download" => "Lejupielādēt", +"Upload too large" => "Datne ir par lielu, lai to augšupielādētu", +"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Augšupielādējamās datnes pārsniedz servera pieļaujamo datņu augšupielādes apjomu", +"Files are being scanned, please wait." => "Datnes šobrīd tiek caurskatītas, lūdzu, uzgaidiet.", +"Current scanning" => "Šobrīd tiek caurskatīts", +"Upgrading filesystem cache..." => "Uzlabo datņu sistēmas kešatmiņu..." ); diff --git a/apps/files/l10n/mk.php b/apps/files/l10n/mk.php index 3f48a69874..2580d1e6a9 100644 --- a/apps/files/l10n/mk.php +++ b/apps/files/l10n/mk.php @@ -18,10 +18,7 @@ "replaced {new_name}" => "земенета {new_name}", "undo" => "врати", "replaced {new_name} with {old_name}" => "заменета {new_name} со {old_name}", -"unshared {files}" => "без споделување {files}", -"deleted {files}" => "избришани {files}", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Неправилно име. , '\\', '/', '<', '>', ':', '\"', '|', '?' и '*' не се дозволени.", -"generating ZIP-file, it may take some time." => "Се генерира ZIP фајлот, ќе треба извесно време.", "Unable to upload your file as it is a directory or has 0 bytes" => "Не може да се преземе вашата датотека бидејќи фолдерот во кој се наоѓа фајлот има големина од 0 бајти", "Upload Error" => "Грешка при преземање", "Close" => "Затвои", @@ -31,8 +28,6 @@ "Upload cancelled." => "Преземањето е прекинато.", "File upload is in progress. Leaving the page now will cancel the upload." => "Подигање на датотека е во тек. Напуштење на страницата ќе го прекине.", "URL cannot be empty." => "Адресата неможе да биде празна.", -"{count} files scanned" => "{count} датотеки скенирани", -"error while scanning" => "грешка при скенирање", "Name" => "Име", "Size" => "Големина", "Modified" => "Променето", @@ -40,6 +35,7 @@ "{count} folders" => "{count} папки", "1 file" => "1 датотека", "{count} files" => "{count} датотеки", +"Upload" => "Подигни", "File handling" => "Ракување со датотеки", "Maximum upload size" => "Максимална големина за подигање", "max. possible: " => "макс. можно:", @@ -52,7 +48,6 @@ "Text file" => "Текстуална датотека", "Folder" => "Папка", "From link" => "Од врска", -"Upload" => "Подигни", "Cancel upload" => "Откажи прикачување", "Nothing in here. Upload something!" => "Тука нема ништо. Снимете нешто!", "Download" => "Преземи", diff --git a/apps/files/l10n/ms_MY.php b/apps/files/l10n/ms_MY.php index 7fa8784084..4ac26d8091 100644 --- a/apps/files/l10n/ms_MY.php +++ b/apps/files/l10n/ms_MY.php @@ -10,7 +10,6 @@ "Delete" => "Padam", "replace" => "ganti", "cancel" => "Batal", -"generating ZIP-file, it may take some time." => "sedang menghasilkan fail ZIP, mungkin mengambil sedikit masa.", "Unable to upload your file as it is a directory or has 0 bytes" => "Tidak boleh memuatnaik fail anda kerana mungkin ianya direktori atau saiz fail 0 bytes", "Upload Error" => "Muat naik ralat", "Close" => "Tutup", @@ -19,6 +18,7 @@ "Name" => "Nama ", "Size" => "Saiz", "Modified" => "Dimodifikasi", +"Upload" => "Muat naik", "File handling" => "Pengendalian fail", "Maximum upload size" => "Saiz maksimum muat naik", "max. possible: " => "maksimum:", @@ -30,7 +30,6 @@ "New" => "Baru", "Text file" => "Fail teks", "Folder" => "Folder", -"Upload" => "Muat naik", "Cancel upload" => "Batal muat naik", "Nothing in here. Upload something!" => "Tiada apa-apa di sini. Muat naik sesuatu!", "Download" => "Muat turun", diff --git a/apps/files/l10n/nb_NO.php b/apps/files/l10n/nb_NO.php index 9be868164b..a6ba6e9c03 100644 --- a/apps/files/l10n/nb_NO.php +++ b/apps/files/l10n/nb_NO.php @@ -17,9 +17,7 @@ "replaced {new_name}" => "erstatt {new_name}", "undo" => "angre", "replaced {new_name} with {old_name}" => "erstatt {new_name} med {old_name}", -"deleted {files}" => "slettet {files}", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Ugyldig navn, '\\', '/', '<', '>', ':', '\"', '|', '?' og '*' er ikke tillatt.", -"generating ZIP-file, it may take some time." => "opprettet ZIP-fil, dette kan ta litt tid", "Unable to upload your file as it is a directory or has 0 bytes" => "Kan ikke laste opp filen din siden det er en mappe eller den har 0 bytes", "Upload Error" => "Opplasting feilet", "Close" => "Lukk", @@ -29,8 +27,6 @@ "Upload cancelled." => "Opplasting avbrutt.", "File upload is in progress. Leaving the page now will cancel the upload." => "Filopplasting pågår. Forlater du siden nå avbrytes opplastingen.", "URL cannot be empty." => "URL-en kan ikke være tom.", -"{count} files scanned" => "{count} filer lest inn", -"error while scanning" => "feil under skanning", "Name" => "Navn", "Size" => "Størrelse", "Modified" => "Endret", @@ -38,6 +34,7 @@ "{count} folders" => "{count} mapper", "1 file" => "1 fil", "{count} files" => "{count} filer", +"Upload" => "Last opp", "File handling" => "Filhåndtering", "Maximum upload size" => "Maksimum opplastingsstørrelse", "max. possible: " => "max. mulige:", @@ -50,7 +47,6 @@ "Text file" => "Tekstfil", "Folder" => "Mappe", "From link" => "Fra link", -"Upload" => "Last opp", "Cancel upload" => "Avbryt opplasting", "Nothing in here. Upload something!" => "Ingenting her. Last opp noe!", "Download" => "Last ned", diff --git a/apps/files/l10n/nl.php b/apps/files/l10n/nl.php index 48c4ac74c2..addd3a9372 100644 --- a/apps/files/l10n/nl.php +++ b/apps/files/l10n/nl.php @@ -1,7 +1,4 @@ "Kon %s niet verplaatsen - Er bestaat al een bestand met deze naam", -"Could not move %s" => "Kon %s niet verplaatsen", -"Unable to rename file" => "Kan bestand niet hernoemen", "No file was uploaded. Unknown error" => "Er was geen bestand geladen. Onbekende fout", "There is no error, the file uploaded with success" => "Geen fout opgetreden, bestand successvol geupload.", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Het geüploade bestand overscheidt de upload_max_filesize optie in php.ini:", @@ -23,12 +20,13 @@ "replaced {new_name}" => "verving {new_name}", "undo" => "ongedaan maken", "replaced {new_name} with {old_name}" => "verving {new_name} met {old_name}", -"unshared {files}" => "delen gestopt {files}", -"deleted {files}" => "verwijderde {files}", +"perform delete operation" => "uitvoeren verwijderactie", "'.' is an invalid file name." => "'.' is een ongeldige bestandsnaam.", "File name cannot be empty." => "Bestandsnaam kan niet leeg zijn.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Onjuiste naam; '\\', '/', '<', '>', ':', '\"', '|', '?' en '*' zijn niet toegestaan.", -"generating ZIP-file, it may take some time." => "aanmaken ZIP-file, dit kan enige tijd duren.", +"Your storage is full, files can not be updated or synced anymore!" => "Uw opslagruimte zit vol, Bestanden kunnen niet meer worden ge-upload of gesynchroniseerd!", +"Your storage is almost full ({usedSpacePercent}%)" => "Uw opslagruimte zit bijna vol ({usedSpacePercent}%)", +"Your download is being prepared. This might take some time if the files are big." => "Uw download wordt voorbereid. Dit kan enige tijd duren bij grote bestanden.", "Unable to upload your file as it is a directory or has 0 bytes" => "uploaden van de file mislukt, het is of een directory of de bestandsgrootte is 0 bytes", "Upload Error" => "Upload Fout", "Close" => "Sluit", @@ -39,8 +37,6 @@ "File upload is in progress. Leaving the page now will cancel the upload." => "Bestandsupload is bezig. Wanneer de pagina nu verlaten wordt, stopt de upload.", "URL cannot be empty." => "URL kan niet leeg zijn.", "Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Ongeldige mapnaam. Gebruik van'Gedeeld' is voorbehouden aan Owncloud", -"{count} files scanned" => "{count} bestanden gescanned", -"error while scanning" => "Fout tijdens het scannen", "Name" => "Naam", "Size" => "Bestandsgrootte", "Modified" => "Laatst aangepast", @@ -48,6 +44,7 @@ "{count} folders" => "{count} mappen", "1 file" => "1 bestand", "{count} files" => "{count} bestanden", +"Upload" => "Upload", "File handling" => "Bestand", "Maximum upload size" => "Maximale bestandsgrootte voor uploads", "max. possible: " => "max. mogelijk: ", @@ -60,12 +57,13 @@ "Text file" => "Tekstbestand", "Folder" => "Map", "From link" => "Vanaf link", -"Upload" => "Upload", +"Trash" => "Verwijderen", "Cancel upload" => "Upload afbreken", "Nothing in here. Upload something!" => "Er bevindt zich hier niets. Upload een bestand!", "Download" => "Download", "Upload too large" => "Bestanden te groot", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "De bestanden die u probeert te uploaden zijn groter dan de maximaal toegestane bestandsgrootte voor deze server.", "Files are being scanned, please wait." => "Bestanden worden gescand, even wachten.", -"Current scanning" => "Er wordt gescand" +"Current scanning" => "Er wordt gescand", +"Upgrading filesystem cache..." => "Upgraden bestandssysteem cache..." ); diff --git a/apps/files/l10n/nn_NO.php b/apps/files/l10n/nn_NO.php index 04e01a39cf..8a4ab91ea7 100644 --- a/apps/files/l10n/nn_NO.php +++ b/apps/files/l10n/nn_NO.php @@ -10,12 +10,12 @@ "Name" => "Namn", "Size" => "Storleik", "Modified" => "Endra", +"Upload" => "Last opp", "Maximum upload size" => "Maksimal opplastingsstorleik", "Save" => "Lagre", "New" => "Ny", "Text file" => "Tekst fil", "Folder" => "Mappe", -"Upload" => "Last opp", "Nothing in here. Upload something!" => "Ingenting her. Last noko opp!", "Download" => "Last ned", "Upload too large" => "For stor opplasting", diff --git a/apps/files/l10n/oc.php b/apps/files/l10n/oc.php index 36bbb43339..78045b299e 100644 --- a/apps/files/l10n/oc.php +++ b/apps/files/l10n/oc.php @@ -13,17 +13,16 @@ "suggest name" => "nom prepausat", "cancel" => "anulla", "undo" => "defar", -"generating ZIP-file, it may take some time." => "Fichièr ZIP a se far, aquò pòt trigar un briu.", "Unable to upload your file as it is a directory or has 0 bytes" => "Impossible d'amontcargar lo teu fichièr qu'es un repertòri o que ten pas que 0 octet.", "Upload Error" => "Error d'amontcargar", "Pending" => "Al esperar", "1 file uploading" => "1 fichièr al amontcargar", "Upload cancelled." => "Amontcargar anullat.", "File upload is in progress. Leaving the page now will cancel the upload." => "Un amontcargar es a se far. Daissar aquesta pagina ara tamparà lo cargament. ", -"error while scanning" => "error pendant l'exploracion", "Name" => "Nom", "Size" => "Talha", "Modified" => "Modificat", +"Upload" => "Amontcarga", "File handling" => "Manejament de fichièr", "Maximum upload size" => "Talha maximum d'amontcargament", "max. possible: " => "max. possible: ", @@ -35,7 +34,6 @@ "New" => "Nòu", "Text file" => "Fichièr de tèxte", "Folder" => "Dorsièr", -"Upload" => "Amontcarga", "Cancel upload" => " Anulla l'amontcargar", "Nothing in here. Upload something!" => "Pas res dedins. Amontcarga qualquaren", "Download" => "Avalcarga", diff --git a/apps/files/l10n/pl.php b/apps/files/l10n/pl.php index 226dae896c..6855850f0d 100644 --- a/apps/files/l10n/pl.php +++ b/apps/files/l10n/pl.php @@ -1,7 +1,4 @@ "Nie można było przenieść %s - Plik o takiej nazwie już istnieje", -"Could not move %s" => "Nie można było przenieść %s", -"Unable to rename file" => "Nie można zmienić nazwy pliku", "No file was uploaded. Unknown error" => "Plik nie został załadowany. Nieznany błąd", "There is no error, the file uploaded with success" => "Przesłano plik", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Wgrany plik przekracza wartość upload_max_filesize zdefiniowaną w php.ini: ", @@ -23,12 +20,9 @@ "replaced {new_name}" => "zastąpiony {new_name}", "undo" => "wróć", "replaced {new_name} with {old_name}" => "zastąpiony {new_name} z {old_name}", -"unshared {files}" => "Udostępniane wstrzymane {files}", -"deleted {files}" => "usunięto {files}", "'.' is an invalid file name." => "'.' jest nieprawidłową nazwą pliku.", "File name cannot be empty." => "Nazwa pliku nie może być pusta.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Niepoprawna nazwa, Znaki '\\', '/', '<', '>', ':', '\"', '|', '?' oraz '*'są niedozwolone.", -"generating ZIP-file, it may take some time." => "Generowanie pliku ZIP, może potrwać pewien czas.", "Unable to upload your file as it is a directory or has 0 bytes" => "Nie można wczytać pliku jeśli jest katalogiem lub ma 0 bajtów", "Upload Error" => "Błąd wczytywania", "Close" => "Zamknij", @@ -39,8 +33,6 @@ "File upload is in progress. Leaving the page now will cancel the upload." => "Wysyłanie pliku jest w toku. Teraz opuszczając stronę wysyłanie zostanie anulowane.", "URL cannot be empty." => "URL nie może być pusty.", "Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Nazwa folderu nieprawidłowa. Wykorzystanie \"Shared\" jest zarezerwowane przez Owncloud", -"{count} files scanned" => "{count} pliki skanowane", -"error while scanning" => "Wystąpił błąd podczas skanowania", "Name" => "Nazwa", "Size" => "Rozmiar", "Modified" => "Czas modyfikacji", @@ -48,6 +40,7 @@ "{count} folders" => "{count} foldery", "1 file" => "1 plik", "{count} files" => "{count} pliki", +"Upload" => "Prześlij", "File handling" => "Zarządzanie plikami", "Maximum upload size" => "Maksymalny rozmiar wysyłanego pliku", "max. possible: " => "max. możliwych", @@ -60,7 +53,6 @@ "Text file" => "Plik tekstowy", "Folder" => "Katalog", "From link" => "Z linku", -"Upload" => "Prześlij", "Cancel upload" => "Przestań wysyłać", "Nothing in here. Upload something!" => "Brak zawartości. Proszę wysłać pliki!", "Download" => "Pobiera element", diff --git a/apps/files/l10n/pt_BR.php b/apps/files/l10n/pt_BR.php index ece24c7a2f..361e81052b 100644 --- a/apps/files/l10n/pt_BR.php +++ b/apps/files/l10n/pt_BR.php @@ -7,6 +7,7 @@ "No file was uploaded" => "Nenhum arquivo foi transferido", "Missing a temporary folder" => "Pasta temporária não encontrada", "Failed to write to disk" => "Falha ao escrever no disco", +"Invalid directory." => "Diretório inválido.", "Files" => "Arquivos", "Unshare" => "Descompartilhar", "Delete" => "Excluir", @@ -18,10 +19,10 @@ "replaced {new_name}" => "substituído {new_name}", "undo" => "desfazer", "replaced {new_name} with {old_name}" => "Substituído {old_name} por {new_name} ", -"unshared {files}" => "{files} não compartilhados", -"deleted {files}" => "{files} apagados", +"'.' is an invalid file name." => "'.' é um nome de arquivo inválido.", +"File name cannot be empty." => "O nome do arquivo não pode estar vazio.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Nome inválido, '\\', '/', '<', '>', ':', '\"', '|', '?' e '*' não são permitidos.", -"generating ZIP-file, it may take some time." => "gerando arquivo ZIP, isso pode levar um tempo.", +"Your download is being prepared. This might take some time if the files are big." => "Seu download está sendo preparado. Isto pode levar algum tempo se os arquivos forem grandes.", "Unable to upload your file as it is a directory or has 0 bytes" => "Impossível enviar seus arquivo como diretório ou ele tem 0 bytes.", "Upload Error" => "Erro de envio", "Close" => "Fechar", @@ -31,8 +32,7 @@ "Upload cancelled." => "Envio cancelado.", "File upload is in progress. Leaving the page now will cancel the upload." => "Upload em andamento. Sair da página agora resultará no cancelamento do envio.", "URL cannot be empty." => "URL não pode ficar em branco", -"{count} files scanned" => "{count} arquivos scaneados", -"error while scanning" => "erro durante verificação", +"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Nome de pasta inválido. O uso de 'Shared' é reservado para o Owncloud", "Name" => "Nome", "Size" => "Tamanho", "Modified" => "Modificado", @@ -40,6 +40,7 @@ "{count} folders" => "{count} pastas", "1 file" => "1 arquivo", "{count} files" => "{count} arquivos", +"Upload" => "Carregar", "File handling" => "Tratamento de Arquivo", "Maximum upload size" => "Tamanho máximo para carregar", "max. possible: " => "max. possível:", @@ -52,7 +53,6 @@ "Text file" => "Arquivo texto", "Folder" => "Pasta", "From link" => "Do link", -"Upload" => "Carregar", "Cancel upload" => "Cancelar upload", "Nothing in here. Upload something!" => "Nada aqui.Carrege alguma coisa!", "Download" => "Baixar", diff --git a/apps/files/l10n/pt_PT.php b/apps/files/l10n/pt_PT.php index fb22894b34..b1d7385bc5 100644 --- a/apps/files/l10n/pt_PT.php +++ b/apps/files/l10n/pt_PT.php @@ -1,7 +1,4 @@ "Não foi possível mover o ficheiro %s - Já existe um ficheiro com esse nome", -"Could not move %s" => "Não foi possível move o ficheiro %s", -"Unable to rename file" => "Não foi possível renomear o ficheiro", "No file was uploaded. Unknown error" => "Nenhum ficheiro foi carregado. Erro desconhecido", "There is no error, the file uploaded with success" => "Sem erro, ficheiro enviado com sucesso", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "O ficheiro enviado excede o limite permitido na directiva do php.ini upload_max_filesize", @@ -18,29 +15,28 @@ "Rename" => "Renomear", "{new_name} already exists" => "O nome {new_name} já existe", "replace" => "substituir", -"suggest name" => "Sugira um nome", +"suggest name" => "sugira um nome", "cancel" => "cancelar", "replaced {new_name}" => "{new_name} substituido", "undo" => "desfazer", "replaced {new_name} with {old_name}" => "substituido {new_name} por {old_name}", -"unshared {files}" => "{files} não partilhado(s)", -"deleted {files}" => "{files} eliminado(s)", +"perform delete operation" => "Executar a tarefa de apagar", "'.' is an invalid file name." => "'.' não é um nome de ficheiro válido!", "File name cannot be empty." => "O nome do ficheiro não pode estar vazio.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Nome Inválido, os caracteres '\\', '/', '<', '>', ':', '\"', '|', '?' e '*' não são permitidos.", -"generating ZIP-file, it may take some time." => "a gerar o ficheiro ZIP, poderá demorar algum tempo.", +"Your storage is full, files can not be updated or synced anymore!" => "O seu armazenamento está cheio, os ficheiros não podem ser sincronizados.", +"Your storage is almost full ({usedSpacePercent}%)" => "O seu espaço de armazenamento está quase cheiro ({usedSpacePercent}%)", +"Your download is being prepared. This might take some time if the files are big." => "O seu download está a ser preparado. Este processo pode demorar algum tempo se os ficheiros forem grandes.", "Unable to upload your file as it is a directory or has 0 bytes" => "Não é possível fazer o envio do ficheiro devido a ser uma pasta ou ter 0 bytes", "Upload Error" => "Erro no envio", "Close" => "Fechar", "Pending" => "Pendente", "1 file uploading" => "A enviar 1 ficheiro", "{count} files uploading" => "A carregar {count} ficheiros", -"Upload cancelled." => "O envio foi cancelado.", +"Upload cancelled." => "Envio cancelado.", "File upload is in progress. Leaving the page now will cancel the upload." => "Envio de ficheiro em progresso. Irá cancelar o envio se sair da página agora.", "URL cannot be empty." => "O URL não pode estar vazio.", "Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Nome de pasta inválido. O Uso de 'shared' é reservado para o ownCloud", -"{count} files scanned" => "{count} ficheiros analisados", -"error while scanning" => "erro ao analisar", "Name" => "Nome", "Size" => "Tamanho", "Modified" => "Modificado", @@ -48,6 +44,7 @@ "{count} folders" => "{count} pastas", "1 file" => "1 ficheiro", "{count} files" => "{count} ficheiros", +"Upload" => "Enviar", "File handling" => "Manuseamento de ficheiros", "Maximum upload size" => "Tamanho máximo de envio", "max. possible: " => "max. possivel: ", @@ -60,12 +57,13 @@ "Text file" => "Ficheiro de texto", "Folder" => "Pasta", "From link" => "Da ligação", -"Upload" => "Enviar", +"Trash" => "Lixo", "Cancel upload" => "Cancelar envio", "Nothing in here. Upload something!" => "Vazio. Envie alguma coisa!", "Download" => "Transferir", "Upload too large" => "Envio muito grande", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Os ficheiros que está a tentar enviar excedem o tamanho máximo de envio permitido neste servidor.", "Files are being scanned, please wait." => "Os ficheiros estão a ser analisados, por favor aguarde.", -"Current scanning" => "Análise actual" +"Current scanning" => "Análise actual", +"Upgrading filesystem cache..." => "Atualizar cache do sistema de ficheiros..." ); diff --git a/apps/files/l10n/ro.php b/apps/files/l10n/ro.php index c34a341e53..7837b1f5b3 100644 --- a/apps/files/l10n/ro.php +++ b/apps/files/l10n/ro.php @@ -1,6 +1,4 @@ "Nu s-a putut muta %s", -"Unable to rename file" => "Nu s-a putut redenumi fișierul", "No file was uploaded. Unknown error" => "Nici un fișier nu a fost încărcat. Eroare necunoscută", "There is no error, the file uploaded with success" => "Nicio eroare, fișierul a fost încărcat cu succes", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Fisierul incarcat depaseste upload_max_filesize permisi in php.ini: ", @@ -22,12 +20,10 @@ "replaced {new_name}" => "inlocuit {new_name}", "undo" => "Anulează ultima acțiune", "replaced {new_name} with {old_name}" => "{new_name} inlocuit cu {old_name}", -"unshared {files}" => "nedistribuit {files}", -"deleted {files}" => "Sterse {files}", "'.' is an invalid file name." => "'.' este un nume invalid de fișier.", "File name cannot be empty." => "Numele fișierului nu poate rămâne gol.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Nume invalid, '\\', '/', '<', '>', ':', '\"', '|', '?' si '*' nu sunt permise.", -"generating ZIP-file, it may take some time." => "se generază fișierul ZIP, va dura ceva timp.", +"Your download is being prepared. This might take some time if the files are big." => "Se pregătește descărcarea. Aceasta poate să dureze ceva timp dacă fișierele sunt mari.", "Unable to upload your file as it is a directory or has 0 bytes" => "Nu s-a putut încărca fișierul tău deoarece pare să fie un director sau are 0 bytes.", "Upload Error" => "Eroare la încărcare", "Close" => "Închide", @@ -37,8 +33,7 @@ "Upload cancelled." => "Încărcare anulată.", "File upload is in progress. Leaving the page now will cancel the upload." => "Fișierul este în curs de încărcare. Părăsirea paginii va întrerupe încărcarea.", "URL cannot be empty." => "Adresa URL nu poate fi goală.", -"{count} files scanned" => "{count} fisiere scanate", -"error while scanning" => "eroare la scanarea", +"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Invalid folder name. Usage of 'Shared' is reserved by Ownclou", "Name" => "Nume", "Size" => "Dimensiune", "Modified" => "Modificat", @@ -46,6 +41,7 @@ "{count} folders" => "{count} foldare", "1 file" => "1 fisier", "{count} files" => "{count} fisiere", +"Upload" => "Încarcă", "File handling" => "Manipulare fișiere", "Maximum upload size" => "Dimensiune maximă admisă la încărcare", "max. possible: " => "max. posibil:", @@ -58,7 +54,6 @@ "Text file" => "Fișier text", "Folder" => "Dosar", "From link" => "de la adresa", -"Upload" => "Încarcă", "Cancel upload" => "Anulează încărcarea", "Nothing in here. Upload something!" => "Nimic aici. Încarcă ceva!", "Download" => "Descarcă", diff --git a/apps/files/l10n/ru.php b/apps/files/l10n/ru.php index 49ead61f67..716afa5f29 100644 --- a/apps/files/l10n/ru.php +++ b/apps/files/l10n/ru.php @@ -1,7 +1,4 @@ "Невозможно переместить %s - файл с таким именем уже существует", -"Could not move %s" => "Невозможно переместить %s", -"Unable to rename file" => "Невозможно переименовать файл", "No file was uploaded. Unknown error" => "Файл не был загружен. Неизвестная ошибка", "There is no error, the file uploaded with success" => "Файл успешно загружен", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Файл превышает размер установленный upload_max_filesize в php.ini:", @@ -23,12 +20,9 @@ "replaced {new_name}" => "заменено {new_name}", "undo" => "отмена", "replaced {new_name} with {old_name}" => "заменено {new_name} на {old_name}", -"unshared {files}" => "не опубликованные {files}", -"deleted {files}" => "удаленные {files}", "'.' is an invalid file name." => "'.' - неправильное имя файла.", "File name cannot be empty." => "Имя файла не может быть пустым.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Неправильное имя, '\\', '/', '<', '>', ':', '\"', '|', '?' и '*' недопустимы.", -"generating ZIP-file, it may take some time." => "создание ZIP-файла, это может занять некоторое время.", "Unable to upload your file as it is a directory or has 0 bytes" => "Не удается загрузить файл размером 0 байт в каталог", "Upload Error" => "Ошибка загрузки", "Close" => "Закрыть", @@ -39,8 +33,6 @@ "File upload is in progress. Leaving the page now will cancel the upload." => "Файл в процессе загрузки. Покинув страницу вы прервёте загрузку.", "URL cannot be empty." => "Ссылка не может быть пустой.", "Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Неправильное имя каталога. Имя 'Shared' зарезервировано.", -"{count} files scanned" => "{count} файлов просканировано", -"error while scanning" => "ошибка во время санирования", "Name" => "Название", "Size" => "Размер", "Modified" => "Изменён", @@ -48,6 +40,7 @@ "{count} folders" => "{count} папок", "1 file" => "1 файл", "{count} files" => "{count} файлов", +"Upload" => "Загрузить", "File handling" => "Управление файлами", "Maximum upload size" => "Максимальный размер загружаемого файла", "max. possible: " => "макс. возможно: ", @@ -60,7 +53,6 @@ "Text file" => "Текстовый файл", "Folder" => "Папка", "From link" => "Из ссылки", -"Upload" => "Загрузить", "Cancel upload" => "Отмена загрузки", "Nothing in here. Upload something!" => "Здесь ничего нет. Загрузите что-нибудь!", "Download" => "Скачать", diff --git a/apps/files/l10n/ru_RU.php b/apps/files/l10n/ru_RU.php index 16bcc54e59..e1952567d3 100644 --- a/apps/files/l10n/ru_RU.php +++ b/apps/files/l10n/ru_RU.php @@ -7,6 +7,8 @@ "No file was uploaded" => "Файл не был загружен", "Missing a temporary folder" => "Отсутствует временная папка", "Failed to write to disk" => "Не удалось записать на диск", +"Not enough space available" => "Не достаточно свободного места", +"Invalid directory." => "Неверный каталог.", "Files" => "Файлы", "Unshare" => "Скрыть", "Delete" => "Удалить", @@ -18,10 +20,9 @@ "replaced {new_name}" => "заменено {новое_имя}", "undo" => "отменить действие", "replaced {new_name} with {old_name}" => "заменено {новое_имя} с {старое_имя}", -"unshared {files}" => "Cовместное использование прекращено {файлы}", -"deleted {files}" => "удалено {файлы}", +"'.' is an invalid file name." => "'.' является неверным именем файла.", +"File name cannot be empty." => "Имя файла не может быть пустым.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Некорректное имя, '\\', '/', '<', '>', ':', '\"', '|', '?' и '*' не допустимы.", -"generating ZIP-file, it may take some time." => "Создание ZIP-файла, это может занять некоторое время.", "Unable to upload your file as it is a directory or has 0 bytes" => "Невозможно загрузить файл,\n так как он имеет нулевой размер или является директорией", "Upload Error" => "Ошибка загрузки", "Close" => "Закрыть", @@ -31,8 +32,7 @@ "Upload cancelled." => "Загрузка отменена", "File upload is in progress. Leaving the page now will cancel the upload." => "Процесс загрузки файла. Если покинуть страницу сейчас, загрузка будет отменена.", "URL cannot be empty." => "URL не должен быть пустым.", -"{count} files scanned" => "{количество} файлов отсканировано", -"error while scanning" => "ошибка при сканировании", +"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Неверное имя папки. Использование наименования 'Опубликовано' зарезервировано Owncloud", "Name" => "Имя", "Size" => "Размер", "Modified" => "Изменен", @@ -40,6 +40,7 @@ "{count} folders" => "{количество} папок", "1 file" => "1 файл", "{count} files" => "{количество} файлов", +"Upload" => "Загрузить ", "File handling" => "Работа с файлами", "Maximum upload size" => "Максимальный размер загружаемого файла", "max. possible: " => "Максимально возможный", @@ -52,12 +53,12 @@ "Text file" => "Текстовый файл", "Folder" => "Папка", "From link" => "По ссылке", -"Upload" => "Загрузить ", "Cancel upload" => "Отмена загрузки", "Nothing in here. Upload something!" => "Здесь ничего нет. Загрузите что-нибудь!", "Download" => "Загрузить", "Upload too large" => "Загрузка слишком велика", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Размер файлов, которые Вы пытаетесь загрузить, превышает максимально допустимый размер для загрузки на данный сервер.", "Files are being scanned, please wait." => "Файлы сканируются, пожалуйста, подождите.", -"Current scanning" => "Текущее сканирование" +"Current scanning" => "Текущее сканирование", +"Upgrading filesystem cache..." => "Обновление кэша файловой системы... " ); diff --git a/apps/files/l10n/si_LK.php b/apps/files/l10n/si_LK.php index e1e06c4f81..316470d839 100644 --- a/apps/files/l10n/si_LK.php +++ b/apps/files/l10n/si_LK.php @@ -14,19 +14,18 @@ "suggest name" => "නමක් යෝජනා කරන්න", "cancel" => "අත් හරින්න", "undo" => "නිෂ්ප්‍රභ කරන්න", -"generating ZIP-file, it may take some time." => "ගොනුවක් සෑදෙමින් පවතී. කෙටි වේලාවක් ගත විය හැක", "Upload Error" => "උඩුගත කිරීමේ දෝශයක්", "Close" => "වසන්න", "1 file uploading" => "1 ගොනුවක් උඩගත කෙරේ", "Upload cancelled." => "උඩුගත කිරීම අත් හරින්න ලදී", "File upload is in progress. Leaving the page now will cancel the upload." => "උඩුගතකිරීමක් සිදුවේ. පිටුව හැර යාමෙන් එය නැවතෙනු ඇත", "URL cannot be empty." => "යොමුව හිස් විය නොහැක", -"error while scanning" => "පරීක්ෂා කිරීමේදී දෝෂයක්", "Name" => "නම", "Size" => "ප්‍රමාණය", "Modified" => "වෙනස් කළ", "1 folder" => "1 ෆොල්ඩරයක්", "1 file" => "1 ගොනුවක්", +"Upload" => "උඩුගත කිරීම", "File handling" => "ගොනු පරිහරණය", "Maximum upload size" => "උඩුගත කිරීමක උපරිම ප්‍රමාණය", "max. possible: " => "හැකි උපරිමය:", @@ -39,7 +38,6 @@ "Text file" => "පෙළ ගොනුව", "Folder" => "ෆෝල්ඩරය", "From link" => "යොමුවෙන්", -"Upload" => "උඩුගත කිරීම", "Cancel upload" => "උඩුගත කිරීම අත් හරින්න", "Nothing in here. Upload something!" => "මෙහි කිසිවක් නොමැත. යමක් උඩුගත කරන්න", "Download" => "බාගත කිරීම", diff --git a/apps/files/l10n/sk_SK.php b/apps/files/l10n/sk_SK.php index 003b1aff22..a94d96f6ae 100644 --- a/apps/files/l10n/sk_SK.php +++ b/apps/files/l10n/sk_SK.php @@ -7,6 +7,8 @@ "No file was uploaded" => "Žiaden súbor nebol nahraný", "Missing a temporary folder" => "Chýbajúci dočasný priečinok", "Failed to write to disk" => "Zápis na disk sa nepodaril", +"Not enough space available" => "Nie je k dispozícii dostatok miesta", +"Invalid directory." => "Neplatný adresár", "Files" => "Súbory", "Unshare" => "Nezdielať", "Delete" => "Odstrániť", @@ -18,10 +20,12 @@ "replaced {new_name}" => "prepísaný {new_name}", "undo" => "vrátiť", "replaced {new_name} with {old_name}" => "prepísaný {new_name} súborom {old_name}", -"unshared {files}" => "zdieľanie zrušené pre {files}", -"deleted {files}" => "zmazané {files}", +"'.' is an invalid file name." => "'.' je neplatné meno súboru.", +"File name cannot be empty." => "Meno súboru nemôže byť prázdne", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Nesprávne meno, '\\', '/', '<', '>', ':', '\"', '|', '?' a '*' nie sú povolené hodnoty.", -"generating ZIP-file, it may take some time." => "generujem ZIP-súbor, môže to chvíľu trvať.", +"Your storage is full, files can not be updated or synced anymore!" => "Vaše úložisko je plné. Súbory nemožno aktualizovať ani synchronizovať!", +"Your storage is almost full ({usedSpacePercent}%)" => "Vaše úložisko je takmer plné ({usedSpacePercent}%)", +"Your download is being prepared. This might take some time if the files are big." => "Vaše sťahovanie sa pripravuje. Ak sú sťahované súbory veľké, môže to chvíľu trvať.", "Unable to upload your file as it is a directory or has 0 bytes" => "Nemôžem nahrať súbor lebo je to priečinok alebo má 0 bajtov.", "Upload Error" => "Chyba odosielania", "Close" => "Zavrieť", @@ -31,8 +35,7 @@ "Upload cancelled." => "Odosielanie zrušené", "File upload is in progress. Leaving the page now will cancel the upload." => "Opustenie stránky zruší práve prebiehajúce odosielanie súboru.", "URL cannot be empty." => "URL nemôže byť prázdne", -"{count} files scanned" => "{count} súborov prehľadaných", -"error while scanning" => "chyba počas kontroly", +"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Neplatné meno adresára. Používanie mena 'Shared' je vyhradené len pre Owncloud", "Name" => "Meno", "Size" => "Veľkosť", "Modified" => "Upravené", @@ -40,6 +43,7 @@ "{count} folders" => "{count} priečinkov", "1 file" => "1 súbor", "{count} files" => "{count} súborov", +"Upload" => "Odoslať", "File handling" => "Nastavenie správanie k súborom", "Maximum upload size" => "Maximálna veľkosť odosielaného súboru", "max. possible: " => "najväčšie možné:", @@ -52,7 +56,6 @@ "Text file" => "Textový súbor", "Folder" => "Priečinok", "From link" => "Z odkazu", -"Upload" => "Odoslať", "Cancel upload" => "Zrušiť odosielanie", "Nothing in here. Upload something!" => "Žiadny súbor. Nahrajte niečo!", "Download" => "Stiahnuť", diff --git a/apps/files/l10n/sl.php b/apps/files/l10n/sl.php index 2a0f450638..d55b4207d2 100644 --- a/apps/files/l10n/sl.php +++ b/apps/files/l10n/sl.php @@ -18,10 +18,7 @@ "replaced {new_name}" => "zamenjano je ime {new_name}", "undo" => "razveljavi", "replaced {new_name} with {old_name}" => "zamenjano ime {new_name} z imenom {old_name}", -"unshared {files}" => "odstranjeno iz souporabe {files}", -"deleted {files}" => "izbrisano {files}", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Neveljavno ime, znaki '\\', '/', '<', '>', ':', '\"', '|', '?' in '*' niso dovoljeni.", -"generating ZIP-file, it may take some time." => "Ustvarjanje datoteke ZIP. To lahko traja nekaj časa.", "Unable to upload your file as it is a directory or has 0 bytes" => "Pošiljanje ni mogoče, saj gre za mapo, ali pa je datoteka velikosti 0 bajtov.", "Upload Error" => "Napaka med nalaganjem", "Close" => "Zapri", @@ -31,8 +28,6 @@ "Upload cancelled." => "Pošiljanje je preklicano.", "File upload is in progress. Leaving the page now will cancel the upload." => "V teku je pošiljanje datoteke. Če zapustite to stran zdaj, bo pošiljanje preklicano.", "URL cannot be empty." => "Naslov URL ne sme biti prazen.", -"{count} files scanned" => "{count} files scanned", -"error while scanning" => "napaka med pregledovanjem datotek", "Name" => "Ime", "Size" => "Velikost", "Modified" => "Spremenjeno", @@ -40,6 +35,7 @@ "{count} folders" => "{count} map", "1 file" => "1 datoteka", "{count} files" => "{count} datotek", +"Upload" => "Pošlji", "File handling" => "Upravljanje z datotekami", "Maximum upload size" => "Največja velikost za pošiljanja", "max. possible: " => "največ mogoče:", @@ -52,7 +48,6 @@ "Text file" => "Besedilna datoteka", "Folder" => "Mapa", "From link" => "Iz povezave", -"Upload" => "Pošlji", "Cancel upload" => "Prekliči pošiljanje", "Nothing in here. Upload something!" => "Tukaj ni ničesar. Naložite kaj!", "Download" => "Prejmi", diff --git a/apps/files/l10n/sr.php b/apps/files/l10n/sr.php index ecde8be4cc..188c8fc0da 100644 --- a/apps/files/l10n/sr.php +++ b/apps/files/l10n/sr.php @@ -17,10 +17,7 @@ "replaced {new_name}" => "замењено {new_name}", "undo" => "опозови", "replaced {new_name} with {old_name}" => "замењено {new_name} са {old_name}", -"unshared {files}" => "укинуто дељење {files}", -"deleted {files}" => "обрисано {files}", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Неисправан назив. Следећи знакови нису дозвољени: \\, /, <, >, :, \", |, ? и *.", -"generating ZIP-file, it may take some time." => "правим ZIP датотеку…", "Unable to upload your file as it is a directory or has 0 bytes" => "Не могу да отпремим датотеку као фасциклу или она има 0 бајтова", "Upload Error" => "Грешка при отпремању", "Close" => "Затвори", @@ -29,8 +26,6 @@ "{count} files uploading" => "Отпремам {count} датотеке/а", "Upload cancelled." => "Отпремање је прекинуто.", "File upload is in progress. Leaving the page now will cancel the upload." => "Отпремање датотеке је у току. Ако сада напустите страницу, прекинућете отпремање.", -"{count} files scanned" => "Скенирано датотека: {count}", -"error while scanning" => "грешка при скенирању", "Name" => "Назив", "Size" => "Величина", "Modified" => "Измењено", @@ -38,6 +33,7 @@ "{count} folders" => "{count} фасцикле/и", "1 file" => "1 датотека", "{count} files" => "{count} датотеке/а", +"Upload" => "Отпреми", "File handling" => "Управљање датотекама", "Maximum upload size" => "Највећа величина датотеке", "max. possible: " => "највећа величина:", @@ -50,7 +46,6 @@ "Text file" => "текстуална датотека", "Folder" => "фасцикла", "From link" => "Са везе", -"Upload" => "Отпреми", "Cancel upload" => "Прекини отпремање", "Nothing in here. Upload something!" => "Овде нема ничег. Отпремите нешто!", "Download" => "Преузми", diff --git a/apps/files/l10n/sr@latin.php b/apps/files/l10n/sr@latin.php index fddaf5840c..0fda24532d 100644 --- a/apps/files/l10n/sr@latin.php +++ b/apps/files/l10n/sr@latin.php @@ -10,9 +10,9 @@ "Name" => "Ime", "Size" => "Veličina", "Modified" => "Zadnja izmena", +"Upload" => "Pošalji", "Maximum upload size" => "Maksimalna veličina pošiljke", "Save" => "Snimi", -"Upload" => "Pošalji", "Nothing in here. Upload something!" => "Ovde nema ničeg. Pošaljite nešto!", "Download" => "Preuzmi", "Upload too large" => "Pošiljka je prevelika", diff --git a/apps/files/l10n/sv.php b/apps/files/l10n/sv.php index 7277ec1785..55493e2494 100644 --- a/apps/files/l10n/sv.php +++ b/apps/files/l10n/sv.php @@ -20,12 +20,13 @@ "replaced {new_name}" => "ersatt {new_name}", "undo" => "ångra", "replaced {new_name} with {old_name}" => "ersatt {new_name} med {old_name}", -"unshared {files}" => "stoppad delning {files}", -"deleted {files}" => "raderade {files}", +"perform delete operation" => "utför raderingen", "'.' is an invalid file name." => "'.' är ett ogiltigt filnamn.", "File name cannot be empty." => "Filnamn kan inte vara tomt.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Ogiltigt namn, '\\', '/', '<', '>', ':', '\"', '|', '?' och '*' är inte tillåtet.", -"generating ZIP-file, it may take some time." => "genererar ZIP-fil, det kan ta lite tid.", +"Your storage is full, files can not be updated or synced anymore!" => "Ditt lagringsutrymme är fullt, filer kan ej längre laddas upp eller synkas!", +"Your storage is almost full ({usedSpacePercent}%)" => "Ditt lagringsutrymme är nästan fullt ({usedSpacePercent}%)", +"Your download is being prepared. This might take some time if the files are big." => "Din nedladdning förbereds. Det kan ta tid om det är stora filer.", "Unable to upload your file as it is a directory or has 0 bytes" => "Kunde inte ladda upp dina filer eftersom det antingen är en mapp eller har 0 bytes.", "Upload Error" => "Uppladdningsfel", "Close" => "Stäng", @@ -36,8 +37,6 @@ "File upload is in progress. Leaving the page now will cancel the upload." => "Filuppladdning pågår. Lämnar du sidan så avbryts uppladdningen.", "URL cannot be empty." => "URL kan inte vara tom.", "Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Ogiltigt mappnamn. Användande av 'Shared' är reserverat av ownCloud", -"{count} files scanned" => "{count} filer skannade", -"error while scanning" => "fel vid skanning", "Name" => "Namn", "Size" => "Storlek", "Modified" => "Ändrad", @@ -45,6 +44,7 @@ "{count} folders" => "{count} mappar", "1 file" => "1 fil", "{count} files" => "{count} filer", +"Upload" => "Ladda upp", "File handling" => "Filhantering", "Maximum upload size" => "Maximal storlek att ladda upp", "max. possible: " => "max. möjligt:", @@ -57,12 +57,13 @@ "Text file" => "Textfil", "Folder" => "Mapp", "From link" => "Från länk", -"Upload" => "Ladda upp", +"Trash" => "Papperskorgen", "Cancel upload" => "Avbryt uppladdning", "Nothing in here. Upload something!" => "Ingenting här. Ladda upp något!", "Download" => "Ladda ner", "Upload too large" => "För stor uppladdning", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Filerna du försöker ladda upp överstiger den maximala storleken för filöverföringar på servern.", "Files are being scanned, please wait." => "Filer skannas, var god vänta", -"Current scanning" => "Aktuell skanning" +"Current scanning" => "Aktuell skanning", +"Upgrading filesystem cache..." => "Uppgraderar filsystemets cache..." ); diff --git a/apps/files/l10n/ta_LK.php b/apps/files/l10n/ta_LK.php index 16cab5cf96..383b4ef6f8 100644 --- a/apps/files/l10n/ta_LK.php +++ b/apps/files/l10n/ta_LK.php @@ -17,10 +17,7 @@ "replaced {new_name}" => "மாற்றப்பட்டது {new_name}", "undo" => "முன் செயல் நீக்கம் ", "replaced {new_name} with {old_name}" => "{new_name} ஆனது {old_name} இனால் மாற்றப்பட்டது", -"unshared {files}" => "பகிரப்படாதது {கோப்புகள்}", -"deleted {files}" => "நீக்கப்பட்டது {கோப்புகள்}", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "செல்லுபடியற்ற பெயர்,'\\', '/', '<', '>', ':', '\"', '|', '?' மற்றும் '*' ஆகியன அனுமதிக்கப்படமாட்டாது.", -"generating ZIP-file, it may take some time." => " ZIP கோப்பு உருவாக்கப்படுகின்றது, இது சில நேரம் ஆகலாம்.", "Unable to upload your file as it is a directory or has 0 bytes" => "அடைவு அல்லது 0 bytes ஐ கொண்டுள்ளதால் உங்களுடைய கோப்பை பதிவேற்ற முடியவில்லை", "Upload Error" => "பதிவேற்றல் வழு", "Close" => "மூடுக", @@ -30,8 +27,6 @@ "Upload cancelled." => "பதிவேற்றல் இரத்து செய்யப்பட்டுள்ளது", "File upload is in progress. Leaving the page now will cancel the upload." => "கோப்பு பதிவேற்றம் செயல்பாட்டில் உள்ளது. இந்தப் பக்கத்திலிருந்து வெறியேறுவதானது பதிவேற்றலை இரத்து செய்யும்.", "URL cannot be empty." => "URL வெறுமையாக இருக்கமுடியாது.", -"{count} files scanned" => "{எண்ணிக்கை} கோப்புகள் வருடப்பட்டது", -"error while scanning" => "வருடும் போதான வழு", "Name" => "பெயர்", "Size" => "அளவு", "Modified" => "மாற்றப்பட்டது", @@ -39,6 +34,7 @@ "{count} folders" => "{எண்ணிக்கை} கோப்புறைகள்", "1 file" => "1 கோப்பு", "{count} files" => "{எண்ணிக்கை} கோப்புகள்", +"Upload" => "பதிவேற்றுக", "File handling" => "கோப்பு கையாளுதல்", "Maximum upload size" => "பதிவேற்றக்கூடிய ஆகக்கூடிய அளவு ", "max. possible: " => "ஆகக் கூடியது:", @@ -51,7 +47,6 @@ "Text file" => "கோப்பு உரை", "Folder" => "கோப்புறை", "From link" => "இணைப்பிலிருந்து", -"Upload" => "பதிவேற்றுக", "Cancel upload" => "பதிவேற்றலை இரத்து செய்க", "Nothing in here. Upload something!" => "இங்கு ஒன்றும் இல்லை. ஏதாவது பதிவேற்றுக!", "Download" => "பதிவிறக்குக", diff --git a/apps/files/l10n/th_TH.php b/apps/files/l10n/th_TH.php index 3fda142a4e..06dab9d8e6 100644 --- a/apps/files/l10n/th_TH.php +++ b/apps/files/l10n/th_TH.php @@ -7,6 +7,8 @@ "No file was uploaded" => "ยังไม่มีไฟล์ที่ถูกอัพโหลด", "Missing a temporary folder" => "แฟ้มเอกสารชั่วคราวเกิดการสูญหาย", "Failed to write to disk" => "เขียนข้อมูลลงแผ่นดิสก์ล้มเหลว", +"Not enough space available" => "มีพื้นที่เหลือไม่เพียงพอ", +"Invalid directory." => "ไดเร็กทอรี่ไม่ถูกต้อง", "Files" => "ไฟล์", "Unshare" => "ยกเลิกการแชร์ข้อมูล", "Delete" => "ลบ", @@ -18,10 +20,13 @@ "replaced {new_name}" => "แทนที่ {new_name} แล้ว", "undo" => "เลิกทำ", "replaced {new_name} with {old_name}" => "แทนที่ {new_name} ด้วย {old_name} แล้ว", -"unshared {files}" => "ยกเลิกการแชร์แล้ว {files} ไฟล์", -"deleted {files}" => "ลบไฟล์แล้ว {files} ไฟล์", +"perform delete operation" => "ดำเนินการตามคำสั่งลบ", +"'.' is an invalid file name." => "'.' เป็นชื่อไฟล์ที่ไม่ถูกต้อง", +"File name cannot be empty." => "ชื่อไฟล์ไม่สามารถเว้นว่างได้", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "ชื่อที่ใช้ไม่ถูกต้อง, '\\', '/', '<', '>', ':', '\"', '|', '?' และ '*' ไม่ได้รับอนุญาตให้ใช้งานได้", -"generating ZIP-file, it may take some time." => "กำลังสร้างไฟล์บีบอัด ZIP อาจใช้เวลาสักครู่", +"Your storage is full, files can not be updated or synced anymore!" => "พื้นที่จัดเก็บข้อมูลของคุณเต็มแล้ว ไม่สามารถอัพเดทหรือผสานไฟล์ต่างๆได้อีกต่อไป", +"Your storage is almost full ({usedSpacePercent}%)" => "พื้นที่จัดเก็บข้อมูลของคุณใกล้เต็มแล้ว ({usedSpacePercent}%)", +"Your download is being prepared. This might take some time if the files are big." => "กำลังเตรียมดาวน์โหลดข้อมูล หากไฟล์มีขนาดใหญ่ อาจใช้เวลาสักครู่", "Unable to upload your file as it is a directory or has 0 bytes" => "ไม่สามารถอัพโหลดไฟล์ของคุณได้ เนื่องจากไฟล์ดังกล่าวเป็นไดเร็กทอรี่หรือมีขนาด 0 ไบต์", "Upload Error" => "เกิดข้อผิดพลาดในการอัพโหลด", "Close" => "ปิด", @@ -31,8 +36,7 @@ "Upload cancelled." => "การอัพโหลดถูกยกเลิก", "File upload is in progress. Leaving the page now will cancel the upload." => "การอัพโหลดไฟล์กำลังอยู่ในระหว่างดำเนินการ การออกจากหน้าเว็บนี้จะทำให้การอัพโหลดถูกยกเลิก", "URL cannot be empty." => "URL ไม่สามารถเว้นว่างได้", -"{count} files scanned" => "สแกนไฟล์แล้ว {count} ไฟล์", -"error while scanning" => "พบข้อผิดพลาดในระหว่างการสแกนไฟล์", +"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "ชื่อโฟลเดอร์ไม่ถูกต้อง การใช้งาน 'แชร์' สงวนไว้สำหรับ Owncloud เท่านั้น", "Name" => "ชื่อ", "Size" => "ขนาด", "Modified" => "ปรับปรุงล่าสุด", @@ -40,6 +44,7 @@ "{count} folders" => "{count} โฟลเดอร์", "1 file" => "1 ไฟล์", "{count} files" => "{count} ไฟล์", +"Upload" => "อัพโหลด", "File handling" => "การจัดกาไฟล์", "Maximum upload size" => "ขนาดไฟล์สูงสุดที่อัพโหลดได้", "max. possible: " => "จำนวนสูงสุดที่สามารถทำได้: ", @@ -52,12 +57,13 @@ "Text file" => "ไฟล์ข้อความ", "Folder" => "แฟ้มเอกสาร", "From link" => "จากลิงก์", -"Upload" => "อัพโหลด", +"Trash" => "ถังขยะ", "Cancel upload" => "ยกเลิกการอัพโหลด", "Nothing in here. Upload something!" => "ยังไม่มีไฟล์ใดๆอยู่ที่นี่ กรุณาอัพโหลดไฟล์!", "Download" => "ดาวน์โหลด", "Upload too large" => "ไฟล์ที่อัพโหลดมีขนาดใหญ่เกินไป", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "ไฟล์ที่คุณพยายามที่จะอัพโหลดมีขนาดเกินกว่าขนาดสูงสุดที่กำหนดไว้ให้อัพโหลดได้สำหรับเซิร์ฟเวอร์นี้", "Files are being scanned, please wait." => "ไฟล์กำลังอยู่ระหว่างการสแกน, กรุณารอสักครู่.", -"Current scanning" => "ไฟล์ที่กำลังสแกนอยู่ขณะนี้" +"Current scanning" => "ไฟล์ที่กำลังสแกนอยู่ขณะนี้", +"Upgrading filesystem cache..." => "กำลังอัพเกรดหน่วยความจำแคชของระบบไฟล์..." ); diff --git a/apps/files/l10n/tr.php b/apps/files/l10n/tr.php index b32da7de25..3412d8ad44 100644 --- a/apps/files/l10n/tr.php +++ b/apps/files/l10n/tr.php @@ -7,6 +7,8 @@ "No file was uploaded" => "Hiç dosya yüklenmedi", "Missing a temporary folder" => "Geçici bir klasör eksik", "Failed to write to disk" => "Diske yazılamadı", +"Not enough space available" => "Yeterli disk alanı yok", +"Invalid directory." => "Geçersiz dizin.", "Files" => "Dosyalar", "Unshare" => "Paylaşılmayan", "Delete" => "Sil", @@ -18,10 +20,10 @@ "replaced {new_name}" => "değiştirilen {new_name}", "undo" => "geri al", "replaced {new_name} with {old_name}" => "{new_name} ismi {old_name} ile değiştirildi", -"unshared {files}" => "paylaşılmamış {files}", -"deleted {files}" => "silinen {files}", +"'.' is an invalid file name." => "'.' geçersiz dosya adı.", +"File name cannot be empty." => "Dosya adı boş olamaz.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Geçersiz isim, '\\', '/', '<', '>', ':', '\"', '|', '?' ve '*' karakterlerine izin verilmemektedir.", -"generating ZIP-file, it may take some time." => "ZIP dosyası oluşturuluyor, biraz sürebilir.", +"Your download is being prepared. This might take some time if the files are big." => "İndirmeniz hazırlanıyor. Dosya büyük ise biraz zaman alabilir.", "Unable to upload your file as it is a directory or has 0 bytes" => "Dosyanızın boyutu 0 byte olduğundan veya bir dizin olduğundan yüklenemedi", "Upload Error" => "Yükleme hatası", "Close" => "Kapat", @@ -31,8 +33,7 @@ "Upload cancelled." => "Yükleme iptal edildi.", "File upload is in progress. Leaving the page now will cancel the upload." => "Dosya yükleme işlemi sürüyor. Şimdi sayfadan ayrılırsanız işleminiz iptal olur.", "URL cannot be empty." => "URL boş olamaz.", -"{count} files scanned" => "{count} dosya tarandı", -"error while scanning" => "tararamada hata oluşdu", +"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Geçersiz dizin adı. Shared isminin kullanımı Owncloud tarafından rezerver edilmiştir.", "Name" => "Ad", "Size" => "Boyut", "Modified" => "Değiştirilme", @@ -40,6 +41,7 @@ "{count} folders" => "{count} dizin", "1 file" => "1 dosya", "{count} files" => "{count} dosya", +"Upload" => "Yükle", "File handling" => "Dosya taşıma", "Maximum upload size" => "Maksimum yükleme boyutu", "max. possible: " => "mümkün olan en fazla: ", @@ -52,7 +54,6 @@ "Text file" => "Metin dosyası", "Folder" => "Klasör", "From link" => "Bağlantıdan", -"Upload" => "Yükle", "Cancel upload" => "Yüklemeyi iptal et", "Nothing in here. Upload something!" => "Burada hiçbir şey yok. Birşeyler yükleyin!", "Download" => "İndir", diff --git a/apps/files/l10n/uk.php b/apps/files/l10n/uk.php index eba48a41cb..9831dfe0f8 100644 --- a/apps/files/l10n/uk.php +++ b/apps/files/l10n/uk.php @@ -18,10 +18,7 @@ "replaced {new_name}" => "замінено {new_name}", "undo" => "відмінити", "replaced {new_name} with {old_name}" => "замінено {new_name} на {old_name}", -"unshared {files}" => "неопубліковано {files}", -"deleted {files}" => "видалено {files}", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Невірне ім'я, '\\', '/', '<', '>', ':', '\"', '|', '?' та '*' не дозволені.", -"generating ZIP-file, it may take some time." => "Створення ZIP-файлу, це може зайняти певний час.", "Unable to upload your file as it is a directory or has 0 bytes" => "Неможливо завантажити ваш файл тому, що він тека або файл розміром 0 байт", "Upload Error" => "Помилка завантаження", "Close" => "Закрити", @@ -31,8 +28,6 @@ "Upload cancelled." => "Завантаження перервано.", "File upload is in progress. Leaving the page now will cancel the upload." => "Виконується завантаження файлу. Закриття цієї сторінки приведе до відміни завантаження.", "URL cannot be empty." => "URL не може бути пустим.", -"{count} files scanned" => "{count} файлів проскановано", -"error while scanning" => "помилка при скануванні", "Name" => "Ім'я", "Size" => "Розмір", "Modified" => "Змінено", @@ -40,6 +35,7 @@ "{count} folders" => "{count} папок", "1 file" => "1 файл", "{count} files" => "{count} файлів", +"Upload" => "Відвантажити", "File handling" => "Робота з файлами", "Maximum upload size" => "Максимальний розмір відвантажень", "max. possible: " => "макс.можливе:", @@ -52,7 +48,6 @@ "Text file" => "Текстовий файл", "Folder" => "Папка", "From link" => "З посилання", -"Upload" => "Відвантажити", "Cancel upload" => "Перервати завантаження", "Nothing in here. Upload something!" => "Тут нічого немає. Відвантажте що-небудь!", "Download" => "Завантажити", diff --git a/apps/files/l10n/vi.php b/apps/files/l10n/vi.php index 7d5c529050..0daf580a2f 100644 --- a/apps/files/l10n/vi.php +++ b/apps/files/l10n/vi.php @@ -17,10 +17,7 @@ "replaced {new_name}" => "đã thay thế {new_name}", "undo" => "lùi lại", "replaced {new_name} with {old_name}" => "đã thay thế {new_name} bằng {old_name}", -"unshared {files}" => "hủy chia sẽ {files}", -"deleted {files}" => "đã xóa {files}", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Tên không hợp lệ, '\\', '/', '<', '>', ':', '\"', '|', '?' và '*' thì không được phép dùng.", -"generating ZIP-file, it may take some time." => "Tạo tập tin ZIP, điều này có thể làm mất một chút thời gian", "Unable to upload your file as it is a directory or has 0 bytes" => "Không thể tải lên tập tin này do nó là một thư mục hoặc kích thước tập tin bằng 0 byte", "Upload Error" => "Tải lên lỗi", "Close" => "Đóng", @@ -30,8 +27,6 @@ "Upload cancelled." => "Hủy tải lên", "File upload is in progress. Leaving the page now will cancel the upload." => "Tập tin tải lên đang được xử lý. Nếu bạn rời khỏi trang bây giờ sẽ hủy quá trình này.", "URL cannot be empty." => "URL không được để trống.", -"{count} files scanned" => "{count} tập tin đã được quét", -"error while scanning" => "lỗi trong khi quét", "Name" => "Tên", "Size" => "Kích cỡ", "Modified" => "Thay đổi", @@ -39,6 +34,7 @@ "{count} folders" => "{count} thư mục", "1 file" => "1 tập tin", "{count} files" => "{count} tập tin", +"Upload" => "Tải lên", "File handling" => "Xử lý tập tin", "Maximum upload size" => "Kích thước tối đa ", "max. possible: " => "tối đa cho phép:", @@ -51,7 +47,6 @@ "Text file" => "Tập tin văn bản", "Folder" => "Thư mục", "From link" => "Từ liên kết", -"Upload" => "Tải lên", "Cancel upload" => "Hủy upload", "Nothing in here. Upload something!" => "Không có gì ở đây .Hãy tải lên một cái gì đó !", "Download" => "Tải xuống", diff --git a/apps/files/l10n/zh_CN.GB2312.php b/apps/files/l10n/zh_CN.GB2312.php index e60df8291a..a38e2d3bc6 100644 --- a/apps/files/l10n/zh_CN.GB2312.php +++ b/apps/files/l10n/zh_CN.GB2312.php @@ -17,9 +17,6 @@ "replaced {new_name}" => "已替换 {new_name}", "undo" => "撤销", "replaced {new_name} with {old_name}" => "已用 {old_name} 替换 {new_name}", -"unshared {files}" => "未分享的 {files}", -"deleted {files}" => "已删除的 {files}", -"generating ZIP-file, it may take some time." => "正在生成ZIP文件,这可能需要点时间", "Unable to upload your file as it is a directory or has 0 bytes" => "不能上传你指定的文件,可能因为它是个文件夹或者大小为0", "Upload Error" => "上传错误", "Close" => "关闭", @@ -29,8 +26,6 @@ "Upload cancelled." => "上传取消了", "File upload is in progress. Leaving the page now will cancel the upload." => "文件正在上传。关闭页面会取消上传。", "URL cannot be empty." => "网址不能为空。", -"{count} files scanned" => "{count} 个文件已扫描", -"error while scanning" => "扫描出错", "Name" => "名字", "Size" => "大小", "Modified" => "修改日期", @@ -38,6 +33,7 @@ "{count} folders" => "{count} 个文件夹", "1 file" => "1 个文件", "{count} files" => "{count} 个文件", +"Upload" => "上传", "File handling" => "文件处理中", "Maximum upload size" => "最大上传大小", "max. possible: " => "最大可能", @@ -50,7 +46,6 @@ "Text file" => "文本文档", "Folder" => "文件夹", "From link" => "来自链接", -"Upload" => "上传", "Cancel upload" => "取消上传", "Nothing in here. Upload something!" => "这里没有东西.上传点什么!", "Download" => "下载", diff --git a/apps/files/l10n/zh_CN.php b/apps/files/l10n/zh_CN.php index 124bb2c3b6..2491d64534 100644 --- a/apps/files/l10n/zh_CN.php +++ b/apps/files/l10n/zh_CN.php @@ -1,7 +1,4 @@ "无法移动 %s - 同名文件已存在", -"Could not move %s" => "无法移动 %s", -"Unable to rename file" => "无法重命名文件", "No file was uploaded. Unknown error" => "没有文件被上传。未知错误", "There is no error, the file uploaded with success" => "没有发生错误,文件上传成功。", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "上传文件大小已超过php.ini中upload_max_filesize所规定的值", @@ -23,12 +20,10 @@ "replaced {new_name}" => "替换 {new_name}", "undo" => "撤销", "replaced {new_name} with {old_name}" => "已将 {old_name}替换成 {new_name}", -"unshared {files}" => "取消了共享 {files}", -"deleted {files}" => "删除了 {files}", "'.' is an invalid file name." => "'.' 是一个无效的文件名。", "File name cannot be empty." => "文件名不能为空。", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "无效名称,'\\', '/', '<', '>', ':', '\"', '|', '?' 和 '*' 不被允许使用。", -"generating ZIP-file, it may take some time." => "正在生成 ZIP 文件,可能需要一些时间", +"Your download is being prepared. This might take some time if the files are big." => "下载正在准备中。如果文件较大可能会花费一些时间。", "Unable to upload your file as it is a directory or has 0 bytes" => "无法上传文件,因为它是一个目录或者大小为 0 字节", "Upload Error" => "上传错误", "Close" => "关闭", @@ -39,8 +34,6 @@ "File upload is in progress. Leaving the page now will cancel the upload." => "文件正在上传中。现在离开此页会导致上传动作被取消。", "URL cannot be empty." => "URL不能为空", "Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "无效文件夹名。'共享' 是 Owncloud 预留的文件夹名。", -"{count} files scanned" => "{count} 个文件已扫描。", -"error while scanning" => "扫描时出错", "Name" => "名称", "Size" => "大小", "Modified" => "修改日期", @@ -48,6 +41,7 @@ "{count} folders" => "{count} 个文件夹", "1 file" => "1 个文件", "{count} files" => "{count} 个文件", +"Upload" => "上传", "File handling" => "文件处理", "Maximum upload size" => "最大上传大小", "max. possible: " => "最大允许: ", @@ -60,7 +54,6 @@ "Text file" => "文本文件", "Folder" => "文件夹", "From link" => "来自链接", -"Upload" => "上传", "Cancel upload" => "取消上传", "Nothing in here. Upload something!" => "这里还什么都没有。上传些东西吧!", "Download" => "下载", diff --git a/apps/files/l10n/zh_TW.php b/apps/files/l10n/zh_TW.php index 7f0f44baca..104cb3a619 100644 --- a/apps/files/l10n/zh_TW.php +++ b/apps/files/l10n/zh_TW.php @@ -1,7 +1,4 @@ "無法移動 %s - 同名的檔案已經存在", -"Could not move %s" => "無法移動 %s", -"Unable to rename file" => "無法重新命名檔案", "No file was uploaded. Unknown error" => "沒有檔案被上傳。未知的錯誤。", "There is no error, the file uploaded with success" => "無錯誤,檔案上傳成功", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "上傳的檔案大小超過 php.ini 當中 upload_max_filesize 參數的設定:", @@ -23,12 +20,13 @@ "replaced {new_name}" => "已取代 {new_name}", "undo" => "復原", "replaced {new_name} with {old_name}" => "使用 {new_name} 取代 {old_name}", -"unshared {files}" => "已取消分享 {files}", -"deleted {files}" => "已刪除 {files}", +"perform delete operation" => "進行刪除動作", "'.' is an invalid file name." => "'.' 是不合法的檔名。", "File name cannot be empty." => "檔名不能為空。", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "檔名不合法,不允許 '\\', '/', '<', '>', ':', '\"', '|', '?' 和 '*' 。", -"generating ZIP-file, it may take some time." => "產生 ZIP 壓縮檔,這可能需要一段時間。", +"Your storage is full, files can not be updated or synced anymore!" => "您的儲存空間已滿,沒有辦法再更新或是同步檔案!", +"Your storage is almost full ({usedSpacePercent}%)" => "您的儲存空間快要滿了 ({usedSpacePercent}%)", +"Your download is being prepared. This might take some time if the files are big." => "正在準備您的下載,若您的檔案較大,將會需要更多時間。", "Unable to upload your file as it is a directory or has 0 bytes" => "無法上傳您的檔案因為它可能是一個目錄或檔案大小為0", "Upload Error" => "上傳發生錯誤", "Close" => "關閉", @@ -39,8 +37,6 @@ "File upload is in progress. Leaving the page now will cancel the upload." => "檔案上傳中。離開此頁面將會取消上傳。", "URL cannot be empty." => "URL 不能為空白.", "Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "無效的資料夾名稱,'Shared' 的使用被 Owncloud 保留", -"{count} files scanned" => "{count} 個檔案已掃描", -"error while scanning" => "掃描時發生錯誤", "Name" => "名稱", "Size" => "大小", "Modified" => "修改", @@ -48,6 +44,7 @@ "{count} folders" => "{count} 個資料夾", "1 file" => "1 個檔案", "{count} files" => "{count} 個檔案", +"Upload" => "上傳", "File handling" => "檔案處理", "Maximum upload size" => "最大上傳檔案大小", "max. possible: " => "最大允許:", @@ -60,12 +57,13 @@ "Text file" => "文字檔", "Folder" => "資料夾", "From link" => "從連結", -"Upload" => "上傳", +"Trash" => "回收筒", "Cancel upload" => "取消上傳", "Nothing in here. Upload something!" => "沒有任何東西。請上傳內容!", "Download" => "下載", "Upload too large" => "上傳過大", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "您試圖上傳的檔案已超過伺服器的最大檔案大小限制。 ", "Files are being scanned, please wait." => "正在掃描檔案,請稍等。", -"Current scanning" => "目前掃描" +"Current scanning" => "目前掃描", +"Upgrading filesystem cache..." => "正在更新檔案系統快取..." ); diff --git a/apps/files/lib/helper.php b/apps/files/lib/helper.php new file mode 100644 index 0000000000..f2b1f142e9 --- /dev/null +++ b/apps/files/lib/helper.php @@ -0,0 +1,20 @@ +t('Upload') . ' max. ' . $maxHumanFilesize; + + // information about storage capacities + $storageInfo = \OC_Helper::getStorageInfo(); + + return array('uploadMaxFilesize' => $maxUploadFilesize, + 'maxHumanFilesize' => $maxHumanFilesize, + 'usedSpacePercent' => (int)$storageInfo['relative']); + } +} diff --git a/apps/files/settings.php b/apps/files/settings.php index 52ec9fd0fe..8687f01313 100644 --- a/apps/files/settings.php +++ b/apps/files/settings.php @@ -21,10 +21,6 @@ * */ - -// Init owncloud - - // Check if we are a user OCP\User::checkLoggedIn(); @@ -36,7 +32,7 @@ OCP\Util::addscript( "files", "files" ); $dir = isset( $_GET['dir'] ) ? $_GET['dir'] : ''; $files = array(); -foreach( OC_Files::getdirectorycontent( $dir ) as $i ) { +foreach( \OC\Files\Filesystem::getDirectoryContent( $dir ) as $i ) { $i["date"] = date( $CONFIG_DATEFORMAT, $i["mtime"] ); $files[] = $i; } diff --git a/apps/files/templates/index.php b/apps/files/templates/index.php index 2e0772443f..2d4ed9ab2d 100644 --- a/apps/files/templates/index.php +++ b/apps/files/templates/index.php @@ -35,6 +35,11 @@ + + +
-
@@ -50,7 +54,6 @@ -
t('Nothing in here. Upload something!')?>
@@ -115,3 +118,4 @@ + diff --git a/apps/files/templates/part.breadcrumb.php b/apps/files/templates/part.breadcrumb.php index 7df2afc1f5..e506ba31f6 100644 --- a/apps/files/templates/part.breadcrumb.php +++ b/apps/files/templates/part.breadcrumb.php @@ -1,10 +1,16 @@ + +
+ + + +
+
svg" - data-dir='' - style='background-image:url("")'> + data-dir=''>
- - - var publicListView = true; - - var publicListView = false; - - + - - + @@ -67,4 +61,4 @@ - + t('Upgrading filesystem cache...');?> +
+
diff --git a/apps/files_encryption/ajax/mode.php b/apps/files_encryption/ajax/mode.php new file mode 100644 index 0000000000..64c5be9440 --- /dev/null +++ b/apps/files_encryption/ajax/mode.php @@ -0,0 +1,38 @@ + + * This file is licensed under the Affero General Public License version 3 or later. + * See the COPYING-README file. + */ + +use OCA\Encryption\Keymanager; + +OCP\JSON::checkAppEnabled('files_encryption'); +OCP\JSON::checkLoggedIn(); +OCP\JSON::callCheck(); + +$mode = $_POST['mode']; +$changePasswd = false; +$passwdChanged = false; + +if ( isset($_POST['newpasswd']) && isset($_POST['oldpasswd']) ) { + $oldpasswd = $_POST['oldpasswd']; + $newpasswd = $_POST['newpasswd']; + $changePasswd = true; + $passwdChanged = Keymanager::changePasswd($oldpasswd, $newpasswd); +} + +$query = \OC_DB::prepare( "SELECT mode FROM *PREFIX*encryption WHERE uid = ?" ); +$result = $query->execute(array(\OCP\User::getUser())); + +if ($result->fetchRow()){ + $query = OC_DB::prepare( 'UPDATE *PREFIX*encryption SET mode = ? WHERE uid = ?' ); +} else { + $query = OC_DB::prepare( 'INSERT INTO *PREFIX*encryption ( mode, uid ) VALUES( ?, ? )' ); +} + +if ( (!$changePasswd || $passwdChanged) && $query->execute(array($mode, \OCP\User::getUser())) ) { + OCP\JSON::success(); +} else { + OCP\JSON::error(); +} \ No newline at end of file diff --git a/apps/files_encryption/appinfo/app.php b/apps/files_encryption/appinfo/app.php index 2a30d0beb6..31b430d37a 100644 --- a/apps/files_encryption/appinfo/app.php +++ b/apps/files_encryption/appinfo/app.php @@ -1,21 +1,37 @@ getPrivateKey( \OCP\USER::getUser() ) +&& OCP\User::isLoggedIn() +&& OCA\Encryption\Crypt::mode() == 'server' +) { + + // Force the user to re-log in if the encryption key isn't unlocked (happens when a user is logged in before the encryption app is enabled) OCP\User::logout(); - header("Location: ".OC::$WEBROOT.'/'); + + header( "Location: " . OC::$WEBROOT.'/' ); + exit(); + } -OCP\App::registerAdmin('files_encryption', 'settings'); \ No newline at end of file +OCP\App::registerAdmin( 'files_encryption', 'settings'); +OCP\App::registerPersonal( 'files_encryption', 'settings-personal' ); \ No newline at end of file diff --git a/apps/files_encryption/appinfo/database.xml b/apps/files_encryption/appinfo/database.xml new file mode 100644 index 0000000000..d294c35d63 --- /dev/null +++ b/apps/files_encryption/appinfo/database.xml @@ -0,0 +1,24 @@ + + + *dbname* + true + false + utf8 + + *dbprefix*encryption + + + uid + text + true + 64 + + + mode + text + true + 64 + + +
+
\ No newline at end of file diff --git a/apps/files_encryption/appinfo/info.xml b/apps/files_encryption/appinfo/info.xml index 48a28fde78..39ea155488 100644 --- a/apps/files_encryption/appinfo/info.xml +++ b/apps/files_encryption/appinfo/info.xml @@ -2,10 +2,10 @@ files_encryption Encryption - Server side encryption of files. DEPRECATED. This app is no longer supported and will be replaced with an improved version in ownCloud 5. Only enable this features if you want to read old encrypted data. Warning: You will lose your data if you enable this App and forget your password. Encryption is not yet compatible with LDAP. + Server side encryption of files. Warning: You will lose your data if you enable this App and forget your password. Encryption is not yet compatible with LDAP. AGPL - Robin Appelman - 4.9 + Sam Tuke + 4 true diff --git a/apps/files_encryption/appinfo/version b/apps/files_encryption/appinfo/version index 2f4536184b..7dff5b8921 100644 --- a/apps/files_encryption/appinfo/version +++ b/apps/files_encryption/appinfo/version @@ -1 +1 @@ -0.2 \ No newline at end of file +0.2.1 \ No newline at end of file diff --git a/apps/files_encryption/hooks/hooks.php b/apps/files_encryption/hooks/hooks.php new file mode 100644 index 0000000000..c2f9724783 --- /dev/null +++ b/apps/files_encryption/hooks/hooks.php @@ -0,0 +1,143 @@ +. + * + */ + +namespace OCA\Encryption; + +/** + * Class for hook specific logic + */ + +class Hooks { + + # TODO: use passphrase for encrypting private key that is separate to the login password + + /** + * @brief Startup encryption backend upon user login + * @note This method should never be called for users using client side encryption + */ + public static function login( $params ) { + +// if ( Crypt::mode( $params['uid'] ) == 'server' ) { + + # TODO: use lots of dependency injection here + + $view = new \OC_FilesystemView( '/' ); + + $util = new Util( $view, $params['uid'] ); + + if ( ! $util->ready() ) { + + \OC_Log::write( 'Encryption library', 'User account "' . $params['uid'] . '" is not ready for encryption; configuration started' , \OC_Log::DEBUG ); + + return $util->setupServerSide( $params['password'] ); + + } + + \OC_FileProxy::$enabled = false; + + $encryptedKey = Keymanager::getPrivateKey( $view, $params['uid'] ); + + \OC_FileProxy::$enabled = true; + + # TODO: dont manually encrypt the private keyfile - use the config options of openssl_pkey_export instead for better mobile compatibility + + $privateKey = Crypt::symmetricDecryptFileContent( $encryptedKey, $params['password'] ); + + $session = new Session(); + + $session->setPrivateKey( $privateKey, $params['uid'] ); + + $view1 = new \OC_FilesystemView( '/' . $params['uid'] ); + + // Set legacy encryption key if it exists, to support + // depreciated encryption system + if ( + $view1->file_exists( 'encryption.key' ) + && $legacyKey = $view1->file_get_contents( 'encryption.key' ) + ) { + + $_SESSION['legacyenckey'] = Crypt::legacyDecrypt( $legacyKey, $params['password'] ); + + } +// } + + return true; + + } + + /** + * @brief Change a user's encryption passphrase + * @param array $params keys: uid, password + */ + public static function setPassphrase( $params ) { + + // Only attempt to change passphrase if server-side encryption + // is in use (client-side encryption does not have access to + // the necessary keys) + if ( Crypt::mode() == 'server' ) { + + // Get existing decrypted private key + $privateKey = $_SESSION['privateKey']; + + // Encrypt private key with new user pwd as passphrase + $encryptedPrivateKey = Crypt::symmetricEncryptFileContent( $privateKey, $params['password'] ); + + // Save private key + Keymanager::setPrivateKey( $encryptedPrivateKey ); + + # NOTE: Session does not need to be updated as the + # private key has not changed, only the passphrase + # used to decrypt it has changed + + } + + } + + /** + * @brief update the encryption key of the file uploaded by the client + */ + public static function updateKeyfile( $params ) { + + if ( Crypt::mode() == 'client' ) { + + if ( isset( $params['properties']['key'] ) ) { + + Keymanager::setFileKey( $params['path'], $params['properties']['key'] ); + + } else { + + \OC_Log::write( + 'Encryption library', "Client side encryption is enabled but the client doesn't provide a encryption key for the file!" + , \OC_Log::ERROR + ); + + error_log( "Client side encryption is enabled but the client doesn't provide an encryption key for the file!" ); + + } + + } + + } + +} + +?> \ No newline at end of file diff --git a/apps/files_encryption/js/settings-personal.js b/apps/files_encryption/js/settings-personal.js new file mode 100644 index 0000000000..1a53e99d2b --- /dev/null +++ b/apps/files_encryption/js/settings-personal.js @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2012, Bjoern Schiessle + * This file is licensed under the Affero General Public License version 3 or later. + * See the COPYING-README file. + */ + +$(document).ready(function(){ + $('input[name=encryption_mode]').change(function(){ + var prevmode = document.getElementById('prev_encryption_mode').value + var client=$('input[value="client"]:checked').val() + ,server=$('input[value="server"]:checked').val() + ,user=$('input[value="user"]:checked').val() + ,none=$('input[value="none"]:checked').val() + if (client) { + $.post(OC.filePath('files_encryption', 'ajax', 'mode.php'), { mode: 'client' }); + if (prevmode == 'server') { + OC.dialogs.info(t('encryption', 'Please switch to your ownCloud client and change your encryption password to complete the conversion.'), t('encryption', 'switched to client side encryption')); + } + } else if (server) { + if (prevmode == 'client') { + OC.dialogs.form([{text:'Login password', name:'newpasswd', type:'password'},{text:'Encryption password used on the client', name:'oldpasswd', type:'password'}],t('encryption', 'Change encryption password to login password'), function(data) { + $.post(OC.filePath('files_encryption', 'ajax', 'mode.php'), { mode: 'server', newpasswd: data[0].value, oldpasswd: data[1].value }, function(result) { + if (result.status != 'success') { + document.getElementById(prevmode+'_encryption').checked = true; + OC.dialogs.alert(t('encryption', 'Please check your passwords and try again.'), t('encryption', 'Could not change your file encryption password to your login password')) + } else { + console.log("alles super"); + } + }, true); + }); + } else { + $.post(OC.filePath('files_encryption', 'ajax', 'mode.php'), { mode: 'server' }); + } + } else { + $.post(OC.filePath('files_encryption', 'ajax', 'mode.php'), { mode: 'none' }); + } + }) +}) \ No newline at end of file diff --git a/apps/files_encryption/js/settings.js b/apps/files_encryption/js/settings.js index 6fc70eba7f..60563bde85 100644 --- a/apps/files_encryption/js/settings.js +++ b/apps/files_encryption/js/settings.js @@ -11,14 +11,36 @@ $(document).ready(function(){ onuncheck:blackListChange, createText:'...', }); - + function blackListChange(){ var blackList=$('#encryption_blacklist').val().join(','); OC.AppConfig.setValue('files_encryption','type_blacklist',blackList); } - $('#enable_encryption').change(function(){ - var checked=$('#enable_encryption').is(':checked'); - OC.AppConfig.setValue('files_encryption','enable_encryption',(checked)?'true':'false'); - }); -}); + //TODO: Handle switch between client and server side encryption + $('input[name=encryption_mode]').change(function(){ + var client=$('input[value="client"]:checked').val() + ,server=$('input[value="server"]:checked').val() + ,user=$('input[value="user"]:checked').val() + ,none=$('input[value="none"]:checked').val() + ,disable=false + if (client) { + OC.AppConfig.setValue('files_encryption','mode','client'); + disable = true; + } else if (server) { + OC.AppConfig.setValue('files_encryption','mode','server'); + disable = true; + } else if (user) { + OC.AppConfig.setValue('files_encryption','mode','user'); + disable = true; + } else { + OC.AppConfig.setValue('files_encryption','mode','none'); + } + if (disable) { + document.getElementById('server_encryption').disabled = true; + document.getElementById('client_encryption').disabled = true; + document.getElementById('user_encryption').disabled = true; + document.getElementById('none_encryption').disabled = true; + } + }) +}) \ No newline at end of file diff --git a/apps/files_encryption/l10n/ar.php b/apps/files_encryption/l10n/ar.php index 756a9d7279..f08585e485 100644 --- a/apps/files_encryption/l10n/ar.php +++ b/apps/files_encryption/l10n/ar.php @@ -1,6 +1,5 @@ "التشفير", "Exclude the following file types from encryption" => "استبعد أنواع الملفات التالية من التشفير", -"None" => "لا شيء", -"Enable Encryption" => "تفعيل التشفير" +"None" => "لا شيء" ); diff --git a/apps/files_encryption/l10n/bg_BG.php b/apps/files_encryption/l10n/bg_BG.php index cb1613ef37..4ceee127af 100644 --- a/apps/files_encryption/l10n/bg_BG.php +++ b/apps/files_encryption/l10n/bg_BG.php @@ -1,6 +1,5 @@ "Криптиране", -"Enable Encryption" => "Включване на криптирането", -"None" => "Няма", -"Exclude the following file types from encryption" => "Изключване на следните файлови типове от криптирането" +"Exclude the following file types from encryption" => "Изключване на следните файлови типове от криптирането", +"None" => "Няма" ); diff --git a/apps/files_encryption/l10n/bn_BD.php b/apps/files_encryption/l10n/bn_BD.php index c8f041d762..29c486b8ca 100644 --- a/apps/files_encryption/l10n/bn_BD.php +++ b/apps/files_encryption/l10n/bn_BD.php @@ -1,6 +1,5 @@ "সংকেতায়ন", -"Enable Encryption" => "সংকেতায়ন সক্রিয় কর", -"None" => "কোনটিই নয়", -"Exclude the following file types from encryption" => "সংকেতায়ন থেকে নিম্নোক্ত ধরণসমূহ বাদ দাও" +"Exclude the following file types from encryption" => "সংকেতায়ন থেকে নিম্নোক্ত ধরণসমূহ বাদ দাও", +"None" => "কোনটিই নয়" ); diff --git a/apps/files_encryption/l10n/ca.php b/apps/files_encryption/l10n/ca.php index 8e087b3462..56c81e747f 100644 --- a/apps/files_encryption/l10n/ca.php +++ b/apps/files_encryption/l10n/ca.php @@ -1,6 +1,16 @@ "Connecteu-vos al client ownCloud i canvieu la contrasenya d'encriptació per completar la conversió.", +"switched to client side encryption" => "s'ha commutat a l'encriptació per part del client", +"Change encryption password to login password" => "Canvia la contrasenya d'encriptació per la d'accés", +"Please check your passwords and try again." => "Comproveu les contrasenyes i proveu-ho de nou.", +"Could not change your file encryption password to your login password" => "No s'ha pogut canviar la contrasenya d'encriptació de fitxers per la d'accés", +"Choose encryption mode:" => "Escolliu el mode d'encriptació:", +"Client side encryption (most secure but makes it impossible to access your data from the web interface)" => "Encriptació per part del client (més segura però fa impossible l'accés a les dades des de la interfície web)", +"Server side encryption (allows you to access your files from the web interface and the desktop client)" => "Encriptació per part del servidor (permet accedir als fitxers des de la interfície web i des del client d'escriptori)", +"None (no encryption at all)" => "Cap (sense encriptació)", +"Important: Once you selected an encryption mode there is no way to change it back" => "Important: quan seleccioneu un mode d'encriptació no hi ha manera de canviar-lo de nou", +"User specific (let the user decide)" => "Específic per usuari (permet que l'usuari ho decideixi)", "Encryption" => "Encriptatge", "Exclude the following file types from encryption" => "Exclou els tipus de fitxers següents de l'encriptatge", -"None" => "Cap", -"Enable Encryption" => "Activa l'encriptatge" +"None" => "Cap" ); diff --git a/apps/files_encryption/l10n/cs_CZ.php b/apps/files_encryption/l10n/cs_CZ.php index 9be2be9809..5948a9b82e 100644 --- a/apps/files_encryption/l10n/cs_CZ.php +++ b/apps/files_encryption/l10n/cs_CZ.php @@ -1,6 +1,16 @@ "Prosím přejděte na svého klienta ownCloud a nastavte šifrovací heslo pro dokončení konverze.", +"switched to client side encryption" => "přepnuto na šifrování na straně klienta", +"Change encryption password to login password" => "Změnit šifrovací heslo na přihlašovací", +"Please check your passwords and try again." => "Zkontrolujte, prosím, své heslo a zkuste to znovu.", +"Could not change your file encryption password to your login password" => "Nelze změnit šifrovací heslo na přihlašovací.", +"Choose encryption mode:" => "Vyberte režim šifrování:", +"Client side encryption (most secure but makes it impossible to access your data from the web interface)" => "Šifrování na straně klienta (nejbezpečnější ale neumožňuje vám přistupovat k souborům z webového rozhraní)", +"Server side encryption (allows you to access your files from the web interface and the desktop client)" => "Šifrování na straně serveru (umožňuje vám přistupovat k souborům pomocí webového rozhraní i aplikací)", +"None (no encryption at all)" => "Žádný (vůbec žádné šifrování)", +"Important: Once you selected an encryption mode there is no way to change it back" => "Důležité: jak si jednou vyberete režim šifrování nelze jej opětovně změnit", +"User specific (let the user decide)" => "Definován uživatelem (umožní uživateli si vybrat)", "Encryption" => "Šifrování", "Exclude the following file types from encryption" => "Při šifrování vynechat následující typy souborů", -"None" => "Žádné", -"Enable Encryption" => "Povolit šifrování" +"None" => "Žádné" ); diff --git a/apps/files_encryption/l10n/da.php b/apps/files_encryption/l10n/da.php index 144c9f9708..d65963f46b 100644 --- a/apps/files_encryption/l10n/da.php +++ b/apps/files_encryption/l10n/da.php @@ -1,6 +1,16 @@ "Skift venligst til din ownCloud-klient og skift krypteringskoden for at fuldføre konverteringen.", +"switched to client side encryption" => "skiftet til kryptering på klientsiden", +"Change encryption password to login password" => "Udskift krypteringskode til login-adgangskode", +"Please check your passwords and try again." => "Check adgangskoder og forsøg igen.", +"Could not change your file encryption password to your login password" => "Kunne ikke udskifte krypteringskode med login-adgangskode", +"Choose encryption mode:" => "Vælg krypteringsform:", +"Client side encryption (most secure but makes it impossible to access your data from the web interface)" => "Kryptering på klientsiden (mere sikker, men udelukker adgang til dataene fra webinterfacet)", +"Server side encryption (allows you to access your files from the web interface and the desktop client)" => "Kryptering på serversiden (gør det muligt at tilgå filer fra webinterfacet såvel som desktopklienten)", +"None (no encryption at all)" => "Ingen (ingen kryptering)", +"Important: Once you selected an encryption mode there is no way to change it back" => "Vigtigt: Når der er valgt krypteringsform, kan det ikke ændres tilbage igen.", +"User specific (let the user decide)" => "Brugerspecifik (lad brugeren bestemme)", "Encryption" => "Kryptering", "Exclude the following file types from encryption" => "Ekskluder følgende filtyper fra kryptering", -"None" => "Ingen", -"Enable Encryption" => "Aktivér kryptering" +"None" => "Ingen" ); diff --git a/apps/files_encryption/l10n/de.php b/apps/files_encryption/l10n/de.php index d486a82322..e187f72ab5 100644 --- a/apps/files_encryption/l10n/de.php +++ b/apps/files_encryption/l10n/de.php @@ -1,6 +1,16 @@ "Bitte wechseln Sie nun zum ownCloud Client und ändern Sie ihr Verschlüsselungspasswort um die Konvertierung abzuschließen.", +"switched to client side encryption" => "Zur Clientseitigen Verschlüsselung gewechselt", +"Change encryption password to login password" => "Ändern des Verschlüsselungspasswortes zum Anmeldepasswort", +"Please check your passwords and try again." => "Bitte überprüfen sie Ihr Passwort und versuchen Sie es erneut.", +"Could not change your file encryption password to your login password" => "Ihr Verschlüsselungspasswort konnte nicht als Anmeldepasswort gesetzt werden.", +"Choose encryption mode:" => "Wählen Sie die Verschlüsselungsart:", +"Client side encryption (most secure but makes it impossible to access your data from the web interface)" => "Clientseitige Verschlüsselung (am sichersten, aber macht es unmöglich auf ihre Daten über das Webinterface zuzugreifen)", +"Server side encryption (allows you to access your files from the web interface and the desktop client)" => "Serverseitige Verschlüsselung (erlaubt es ihnen auf ihre Daten über das Webinterface und den Desktop-Client zuzugreifen)", +"None (no encryption at all)" => "Keine (ohne Verschlüsselung)", +"Important: Once you selected an encryption mode there is no way to change it back" => "Wichtig: Sobald sie eine Verschlüsselungsmethode gewählt haben, können Sie diese nicht ändern!", +"User specific (let the user decide)" => "Benutzerspezifisch (der Benutzer kann entscheiden)", "Encryption" => "Verschlüsselung", "Exclude the following file types from encryption" => "Die folgenden Dateitypen von der Verschlüsselung ausnehmen", -"None" => "Keine", -"Enable Encryption" => "Verschlüsselung aktivieren" +"None" => "Keine" ); diff --git a/apps/files_encryption/l10n/de_DE.php b/apps/files_encryption/l10n/de_DE.php index d486a82322..be4369ebf0 100644 --- a/apps/files_encryption/l10n/de_DE.php +++ b/apps/files_encryption/l10n/de_DE.php @@ -1,6 +1,16 @@ "Bitte wechseln Sie nun zum ownCloud Client und ändern Sie ihr Verschlüsselungspasswort um die Konvertierung abzuschließen.", +"switched to client side encryption" => "Zur Clientseitigen Verschlüsselung gewechselt", +"Change encryption password to login password" => "Ändern des Verschlüsselungspasswortes zum Anmeldepasswort", +"Please check your passwords and try again." => "Bitte überprüfen sie Ihr Passwort und versuchen Sie es erneut.", +"Could not change your file encryption password to your login password" => "Ihr Verschlüsselungspasswort konnte nicht als Anmeldepasswort gesetzt werden.", +"Choose encryption mode:" => "Wählen Sie die Verschlüsselungsmethode:", +"Client side encryption (most secure but makes it impossible to access your data from the web interface)" => "Clientseitige Verschlüsselung (am sichersten, aber macht es unmöglich auf ihre Daten über das Webinterface zuzugreifen)", +"Server side encryption (allows you to access your files from the web interface and the desktop client)" => "Serverseitige Verschlüsselung (erlaubt es ihnen auf ihre Daten über das Webinterface und den Desktop-Client zuzugreifen)", +"None (no encryption at all)" => "Keine (ohne Verschlüsselung)", +"Important: Once you selected an encryption mode there is no way to change it back" => "Wichtig: Sobald sie eine Verschlüsselungsmethode gewählt haben, können Sie diese nicht ändern!", +"User specific (let the user decide)" => "Benutzerspezifisch (der Benutzer kann entscheiden)", "Encryption" => "Verschlüsselung", "Exclude the following file types from encryption" => "Die folgenden Dateitypen von der Verschlüsselung ausnehmen", -"None" => "Keine", -"Enable Encryption" => "Verschlüsselung aktivieren" +"None" => "Keine" ); diff --git a/apps/files_encryption/l10n/el.php b/apps/files_encryption/l10n/el.php index 40a7c6a367..50b812c82d 100644 --- a/apps/files_encryption/l10n/el.php +++ b/apps/files_encryption/l10n/el.php @@ -1,6 +1,9 @@ "Αλλαγή συνθηματικού κρυπτογράφησης στο συνθηματικό εισόδου ", +"Please check your passwords and try again." => "Παρακαλώ ελέγξτε το συνθηματικό σας και προσπαθήστε ξανά.", +"Could not change your file encryption password to your login password" => "Αδυναμία αλλαγής συνθηματικού κρυπτογράφησης αρχείων στο συνθηματικό εισόδου σας", +"Choose encryption mode:" => "Επιλογή κατάστασης κρυπτογράφησης:", "Encryption" => "Κρυπτογράφηση", "Exclude the following file types from encryption" => "Εξαίρεση των παρακάτω τύπων αρχείων από την κρυπτογράφηση", -"None" => "Καμία", -"Enable Encryption" => "Ενεργοποίηση Κρυπτογράφησης" +"None" => "Καμία" ); diff --git a/apps/files_encryption/l10n/eo.php b/apps/files_encryption/l10n/eo.php index af3c9ae98e..c6f82dcb8a 100644 --- a/apps/files_encryption/l10n/eo.php +++ b/apps/files_encryption/l10n/eo.php @@ -1,6 +1,5 @@ "Ĉifrado", "Exclude the following file types from encryption" => "Malinkluzivigi la jenajn dosiertipojn el ĉifrado", -"None" => "Nenio", -"Enable Encryption" => "Kapabligi ĉifradon" +"None" => "Nenio" ); diff --git a/apps/files_encryption/l10n/es.php b/apps/files_encryption/l10n/es.php index b7e7601b35..2c6b650960 100644 --- a/apps/files_encryption/l10n/es.php +++ b/apps/files_encryption/l10n/es.php @@ -1,6 +1,16 @@ "Por favor, cambie su cliente de ownCloud y cambie su clave de cifrado para completar la conversión.", +"switched to client side encryption" => "Cambiar a cifrado del lado del cliente", +"Change encryption password to login password" => "Cambie la clave de cifrado para su contraseña de inicio de sesión", +"Please check your passwords and try again." => "Por favor revise su contraseña e intentelo de nuevo.", +"Could not change your file encryption password to your login password" => "No se pudo cambiar la contraseña de cifrado de archivos de su contraseña de inicio de sesión", +"Choose encryption mode:" => "Elegir el modo de cifrado:", +"Client side encryption (most secure but makes it impossible to access your data from the web interface)" => "Cifrado del lado del Cliente ( es el más seguro, pero hace que sea imposible acceder a sus datos desde la interfaz web)", +"Server side encryption (allows you to access your files from the web interface and the desktop client)" => "Cifrado del lado del Servidor (le permite acceder a sus archivos desde la interfaz web y el cliente de escritorio)", +"None (no encryption at all)" => "Ninguno (ningún cifrado en absoluto)", +"Important: Once you selected an encryption mode there is no way to change it back" => "Importante: Una vez que haya seleccionado un modo de cifrado no existe forma de cambiarlo de nuevo", +"User specific (let the user decide)" => "Específico del usuario (dejar que el usuario decida)", "Encryption" => "Cifrado", "Exclude the following file types from encryption" => "Excluir del cifrado los siguientes tipos de archivo", -"None" => "Ninguno", -"Enable Encryption" => "Habilitar cifrado" +"None" => "Ninguno" ); diff --git a/apps/files_encryption/l10n/es_AR.php b/apps/files_encryption/l10n/es_AR.php index a15c37e730..5cf0b8e4ad 100644 --- a/apps/files_encryption/l10n/es_AR.php +++ b/apps/files_encryption/l10n/es_AR.php @@ -1,6 +1,16 @@ "Por favor, cambiá uu cliente de ownCloud y cambiá tu clave de encriptado para completar la conversión.", +"switched to client side encryption" => "Cambiado a encriptación por parte del cliente", +"Change encryption password to login password" => "Cambiá la clave de encriptado para tu contraseña de inicio de sesión", +"Please check your passwords and try again." => "Por favor, revisá tu contraseña e intentalo de nuevo.", +"Could not change your file encryption password to your login password" => "No se pudo cambiar la contraseña de encriptación de archivos de tu contraseña de inicio de sesión", +"Choose encryption mode:" => "Elegir el modo de encriptación:", +"Client side encryption (most secure but makes it impossible to access your data from the web interface)" => "Encriptación por parte del cliente (es el modo más seguro, pero hace que sea imposible acceder a tus datos desde la interfaz web)", +"Server side encryption (allows you to access your files from the web interface and the desktop client)" => "Encriptación por parte del servidor (te permite acceder a tus archivos desde la interfaz web y desde el cliente de escritorio)", +"None (no encryption at all)" => "Ninguno (ninguna encriptación en absoluto)", +"Important: Once you selected an encryption mode there is no way to change it back" => "Importante: Una vez que haya seleccionado un modo de encriptación, no existe forma de cambiarlo nuevamente", +"User specific (let the user decide)" => "Específico por usuario (deja que el usuario decida)", "Encryption" => "Encriptación", "Exclude the following file types from encryption" => "Exceptuar de la encriptación los siguientes tipos de archivo", -"None" => "Ninguno", -"Enable Encryption" => "Habilitar encriptación" +"None" => "Ninguno" ); diff --git a/apps/files_encryption/l10n/et_EE.php b/apps/files_encryption/l10n/et_EE.php index a7cd9395bf..0c0ef23114 100644 --- a/apps/files_encryption/l10n/et_EE.php +++ b/apps/files_encryption/l10n/et_EE.php @@ -1,6 +1,5 @@ "Krüpteerimine", "Exclude the following file types from encryption" => "Järgnevaid failitüüpe ära krüpteeri", -"None" => "Pole", -"Enable Encryption" => "Luba krüpteerimine" +"None" => "Pole" ); diff --git a/apps/files_encryption/l10n/eu.php b/apps/files_encryption/l10n/eu.php index 57b6a4927b..e7372937e4 100644 --- a/apps/files_encryption/l10n/eu.php +++ b/apps/files_encryption/l10n/eu.php @@ -1,6 +1,9 @@ "Mesedez egiaztatu zure pasahitza eta saia zaitez berriro:", +"Choose encryption mode:" => "Hautatu enkriptazio modua:", +"None (no encryption at all)" => "Bat ere ez (enkriptaziorik gabe)", +"User specific (let the user decide)" => "Erabiltzaileak zehaztuta (utzi erabiltzaileari hautatzen)", "Encryption" => "Enkriptazioa", "Exclude the following file types from encryption" => "Ez enkriptatu hurrengo fitxategi motak", -"None" => "Bat ere ez", -"Enable Encryption" => "Gaitu enkriptazioa" +"None" => "Bat ere ez" ); diff --git a/apps/files_encryption/l10n/fa.php b/apps/files_encryption/l10n/fa.php index 01582e48e6..0cdee74f5a 100644 --- a/apps/files_encryption/l10n/fa.php +++ b/apps/files_encryption/l10n/fa.php @@ -1,6 +1,5 @@ "رمزگذاری", "Exclude the following file types from encryption" => "نادیده گرفتن فایل های زیر برای رمز گذاری", -"None" => "هیچ‌کدام", -"Enable Encryption" => "فعال کردن رمزگذاری" +"None" => "هیچ‌کدام" ); diff --git a/apps/files_encryption/l10n/fi_FI.php b/apps/files_encryption/l10n/fi_FI.php index 5796499a26..33756c2831 100644 --- a/apps/files_encryption/l10n/fi_FI.php +++ b/apps/files_encryption/l10n/fi_FI.php @@ -1,6 +1,9 @@ "Tarkista salasanasi ja yritä uudelleen.", +"Choose encryption mode:" => "Choose encryption mode:", +"None (no encryption at all)" => "Ei mitään (ei lainkaan salausta)", +"Important: Once you selected an encryption mode there is no way to change it back" => "Tärkeä huomautus: Kun olet valinnut salaustatavan, sitä ei ole mahdollista vaihtaa", "Encryption" => "Salaus", "Exclude the following file types from encryption" => "Jätä seuraavat tiedostotyypit salaamatta", -"None" => "Ei mitään", -"Enable Encryption" => "Käytä salausta" +"None" => "Ei mitään" ); diff --git a/apps/files_encryption/l10n/fr.php b/apps/files_encryption/l10n/fr.php index c9367d1a31..41e37134d4 100644 --- a/apps/files_encryption/l10n/fr.php +++ b/apps/files_encryption/l10n/fr.php @@ -1,6 +1,16 @@ "Veuillez vous connecter depuis votre client de synchronisation ownCloud et changer votre mot de passe de chiffrement pour finaliser la conversion.", +"switched to client side encryption" => "Mode de chiffrement changé en chiffrement côté client", +"Change encryption password to login password" => "Convertir le mot de passe de chiffrement en mot de passe de connexion", +"Please check your passwords and try again." => "Veuillez vérifier vos mots de passe et réessayer.", +"Could not change your file encryption password to your login password" => "Impossible de convertir votre mot de passe de chiffrement en mot de passe de connexion", +"Choose encryption mode:" => "Choix du type de chiffrement :", +"Client side encryption (most secure but makes it impossible to access your data from the web interface)" => "Chiffrement côté client (plus sécurisé, mais ne permet pas l'accès à vos données depuis l'interface web)", +"Server side encryption (allows you to access your files from the web interface and the desktop client)" => "Chiffrement côté serveur (vous permet d'accéder à vos fichiers depuis l'interface web et depuis le client de synchronisation)", +"None (no encryption at all)" => "Aucun (pas de chiffrement)", +"Important: Once you selected an encryption mode there is no way to change it back" => "Important : Une fois le mode de chiffrement choisi, il est impossible de revenir en arrière", +"User specific (let the user decide)" => "Propre à l'utilisateur (laisse le choix à l'utilisateur)", "Encryption" => "Chiffrement", "Exclude the following file types from encryption" => "Ne pas chiffrer les fichiers dont les types sont les suivants", -"None" => "Aucun", -"Enable Encryption" => "Activer le chiffrement" +"None" => "Aucun" ); diff --git a/apps/files_encryption/l10n/gl.php b/apps/files_encryption/l10n/gl.php index 91d155ccad..42fcfce1cc 100644 --- a/apps/files_encryption/l10n/gl.php +++ b/apps/files_encryption/l10n/gl.php @@ -1,6 +1,5 @@ "Cifrado", "Exclude the following file types from encryption" => "Excluír os seguintes tipos de ficheiro do cifrado", -"None" => "Nada", -"Enable Encryption" => "Activar o cifrado" +"None" => "Nada" ); diff --git a/apps/files_encryption/l10n/he.php b/apps/files_encryption/l10n/he.php index 0332d59520..9adb6d2b92 100644 --- a/apps/files_encryption/l10n/he.php +++ b/apps/files_encryption/l10n/he.php @@ -1,6 +1,5 @@ "הצפנה", -"Enable Encryption" => "הפעל הצפנה", -"None" => "כלום", -"Exclude the following file types from encryption" => "הוצא את סוגי הקבצים הבאים מהצפנה" +"Exclude the following file types from encryption" => "הוצא את סוגי הקבצים הבאים מהצפנה", +"None" => "כלום" ); diff --git a/apps/files_encryption/l10n/hu_HU.php b/apps/files_encryption/l10n/hu_HU.php index 8ea0f73173..e32de01f97 100644 --- a/apps/files_encryption/l10n/hu_HU.php +++ b/apps/files_encryption/l10n/hu_HU.php @@ -1,6 +1,16 @@ "Kérjük, hogy váltson át az ownCloud kliensére, és változtassa meg a titkosítási jelszót az átalakítás befejezéséhez.", +"switched to client side encryption" => "átváltva a kliens oldalai titkosításra", +"Change encryption password to login password" => "Titkosítási jelszó módosítása a bejelentkezési jelszóra", +"Please check your passwords and try again." => "Kérjük, ellenőrizze a jelszavait, és próbálja meg újra.", +"Could not change your file encryption password to your login password" => "Nem módosíthatja a fájltitkosítási jelszavát a bejelentkezési jelszavára", +"Choose encryption mode:" => "Válassza ki a titkosítási módot:", +"Client side encryption (most secure but makes it impossible to access your data from the web interface)" => "Kliens oldali titkosítás (biztonságosabb, de lehetetlenné teszi a fájlok elérését a böngészőből)", +"Server side encryption (allows you to access your files from the web interface and the desktop client)" => "Kiszolgáló oldali titkosítás (lehetővé teszi a fájlok elérését úgy böngészőből mint az asztali kliensből)", +"None (no encryption at all)" => "Semmi (semmilyen titkosítás)", +"Important: Once you selected an encryption mode there is no way to change it back" => "Fontos: Ha egyszer kiválasztotta a titkosítás módját, többé már nem lehet megváltoztatni", +"User specific (let the user decide)" => "Felhasználó specifikus (a felhasználó választhat)", "Encryption" => "Titkosítás", -"Enable Encryption" => "A titkosítás engedélyezése", -"None" => "Egyik sem", -"Exclude the following file types from encryption" => "A következő fájltípusok kizárása a titkosításból" +"Exclude the following file types from encryption" => "A következő fájltípusok kizárása a titkosításból", +"None" => "Egyik sem" ); diff --git a/apps/files_encryption/l10n/id.php b/apps/files_encryption/l10n/id.php index 824ae88304..20f33b8782 100644 --- a/apps/files_encryption/l10n/id.php +++ b/apps/files_encryption/l10n/id.php @@ -1,6 +1,5 @@ "enkripsi", "Exclude the following file types from encryption" => "pengecualian untuk tipe file berikut dari enkripsi", -"None" => "tidak ada", -"Enable Encryption" => "aktifkan enkripsi" +"None" => "tidak ada" ); diff --git a/apps/files_encryption/l10n/is.php b/apps/files_encryption/l10n/is.php index 3210ecb4f8..a2559cf2b7 100644 --- a/apps/files_encryption/l10n/is.php +++ b/apps/files_encryption/l10n/is.php @@ -1,6 +1,5 @@ "Dulkóðun", -"Enable Encryption" => "Virkja dulkóðun", -"None" => "Ekkert", -"Exclude the following file types from encryption" => "Undanskilja eftirfarandi skráartegundir frá dulkóðun" +"Exclude the following file types from encryption" => "Undanskilja eftirfarandi skráartegundir frá dulkóðun", +"None" => "Ekkert" ); diff --git a/apps/files_encryption/l10n/it.php b/apps/files_encryption/l10n/it.php index 5136b06179..0c394564e0 100644 --- a/apps/files_encryption/l10n/it.php +++ b/apps/files_encryption/l10n/it.php @@ -1,6 +1,16 @@ "Passa al tuo client ownCloud e cambia la password di cifratura per completare la conversione.", +"switched to client side encryption" => "passato alla cifratura lato client", +"Change encryption password to login password" => "Converti la password di cifratura nella password di accesso", +"Please check your passwords and try again." => "Controlla la password e prova ancora.", +"Could not change your file encryption password to your login password" => "Impossibile convertire la password di cifratura nella password di accesso", +"Choose encryption mode:" => "Scegli la modalità di cifratura.", +"Client side encryption (most secure but makes it impossible to access your data from the web interface)" => "Cifratura lato client (più sicura ma rende impossibile accedere ai propri dati dall'interfaccia web)", +"Server side encryption (allows you to access your files from the web interface and the desktop client)" => "Cifratura lato server (ti consente di accedere ai tuoi file dall'interfaccia web e dal client desktop)", +"None (no encryption at all)" => "Nessuna (senza alcuna cifratura)", +"Important: Once you selected an encryption mode there is no way to change it back" => "Importante: una volta selezionata la modalità di cifratura non sarà possibile tornare indietro", +"User specific (let the user decide)" => "Specificato dall'utente (lascia decidere all'utente)", "Encryption" => "Cifratura", "Exclude the following file types from encryption" => "Escludi i seguenti tipi di file dalla cifratura", -"None" => "Nessuna", -"Enable Encryption" => "Abilita cifratura" +"None" => "Nessuna" ); diff --git a/apps/files_encryption/l10n/ja_JP.php b/apps/files_encryption/l10n/ja_JP.php index 2c3e5410de..4100908e00 100644 --- a/apps/files_encryption/l10n/ja_JP.php +++ b/apps/files_encryption/l10n/ja_JP.php @@ -1,6 +1,16 @@ "変換を完了するために、ownCloud クライアントに切り替えて、暗号化パスワードを変更してください。", +"switched to client side encryption" => "クライアントサイドの暗号化に切り替えました", +"Change encryption password to login password" => "暗号化パスワードをログインパスワードに変更", +"Please check your passwords and try again." => "パスワードを確認してもう一度行なってください。", +"Could not change your file encryption password to your login password" => "ファイル暗号化パスワードをログインパスワードに変更できませんでした。", +"Choose encryption mode:" => "暗号化モードを選択:", +"Client side encryption (most secure but makes it impossible to access your data from the web interface)" => "クライアントサイドの暗号化(最もセキュアですが、WEBインターフェースからデータにアクセスできなくなります)", +"Server side encryption (allows you to access your files from the web interface and the desktop client)" => "サーバサイド暗号化(WEBインターフェースおよびデスクトップクライアントからファイルにアクセスすることができます)", +"None (no encryption at all)" => "暗号化無し(何も暗号化しません)", +"Important: Once you selected an encryption mode there is no way to change it back" => "重要: 一度暗号化を選択してしまうと、もとに戻す方法はありません", +"User specific (let the user decide)" => "ユーザ指定(ユーザが選べるようにする)", "Encryption" => "暗号化", "Exclude the following file types from encryption" => "暗号化から除外するファイルタイプ", -"None" => "なし", -"Enable Encryption" => "暗号化を有効にする" +"None" => "なし" ); diff --git a/apps/files_encryption/l10n/ko.php b/apps/files_encryption/l10n/ko.php index 4702753435..901c41e12e 100644 --- a/apps/files_encryption/l10n/ko.php +++ b/apps/files_encryption/l10n/ko.php @@ -1,6 +1,16 @@ "ownCloud로 전환한 다음 암호화에 사용할 암호를 변경하면 변환이 완료됩니다.", +"switched to client side encryption" => "클라이언트 암호화로 변경됨", +"Change encryption password to login password" => "암호화 암호를 로그인 암호로 변경", +"Please check your passwords and try again." => "암호를 확인한 다음 다시 시도하십시오.", +"Could not change your file encryption password to your login password" => "암호화 암호를 로그인 암호로 변경할 수 없습니다", +"Choose encryption mode:" => "암호화 모드 선택:", +"Client side encryption (most secure but makes it impossible to access your data from the web interface)" => "클라이언트 암호화 (안전하지만 웹에서 데이터에 접근할 수 없음)", +"Server side encryption (allows you to access your files from the web interface and the desktop client)" => "서버 암호화 (웹 및 데스크톱 클라이언트에서 데이터에 접근할 수 있음)", +"None (no encryption at all)" => "없음 (암호화하지 않음)", +"Important: Once you selected an encryption mode there is no way to change it back" => "알림: 암호화 모드를 선택하면 다른 것으로 변경할 수 없습니다", +"User specific (let the user decide)" => "사용자 지정 (사용자별 설정)", "Encryption" => "암호화", "Exclude the following file types from encryption" => "다음 파일 형식은 암호화하지 않음", -"None" => "없음", -"Enable Encryption" => "암호화 사용" +"None" => "없음" ); diff --git a/apps/files_encryption/l10n/ku_IQ.php b/apps/files_encryption/l10n/ku_IQ.php index bd8977ac51..06bb9b9325 100644 --- a/apps/files_encryption/l10n/ku_IQ.php +++ b/apps/files_encryption/l10n/ku_IQ.php @@ -1,6 +1,5 @@ "نهێنیکردن", "Exclude the following file types from encryption" => "به‌ربه‌ست کردنی ئه‌م جۆره‌ په‌ڕگانه له‌ نهێنیکردن", -"None" => "هیچ", -"Enable Encryption" => "چالاکردنی نهێنیکردن" +"None" => "هیچ" ); diff --git a/apps/files_encryption/l10n/lt_LT.php b/apps/files_encryption/l10n/lt_LT.php index b939df164c..22cbe7a4ff 100644 --- a/apps/files_encryption/l10n/lt_LT.php +++ b/apps/files_encryption/l10n/lt_LT.php @@ -1,6 +1,5 @@ "Šifravimas", "Exclude the following file types from encryption" => "Nešifruoti pasirinkto tipo failų", -"None" => "Nieko", -"Enable Encryption" => "Įjungti šifravimą" +"None" => "Nieko" ); diff --git a/apps/files_encryption/l10n/mk.php b/apps/files_encryption/l10n/mk.php index dfcaed9f37..7ccf8ac2d5 100644 --- a/apps/files_encryption/l10n/mk.php +++ b/apps/files_encryption/l10n/mk.php @@ -1,6 +1,5 @@ "Енкрипција", "Exclude the following file types from encryption" => "Исклучи ги следните типови на датотеки од енкрипција", -"None" => "Ништо", -"Enable Encryption" => "Овозможи енкрипција" +"None" => "Ништо" ); diff --git a/apps/files_encryption/l10n/nb_NO.php b/apps/files_encryption/l10n/nb_NO.php index e65df7b6ce..2ec6670e92 100644 --- a/apps/files_encryption/l10n/nb_NO.php +++ b/apps/files_encryption/l10n/nb_NO.php @@ -1,6 +1,5 @@ "Kryptering", "Exclude the following file types from encryption" => "Ekskluder følgende filer fra kryptering", -"None" => "Ingen", -"Enable Encryption" => "Slå på kryptering" +"None" => "Ingen" ); diff --git a/apps/files_encryption/l10n/nl.php b/apps/files_encryption/l10n/nl.php index 1ea56006fc..02cb0d970f 100644 --- a/apps/files_encryption/l10n/nl.php +++ b/apps/files_encryption/l10n/nl.php @@ -1,6 +1,12 @@ "overgeschakeld naar client side encryptie", +"Change encryption password to login password" => "Verander encryptie wachtwoord naar login wachtwoord", +"Please check your passwords and try again." => "Controleer uw wachtwoorden en probeer het opnieuw.", +"Could not change your file encryption password to your login password" => "Kon het bestandsencryptie wachtwoord niet veranderen naar het login wachtwoord", +"Choose encryption mode:" => "Kies encryptie mode:", +"None (no encryption at all)" => "Geen (zonder encryptie)", +"Important: Once you selected an encryption mode there is no way to change it back" => "Belangrijk: Zodra er voor een encryptie mode is gekozen kan deze niet meer worden gewijzigd.", "Encryption" => "Versleuteling", "Exclude the following file types from encryption" => "Versleutel de volgende bestand types niet", -"None" => "Geen", -"Enable Encryption" => "Zet versleuteling aan" +"None" => "Geen" ); diff --git a/apps/files_encryption/l10n/pl.php b/apps/files_encryption/l10n/pl.php index 5cfc707450..896086108e 100644 --- a/apps/files_encryption/l10n/pl.php +++ b/apps/files_encryption/l10n/pl.php @@ -1,6 +1,5 @@ "Szyfrowanie", "Exclude the following file types from encryption" => "Wyłącz następujące typy plików z szyfrowania", -"None" => "Brak", -"Enable Encryption" => "Włącz szyfrowanie" +"None" => "Brak" ); diff --git a/apps/files_encryption/l10n/pt_BR.php b/apps/files_encryption/l10n/pt_BR.php index 5c02f52217..8bd6492a8f 100644 --- a/apps/files_encryption/l10n/pt_BR.php +++ b/apps/files_encryption/l10n/pt_BR.php @@ -1,6 +1,16 @@ "Por favor, vá ao seu cliente ownCloud e mude sua criptografia de senha para completar a conversão.", +"switched to client side encryption" => "alterado para criptografia por parte do cliente", +"Change encryption password to login password" => "Mudar senha de criptografia para senha de login", +"Please check your passwords and try again." => "Por favor, verifique suas senhas e tente novamente.", +"Could not change your file encryption password to your login password" => "Não foi possível mudar sua senha de criptografia de arquivos para sua senha de login", +"Choose encryption mode:" => "Escolha o modo de criptografia:", +"Client side encryption (most secure but makes it impossible to access your data from the web interface)" => "Criptografia por parte do cliente (mais segura, mas torna impossível acessar seus dados a partir da interface web)", +"Server side encryption (allows you to access your files from the web interface and the desktop client)" => "Criptografia por parte do servidor (permite que você acesse seus arquivos da interface web e do cliente desktop)", +"None (no encryption at all)" => "Nenhuma (sem qualquer criptografia)", +"Important: Once you selected an encryption mode there is no way to change it back" => "Importante: Uma vez que tiver escolhido um modo de criptografia, não há um meio de voltar atrás", +"User specific (let the user decide)" => "Específico por usuário (deixa o usuário decidir)", "Encryption" => "Criptografia", "Exclude the following file types from encryption" => "Excluir os seguintes tipos de arquivo da criptografia", -"None" => "Nenhuma", -"Enable Encryption" => "Habilitar Criptografia" +"None" => "Nenhuma" ); diff --git a/apps/files_encryption/l10n/pt_PT.php b/apps/files_encryption/l10n/pt_PT.php index 570462b414..b6eedcdc50 100644 --- a/apps/files_encryption/l10n/pt_PT.php +++ b/apps/files_encryption/l10n/pt_PT.php @@ -1,6 +1,16 @@ "Por favor, use o seu cliente de sincronização do ownCloud e altere a sua password de encriptação para concluír a conversão.", +"switched to client side encryption" => "Alterado para encriptação do lado do cliente", +"Change encryption password to login password" => "Alterar a password de encriptação para a password de login", +"Please check your passwords and try again." => "Por favor verifique as suas paswords e tente de novo.", +"Could not change your file encryption password to your login password" => "Não foi possível alterar a password de encriptação de ficheiros para a sua password de login", +"Choose encryption mode:" => "Escolha o método de encriptação", +"Client side encryption (most secure but makes it impossible to access your data from the web interface)" => "Encriptação do lado do cliente (mais seguro mas torna possível o acesso aos dados através do interface web)", +"Server side encryption (allows you to access your files from the web interface and the desktop client)" => "Encriptação do lado do servidor (permite o acesso aos seus ficheiros através do interface web e do cliente de sincronização)", +"None (no encryption at all)" => "Nenhuma (sem encriptação)", +"Important: Once you selected an encryption mode there is no way to change it back" => "Importante: Uma vez escolhido o modo de encriptação, não existe maneira de o alterar!", +"User specific (let the user decide)" => "Escolhido pelo utilizador", "Encryption" => "Encriptação", "Exclude the following file types from encryption" => "Excluir da encriptação os seguintes tipo de ficheiros", -"None" => "Nenhum", -"Enable Encryption" => "Activar Encriptação" +"None" => "Nenhum" ); diff --git a/apps/files_encryption/l10n/ro.php b/apps/files_encryption/l10n/ro.php index 97f3f262d7..f958692dd8 100644 --- a/apps/files_encryption/l10n/ro.php +++ b/apps/files_encryption/l10n/ro.php @@ -1,6 +1,16 @@ "Te rugăm să mergi în clientul ownCloud și să schimbi parola pentru a finisa conversia", +"switched to client side encryption" => "setat la encriptare locală", +"Change encryption password to login password" => "Schimbă parola de ecriptare în parolă de acces", +"Please check your passwords and try again." => "Verifică te rog parolele și înceracă din nou.", +"Could not change your file encryption password to your login password" => "Nu s-a putut schimba parola de encripție a fișierelor ca parolă de acces", +"Choose encryption mode:" => "Alege tipul de ecripție", +"Client side encryption (most secure but makes it impossible to access your data from the web interface)" => "Encripție locală (cea mai sigură, dar face ca datele să nu mai fie accesibile din interfața web)", +"Server side encryption (allows you to access your files from the web interface and the desktop client)" => "Encripție pe server (permite să accesezi datele tale din interfața web și din clientul pentru calculator)", +"None (no encryption at all)" => "Fără (nici un fel de ecriptare)", +"Important: Once you selected an encryption mode there is no way to change it back" => "Important: Din moment ce ai setat un mod de encriptare, nu mai există metode de a-l schimba înapoi", +"User specific (let the user decide)" => "Spefic fiecărui utilizator (lasă utilizatorul să decidă)", "Encryption" => "Încriptare", "Exclude the following file types from encryption" => "Exclude următoarele tipuri de fișiere de la încriptare", -"None" => "Niciuna", -"Enable Encryption" => "Activare încriptare" +"None" => "Niciuna" ); diff --git a/apps/files_encryption/l10n/ru.php b/apps/files_encryption/l10n/ru.php index 3a7e84b6d0..14115c1268 100644 --- a/apps/files_encryption/l10n/ru.php +++ b/apps/files_encryption/l10n/ru.php @@ -1,6 +1,5 @@ "Шифрование", "Exclude the following file types from encryption" => "Исключить шифрование следующих типов файлов", -"None" => "Ничего", -"Enable Encryption" => "Включить шифрование" +"None" => "Ничего" ); diff --git a/apps/files_encryption/l10n/ru_RU.php b/apps/files_encryption/l10n/ru_RU.php index 1328b0d035..1149ac64f3 100644 --- a/apps/files_encryption/l10n/ru_RU.php +++ b/apps/files_encryption/l10n/ru_RU.php @@ -1,6 +1,14 @@ "Пожалуйста, переключитесь на ownCloud-клиент и измените Ваш пароль шифрования для завершения конвертации.", +"switched to client side encryption" => "переключено на шифрование на клиентской стороне", +"Please check your passwords and try again." => "Пожалуйста, проверьте Ваш пароль и попробуйте снова", +"Choose encryption mode:" => "Выберите способ шифрования:", +"Client side encryption (most secure but makes it impossible to access your data from the web interface)" => "Шифрование на стороне клиента (наиболее безопасно, но делает невозможным получение доступа к Вашим данным по вэб-интерфейсу)", +"Server side encryption (allows you to access your files from the web interface and the desktop client)" => "Шифрование на стороне сервера (позволяет Вам получить доступ к Вашим файлам по вэб-интерфейсу и десктопному клиенту)", +"None (no encryption at all)" => "Нет (шифрование полностью отсутствует)", +"Important: Once you selected an encryption mode there is no way to change it back" => "Важно: Невозможно будет изменить выбранный способ шифрования", +"User specific (let the user decide)" => "Специфика пользователя (позволено решить пользователю)", "Encryption" => "Шифрование", "Exclude the following file types from encryption" => "Исключите следующие типы файлов из шифрования", -"None" => "Ни один", -"Enable Encryption" => "Включить шифрование" +"None" => "Ни один" ); diff --git a/apps/files_encryption/l10n/si_LK.php b/apps/files_encryption/l10n/si_LK.php index a29884afff..2d61bec45b 100644 --- a/apps/files_encryption/l10n/si_LK.php +++ b/apps/files_encryption/l10n/si_LK.php @@ -1,6 +1,5 @@ "ගුප්ත කේතනය", "Exclude the following file types from encryption" => "මෙම ගොනු වර්ග ගුප්ත කේතනය කිරීමෙන් බැහැරව තබන්න", -"None" => "කිසිවක් නැත", -"Enable Encryption" => "ගුප්ත කේතනය සක්‍රිය කරන්න" +"None" => "කිසිවක් නැත" ); diff --git a/apps/files_encryption/l10n/sk_SK.php b/apps/files_encryption/l10n/sk_SK.php index 598f1294f6..355b45a4ce 100644 --- a/apps/files_encryption/l10n/sk_SK.php +++ b/apps/files_encryption/l10n/sk_SK.php @@ -1,6 +1,16 @@ "Prosím, prejdite do svojho klienta ownCloud a zmente šifrovacie heslo na dokončenie konverzie.", +"switched to client side encryption" => "prepnuté na šifrovanie prostredníctvom klienta", +"Change encryption password to login password" => "Zmeniť šifrovacie heslo na prihlasovacie", +"Please check your passwords and try again." => "Skontrolujte si heslo a skúste to znovu.", +"Could not change your file encryption password to your login password" => "Nie je možné zmeniť šifrovacie heslo na prihlasovacie", +"Choose encryption mode:" => "Vyberte režim šifrovania:", +"Client side encryption (most secure but makes it impossible to access your data from the web interface)" => "Šifrovanie prostredníctvom klienta (najbezpečnejšia voľba, neumožňuje však prístup k súborom z webového rozhrania)", +"Server side encryption (allows you to access your files from the web interface and the desktop client)" => "Šifrovanie na serveri (umožňuje pristupovať k súborom z webového rozhrania a desktopového klienta)", +"None (no encryption at all)" => "Žiadne (žiadne šifrovanie)", +"Important: Once you selected an encryption mode there is no way to change it back" => "Dôležité: ak si zvolíte režim šifrovania, nie je možné ho znovu zrušiť", +"User specific (let the user decide)" => "Definovaný používateľom (umožňuje používateľovi vybrať si)", "Encryption" => "Šifrovanie", "Exclude the following file types from encryption" => "Vynechať nasledujúce súbory pri šifrovaní", -"None" => "Žiadne", -"Enable Encryption" => "Zapnúť šifrovanie" +"None" => "Žiadne" ); diff --git a/apps/files_encryption/l10n/sl.php b/apps/files_encryption/l10n/sl.php index f62fe781c6..db963ef2f8 100644 --- a/apps/files_encryption/l10n/sl.php +++ b/apps/files_encryption/l10n/sl.php @@ -1,6 +1,5 @@ "Šifriranje", "Exclude the following file types from encryption" => "Navedene vrste datotek naj ne bodo šifrirane", -"None" => "Brez", -"Enable Encryption" => "Omogoči šifriranje" +"None" => "Brez" ); diff --git a/apps/files_encryption/l10n/sr.php b/apps/files_encryption/l10n/sr.php index 4718780ee5..198bcc94ef 100644 --- a/apps/files_encryption/l10n/sr.php +++ b/apps/files_encryption/l10n/sr.php @@ -1,6 +1,5 @@ "Шифровање", "Exclude the following file types from encryption" => "Не шифруј следеће типове датотека", -"None" => "Ништа", -"Enable Encryption" => "Омогући шифровање" +"None" => "Ништа" ); diff --git a/apps/files_encryption/l10n/sv.php b/apps/files_encryption/l10n/sv.php index 0a477f8346..9b6ce14178 100644 --- a/apps/files_encryption/l10n/sv.php +++ b/apps/files_encryption/l10n/sv.php @@ -1,6 +1,16 @@ "Vänligen växla till ownCloud klienten och ändra ditt krypteringslösenord för att slutföra omvandlingen.", +"switched to client side encryption" => "Bytte till kryptering på klientsidan", +"Change encryption password to login password" => "Ändra krypteringslösenord till loginlösenord", +"Please check your passwords and try again." => "Kontrollera dina lösenord och försök igen.", +"Could not change your file encryption password to your login password" => "Kunde inte ändra ditt filkrypteringslösenord till ditt loginlösenord", +"Choose encryption mode:" => "Välj krypteringsläge:", +"Client side encryption (most secure but makes it impossible to access your data from the web interface)" => "Kryptering på klientsidan (säkraste men gör det omöjligt att komma åt dina filer med en webbläsare)", +"Server side encryption (allows you to access your files from the web interface and the desktop client)" => "Kryptering på serversidan (kan komma åt dina filer från webbläsare och datorklient)", +"None (no encryption at all)" => "Ingen (ingen kryptering alls)", +"Important: Once you selected an encryption mode there is no way to change it back" => "Viktigt: När du har valt ett krypteringsläge finns det inget sätt att ändra tillbaka", +"User specific (let the user decide)" => "Användarspecifik (låter användaren bestämma)", "Encryption" => "Kryptering", "Exclude the following file types from encryption" => "Exkludera följande filtyper från kryptering", -"None" => "Ingen", -"Enable Encryption" => "Aktivera kryptering" +"None" => "Ingen" ); diff --git a/apps/files_encryption/l10n/ta_LK.php b/apps/files_encryption/l10n/ta_LK.php index 1d1ef74007..aab628b551 100644 --- a/apps/files_encryption/l10n/ta_LK.php +++ b/apps/files_encryption/l10n/ta_LK.php @@ -1,6 +1,5 @@ "மறைக்குறியீடு", "Exclude the following file types from encryption" => "மறைக்குறியாக்கலில் பின்வரும் கோப்பு வகைகளை நீக்கவும்", -"None" => "ஒன்றுமில்லை", -"Enable Encryption" => "மறைக்குறியாக்கலை இயலுமைப்படுத்துக" +"None" => "ஒன்றுமில்லை" ); diff --git a/apps/files_encryption/l10n/th_TH.php b/apps/files_encryption/l10n/th_TH.php index c2685de6e3..f8c19456ab 100644 --- a/apps/files_encryption/l10n/th_TH.php +++ b/apps/files_encryption/l10n/th_TH.php @@ -1,6 +1,16 @@ "กรุณาสลับไปที่โปรแกรมไคลเอนต์ ownCloud ของคุณ แล้วเปลี่ยนรหัสผ่านสำหรับการเข้ารหัสเพื่อแปลงข้อมูลให้เสร็จสมบูรณ์", +"switched to client side encryption" => "สลับไปใช้การเข้ารหัสจากโปรแกรมไคลเอนต์", +"Change encryption password to login password" => "เปลี่ยนรหัสผ่านสำหรับเข้ารหัสไปเป็นรหัสผ่านสำหรับการเข้าสู่ระบบ", +"Please check your passwords and try again." => "กรุณาตรวจสอบรหัสผ่านของคุณแล้วลองใหม่อีกครั้ง", +"Could not change your file encryption password to your login password" => "ไม่สามารถเปลี่ยนรหัสผ่านสำหรับการเข้ารหัสไฟล์ของคุณไปเป็นรหัสผ่านสำหรับการเข้าสู่ระบบของคุณได้", +"Choose encryption mode:" => "เลือกรูปแบบการเข้ารหัส:", +"Client side encryption (most secure but makes it impossible to access your data from the web interface)" => "การเข้ารหัสด้วยโปรแกรมไคลเอนต์ (ปลอดภัยที่สุด แต่จะทำให้คุณไม่สามารถเข้าถึงข้อมูลต่างๆจากหน้าจอเว็บไซต์ได้)", +"Server side encryption (allows you to access your files from the web interface and the desktop client)" => "การเข้ารหัสจากทางฝั่งเซิร์ฟเวอร์ (อนุญาตให้คุณเข้าถึงไฟล์ของคุณจากหน้าจอเว็บไซต์ และโปรแกรมไคลเอนต์จากเครื่องเดสก์ท็อปได้)", +"None (no encryption at all)" => "ไม่ต้อง (ไม่มีการเข้ารหัสเลย)", +"Important: Once you selected an encryption mode there is no way to change it back" => "ข้อความสำคัญ: หลังจากที่คุณได้เลือกรูปแบบการเข้ารหัสแล้ว จะไม่สามารถเปลี่ยนกลับมาใหม่ได้อีก", +"User specific (let the user decide)" => "ให้ผู้ใช้งานเลือกเอง (ปล่อยให้ผู้ใช้งานตัดสินใจเอง)", "Encryption" => "การเข้ารหัส", "Exclude the following file types from encryption" => "ไม่ต้องรวมชนิดของไฟล์ดังต่อไปนี้จากการเข้ารหัส", -"None" => "ไม่ต้อง", -"Enable Encryption" => "เปิดใช้งานการเข้ารหัส" +"None" => "ไม่ต้อง" ); diff --git a/apps/files_encryption/l10n/tr.php b/apps/files_encryption/l10n/tr.php index 474ee42b84..07f78d148c 100644 --- a/apps/files_encryption/l10n/tr.php +++ b/apps/files_encryption/l10n/tr.php @@ -1,6 +1,5 @@ "Şifreleme", -"Enable Encryption" => "Şifrelemeyi Etkinleştir", -"None" => "Hiçbiri", -"Exclude the following file types from encryption" => "Aşağıdaki dosya tiplerini şifrelemeye dahil etme" +"Exclude the following file types from encryption" => "Aşağıdaki dosya tiplerini şifrelemeye dahil etme", +"None" => "Hiçbiri" ); diff --git a/apps/files_encryption/l10n/uk.php b/apps/files_encryption/l10n/uk.php index 3c15bb2843..e358921565 100644 --- a/apps/files_encryption/l10n/uk.php +++ b/apps/files_encryption/l10n/uk.php @@ -1,6 +1,5 @@ "Шифрування", "Exclude the following file types from encryption" => "Не шифрувати файли наступних типів", -"None" => "Жоден", -"Enable Encryption" => "Включити шифрування" +"None" => "Жоден" ); diff --git a/apps/files_encryption/l10n/vi.php b/apps/files_encryption/l10n/vi.php index 6365084fdc..218285b675 100644 --- a/apps/files_encryption/l10n/vi.php +++ b/apps/files_encryption/l10n/vi.php @@ -1,6 +1,5 @@ "Mã hóa", "Exclude the following file types from encryption" => "Loại trừ các loại tập tin sau đây từ mã hóa", -"None" => "Không có gì hết", -"Enable Encryption" => "BẬT mã hóa" +"None" => "Không có gì hết" ); diff --git a/apps/files_encryption/l10n/zh_CN.GB2312.php b/apps/files_encryption/l10n/zh_CN.GB2312.php index 297444fcf5..31a3d3b49b 100644 --- a/apps/files_encryption/l10n/zh_CN.GB2312.php +++ b/apps/files_encryption/l10n/zh_CN.GB2312.php @@ -1,6 +1,5 @@ "加密", "Exclude the following file types from encryption" => "从加密中排除如下文件类型", -"None" => "无", -"Enable Encryption" => "启用加密" +"None" => "无" ); diff --git a/apps/files_encryption/l10n/zh_CN.php b/apps/files_encryption/l10n/zh_CN.php index 1e1247d15f..aa4817b590 100644 --- a/apps/files_encryption/l10n/zh_CN.php +++ b/apps/files_encryption/l10n/zh_CN.php @@ -1,6 +1,5 @@ "加密", "Exclude the following file types from encryption" => "从加密中排除列出的文件类型", -"None" => "None", -"Enable Encryption" => "开启加密" +"None" => "None" ); diff --git a/apps/files_encryption/l10n/zh_TW.php b/apps/files_encryption/l10n/zh_TW.php index 4c62130cf4..146724def0 100644 --- a/apps/files_encryption/l10n/zh_TW.php +++ b/apps/files_encryption/l10n/zh_TW.php @@ -1,6 +1,16 @@ "請至您的 ownCloud 客戶端程式修改您的加密密碼以完成轉換。", +"switched to client side encryption" => "已切換為客戶端加密", +"Change encryption password to login password" => "將加密密碼修改為登入密碼", +"Please check your passwords and try again." => "請檢查您的密碼並再試一次。", +"Could not change your file encryption password to your login password" => "無法變更您的檔案加密密碼為登入密碼", +"Choose encryption mode:" => "選擇加密模式:", +"Client side encryption (most secure but makes it impossible to access your data from the web interface)" => "客戶端加密 (最安全但是會使您無法從網頁界面存取您的檔案)", +"Server side encryption (allows you to access your files from the web interface and the desktop client)" => "伺服器端加密 (您可以從網頁界面及客戶端程式存取您的檔案)", +"None (no encryption at all)" => "無 (不加密)", +"Important: Once you selected an encryption mode there is no way to change it back" => "重要:一旦您選擇了加密就無法再改回來", +"User specific (let the user decide)" => "使用者自訂 (讓使用者自己決定)", "Encryption" => "加密", "Exclude the following file types from encryption" => "下列的檔案類型不加密", -"None" => "無", -"Enable Encryption" => "啟用加密" +"None" => "無" ); diff --git a/apps/files_encryption/lib/crypt.php b/apps/files_encryption/lib/crypt.php old mode 100644 new mode 100755 index 666fedb4e1..fddc89dae5 --- a/apps/files_encryption/lib/crypt.php +++ b/apps/files_encryption/lib/crypt.php @@ -1,220 +1,732 @@ -. - * - */ - - - -// Todo: -// - Crypt/decrypt button in the userinterface -// - Setting if crypto should be on by default -// - Add a setting "Don´t encrypt files larger than xx because of performance reasons" -// - Transparent decrypt/encrypt in filesystem.php. Autodetect if a file is encrypted (.encrypted extension) -// - Don't use a password directly as encryption key, but a key which is stored on the server and encrypted with the -// user password. -> password change faster -// - IMPORTANT! Check if the block lenght of the encrypted data stays the same - - -require_once 'Crypt_Blowfish/Blowfish.php'; - -/** - * This class is for crypting and decrypting - */ -class OC_Crypt { - static private $bf = null; - - public static function loginListener($params) { - self::init($params['uid'], $params['password']); - } - - public static function init($login, $password) { - $view=new OC_FilesystemView('/'); - if ( ! $view->file_exists('/'.$login)) { - $view->mkdir('/'.$login); - } - - OC_FileProxy::$enabled=false; - if ( ! $view->file_exists('/'.$login.'/encryption.key')) {// does key exist? - OC_Crypt::createkey($login, $password); - } - $key=$view->file_get_contents('/'.$login.'/encryption.key'); - OC_FileProxy::$enabled=true; - $_SESSION['enckey']=OC_Crypt::decrypt($key, $password); - } - - - /** - * get the blowfish encryption handeler for a key - * @param string $key (optional) - * @return Crypt_Blowfish - * - * if the key is left out, the default handeler will be used - */ - public static function getBlowfish($key='') { - if ($key) { - return new Crypt_Blowfish($key); - } else { - if ( ! isset($_SESSION['enckey'])) { - return false; - } - if ( ! self::$bf) { - self::$bf=new Crypt_Blowfish($_SESSION['enckey']); - } - return self::$bf; - } - } - - public static function createkey($username, $passcode) { - // generate a random key - $key=mt_rand(10000, 99999).mt_rand(10000, 99999).mt_rand(10000, 99999).mt_rand(10000, 99999); - - // encrypt the key with the passcode of the user - $enckey=OC_Crypt::encrypt($key, $passcode); - - // Write the file - $proxyEnabled=OC_FileProxy::$enabled; - OC_FileProxy::$enabled=false; - $view=new OC_FilesystemView('/'.$username); - $view->file_put_contents('/encryption.key', $enckey); - OC_FileProxy::$enabled=$proxyEnabled; - } - - public static function changekeypasscode($oldPassword, $newPassword) { - if (OCP\User::isLoggedIn()) { - $username=OCP\USER::getUser(); - $view=new OC_FilesystemView('/'.$username); - - // read old key - $key=$view->file_get_contents('/encryption.key'); - - // decrypt key with old passcode - $key=OC_Crypt::decrypt($key, $oldPassword); - - // encrypt again with new passcode - $key=OC_Crypt::encrypt($key, $newPassword); - - // store the new key - $view->file_put_contents('/encryption.key', $key ); - } - } - - /** - * @brief encrypts an content - * @param $content the cleartext message you want to encrypt - * @param $key the encryption key (optional) - * @returns encrypted content - * - * This function encrypts an content - */ - public static function encrypt( $content, $key='') { - $bf = self::getBlowfish($key); - return $bf->encrypt($content); - } - - /** - * @brief decryption of an content - * @param $content the cleartext message you want to decrypt - * @param $key the encryption key (optional) - * @returns cleartext content - * - * This function decrypts an content - */ - public static function decrypt( $content, $key='') { - $bf = self::getBlowfish($key); - $data=$bf->decrypt($content); - return $data; - } - - /** - * @brief encryption of a file - * @param string $source - * @param string $target - * @param string $key the decryption key - * - * This function encrypts a file - */ - public static function encryptFile( $source, $target, $key='') { - $handleread = fopen($source, "rb"); - if ($handleread!=false) { - $handlewrite = fopen($target, "wb"); - while (!feof($handleread)) { - $content = fread($handleread, 8192); - $enccontent=OC_CRYPT::encrypt( $content, $key); - fwrite($handlewrite, $enccontent); - } - fclose($handlewrite); - fclose($handleread); - } - } - - - /** - * @brief decryption of a file - * @param string $source - * @param string $target - * @param string $key the decryption key - * - * This function decrypts a file - */ - public static function decryptFile( $source, $target, $key='') { - $handleread = fopen($source, "rb"); - if ($handleread!=false) { - $handlewrite = fopen($target, "wb"); - while (!feof($handleread)) { - $content = fread($handleread, 8192); - $enccontent=OC_CRYPT::decrypt( $content, $key); - if (feof($handleread)) { - $enccontent=rtrim($enccontent, "\0"); - } - fwrite($handlewrite, $enccontent); - } - fclose($handlewrite); - fclose($handleread); - } - } - - /** - * encrypt data in 8192b sized blocks - */ - public static function blockEncrypt($data, $key='') { - $result=''; - while (strlen($data)) { - $result.=self::encrypt(substr($data, 0, 8192), $key); - $data=substr($data, 8192); - } - return $result; - } - - /** - * decrypt data in 8192b sized blocks - */ - public static function blockDecrypt($data, $key='', $maxLength=0) { - $result=''; - while (strlen($data)) { - $result.=self::decrypt(substr($data, 0, 8192), $key); - $data=substr($data, 8192); - } - if ($maxLength>0) { - return substr($result, 0, $maxLength); - } else { - return rtrim($result, "\0"); - } - } -} +. + * + */ + +namespace OCA\Encryption; + +require_once 'Crypt_Blowfish/Blowfish.php'; + +// Todo: +// - Crypt/decrypt button in the userinterface +// - Setting if crypto should be on by default +// - Add a setting "Don´t encrypt files larger than xx because of performance reasons" +// - Transparent decrypt/encrypt in filesystem.php. Autodetect if a file is encrypted (.encrypted extension) +// - Don't use a password directly as encryption key. but a key which is stored on the server and encrypted with the user password. -> password change faster +// - IMPORTANT! Check if the block lenght of the encrypted data stays the same + +/** + * Class for common cryptography functionality + */ + +class Crypt { + + /** + * @brief return encryption mode client or server side encryption + * @param string user name (use system wide setting if name=null) + * @return string 'client' or 'server' + */ + public static function mode( $user = null ) { + +// $mode = \OC_Appconfig::getValue( 'files_encryption', 'mode', 'none' ); +// +// if ( $mode == 'user') { +// if ( !$user ) { +// $user = \OCP\User::getUser(); +// } +// $mode = 'none'; +// if ( $user ) { +// $query = \OC_DB::prepare( "SELECT mode FROM *PREFIX*encryption WHERE uid = ?" ); +// $result = $query->execute(array($user)); +// if ($row = $result->fetchRow()){ +// $mode = $row['mode']; +// } +// } +// } +// +// return $mode; + + return 'server'; + + } + + /** + * @brief Create a new encryption keypair + * @return array publicKey, privatekey + */ + public static function createKeypair() { + + $res = openssl_pkey_new(); + + // Get private key + openssl_pkey_export( $res, $privateKey ); + + // Get public key + $publicKey = openssl_pkey_get_details( $res ); + + $publicKey = $publicKey['key']; + + return( array( 'publicKey' => $publicKey, 'privateKey' => $privateKey ) ); + + } + + /** + * @brief Add arbitrary padding to encrypted data + * @param string $data data to be padded + * @return padded data + * @note In order to end up with data exactly 8192 bytes long we must add two letters. It is impossible to achieve exactly 8192 length blocks with encryption alone, hence padding is added to achieve the required length. + */ + public static function addPadding( $data ) { + + $padded = $data . 'xx'; + + return $padded; + + } + + /** + * @brief Remove arbitrary padding to encrypted data + * @param string $padded padded data to remove padding from + * @return unpadded data on success, false on error + */ + public static function removePadding( $padded ) { + + if ( substr( $padded, -2 ) == 'xx' ) { + + $data = substr( $padded, 0, -2 ); + + return $data; + + } else { + + # TODO: log the fact that unpadded data was submitted for removal of padding + return false; + + } + + } + + /** + * @brief Check if a file's contents contains an IV and is symmetrically encrypted + * @return true / false + * @note see also OCA\Encryption\Util->isEncryptedPath() + */ + public static function isEncryptedContent( $content ) { + + if ( !$content ) { + + return false; + + } + + $noPadding = self::removePadding( $content ); + + // Fetch encryption metadata from end of file + $meta = substr( $noPadding, -22 ); + + // Fetch IV from end of file + $iv = substr( $meta, -16 ); + + // Fetch identifier from start of metadata + $identifier = substr( $meta, 0, 6 ); + + if ( $identifier == '00iv00') { + + return true; + + } else { + + return false; + + } + + } + + /** + * Check if a file is encrypted according to database file cache + * @param string $path + * @return bool + */ + public static function isEncryptedMeta( $path ) { + + # TODO: Use DI to get OC_FileCache_Cached out of here + + // Fetch all file metadata from DB + $metadata = \OC_FileCache_Cached::get( $path, '' ); + + // Return encryption status + return isset( $metadata['encrypted'] ) and ( bool )$metadata['encrypted']; + + } + + /** + * @brief Check if a file is encrypted via legacy system + * @return true / false + */ + public static function isLegacyEncryptedContent( $content ) { + + // Fetch all file metadata from DB + $metadata = \OC_FileCache_Cached::get( $content, '' ); + + // If a file is flagged with encryption in DB, but isn't a valid content + IV combination, it's probably using the legacy encryption system + if ( + $content + and isset( $metadata['encrypted'] ) + and $metadata['encrypted'] === true + and !self::isEncryptedContent( $content ) + ) { + + return true; + + } else { + + return false; + + } + + } + + /** + * @brief Symmetrically encrypt a string + * @returns encrypted file + */ + public static function encrypt( $plainContent, $iv, $passphrase = '' ) { + + if ( $encryptedContent = openssl_encrypt( $plainContent, 'AES-128-CFB', $passphrase, false, $iv ) ) { + + return $encryptedContent; + + } else { + + \OC_Log::write( 'Encryption library', 'Encryption (symmetric) of content failed' , \OC_Log::ERROR ); + + return false; + + } + + } + + /** + * @brief Symmetrically decrypt a string + * @returns decrypted file + */ + public static function decrypt( $encryptedContent, $iv, $passphrase ) { + + if ( $plainContent = openssl_decrypt( $encryptedContent, 'AES-128-CFB', $passphrase, false, $iv ) ) { + + return $plainContent; + + + } else { + + throw new \Exception( 'Encryption library: Decryption (symmetric) of content failed' ); + + return false; + + } + + } + + /** + * @brief Concatenate encrypted data with its IV and padding + * @param string $content content to be concatenated + * @param string $iv IV to be concatenated + * @returns string concatenated content + */ + public static function concatIv ( $content, $iv ) { + + $combined = $content . '00iv00' . $iv; + + return $combined; + + } + + /** + * @brief Split concatenated data and IV into respective parts + * @param string $catFile concatenated data to be split + * @returns array keys: encrypted, iv + */ + public static function splitIv ( $catFile ) { + + // Fetch encryption metadata from end of file + $meta = substr( $catFile, -22 ); + + // Fetch IV from end of file + $iv = substr( $meta, -16 ); + + // Remove IV and IV identifier text to expose encrypted content + $encrypted = substr( $catFile, 0, -22 ); + + $split = array( + 'encrypted' => $encrypted + , 'iv' => $iv + ); + + return $split; + + } + + /** + * @brief Symmetrically encrypts a string and returns keyfile content + * @param $plainContent content to be encrypted in keyfile + * @returns encrypted content combined with IV + * @note IV need not be specified, as it will be stored in the returned keyfile + * and remain accessible therein. + */ + public static function symmetricEncryptFileContent( $plainContent, $passphrase = '' ) { + + if ( !$plainContent ) { + + return false; + + } + + $iv = self::generateIv(); + + if ( $encryptedContent = self::encrypt( $plainContent, $iv, $passphrase ) ) { + + // Combine content to encrypt with IV identifier and actual IV + $catfile = self::concatIv( $encryptedContent, $iv ); + + $padded = self::addPadding( $catfile ); + + return $padded; + + } else { + + \OC_Log::write( 'Encryption library', 'Encryption (symmetric) of keyfile content failed' , \OC_Log::ERROR ); + + return false; + + } + + } + + + /** + * @brief Symmetrically decrypts keyfile content + * @param string $source + * @param string $target + * @param string $key the decryption key + * @returns decrypted content + * + * This function decrypts a file + */ + public static function symmetricDecryptFileContent( $keyfileContent, $passphrase = '' ) { + + if ( !$keyfileContent ) { + + throw new \Exception( 'Encryption library: no data provided for decryption' ); + + } + + // Remove padding + $noPadding = self::removePadding( $keyfileContent ); + + // Split into enc data and catfile + $catfile = self::splitIv( $noPadding ); + + if ( $plainContent = self::decrypt( $catfile['encrypted'], $catfile['iv'], $passphrase ) ) { + + return $plainContent; + + } + + } + + /** + * @brief Creates symmetric keyfile content using a generated key + * @param string $plainContent content to be encrypted + * @returns array keys: key, encrypted + * @note symmetricDecryptFileContent() can be used to decrypt files created using this method + * + * This function decrypts a file + */ + public static function symmetricEncryptFileContentKeyfile( $plainContent ) { + + $key = self::generateKey(); + + if( $encryptedContent = self::symmetricEncryptFileContent( $plainContent, $key ) ) { + + return array( + 'key' => $key + , 'encrypted' => $encryptedContent + ); + + } else { + + return false; + + } + + } + + /** + * @brief Create asymmetrically encrypted keyfile content using a generated key + * @param string $plainContent content to be encrypted + * @returns array keys: key, encrypted + * @note symmetricDecryptFileContent() can be used to decrypt files created using this method + * + * This function decrypts a file + */ + public static function multiKeyEncrypt( $plainContent, array $publicKeys ) { + + $envKeys = array(); + + if( openssl_seal( $plainContent, $sealed, $envKeys, $publicKeys ) ) { + + return array( + 'keys' => $envKeys + , 'encrypted' => $sealed + ); + + } else { + + return false; + + } + + } + + /** + * @brief Asymmetrically encrypt a file using multiple public keys + * @param string $plainContent content to be encrypted + * @returns string $plainContent decrypted string + * @note symmetricDecryptFileContent() can be used to decrypt files created using this method + * + * This function decrypts a file + */ + public static function multiKeyDecrypt( $encryptedContent, $envKey, $privateKey ) { + + if ( !$encryptedContent ) { + + return false; + + } + + if ( openssl_open( $encryptedContent, $plainContent, $envKey, $privateKey ) ) { + + return $plainContent; + + } else { + + \OC_Log::write( 'Encryption library', 'Decryption (asymmetric) of sealed content failed' , \OC_Log::ERROR ); + + return false; + + } + + } + + /** + * @brief Asymetrically encrypt a string using a public key + * @returns encrypted file + */ + public static function keyEncrypt( $plainContent, $publicKey ) { + + openssl_public_encrypt( $plainContent, $encryptedContent, $publicKey ); + + return $encryptedContent; + + } + + /** + * @brief Asymetrically decrypt a file using a private key + * @returns decrypted file + */ + public static function keyDecrypt( $encryptedContent, $privatekey ) { + + openssl_private_decrypt( $encryptedContent, $plainContent, $privatekey ); + + return $plainContent; + + } + + /** + * @brief Encrypts content symmetrically and generates keyfile asymmetrically + * @returns array containing catfile and new keyfile. + * keys: data, key + * @note this method is a wrapper for combining other crypt class methods + */ + public static function keyEncryptKeyfile( $plainContent, $publicKey ) { + + // Encrypt plain data, generate keyfile & encrypted file + $cryptedData = self::symmetricEncryptFileContentKeyfile( $plainContent ); + + // Encrypt keyfile + $cryptedKey = self::keyEncrypt( $cryptedData['key'], $publicKey ); + + return array( 'data' => $cryptedData['encrypted'], 'key' => $cryptedKey ); + + } + + /** + * @brief Takes catfile, keyfile, and private key, and + * performs decryption + * @returns decrypted content + * @note this method is a wrapper for combining other crypt class methods + */ + public static function keyDecryptKeyfile( $catfile, $keyfile, $privateKey ) { + + // Decrypt the keyfile with the user's private key + $decryptedKeyfile = self::keyDecrypt( $keyfile, $privateKey ); + + // Decrypt the catfile symmetrically using the decrypted keyfile + $decryptedData = self::symmetricDecryptFileContent( $catfile, $decryptedKeyfile ); + + return $decryptedData; + + } + + /** + * @brief Symmetrically encrypt a file by combining encrypted component data blocks + */ + public static function symmetricBlockEncryptFileContent( $plainContent, $key ) { + + $crypted = ''; + + $remaining = $plainContent; + + $testarray = array(); + + while( strlen( $remaining ) ) { + + //echo "\n\n\$block = ".substr( $remaining, 0, 6126 ); + + // Encrypt a chunk of unencrypted data and add it to the rest + $block = self::symmetricEncryptFileContent( substr( $remaining, 0, 6126 ), $key ); + + $padded = self::addPadding( $block ); + + $crypted .= $block; + + $testarray[] = $block; + + // Remove the data already encrypted from remaining unencrypted data + $remaining = substr( $remaining, 6126 ); + + } + + //echo "hags "; + + //echo "\n\n\n\$crypted = $crypted\n\n\n"; + + //print_r($testarray); + + return $crypted; + + } + + + /** + * @brief Symmetrically decrypt a file by combining encrypted component data blocks + */ + public static function symmetricBlockDecryptFileContent( $crypted, $key ) { + + $decrypted = ''; + + $remaining = $crypted; + + $testarray = array(); + + while( strlen( $remaining ) ) { + + $testarray[] = substr( $remaining, 0, 8192 ); + + // Decrypt a chunk of unencrypted data and add it to the rest + $decrypted .= self::symmetricDecryptFileContent( $remaining, $key ); + + // Remove the data already encrypted from remaining unencrypted data + $remaining = substr( $remaining, 8192 ); + + } + + //echo "\n\n\$testarray = "; print_r($testarray); + + return $decrypted; + + } + + /** + * @brief Generates a pseudo random initialisation vector + * @return String $iv generated IV + */ + public static function generateIv() { + + if ( $random = openssl_random_pseudo_bytes( 12, $strong ) ) { + + if ( !$strong ) { + + // If OpenSSL indicates randomness is insecure, log error + \OC_Log::write( 'Encryption library', 'Insecure symmetric key was generated using openssl_random_pseudo_bytes()' , \OC_Log::WARN ); + + } + + // We encode the iv purely for string manipulation + // purposes - it gets decoded before use + $iv = base64_encode( $random ); + + return $iv; + + } else { + + throw new Exception( 'Generating IV failed' ); + + } + + } + + /** + * @brief Generate a pseudo random 1024kb ASCII key + * @returns $key Generated key + */ + public static function generateKey() { + + // Generate key + if ( $key = base64_encode( openssl_random_pseudo_bytes( 183, $strong ) ) ) { + + if ( !$strong ) { + + // If OpenSSL indicates randomness is insecure, log error + throw new Exception ( 'Encryption library, Insecure symmetric key was generated using openssl_random_pseudo_bytes()' ); + + } + + return $key; + + } else { + + return false; + + } + + } + + public static function changekeypasscode($oldPassword, $newPassword) { + + if(\OCP\User::isLoggedIn()){ + $key = Keymanager::getPrivateKey( $user, $view ); + if ( ($key = Crypt::symmetricDecryptFileContent($key,$oldpasswd)) ) { + if ( ($key = Crypt::symmetricEncryptFileContent($key, $newpasswd)) ) { + Keymanager::setPrivateKey($key); + return true; + } + } + } + return false; + } + + /** + * @brief Get the blowfish encryption handeler for a key + * @param $key string (optional) + * @return Crypt_Blowfish blowfish object + * + * if the key is left out, the default handeler will be used + */ + public static function getBlowfish( $key = '' ) { + + if ( $key ) { + + return new \Crypt_Blowfish( $key ); + + } else { + + return false; + + } + + } + + public static function legacyCreateKey( $passphrase ) { + + // Generate a random integer + $key = mt_rand( 10000, 99999 ) . mt_rand( 10000, 99999 ) . mt_rand( 10000, 99999 ) . mt_rand( 10000, 99999 ); + + // Encrypt the key with the passphrase + $legacyEncKey = self::legacyEncrypt( $key, $passphrase ); + + return $legacyEncKey; + + } + + /** + * @brief encrypts content using legacy blowfish system + * @param $content the cleartext message you want to encrypt + * @param $key the encryption key (optional) + * @returns encrypted content + * + * This function encrypts an content + */ + public static function legacyEncrypt( $content, $passphrase = '' ) { + + $bf = self::getBlowfish( $passphrase ); + + return $bf->encrypt( $content ); + + } + + /** + * @brief decrypts content using legacy blowfish system + * @param $content the cleartext message you want to decrypt + * @param $key the encryption key (optional) + * @returns cleartext content + * + * This function decrypts an content + */ + public static function legacyDecrypt( $content, $passphrase = '' ) { + + $bf = self::getBlowfish( $passphrase ); + + $decrypted = $bf->decrypt( $content ); + + $trimmed = rtrim( $decrypted, "\0" ); + + return $trimmed; + + } + + public static function legacyKeyRecryptKeyfile( $legacyEncryptedContent, $legacyPassphrase, $publicKey, $newPassphrase ) { + + $decrypted = self::legacyDecrypt( $legacyEncryptedContent, $legacyPassphrase ); + + $recrypted = self::keyEncryptKeyfile( $decrypted, $publicKey ); + + return $recrypted; + + } + + /** + * @brief Re-encryptes a legacy blowfish encrypted file using AES with integrated IV + * @param $legacyContent the legacy encrypted content to re-encrypt + * @returns cleartext content + * + * This function decrypts an content + */ + public static function legacyRecrypt( $legacyContent, $legacyPassphrase, $newPassphrase ) { + + # TODO: write me + + } + +} + +?> \ No newline at end of file diff --git a/apps/files_encryption/lib/cryptstream.php b/apps/files_encryption/lib/cryptstream.php deleted file mode 100644 index d516c0c21b..0000000000 --- a/apps/files_encryption/lib/cryptstream.php +++ /dev/null @@ -1,177 +0,0 @@ -. - * - */ - -/** - * transparently encrypted filestream - * - * you can use it as wrapper around an existing stream by setting - * OC_CryptStream::$sourceStreams['foo']=array('path'=>$path, 'stream'=>$stream) - * and then fopen('crypt://streams/foo'); - */ - -class OC_CryptStream{ - public static $sourceStreams=array(); - private $source; - private $path; - private $meta=array();//header/meta for source stream - private $writeCache; - private $size; - private static $rootView; - - public function stream_open($path, $mode, $options, &$opened_path) { - if ( ! self::$rootView) { - self::$rootView=new OC_FilesystemView(''); - } - $path=str_replace('crypt://', '', $path); - if (dirname($path)=='streams' and isset(self::$sourceStreams[basename($path)])) { - $this->source=self::$sourceStreams[basename($path)]['stream']; - $this->path=self::$sourceStreams[basename($path)]['path']; - $this->size=self::$sourceStreams[basename($path)]['size']; - } else { - $this->path=$path; - if ($mode=='w' or $mode=='w+' or $mode=='wb' or $mode=='wb+') { - $this->size=0; - } else { - $this->size=self::$rootView->filesize($path, $mode); - } - OC_FileProxy::$enabled=false;//disable fileproxies so we can open the source file - $this->source=self::$rootView->fopen($path, $mode); - OC_FileProxy::$enabled=true; - if ( ! is_resource($this->source)) { - OCP\Util::writeLog('files_encryption', 'failed to open '.$path, OCP\Util::ERROR); - } - } - if (is_resource($this->source)) { - $this->meta=stream_get_meta_data($this->source); - } - return is_resource($this->source); - } - - public function stream_seek($offset, $whence=SEEK_SET) { - $this->flush(); - fseek($this->source, $offset, $whence); - } - - public function stream_tell() { - return ftell($this->source); - } - - public function stream_read($count) { - //$count will always be 8192 https://bugs.php.net/bug.php?id=21641 - //This makes this function a lot simpler but will breake everything the moment it's fixed - $this->writeCache=''; - if ($count!=8192) { - OCP\Util::writeLog('files_encryption', - 'php bug 21641 no longer holds, decryption will not work', - OCP\Util::FATAL); - die(); - } - $pos=ftell($this->source); - $data=fread($this->source, 8192); - if (strlen($data)) { - $result=OC_Crypt::decrypt($data); - } else { - $result=''; - } - $length=$this->size-$pos; - if ($length<8192) { - $result=substr($result, 0, $length); - } - return $result; - } - - public function stream_write($data) { - $length=strlen($data); - $currentPos=ftell($this->source); - if ($this->writeCache) { - $data=$this->writeCache.$data; - $this->writeCache=''; - } - if ($currentPos%8192!=0) { - //make sure we always start on a block start - fseek($this->source, -($currentPos%8192), SEEK_CUR); - $encryptedBlock=fread($this->source, 8192); - fseek($this->source, -($currentPos%8192), SEEK_CUR); - $block=OC_Crypt::decrypt($encryptedBlock); - $data=substr($block, 0, $currentPos%8192).$data; - fseek($this->source, -($currentPos%8192), SEEK_CUR); - } - $currentPos=ftell($this->source); - while ($remainingLength=strlen($data)>0) { - if ($remainingLength<8192) { - $this->writeCache=$data; - $data=''; - } else { - $encrypted=OC_Crypt::encrypt(substr($data, 0, 8192)); - fwrite($this->source, $encrypted); - $data=substr($data, 8192); - } - } - $this->size=max($this->size, $currentPos+$length); - return $length; - } - - public function stream_set_option($option, $arg1, $arg2) { - switch($option) { - case STREAM_OPTION_BLOCKING: - stream_set_blocking($this->source, $arg1); - break; - case STREAM_OPTION_READ_TIMEOUT: - stream_set_timeout($this->source, $arg1, $arg2); - break; - case STREAM_OPTION_WRITE_BUFFER: - stream_set_write_buffer($this->source, $arg1, $arg2); - } - } - - public function stream_stat() { - return fstat($this->source); - } - - public function stream_lock($mode) { - flock($this->source, $mode); - } - - public function stream_flush() { - return fflush($this->source); - } - - public function stream_eof() { - return feof($this->source); - } - - private function flush() { - if ($this->writeCache) { - $encrypted=OC_Crypt::encrypt($this->writeCache); - fwrite($this->source, $encrypted); - $this->writeCache=''; - } - } - - public function stream_close() { - $this->flush(); - if ($this->meta['mode']!='r' and $this->meta['mode']!='rb') { - OC_FileCache::put($this->path, array('encrypted'=>true, 'size'=>$this->size), ''); - } - return fclose($this->source); - } -} diff --git a/apps/files_encryption/lib/keymanager.php b/apps/files_encryption/lib/keymanager.php new file mode 100755 index 0000000000..706e1c2661 --- /dev/null +++ b/apps/files_encryption/lib/keymanager.php @@ -0,0 +1,365 @@ + + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see . + * + */ + +namespace OCA\Encryption; + +/** + * @brief Class to manage storage and retrieval of encryption keys + * @note Where a method requires a view object, it's root must be '/' + */ +class Keymanager { + + # TODO: make all dependencies (including static classes) explicit, such as ocfsview objects, by adding them as method arguments (dependency injection) + + /** + * @brief retrieve the ENCRYPTED private key from a user + * + * @return string private key or false + * @note the key returned by this method must be decrypted before use + */ + public static function getPrivateKey( \OC_FilesystemView $view, $user ) { + + $path = '/' . $user . '/' . 'files_encryption' . '/' . $user.'.private.key'; + + $key = $view->file_get_contents( $path ); + + return $key; + } + + /** + * @brief retrieve public key for a specified user + * @return string public key or false + */ + public static function getPublicKey( \OC_FilesystemView $view, $userId ) { + + return $view->file_get_contents( '/public-keys/' . '/' . $userId . '.public.key' ); + + } + + /** + * @brief retrieve both keys from a user (private and public) + * @return array keys: privateKey, publicKey + */ + public static function getUserKeys( \OC_FilesystemView $view, $userId ) { + + return array( + 'publicKey' => self::getPublicKey( $view, $userId ) + , 'privateKey' => self::getPrivateKey( $view, $userId ) + ); + + } + + /** + * @brief Retrieve public keys of all users with access to a file + * @param string $path Path to file + * @return array of public keys for the given file + * @note Checks that the sharing app is enabled should be performed + * by client code, that isn't checked here + */ + public static function getPublicKeys( \OC_FilesystemView $view, $userId, $filePath ) { + + $path = ltrim( $path, '/' ); + + $filepath = '/' . $userId . '/files/' . $filePath; + + // Check if sharing is enabled + if ( OC_App::isEnabled( 'files_sharing' ) ) { + +// // Check if file was shared with other users +// $query = \OC_DB::prepare( " +// SELECT +// uid_owner +// , source +// , target +// , uid_shared_with +// FROM +// `*PREFIX*sharing` +// WHERE +// ( target = ? AND uid_shared_with = ? ) +// OR source = ? +// " ); +// +// $result = $query->execute( array ( $filepath, $userId, $filepath ) ); +// +// $users = array(); +// +// if ( $row = $result->fetchRow() ) +// { +// $source = $row['source']; +// $owner = $row['uid_owner']; +// $users[] = $owner; +// // get the uids of all user with access to the file +// $query = \OC_DB::prepare( "SELECT source, uid_shared_with FROM `*PREFIX*sharing` WHERE source = ?" ); +// $result = $query->execute( array ($source)); +// while ( ($row = $result->fetchRow()) ) { +// $users[] = $row['uid_shared_with']; +// +// } +// +// } + + } else { + + // check if it is a file owned by the user and not shared at all + $userview = new \OC_FilesystemView( '/'.$userId.'/files/' ); + + if ( $userview->file_exists( $path ) ) { + + $users[] = $userId; + + } + + } + + $view = new \OC_FilesystemView( '/public-keys/' ); + + $keylist = array(); + + $count = 0; + + foreach ( $users as $user ) { + + $keylist['key'.++$count] = $view->file_get_contents( $user.'.public.key' ); + + } + + return $keylist; + + } + + /** + * @brief retrieve keyfile for an encrypted file + * @param string file name + * @return string file key or false + * @note The keyfile returned is asymmetrically encrypted. Decryption + * of the keyfile must be performed by client code + */ + public static function getFileKey( \OC_FilesystemView $view, $userId, $filePath ) { + + $filePath_f = ltrim( $filePath, '/' ); + +// // update $keypath and $userId if path point to a file shared by someone else +// $query = \OC_DB::prepare( "SELECT uid_owner, source, target FROM `*PREFIX*sharing` WHERE target = ? AND uid_shared_with = ?" ); +// +// $result = $query->execute( array ('/'.$userId.'/files/'.$keypath, $userId)); +// +// if ($row = $result->fetchRow()) { +// +// $keypath = $row['source']; +// $keypath_parts = explode( '/', $keypath ); +// $userId = $keypath_parts[1]; +// $keypath = str_replace( '/' . $userId . '/files/', '', $keypath ); +// +// } + + return $view->file_get_contents( '/' . $userId . '/files_encryption/keyfiles/' . $filePath_f . '.key' ); + + } + + /** + * @brief retrieve file encryption key + * + * @param string file name + * @return string file key or false + */ + public static function deleteFileKey( $path, $staticUserClass = 'OCP\User' ) { + + $keypath = ltrim( $path, '/' ); + $user = $staticUserClass::getUser(); + + // update $keypath and $user if path point to a file shared by someone else +// $query = \OC_DB::prepare( "SELECT uid_owner, source, target FROM `*PREFIX*sharing` WHERE target = ? AND uid_shared_with = ?" ); +// +// $result = $query->execute( array ('/'.$user.'/files/'.$keypath, $user)); +// +// if ($row = $result->fetchRow()) { +// +// $keypath = $row['source']; +// $keypath_parts = explode( '/', $keypath ); +// $user = $keypath_parts[1]; +// $keypath = str_replace( '/' . $user . '/files/', '', $keypath ); +// +// } + + $view = new \OC_FilesystemView('/'.$user.'/files_encryption/keyfiles/'); + + return $view->unlink( $keypath . '.key' ); + + } + + /** + * @brief store private key from the user + * @param string key + * @return bool + * @note Encryption of the private key must be performed by client code + * as no encryption takes place here + */ + public static function setPrivateKey( $key ) { + + $user = \OCP\User::getUser(); + + $view = new \OC_FilesystemView( '/' . $user . '/files_encryption' ); + + \OC_FileProxy::$enabled = false; + + if ( !$view->file_exists( '' ) ) $view->mkdir( '' ); + + return $view->file_put_contents( $user . '.private.key', $key ); + + \OC_FileProxy::$enabled = true; + + } + + /** + * @brief store private keys from the user + * + * @param string privatekey + * @param string publickey + * @return bool true/false + */ + public static function setUserKeys($privatekey, $publickey) { + + return (self::setPrivateKey($privatekey) && self::setPublicKey($publickey)); + + } + + /** + * @brief store public key of the user + * + * @param string key + * @return bool true/false + */ + public static function setPublicKey( $key ) { + + $view = new \OC_FilesystemView( '/public-keys' ); + + \OC_FileProxy::$enabled = false; + + if ( !$view->file_exists( '' ) ) $view->mkdir( '' ); + + return $view->file_put_contents( \OCP\User::getUser() . '.public.key', $key ); + + \OC_FileProxy::$enabled = true; + + } + + /** + * @brief store file encryption key + * + * @param string $path relative path of the file, including filename + * @param string $key + * @return bool true/false + * @note The keyfile is not encrypted here. Client code must + * asymmetrically encrypt the keyfile before passing it to this method + */ + public static function setFileKey( $path, $key, $view = Null, $dbClassName = '\OC_DB') { + + $targetPath = ltrim( $path, '/' ); + $user = \OCP\User::getUser(); + +// // update $keytarget and $user if key belongs to a file shared by someone else +// $query = $dbClassName::prepare( "SELECT uid_owner, source, target FROM `*PREFIX*sharing` WHERE target = ? AND uid_shared_with = ?" ); +// +// $result = $query->execute( array ( '/'.$user.'/files/'.$targetPath, $user ) ); +// +// if ( $row = $result->fetchRow( ) ) { +// +// $targetPath = $row['source']; +// +// $targetPath_parts = explode( '/', $targetPath ); +// +// $user = $targetPath_parts[1]; +// +// $rootview = new \OC_FilesystemView( '/' ); +// +// if ( ! $rootview->is_writable( $targetPath ) ) { +// +// \OC_Log::write( 'Encryption library', "File Key not updated because you don't have write access for the corresponding file", \OC_Log::ERROR ); +// +// return false; +// +// } +// +// $targetPath = str_replace( '/'.$user.'/files/', '', $targetPath ); +// +// //TODO: check for write permission on shared file once the new sharing API is in place +// +// } + + $path_parts = pathinfo( $targetPath ); + + if ( !$view ) { + + $view = new \OC_FilesystemView( '/' ); + + } + + $view->chroot( '/' . $user . '/files_encryption/keyfiles' ); + + // If the file resides within a subdirectory, create it + if ( + isset( $path_parts['dirname'] ) + && ! $view->file_exists( $path_parts['dirname'] ) + ) { + + $view->mkdir( $path_parts['dirname'] ); + + } + + // Save the keyfile in parallel directory + return $view->file_put_contents( '/' . $targetPath . '.key', $key ); + + } + + /** + * @brief change password of private encryption key + * + * @param string $oldpasswd old password + * @param string $newpasswd new password + * @return bool true/false + */ + public static function changePasswd($oldpasswd, $newpasswd) { + + if ( \OCP\User::checkPassword(\OCP\User::getUser(), $newpasswd) ) { + return Crypt::changekeypasscode($oldpasswd, $newpasswd); + } + return false; + + } + + /** + * @brief Fetch the legacy encryption key from user files + * @param string $login used to locate the legacy key + * @param string $passphrase used to decrypt the legacy key + * @return true / false + * + * if the key is left out, the default handeler will be used + */ + public function getLegacyKey() { + + $user = \OCP\User::getUser(); + $view = new \OC_FilesystemView( '/' . $user ); + return $view->file_get_contents( 'encryption.key' ); + + } + +} \ No newline at end of file diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php index e8dbd95c29..52f47dba29 100644 --- a/apps/files_encryption/lib/proxy.php +++ b/apps/files_encryption/lib/proxy.php @@ -3,8 +3,9 @@ /** * ownCloud * -* @author Robin Appelman -* @copyright 2011 Robin Appelman icewind1991@gmail.com +* @author Sam Tuke, Robin Appelman +* @copyright 2012 Sam Tuke samtuke@owncloud.com, Robin Appelman +* icewind1991@gmail.com * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -21,111 +22,267 @@ * */ -/** - * transparent encryption - */ +namespace OCA\Encryption; -class OC_FileProxy_Encryption extends OC_FileProxy{ - private static $blackList=null; //mimetypes blacklisted from encryption - private static $enableEncryption=null; +class Proxy extends \OC_FileProxy { + private static $blackList = null; //mimetypes blacklisted from encryption + + private static $enableEncryption = null; + /** - * check if a file should be encrypted during write + * Check if a file requires encryption * @param string $path * @return bool + * + * Tests if server side encryption is enabled, and file is allowed by blacklists */ - private static function shouldEncrypt($path) { - if (is_null(self::$enableEncryption)) { - self::$enableEncryption=(OCP\Config::getAppValue('files_encryption', 'enable_encryption', 'true')=='true'); + private static function shouldEncrypt( $path ) { + + if ( is_null( self::$enableEncryption ) ) { + + if ( + \OCP\Config::getAppValue( 'files_encryption', 'enable_encryption', 'true' ) == 'true' + && Crypt::mode() == 'server' + ) { + + self::$enableEncryption = true; + + } else { + + self::$enableEncryption = false; + + } + } - if ( ! self::$enableEncryption) { + + if ( !self::$enableEncryption ) { + return false; + } - if (is_null(self::$blackList)) { - self::$blackList=explode(',', OCP\Config::getAppValue('files_encryption', - 'type_blacklist', - 'jpg,png,jpeg,avi,mpg,mpeg,mkv,mp3,oga,ogv,ogg')); + + if ( is_null(self::$blackList ) ) { + + self::$blackList = explode(',', \OCP\Config::getAppValue( 'files_encryption','type_blacklist','jpg,png,jpeg,avi,mpg,mpeg,mkv,mp3,oga,ogv,ogg' ) ); + } - if (self::isEncrypted($path)) { + + if ( Crypt::isEncryptedContent( $path ) ) { + return true; + } - $extension=substr($path, strrpos($path, '.')+1); - if (array_search($extension, self::$blackList)===false) { + + $extension = substr( $path, strrpos( $path,'.' ) +1 ); + + if ( array_search( $extension, self::$blackList ) === false ){ + return true; + } + + return false; } - + + public function preFile_put_contents( $path, &$data ) { + + if ( self::shouldEncrypt( $path ) ) { + + if ( !is_resource( $data ) ) { //stream put contents should have been converted to fopen + + $userId = \OCP\USER::getUser(); + + $rootView = new \OC_FilesystemView( '/' ); + + // Set the filesize for userland, before encrypting + $size = strlen( $data ); + + // Disable encryption proxy to prevent recursive calls + \OC_FileProxy::$enabled = false; + + // Encrypt plain data and fetch key + $encrypted = Crypt::keyEncryptKeyfile( $data, Keymanager::getPublicKey( $rootView, $userId ) ); + + // Replace plain content with encrypted content by reference + $data = $encrypted['data']; + + $filePath = explode( '/', $path ); + + $filePath = array_slice( $filePath, 3 ); + + $filePath = '/' . implode( '/', $filePath ); + + # TODO: make keyfile dir dynamic from app config + $view = new \OC_FilesystemView( '/' . $userId . '/files_encryption/keyfiles' ); + + // Save keyfile for newly encrypted file in parallel directory tree + Keymanager::setFileKey( $filePath, $encrypted['key'], $view, '\OC_DB' ); + + // Update the file cache with file info + \OC_FileCache::put( $path, array( 'encrypted'=>true, 'size' => $size ), '' ); + + // Re-enable proxy - our work is done + \OC_FileProxy::$enabled = true; + + } + } + + } + /** - * check if a file is encrypted - * @param string $path - * @return bool + * @param string $path Path of file from which has been read + * @param string $data Data that has been read from file */ - private static function isEncrypted($path) { - $metadata=OC_FileCache_Cached::get($path, ''); - return isset($metadata['encrypted']) and (bool)$metadata['encrypted']; - } + public function postFile_get_contents( $path, $data ) { + + # TODO: Use dependency injection to add required args for view and user etc. to this method - public function preFile_put_contents($path,&$data) { - if (self::shouldEncrypt($path)) { - if ( ! is_resource($data)) {//stream put contents should have been converter to fopen - $size=strlen($data); - $data=OC_Crypt::blockEncrypt($data); - OC_FileCache::put($path, array('encrypted'=>true,'size'=>$size), ''); - } + // Disable encryption proxy to prevent recursive calls + \OC_FileProxy::$enabled = false; + + // If data is a catfile + if ( + Crypt::mode() == 'server' + && Crypt::isEncryptedContent( $data ) + ) { + + $split = explode( '/', $path ); + + $filePath = array_slice( $split, 3 ); + + $filePath = '/' . implode( '/', $filePath ); + + //$cached = \OC_FileCache_Cached::get( $path, '' ); + + $view = new \OC_FilesystemView( '' ); + + $userId = \OCP\USER::getUser(); + + $encryptedKeyfile = Keymanager::getFileKey( $view, $userId, $filePath ); + + $session = new Session(); + + $decrypted = Crypt::keyDecryptKeyfile( $data, $encryptedKeyfile, $session->getPrivateKey( $split[1] ) ); + + } elseif ( + Crypt::mode() == 'server' + && isset( $_SESSION['legacyenckey'] ) + && Crypt::isEncryptedMeta( $path ) + ) { + + $decrypted = Crypt::legacyDecrypt( $data, $_SESSION['legacyenckey'] ); + } - } - - public function postFile_get_contents($path, $data) { - if (self::isEncrypted($path)) { - $cached=OC_FileCache_Cached::get($path, ''); - $data=OC_Crypt::blockDecrypt($data, '', $cached['size']); + + \OC_FileProxy::$enabled = true; + + if ( ! isset( $decrypted ) ) { + + $decrypted = $data; + } - return $data; + + return $decrypted; + } - - public function postFopen($path,&$result) { - if ( ! $result) { + + public function postFopen( $path, &$result ){ + + if ( !$result ) { + return $result; + } - $meta=stream_get_meta_data($result); - if (self::isEncrypted($path)) { - fclose($result); - $result=fopen('crypt://'.$path, $meta['mode']); - } elseif (self::shouldEncrypt($path) and $meta['mode']!='r' and $meta['mode']!='rb') { - if (OC_Filesystem::file_exists($path) and OC_Filesystem::filesize($path)>0) { - //first encrypt the target file so we don't end up with a half encrypted file - OCP\Util::writeLog('files_encryption', 'Decrypting '.$path.' before writing', OCP\Util::DEBUG); - $tmp=fopen('php://temp'); - OCP\Files::streamCopy($result, $tmp); - fclose($result); - OC_Filesystem::file_put_contents($path, $tmp); - fclose($tmp); + + // Reformat path for use with OC_FSV + $path_split = explode( '/', $path ); + $path_f = implode( array_slice( $path_split, 3 ) ); + + // Disable encryption proxy to prevent recursive calls + \OC_FileProxy::$enabled = false; + + $meta = stream_get_meta_data( $result ); + + $view = new \OC_FilesystemView( '' ); + + $util = new Util( $view, \OCP\USER::getUser()); + + // If file is already encrypted, decrypt using crypto protocol + if ( + Crypt::mode() == 'server' + && $util->isEncryptedPath( $path ) + ) { + + // Close the original encrypted file + fclose( $result ); + + // Open the file using the crypto stream wrapper + // protocol and let it do the decryption work instead + $result = fopen( 'crypt://' . $path_f, $meta['mode'] ); + + + } elseif ( + self::shouldEncrypt( $path ) + and $meta ['mode'] != 'r' + and $meta['mode'] != 'rb' + ) { + // If the file is not yet encrypted, but should be + // encrypted when it's saved (it's not read only) + + // NOTE: this is the case for new files saved via WebDAV + + if ( + $view->file_exists( $path ) + and $view->filesize( $path ) > 0 + ) { + $x = $view->file_get_contents( $path ); + + $tmp = tmpfile(); + +// // Make a temporary copy of the original file +// \OCP\Files::streamCopy( $result, $tmp ); +// +// // Close the original stream, we'll return another one +// fclose( $result ); +// +// $view->file_put_contents( $path_f, $tmp ); +// +// fclose( $tmp ); + } - $result=fopen('crypt://'.$path, $meta['mode']); + + $result = fopen( 'crypt://'.$path_f, $meta['mode'] ); + } + + // Re-enable the proxy + \OC_FileProxy::$enabled = true; + return $result; + } - public function postGetMimeType($path, $mime) { - if (self::isEncrypted($path)) { - $mime=OCP\Files::getMimeType('crypt://'.$path, 'w'); + public function postGetMimeType($path,$mime){ + if( Crypt::isEncryptedContent($path)){ + $mime = \OCP\Files::getMimeType('crypt://'.$path,'w'); } return $mime; } - public function postStat($path, $data) { - if (self::isEncrypted($path)) { - $cached=OC_FileCache_Cached::get($path, ''); + public function postStat($path,$data){ + if( Crypt::isEncryptedContent($path)){ + $cached= \OC_FileCache_Cached::get($path,''); $data['size']=$cached['size']; } return $data; } - public function postFileSize($path, $size) { - if (self::isEncrypted($path)) { - $cached=OC_FileCache_Cached::get($path, ''); + public function postFileSize($path,$size){ + if( Crypt::isEncryptedContent($path)){ + $cached = \OC_FileCache_Cached::get($path,''); return $cached['size']; - } else { + }else{ return $size; } } diff --git a/apps/files_encryption/lib/session.php b/apps/files_encryption/lib/session.php new file mode 100644 index 0000000000..85d533fde7 --- /dev/null +++ b/apps/files_encryption/lib/session.php @@ -0,0 +1,66 @@ +. + * + */ + +namespace OCA\Encryption; + +/** + * Class for handling encryption related session data + */ + +class Session { + + /** + * @brief Sets user id for session and triggers emit + * @return bool + * + */ + public function setPrivateKey( $privateKey, $userId ) { + + $_SESSION['privateKey'] = $privateKey; + + return true; + + } + + /** + * @brief Gets user id for session and triggers emit + * @returns string $privateKey The user's plaintext private key + * + */ + public function getPrivateKey( $userId ) { + + if ( + isset( $_SESSION['privateKey'] ) + && !empty( $_SESSION['privateKey'] ) + ) { + + return $_SESSION['privateKey']; + + } else { + + return false; + + } + + } + +} \ No newline at end of file diff --git a/apps/files_encryption/lib/stream.php b/apps/files_encryption/lib/stream.php new file mode 100644 index 0000000000..f482e2d75a --- /dev/null +++ b/apps/files_encryption/lib/stream.php @@ -0,0 +1,464 @@ +, 2011 Robin Appelman + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see . + * + */ + +/** + * transparently encrypted filestream + * + * you can use it as wrapper around an existing stream by setting CryptStream::$sourceStreams['foo']=array('path'=>$path,'stream'=>$stream) + * and then fopen('crypt://streams/foo'); + */ + +namespace OCA\Encryption; + +/** + * @brief Provides 'crypt://' stream wrapper protocol. + * @note We use a stream wrapper because it is the most secure way to handle + * decrypted content transfers. There is no safe way to decrypt the entire file + * somewhere on the server, so we have to encrypt and decrypt blocks on the fly. + * @note Paths used with this protocol MUST BE RELATIVE. Use URLs like: + * crypt://filename, or crypt://subdirectory/filename, NOT + * crypt:///home/user/owncloud/data. Otherwise keyfiles will be put in + * [owncloud]/data/user/files_encryption/keyfiles/home/user/owncloud/data and + * will not be accessible to other methods. + * @note Data read and written must always be 8192 bytes long, as this is the + * buffer size used internally by PHP. The encryption process makes the input + * data longer, and input is chunked into smaller pieces in order to result in + * a 8192 encrypted block size. + */ +class Stream { + + public static $sourceStreams = array(); + + # TODO: make all below properties private again once unit testing is configured correctly + public $rawPath; // The raw path received by stream_open + public $path_f; // The raw path formatted to include username and data directory + private $userId; + private $handle; // Resource returned by fopen + private $path; + private $readBuffer; // For streams that dont support seeking + private $meta = array(); // Header / meta for source stream + private $count; + private $writeCache; + public $size; + private $publicKey; + private $keyfile; + private $encKeyfile; + private static $view; // a fsview object set to user dir + private $rootView; // a fsview object set to '/' + + public function stream_open( $path, $mode, $options, &$opened_path ) { + + // Get access to filesystem via filesystemview object + if ( !self::$view ) { + + self::$view = new \OC_FilesystemView( $this->userId . '/' ); + + } + + // Set rootview object if necessary + if ( ! $this->rootView ) { + + $this->rootView = new \OC_FilesystemView( $this->userId . '/' ); + + } + + $this->userId = \OCP\User::getUser(); + + // Get the bare file path + $path = str_replace( 'crypt://', '', $path ); + + $this->rawPath = $path; + + $this->path_f = $this->userId . '/files/' . $path; + + if ( + dirname( $path ) == 'streams' + and isset( self::$sourceStreams[basename( $path )] ) + ) { + + // Is this just for unit testing purposes? + + $this->handle = self::$sourceStreams[basename( $path )]['stream']; + + $this->path = self::$sourceStreams[basename( $path )]['path']; + + $this->size = self::$sourceStreams[basename( $path )]['size']; + + } else { + + if ( + $mode == 'w' + or $mode == 'w+' + or $mode == 'wb' + or $mode == 'wb+' + ) { + + $this->size = 0; + + } else { + + + + $this->size = self::$view->filesize( $this->path_f, $mode ); + + //$this->size = filesize( $path ); + + } + + // Disable fileproxies so we can open the source file without recursive encryption + \OC_FileProxy::$enabled = false; + + //$this->handle = fopen( $path, $mode ); + + $this->handle = self::$view->fopen( $this->path_f, $mode ); + + \OC_FileProxy::$enabled = true; + + if ( !is_resource( $this->handle ) ) { + + \OCP\Util::writeLog( 'files_encryption', 'failed to open '.$path, \OCP\Util::ERROR ); + + } + + } + + if ( is_resource( $this->handle ) ) { + + $this->meta = stream_get_meta_data( $this->handle ); + + } + + return is_resource( $this->handle ); + + } + + public function stream_seek( $offset, $whence = SEEK_SET ) { + + $this->flush(); + + fseek( $this->handle, $offset, $whence ); + + } + + public function stream_tell() { + return ftell($this->handle); + } + + public function stream_read( $count ) { + + $this->writeCache = ''; + + if ( $count != 8192 ) { + + // $count will always be 8192 https://bugs.php.net/bug.php?id=21641 + // This makes this function a lot simpler, but will break this class if the above 'bug' gets 'fixed' + \OCP\Util::writeLog( 'files_encryption', 'PHP "bug" 21641 no longer holds, decryption system requires refactoring', OCP\Util::FATAL ); + + die(); + + } + +// $pos = ftell( $this->handle ); +// + // Get the data from the file handle + $data = fread( $this->handle, 8192 ); + + if ( strlen( $data ) ) { + + $this->getKey(); + + $result = Crypt::symmetricDecryptFileContent( $data, $this->keyfile ); + + } else { + + $result = ''; + + } + +// $length = $this->size - $pos; +// +// if ( $length < 8192 ) { +// +// $result = substr( $result, 0, $length ); +// +// } + + return $result; + + } + + /** + * @brief Encrypt and pad data ready for writting to disk + * @param string $plainData data to be encrypted + * @param string $key key to use for encryption + * @return encrypted data on success, false on failure + */ + public function preWriteEncrypt( $plainData, $key ) { + + // Encrypt data to 'catfile', which includes IV + if ( $encrypted = Crypt::symmetricEncryptFileContent( $plainData, $key ) ) { + + return $encrypted; + + } else { + + return false; + + } + + } + + /** + * @brief Get the keyfile for the current file, generate one if necessary + * @param bool $generate if true, a new key will be generated if none can be found + * @return bool true on key found and set, false on key not found and new key generated and set + */ + public function getKey() { + + // If a keyfile already exists for a file named identically to file to be written + if ( self::$view->file_exists( $this->userId . '/'. 'files_encryption' . '/' . 'keyfiles' . '/' . $this->rawPath . '.key' ) ) { + + # TODO: add error handling for when file exists but no keyfile + + // Fetch existing keyfile + $this->encKeyfile = Keymanager::getFileKey( $this->rootView, $this->userId, $this->rawPath ); + + $this->getUser(); + + $session = new Session(); + + $privateKey = $session->getPrivateKey( $this->userId ); + + $this->keyfile = Crypt::keyDecrypt( $this->encKeyfile, $privateKey ); + + return true; + + } else { + + return false; + + } + + } + + public function getuser() { + + // Only get the user again if it isn't already set + if ( empty( $this->userId ) ) { + + # TODO: Move this user call out of here - it belongs elsewhere + $this->userId = \OCP\User::getUser(); + + } + + # TODO: Add a method for getting the user in case OCP\User:: + # getUser() doesn't work (can that scenario ever occur?) + + } + + /** + * @brief Handle plain data from the stream, and write it in 8192 byte blocks + * @param string $data data to be written to disk + * @note the data will be written to the path stored in the stream handle, set in stream_open() + * @note $data is only ever be a maximum of 8192 bytes long. This is set by PHP internally. stream_write() is called multiple times in a loop on data larger than 8192 bytes + * @note Because the encryption process used increases the length of $data, a writeCache is used to carry over data which would not fit in the required block size + * @note Padding is added to each encrypted block to ensure that the resulting block is exactly 8192 bytes. This is removed during stream_read + * @note PHP automatically updates the file pointer after writing data to reflect it's length. There is generally no need to update the poitner manually using fseek + */ + public function stream_write( $data ) { + + // Disable the file proxies so that encryption is not automatically attempted when the file is written to disk - we are handling that separately here and we don't want to get into an infinite loop + \OC_FileProxy::$enabled = false; + + // Get the length of the unencrypted data that we are handling + $length = strlen( $data ); + + // So far this round, no data has been written + $written = 0; + + // Find out where we are up to in the writing of data to the file + $pointer = ftell( $this->handle ); + + // Make sure the userId is set + $this->getuser(); + + // Get / generate the keyfile for the file we're handling + // If we're writing a new file (not overwriting an existing one), save the newly generated keyfile + if ( ! $this->getKey() ) { + + $this->keyfile = Crypt::generateKey(); + + $this->publicKey = Keymanager::getPublicKey( $this->rootView, $this->userId ); + + $this->encKeyfile = Crypt::keyEncrypt( $this->keyfile, $this->publicKey ); + + // Save the new encrypted file key + Keymanager::setFileKey( $this->rawPath, $this->encKeyfile, new \OC_FilesystemView( '/' ) ); + + # TODO: move this new OCFSV out of here some how, use DI + + } + + // If extra data is left over from the last round, make sure it is integrated into the next 6126 / 8192 block + if ( $this->writeCache ) { + + // Concat writeCache to start of $data + $data = $this->writeCache . $data; + + // Clear the write cache, ready for resuse - it has been flushed and its old contents processed + $this->writeCache = ''; + + } +// +// // Make sure we always start on a block start + if ( 0 != ( $pointer % 8192 ) ) { // if the current positoin of file indicator is not aligned to a 8192 byte block, fix it so that it is + +// fseek( $this->handle, - ( $pointer % 8192 ), SEEK_CUR ); +// +// $pointer = ftell( $this->handle ); +// +// $unencryptedNewBlock = fread( $this->handle, 8192 ); +// +// fseek( $this->handle, - ( $currentPos % 8192 ), SEEK_CUR ); +// +// $block = Crypt::symmetricDecryptFileContent( $unencryptedNewBlock, $this->keyfile ); +// +// $x = substr( $block, 0, $currentPos % 8192 ); +// +// $data = $x . $data; +// +// fseek( $this->handle, - ( $currentPos % 8192 ), SEEK_CUR ); +// + } + +// $currentPos = ftell( $this->handle ); + +// // While there still remains somed data to be processed & written + while( strlen( $data ) > 0 ) { +// +// // Remaining length for this iteration, not of the entire file (may be greater than 8192 bytes) +// $remainingLength = strlen( $data ); +// +// // If data remaining to be written is less than the size of 1 6126 byte block + if ( strlen( $data ) < 6126 ) { + + // Set writeCache to contents of $data + // The writeCache will be carried over to the next write round, and added to the start of $data to ensure that written blocks are always the correct length. If there is still data in writeCache after the writing round has finished, then the data will be written to disk by $this->flush(). + $this->writeCache = $data; + + // Clear $data ready for next round + $data = ''; +// + } else { + + // Read the chunk from the start of $data + $chunk = substr( $data, 0, 6126 ); + + $encrypted = $this->preWriteEncrypt( $chunk, $this->keyfile ); + + // Write the data chunk to disk. This will be addended to the last data chunk if the file being handled totals more than 6126 bytes + fwrite( $this->handle, $encrypted ); + + $writtenLen = strlen( $encrypted ); + //fseek( $this->handle, $writtenLen, SEEK_CUR ); + + // Remove the chunk we just processed from $data, leaving only unprocessed data in $data var, for handling on the next round + $data = substr( $data, 6126 ); + + } + + } + + $this->size = max( $this->size, $pointer + $length ); + + return $length; + + } + + + public function stream_set_option($option,$arg1,$arg2) { + switch($option) { + case STREAM_OPTION_BLOCKING: + stream_set_blocking($this->handle,$arg1); + break; + case STREAM_OPTION_READ_TIMEOUT: + stream_set_timeout($this->handle,$arg1,$arg2); + break; + case STREAM_OPTION_WRITE_BUFFER: + stream_set_write_buffer($this->handle,$arg1,$arg2); + } + } + + public function stream_stat() { + return fstat($this->handle); + } + + public function stream_lock($mode) { + flock($this->handle,$mode); + } + + public function stream_flush() { + + return fflush($this->handle); // Not a typo: http://php.net/manual/en/function.fflush.php + + } + + public function stream_eof() { + return feof($this->handle); + } + + private function flush() { + + if ( $this->writeCache ) { + + // Set keyfile property for file in question + $this->getKey(); + + $encrypted = $this->preWriteEncrypt( $this->writeCache, $this->keyfile ); + + fwrite( $this->handle, $encrypted ); + + $this->writeCache = ''; + + } + + } + + public function stream_close() { + + $this->flush(); + + if ( + $this->meta['mode']!='r' + and $this->meta['mode']!='rb' + ) { + + \OC_FileCache::put( $this->path, array( 'encrypted' => true, 'size' => $this->size ), '' ); + + } + + return fclose( $this->handle ); + + } + +} diff --git a/apps/files_encryption/lib/util.php b/apps/files_encryption/lib/util.php new file mode 100644 index 0000000000..cd46d23108 --- /dev/null +++ b/apps/files_encryption/lib/util.php @@ -0,0 +1,330 @@ +. + * + */ + +// Todo: +// - Crypt/decrypt button in the userinterface +// - Setting if crypto should be on by default +// - Add a setting "Don´t encrypt files larger than xx because of performance reasons" +// - Transparent decrypt/encrypt in filesystem.php. Autodetect if a file is encrypted (.encrypted extension) +// - Don't use a password directly as encryption key. but a key which is stored on the server and encrypted with the user password. -> password change faster +// - IMPORTANT! Check if the block lenght of the encrypted data stays the same + +namespace OCA\Encryption; + +/** + * @brief Class for utilities relating to encrypted file storage system + * @param $view OC_FilesystemView object, expected to have OC '/' as root path + * @param $client flag indicating status of client side encryption. Currently + * unused, likely to become obsolete shortly + */ + +class Util { + + + # Web UI: + + ## DONE: files created via web ui are encrypted + ## DONE: file created & encrypted via web ui are readable in web ui + ## DONE: file created & encrypted via web ui are readable via webdav + + + # WebDAV: + + ## DONE: new data filled files added via webdav get encrypted + ## DONE: new data filled files added via webdav are readable via webdav + ## DONE: reading unencrypted files when encryption is enabled works via webdav + ## DONE: files created & encrypted via web ui are readable via webdav + + + # Legacy support: + + ## DONE: add method to check if file is encrypted using new system + ## DONE: add method to check if file is encrypted using old system + ## DONE: add method to fetch legacy key + ## DONE: add method to decrypt legacy encrypted data + + ## TODO: add method to encrypt all user files using new system + ## TODO: add method to decrypt all user files using new system + ## TODO: add method to encrypt all user files using old system + ## TODO: add method to decrypt all user files using old system + + + # Admin UI: + + ## DONE: changing user password also changes encryption passphrase + + ## TODO: add support for optional recovery in case of lost passphrase / keys + ## TODO: add admin optional required long passphrase for users + ## TODO: add UI buttons for encrypt / decrypt everything + ## TODO: implement flag system to allow user to specify encryption by folder, subfolder, etc. + + + # Sharing: + + ## TODO: add support for encrypting to multiple public keys + ## TODO: add support for decrypting to multiple private keys + + + # Integration testing: + + ## TODO: test new encryption with webdav + ## TODO: test new encryption with versioning + ## TODO: test new encryption with sharing + ## TODO: test new encryption with proxies + + + private $view; // OC_FilesystemView object for filesystem operations + private $pwd; // User Password + private $client; // Client side encryption mode flag + private $publicKeyDir; // Directory containing all public user keys + private $encryptionDir; // Directory containing user's files_encryption + private $keyfilesPath; // Directory containing user's keyfiles + private $publicKeyPath; // Path to user's public key + private $privateKeyPath; // Path to user's private key + + public function __construct( \OC_FilesystemView $view, $userId, $client = false ) { + + $this->view = $view; + $this->userId = $userId; + $this->client = $client; + $this->publicKeyDir = '/' . 'public-keys'; + $this->encryptionDir = '/' . $this->userId . '/' . 'files_encryption'; + $this->keyfilesPath = $this->encryptionDir . '/' . 'keyfiles'; + $this->publicKeyPath = $this->publicKeyDir . '/' . $this->userId . '.public.key'; // e.g. data/public-keys/admin.public.key + $this->privateKeyPath = $this->encryptionDir . '/' . $this->userId . '.private.key'; // e.g. data/admin/admin.private.key + + } + + public function ready() { + + if( + !$this->view->file_exists( $this->keyfilesPath ) + or !$this->view->file_exists( $this->publicKeyPath ) + or !$this->view->file_exists( $this->privateKeyPath ) + ) { + + return false; + + } else { + + return true; + + } + + } + + /** + * @brief Sets up user folders and keys for serverside encryption + * @param $passphrase passphrase to encrypt server-stored private key with + */ + public function setupServerSide( $passphrase = null ) { + + // Create shared public key directory + if( !$this->view->file_exists( $this->publicKeyDir ) ) { + + $this->view->mkdir( $this->publicKeyDir ); + + } + + // Create encryption app directory + if( !$this->view->file_exists( $this->encryptionDir ) ) { + + $this->view->mkdir( $this->encryptionDir ); + + } + + // Create mirrored keyfile directory + if( !$this->view->file_exists( $this->keyfilesPath ) ) { + + $this->view->mkdir( $this->keyfilesPath ); + + } + + // Create user keypair + if ( + !$this->view->file_exists( $this->publicKeyPath ) + or !$this->view->file_exists( $this->privateKeyPath ) + ) { + + // Generate keypair + $keypair = Crypt::createKeypair(); + + \OC_FileProxy::$enabled = false; + + // Save public key + $this->view->file_put_contents( $this->publicKeyPath, $keypair['publicKey'] ); + + // Encrypt private key with user pwd as passphrase + $encryptedPrivateKey = Crypt::symmetricEncryptFileContent( $keypair['privateKey'], $passphrase ); + + // Save private key + $this->view->file_put_contents( $this->privateKeyPath, $encryptedPrivateKey ); + + \OC_FileProxy::$enabled = true; + + } + + return true; + + } + + public function findFiles( $directory, $type = 'plain' ) { + + # TODO: test finding non plain content + + if ( $handle = $this->view->opendir( $directory ) ) { + + while ( false !== ( $file = readdir( $handle ) ) ) { + + if ( + $file != "." + && $file != ".." + ) { + + $filePath = $directory . '/' . $this->view->getRelativePath( '/' . $file ); + + var_dump($filePath); + + if ( $this->view->is_dir( $filePath ) ) { + + $this->findFiles( $filePath ); + + } elseif ( $this->view->is_file( $filePath ) ) { + + if ( $type == 'plain' ) { + + $this->files[] = array( 'name' => $file, 'path' => $filePath ); + + } elseif ( $type == 'encrypted' ) { + + if ( Crypt::isEncryptedContent( $this->view->file_get_contents( $filePath ) ) ) { + + $this->files[] = array( 'name' => $file, 'path' => $filePath ); + + } + + } elseif ( $type == 'legacy' ) { + + if ( Crypt::isLegacyEncryptedContent( $this->view->file_get_contents( $filePath ) ) ) { + + $this->files[] = array( 'name' => $file, 'path' => $filePath ); + + } + + } + + } + + } + + } + + if ( !empty( $this->files ) ) { + + return $this->files; + + } else { + + return false; + + } + + } + + return false; + + } + + /** + * @brief Check if a given path identifies an encrypted file + * @return true / false + */ + public function isEncryptedPath( $path ) { + + // Disable encryption proxy so data retreived is in its + // original form + \OC_FileProxy::$enabled = false; + + $data = $this->view->file_get_contents( $path ); + + \OC_FileProxy::$enabled = true; + + return Crypt::isEncryptedContent( $data ); + + } + + public function encryptAll( $directory ) { + + $plainFiles = $this->findFiles( $this->view, 'plain' ); + + if ( $this->encryptFiles( $plainFiles ) ) { + + return true; + + } else { + + return false; + + } + + } + + public function getPath( $pathName ) { + + switch ( $pathName ) { + + case 'publicKeyDir': + + return $this->publicKeyDir; + + break; + + case 'encryptionDir': + + return $this->encryptionDir; + + break; + + case 'keyfilesPath': + + return $this->keyfilesPath; + + break; + + case 'publicKeyPath': + + return $this->publicKeyPath; + + break; + + case 'privateKeyPath': + + return $this->privateKeyPath; + + break; + + } + + } + +} diff --git a/apps/files_encryption/settings-personal.php b/apps/files_encryption/settings-personal.php new file mode 100644 index 0000000000..014288f2ef --- /dev/null +++ b/apps/files_encryption/settings-personal.php @@ -0,0 +1,29 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +$sysEncMode = \OC_Appconfig::getValue('files_encryption', 'mode', 'none'); + +if ($sysEncMode == 'user') { + + $tmpl = new OCP\Template( 'files_encryption', 'settings-personal'); + + $query = \OC_DB::prepare( "SELECT mode FROM *PREFIX*encryption WHERE uid = ?" ); + $result = $query->execute(array(\OCP\User::getUser())); + + if ($row = $result->fetchRow()){ + $mode = $row['mode']; + } else { + $mode = 'none'; + } + + OCP\Util::addscript('files_encryption','settings-personal'); + $tmpl->assign('encryption_mode', $mode); + return $tmpl->fetchPage(); +} + +return null; diff --git a/apps/files_encryption/settings.php b/apps/files_encryption/settings.php index 94ff5ab94b..d1260f44e9 100644 --- a/apps/files_encryption/settings.php +++ b/apps/files_encryption/settings.php @@ -6,17 +6,16 @@ * See the COPYING-README file. */ -OC_Util::checkAdminUser(); +\OC_Util::checkAdminUser(); -$tmpl = new OCP\Template( 'files_encryption', 'settings'); -$blackList=explode(',', OCP\Config::getAppValue('files_encryption', - 'type_blacklist', - 'jpg,png,jpeg,avi,mpg,mpeg,mkv,mp3,oga,ogv,ogg')); -$enabled=(OCP\Config::getAppValue('files_encryption', 'enable_encryption', 'true')=='true'); -$tmpl->assign('blacklist', $blackList); -$tmpl->assign('encryption_enabled', $enabled); +$tmpl = new OCP\Template( 'files_encryption', 'settings' ); -OCP\Util::addscript('files_encryption', 'settings'); -OCP\Util::addscript('core', 'multiselect'); +$blackList = explode( ',', \OCP\Config::getAppValue( 'files_encryption', 'type_blacklist', 'jpg,png,jpeg,avi,mpg,mpeg,mkv,mp3,oga,ogv,ogg' ) ); + +$tmpl->assign( 'blacklist', $blackList ); +$tmpl->assign( 'encryption_mode', \OC_Appconfig::getValue( 'files_encryption', 'mode', 'none' ) ); + +\OCP\Util::addscript( 'files_encryption', 'settings' ); +\OCP\Util::addscript( 'core', 'multiselect' ); return $tmpl->fetchPage(); diff --git a/apps/files_encryption/templates/settings-personal.php b/apps/files_encryption/templates/settings-personal.php new file mode 100644 index 0000000000..1274bd3bb5 --- /dev/null +++ b/apps/files_encryption/templates/settings-personal.php @@ -0,0 +1,45 @@ +
+
+ t('Choose encryption mode:'); ?> +

+ + + + /> + t('Client side encryption (most secure but makes it impossible to access your data from the web interface)'); ?> +
+ + + /> + t('Server side encryption (allows you to access your files from the web interface and the desktop client)'); ?> +
+ + + /> + t('None (no encryption at all)'); ?> +
+

+
+
diff --git a/apps/files_encryption/templates/settings.php b/apps/files_encryption/templates/settings.php index 61bfe849c7..544ec793f3 100644 --- a/apps/files_encryption/templates/settings.php +++ b/apps/files_encryption/templates/settings.php @@ -1,14 +1,79 @@ -
+
- t('Encryption');?> - checked="checked" - id='enable_encryption' /> -
- + /> + + t("Client side encryption (most secure but makes it impossible to access your data from the web interface)"); ?> +
+ + + /> + + t('Server side encryption (allows you to access your files from the web interface and the desktop client)'); ?> +
+ + + /> + + t('User specific (let the user decide)'); ?> +
+ + + /> + + t('None (no encryption at all)'); ?> +
+ +

+

+ t('Encryption'); ?> + + t("Exclude the following file types from encryption"); ?> + +
- t('Exclude the following file types from encryption'); ?> + +

diff --git a/apps/files_encryption/tests/binary b/apps/files_encryption/test/binary similarity index 100% rename from apps/files_encryption/tests/binary rename to apps/files_encryption/test/binary diff --git a/apps/files_encryption/test/crypt.php b/apps/files_encryption/test/crypt.php new file mode 100755 index 0000000000..19c10ab0ab --- /dev/null +++ b/apps/files_encryption/test/crypt.php @@ -0,0 +1,667 @@ +, and + * Robin Appelman + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +//require_once "PHPUnit/Framework/TestCase.php"; +require_once realpath( dirname(__FILE__).'/../../../3rdparty/Crypt_Blowfish/Blowfish.php' ); +require_once realpath( dirname(__FILE__).'/../../../lib/base.php' ); +require_once realpath( dirname(__FILE__).'/../lib/crypt.php' ); +require_once realpath( dirname(__FILE__).'/../lib/keymanager.php' ); +require_once realpath( dirname(__FILE__).'/../lib/proxy.php' ); +require_once realpath( dirname(__FILE__).'/../lib/stream.php' ); +require_once realpath( dirname(__FILE__).'/../lib/util.php' ); +require_once realpath( dirname(__FILE__).'/../appinfo/app.php' ); + +use OCA\Encryption; + +// This has to go here because otherwise session errors arise, and the private +// encryption key needs to be saved in the session +\OC_User::login( 'admin', 'admin' ); + +/** + * @note It would be better to use Mockery here for mocking out the session + * handling process, and isolate calls to session class and data from the unit + * tests relating to them (stream etc.). However getting mockery to work and + * overload classes whilst also using the OC autoloader is difficult due to + * load order Pear errors. + */ + +class Test_Crypt extends \PHPUnit_Framework_TestCase { + + function setUp() { + + // set content for encrypting / decrypting in tests + $this->dataLong = file_get_contents( realpath( dirname(__FILE__).'/../lib/crypt.php' ) ); + $this->dataShort = 'hats'; + $this->dataUrl = realpath( dirname(__FILE__).'/../lib/crypt.php' ); + $this->legacyData = realpath( dirname(__FILE__).'/legacy-text.txt' ); + $this->legacyEncryptedData = realpath( dirname(__FILE__).'/legacy-encrypted-text.txt' ); + $this->randomKey = Encryption\Crypt::generateKey(); + + $keypair = Encryption\Crypt::createKeypair(); + $this->genPublicKey = $keypair['publicKey']; + $this->genPrivateKey = $keypair['privateKey']; + + $this->view = new \OC_FilesystemView( '/' ); + + \OC_User::setUserId( 'admin' ); + $this->userId = 'admin'; + $this->pass = 'admin'; + + \OC_Filesystem::init( '/' ); + \OC_Filesystem::mount( 'OC_Filestorage_Local', array('datadir' => \OC_User::getHome($this->userId)), '/' ); + + } + + function tearDown() { + + } + + function testGenerateKey() { + + # TODO: use more accurate (larger) string length for test confirmation + + $key = Encryption\Crypt::generateKey(); + + $this->assertTrue( strlen( $key ) > 16 ); + + } + + function testGenerateIv() { + + $iv = Encryption\Crypt::generateIv(); + + $this->assertEquals( 16, strlen( $iv ) ); + + return $iv; + + } + + /** + * @depends testGenerateIv + */ + function testConcatIv( $iv ) { + + $catFile = Encryption\Crypt::concatIv( $this->dataLong, $iv ); + + // Fetch encryption metadata from end of file + $meta = substr( $catFile, -22 ); + + $identifier = substr( $meta, 0, 6); + + // Fetch IV from end of file + $foundIv = substr( $meta, 6 ); + + $this->assertEquals( '00iv00', $identifier ); + + $this->assertEquals( $iv, $foundIv ); + + // Remove IV and IV identifier text to expose encrypted content + $data = substr( $catFile, 0, -22 ); + + $this->assertEquals( $this->dataLong, $data ); + + return array( + 'iv' => $iv + , 'catfile' => $catFile + ); + + } + + /** + * @depends testConcatIv + */ + function testSplitIv( $testConcatIv ) { + + // Split catfile into components + $splitCatfile = Encryption\Crypt::splitIv( $testConcatIv['catfile'] ); + + // Check that original IV and split IV match + $this->assertEquals( $testConcatIv['iv'], $splitCatfile['iv'] ); + + // Check that original data and split data match + $this->assertEquals( $this->dataLong, $splitCatfile['encrypted'] ); + + } + + function testAddPadding() { + + $padded = Encryption\Crypt::addPadding( $this->dataLong ); + + $padding = substr( $padded, -2 ); + + $this->assertEquals( 'xx' , $padding ); + + return $padded; + + } + + /** + * @depends testAddPadding + */ + function testRemovePadding( $padded ) { + + $noPadding = Encryption\Crypt::RemovePadding( $padded ); + + $this->assertEquals( $this->dataLong, $noPadding ); + + } + + function testEncrypt() { + + $random = openssl_random_pseudo_bytes( 13 ); + + $iv = substr( base64_encode( $random ), 0, -4 ); // i.e. E5IG033j+mRNKrht + + $crypted = Encryption\Crypt::encrypt( $this->dataUrl, $iv, 'hat' ); + + $this->assertNotEquals( $this->dataUrl, $crypted ); + + } + + function testDecrypt() { + + $random = openssl_random_pseudo_bytes( 13 ); + + $iv = substr( base64_encode( $random ), 0, -4 ); // i.e. E5IG033j+mRNKrht + + $crypted = Encryption\Crypt::encrypt( $this->dataUrl, $iv, 'hat' ); + + $decrypt = Encryption\Crypt::decrypt( $crypted, $iv, 'hat' ); + + $this->assertEquals( $this->dataUrl, $decrypt ); + + } + + function testSymmetricEncryptFileContent() { + + # TODO: search in keyfile for actual content as IV will ensure this test always passes + + $crypted = Encryption\Crypt::symmetricEncryptFileContent( $this->dataShort, 'hat' ); + + $this->assertNotEquals( $this->dataShort, $crypted ); + + + $decrypt = Encryption\Crypt::symmetricDecryptFileContent( $crypted, 'hat' ); + + $this->assertEquals( $this->dataShort, $decrypt ); + + } + + // These aren't used for now +// function testSymmetricBlockEncryptShortFileContent() { +// +// $crypted = Encryption\Crypt::symmetricBlockEncryptFileContent( $this->dataShort, $this->randomKey ); +// +// $this->assertNotEquals( $this->dataShort, $crypted ); +// +// +// $decrypt = Encryption\Crypt::symmetricBlockDecryptFileContent( $crypted, $this->randomKey ); +// +// $this->assertEquals( $this->dataShort, $decrypt ); +// +// } +// +// function testSymmetricBlockEncryptLongFileContent() { +// +// $crypted = Encryption\Crypt::symmetricBlockEncryptFileContent( $this->dataLong, $this->randomKey ); +// +// $this->assertNotEquals( $this->dataLong, $crypted ); +// +// +// $decrypt = Encryption\Crypt::symmetricBlockDecryptFileContent( $crypted, $this->randomKey ); +// +// $this->assertEquals( $this->dataLong, $decrypt ); +// +// } + + function testSymmetricStreamEncryptShortFileContent() { + + $filename = 'tmp-'.time(); + + $cryptedFile = file_put_contents( 'crypt://' . $filename, $this->dataShort ); + + // Test that data was successfully written + $this->assertTrue( is_int( $cryptedFile ) ); + + + // Get file contents without using any wrapper to get it's actual contents on disk + $retreivedCryptedFile = $this->view->file_get_contents( $this->userId . '/files/' . $filename ); + + // Check that the file was encrypted before being written to disk + $this->assertNotEquals( $this->dataShort, $retreivedCryptedFile ); + + // Get private key + $encryptedPrivateKey = Encryption\Keymanager::getPrivateKey( $this->view, $this->userId ); + + $decryptedPrivateKey = Encryption\Crypt::symmetricDecryptFileContent( $encryptedPrivateKey, $this->pass ); + + + // Get keyfile + $encryptedKeyfile = Encryption\Keymanager::getFileKey( $this->view, $this->userId, $filename ); + + $decryptedKeyfile = Encryption\Crypt::keyDecrypt( $encryptedKeyfile, $decryptedPrivateKey ); + + + // Manually decrypt + $manualDecrypt = Encryption\Crypt::symmetricBlockDecryptFileContent( $retreivedCryptedFile, $decryptedKeyfile ); + + // Check that decrypted data matches + $this->assertEquals( $this->dataShort, $manualDecrypt ); + + } + + /** + * @brief Test that data that is written by the crypto stream wrapper + * @note Encrypted data is manually prepared and decrypted here to avoid dependency on success of stream_read + * @note If this test fails with truncate content, check that enough array slices are being rejoined to form $e, as the crypt.php file may have gotten longer and broken the manual + * reassembly of its data + */ + function testSymmetricStreamEncryptLongFileContent() { + + // Generate a a random filename + $filename = 'tmp-'.time(); + + // Save long data as encrypted file using stream wrapper + $cryptedFile = file_put_contents( 'crypt://' . $filename, $this->dataLong.$this->dataLong ); + + // Test that data was successfully written + $this->assertTrue( is_int( $cryptedFile ) ); + + // Get file contents without using any wrapper to get it's actual contents on disk + $retreivedCryptedFile = $this->view->file_get_contents( $this->userId . '/files/' . $filename ); + +// echo "\n\n\$retreivedCryptedFile = $retreivedCryptedFile\n\n"; + + // Check that the file was encrypted before being written to disk + $this->assertNotEquals( $this->dataLong.$this->dataLong, $retreivedCryptedFile ); + + // Manuallly split saved file into separate IVs and encrypted chunks + $r = preg_split('/(00iv00.{16,18})/', $retreivedCryptedFile, NULL, PREG_SPLIT_DELIM_CAPTURE); + + //print_r($r); + + // Join IVs and their respective data chunks + $e = array( $r[0].$r[1], $r[2].$r[3], $r[4].$r[5], $r[6].$r[7], $r[8].$r[9], $r[10].$r[11], $r[12].$r[13] );//.$r[11], $r[12].$r[13], $r[14] ); + + //print_r($e); + + + // Get private key + $encryptedPrivateKey = Encryption\Keymanager::getPrivateKey( $this->view, $this->userId ); + + $decryptedPrivateKey = Encryption\Crypt::symmetricDecryptFileContent( $encryptedPrivateKey, $this->pass ); + + + // Get keyfile + $encryptedKeyfile = Encryption\Keymanager::getFileKey( $this->view, $this->userId, $filename ); + + $decryptedKeyfile = Encryption\Crypt::keyDecrypt( $encryptedKeyfile, $decryptedPrivateKey ); + + + // Set var for reassembling decrypted content + $decrypt = ''; + + // Manually decrypt chunk + foreach ($e as $e) { + +// echo "\n\$e = $e"; + + $chunkDecrypt = Encryption\Crypt::symmetricDecryptFileContent( $e, $decryptedKeyfile ); + + // Assemble decrypted chunks + $decrypt .= $chunkDecrypt; + +// echo "\n\$chunkDecrypt = $chunkDecrypt"; + + } + +// echo "\n\$decrypt = $decrypt"; + + $this->assertEquals( $this->dataLong.$this->dataLong, $decrypt ); + + // Teardown + + $this->view->unlink( $filename ); + + Encryption\Keymanager::deleteFileKey( $filename ); + + } + + /** + * @brief Test that data that is read by the crypto stream wrapper + */ + function testSymmetricStreamDecryptShortFileContent() { + + $filename = 'tmp-'.time(); + + // Save long data as encrypted file using stream wrapper + $cryptedFile = file_put_contents( 'crypt://' . $filename, $this->dataShort ); + + // Test that data was successfully written + $this->assertTrue( is_int( $cryptedFile ) ); + + + // Get file contents without using any wrapper to get it's actual contents on disk + $retreivedCryptedFile = $this->view->file_get_contents( $this->userId . '/files/' . $filename ); + + $decrypt = file_get_contents( 'crypt://' . $filename ); + + $this->assertEquals( $this->dataShort, $decrypt ); + + } + + function testSymmetricStreamDecryptLongFileContent() { + + $filename = 'tmp-'.time(); + + // Save long data as encrypted file using stream wrapper + $cryptedFile = file_put_contents( 'crypt://' . $filename, $this->dataLong ); + + // Test that data was successfully written + $this->assertTrue( is_int( $cryptedFile ) ); + + + // Get file contents without using any wrapper to get it's actual contents on disk + $retreivedCryptedFile = $this->view->file_get_contents( $this->userId . '/files/' . $filename ); + + $decrypt = file_get_contents( 'crypt://' . $filename ); + + $this->assertEquals( $this->dataLong, $decrypt ); + + } + + // Is this test still necessary? +// function testSymmetricBlockStreamDecryptFileContent() { +// +// \OC_User::setUserId( 'admin' ); +// +// // Disable encryption proxy to prevent unwanted en/decryption +// \OC_FileProxy::$enabled = false; +// +// $cryptedFile = file_put_contents( 'crypt://' . '/blockEncrypt', $this->dataUrl ); +// +// // Disable encryption proxy to prevent unwanted en/decryption +// \OC_FileProxy::$enabled = false; +// +// echo "\n\n\$cryptedFile = " . $this->view->file_get_contents( '/blockEncrypt' ); +// +// $retreivedCryptedFile = file_get_contents( 'crypt://' . '/blockEncrypt' ); +// +// $this->assertEquals( $this->dataUrl, $retreivedCryptedFile ); +// +// \OC_FileProxy::$enabled = false; +// +// } + + function testSymmetricEncryptFileContentKeyfile() { + + # TODO: search in keyfile for actual content as IV will ensure this test always passes + + $crypted = Encryption\Crypt::symmetricEncryptFileContentKeyfile( $this->dataUrl ); + + $this->assertNotEquals( $this->dataUrl, $crypted['encrypted'] ); + + + $decrypt = Encryption\Crypt::symmetricDecryptFileContent( $crypted['encrypted'], $crypted['key'] ); + + $this->assertEquals( $this->dataUrl, $decrypt ); + + } + + function testIsEncryptedContent() { + + $this->assertFalse( Encryption\Crypt::isEncryptedContent( $this->dataUrl ) ); + + $this->assertFalse( Encryption\Crypt::isEncryptedContent( $this->legacyEncryptedData ) ); + + $keyfileContent = Encryption\Crypt::symmetricEncryptFileContent( $this->dataUrl, 'hat' ); + + $this->assertTrue( Encryption\Crypt::isEncryptedContent( $keyfileContent ) ); + + } + + function testMultiKeyEncrypt() { + + # TODO: search in keyfile for actual content as IV will ensure this test always passes + + $pair1 = Encryption\Crypt::createKeypair(); + + $this->assertEquals( 2, count( $pair1 ) ); + + $this->assertTrue( strlen( $pair1['publicKey'] ) > 1 ); + + $this->assertTrue( strlen( $pair1['privateKey'] ) > 1 ); + + + $crypted = Encryption\Crypt::multiKeyEncrypt( $this->dataUrl, array( $pair1['publicKey'] ) ); + + $this->assertNotEquals( $this->dataUrl, $crypted['encrypted'] ); + + + $decrypt = Encryption\Crypt::multiKeyDecrypt( $crypted['encrypted'], $crypted['keys'][0], $pair1['privateKey'] ); + + $this->assertEquals( $this->dataUrl, $decrypt ); + + } + + function testKeyEncrypt() { + + // Generate keypair + $pair1 = Encryption\Crypt::createKeypair(); + + // Encrypt data + $crypted = Encryption\Crypt::keyEncrypt( $this->dataUrl, $pair1['publicKey'] ); + + $this->assertNotEquals( $this->dataUrl, $crypted ); + + // Decrypt data + $decrypt = Encryption\Crypt::keyDecrypt( $crypted, $pair1['privateKey'] ); + + $this->assertEquals( $this->dataUrl, $decrypt ); + + } + + // What is the point of this test? It doesn't use keyEncryptKeyfile() + function testKeyEncryptKeyfile() { + + # TODO: Don't repeat encryption from previous tests, use PHPUnit test interdependency instead + + // Generate keypair + $pair1 = Encryption\Crypt::createKeypair(); + + // Encrypt plain data, generate keyfile & encrypted file + $cryptedData = Encryption\Crypt::symmetricEncryptFileContentKeyfile( $this->dataUrl ); + + // Encrypt keyfile + $cryptedKey = Encryption\Crypt::keyEncrypt( $cryptedData['key'], $pair1['publicKey'] ); + + // Decrypt keyfile + $decryptKey = Encryption\Crypt::keyDecrypt( $cryptedKey, $pair1['privateKey'] ); + + // Decrypt encrypted file + $decryptData = Encryption\Crypt::symmetricDecryptFileContent( $cryptedData['encrypted'], $decryptKey ); + + $this->assertEquals( $this->dataUrl, $decryptData ); + + } + + /** + * @brief test functionality of keyEncryptKeyfile() and + * keyDecryptKeyfile() + */ + function testKeyDecryptKeyfile() { + + $encrypted = Encryption\Crypt::keyEncryptKeyfile( $this->dataShort, $this->genPublicKey ); + + $this->assertNotEquals( $encrypted['data'], $this->dataShort ); + + $decrypted = Encryption\Crypt::keyDecryptKeyfile( $encrypted['data'], $encrypted['key'], $this->genPrivateKey ); + + $this->assertEquals( $decrypted, $this->dataShort ); + + } + + + /** + * @brief test encryption using legacy blowfish method + */ + function testLegacyEncryptShort() { + + $crypted = Encryption\Crypt::legacyEncrypt( $this->dataShort, $this->pass ); + + $this->assertNotEquals( $this->dataShort, $crypted ); + + # TODO: search inencrypted text for actual content to ensure it + # genuine transformation + + return $crypted; + + } + + /** + * @brief test decryption using legacy blowfish method + * @depends testLegacyEncryptShort + */ + function testLegacyDecryptShort( $crypted ) { + + $decrypted = Encryption\Crypt::legacyDecrypt( $crypted, $this->pass ); + + $this->assertEquals( $this->dataShort, $decrypted ); + + } + + /** + * @brief test encryption using legacy blowfish method + */ + function testLegacyEncryptLong() { + + $crypted = Encryption\Crypt::legacyEncrypt( $this->dataLong, $this->pass ); + + $this->assertNotEquals( $this->dataLong, $crypted ); + + # TODO: search inencrypted text for actual content to ensure it + # genuine transformation + + return $crypted; + + } + + /** + * @brief test decryption using legacy blowfish method + * @depends testLegacyEncryptLong + */ + function testLegacyDecryptLong( $crypted ) { + + $decrypted = Encryption\Crypt::legacyDecrypt( $crypted, $this->pass ); + + $this->assertEquals( $this->dataLong, $decrypted ); + + } + + /** + * @brief test generation of legacy encryption key + * @depends testLegacyDecryptShort + */ + function testLegacyCreateKey() { + + // Create encrypted key + $encKey = Encryption\Crypt::legacyCreateKey( $this->pass ); + + // Decrypt key + $key = Encryption\Crypt::legacyDecrypt( $encKey, $this->pass ); + + $this->assertTrue( is_numeric( $key ) ); + + // Check that key is correct length + $this->assertEquals( 20, strlen( $key ) ); + + } + + /** + * @brief test decryption using legacy blowfish method + * @depends testLegacyEncryptLong + */ + function testLegacyKeyRecryptKeyfileEncrypt( $crypted ) { + + $recrypted = Encryption\Crypt::LegacyKeyRecryptKeyfile( $crypted, $this->pass, $this->genPublicKey, $this->pass ); + + $this->assertNotEquals( $this->dataLong, $recrypted['data'] ); + + return $recrypted; + + # TODO: search inencrypted text for actual content to ensure it + # genuine transformation + + } + +// function testEncryption(){ +// +// $key=uniqid(); +// $file=OC::$SERVERROOT.'/3rdparty/MDB2.php'; +// $source=file_get_contents($file); //nice large text file +// $encrypted=OC_Encryption\Crypt::encrypt($source,$key); +// $decrypted=OC_Encryption\Crypt::decrypt($encrypted,$key); +// $decrypted=rtrim($decrypted, "\0"); +// $this->assertNotEquals($encrypted,$source); +// $this->assertEquals($decrypted,$source); +// +// $chunk=substr($source,0,8192); +// $encrypted=OC_Encryption\Crypt::encrypt($chunk,$key); +// $this->assertEquals(strlen($chunk),strlen($encrypted)); +// $decrypted=OC_Encryption\Crypt::decrypt($encrypted,$key); +// $decrypted=rtrim($decrypted, "\0"); +// $this->assertEquals($decrypted,$chunk); +// +// $encrypted=OC_Encryption\Crypt::blockEncrypt($source,$key); +// $decrypted=OC_Encryption\Crypt::blockDecrypt($encrypted,$key); +// $this->assertNotEquals($encrypted,$source); +// $this->assertEquals($decrypted,$source); +// +// $tmpFileEncrypted=OCP\Files::tmpFile(); +// OC_Encryption\Crypt::encryptfile($file,$tmpFileEncrypted,$key); +// $encrypted=file_get_contents($tmpFileEncrypted); +// $decrypted=OC_Encryption\Crypt::blockDecrypt($encrypted,$key); +// $this->assertNotEquals($encrypted,$source); +// $this->assertEquals($decrypted,$source); +// +// $tmpFileDecrypted=OCP\Files::tmpFile(); +// OC_Encryption\Crypt::decryptfile($tmpFileEncrypted,$tmpFileDecrypted,$key); +// $decrypted=file_get_contents($tmpFileDecrypted); +// $this->assertEquals($decrypted,$source); +// +// $file=OC::$SERVERROOT.'/core/img/weather-clear.png'; +// $source=file_get_contents($file); //binary file +// $encrypted=OC_Encryption\Crypt::encrypt($source,$key); +// $decrypted=OC_Encryption\Crypt::decrypt($encrypted,$key); +// $decrypted=rtrim($decrypted, "\0"); +// $this->assertEquals($decrypted,$source); +// +// $encrypted=OC_Encryption\Crypt::blockEncrypt($source,$key); +// $decrypted=OC_Encryption\Crypt::blockDecrypt($encrypted,$key); +// $this->assertEquals($decrypted,$source); +// +// } +// +// function testBinary(){ +// $key=uniqid(); +// +// $file=__DIR__.'/binary'; +// $source=file_get_contents($file); //binary file +// $encrypted=OC_Encryption\Crypt::encrypt($source,$key); +// $decrypted=OC_Encryption\Crypt::decrypt($encrypted,$key); +// +// $decrypted=rtrim($decrypted, "\0"); +// $this->assertEquals($decrypted,$source); +// +// $encrypted=OC_Encryption\Crypt::blockEncrypt($source,$key); +// $decrypted=OC_Encryption\Crypt::blockDecrypt($encrypted,$key,strlen($source)); +// $this->assertEquals($decrypted,$source); +// } + +} diff --git a/apps/files_encryption/test/keymanager.php b/apps/files_encryption/test/keymanager.php new file mode 100644 index 0000000000..f02d6eb5f7 --- /dev/null +++ b/apps/files_encryption/test/keymanager.php @@ -0,0 +1,132 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +//require_once "PHPUnit/Framework/TestCase.php"; +require_once realpath( dirname(__FILE__).'/../../../lib/base.php' ); +require_once realpath( dirname(__FILE__).'/../lib/crypt.php' ); +require_once realpath( dirname(__FILE__).'/../lib/keymanager.php' ); +require_once realpath( dirname(__FILE__).'/../lib/proxy.php' ); +require_once realpath( dirname(__FILE__).'/../lib/stream.php' ); +require_once realpath( dirname(__FILE__).'/../lib/util.php' ); +require_once realpath( dirname(__FILE__).'/../appinfo/app.php' ); + +use OCA\Encryption; + +// This has to go here because otherwise session errors arise, and the private +// encryption key needs to be saved in the session +\OC_User::login( 'admin', 'admin' ); + +class Test_Keymanager extends \PHPUnit_Framework_TestCase { + + function setUp() { + + \OC_FileProxy::$enabled = false; + + // set content for encrypting / decrypting in tests + $this->dataLong = file_get_contents( realpath( dirname(__FILE__).'/../lib/crypt.php' ) ); + $this->dataShort = 'hats'; + $this->dataUrl = realpath( dirname(__FILE__).'/../lib/crypt.php' ); + $this->legacyData = realpath( dirname(__FILE__).'/legacy-text.txt' ); + $this->legacyEncryptedData = realpath( dirname(__FILE__).'/legacy-encrypted-text.txt' ); + $this->randomKey = Encryption\Crypt::generateKey(); + + $keypair = Encryption\Crypt::createKeypair(); + $this->genPublicKey = $keypair['publicKey']; + $this->genPrivateKey = $keypair['privateKey']; + + $this->view = new \OC_FilesystemView( '/' ); + + \OC_User::setUserId( 'admin' ); + $this->userId = 'admin'; + $this->pass = 'admin'; + + \OC_Filesystem::init( '/' ); + \OC_Filesystem::mount( 'OC_Filestorage_Local', array('datadir' => \OC_User::getHome($this->userId)), '/' ); + + } + + function tearDown(){ + + \OC_FileProxy::$enabled = true; + + } + + function testGetPrivateKey() { + + $key = Encryption\Keymanager::getPrivateKey( $this->view, $this->userId ); + + // Will this length vary? Perhaps we should use a range instead + $this->assertEquals( 2296, strlen( $key ) ); + + } + + function testGetPublicKey() { + + $key = Encryption\Keymanager::getPublicKey( $this->view, $this->userId ); + + $this->assertEquals( 451, strlen( $key ) ); + + $this->assertEquals( '-----BEGIN PUBLIC KEY-----', substr( $key, 0, 26 ) ); + } + + function testSetFileKey() { + + # NOTE: This cannot be tested until we are able to break out + # of the FileSystemView data directory root + +// $key = Crypt::symmetricEncryptFileContentKeyfile( $this->data, 'hat' ); +// +// $tmpPath = sys_get_temp_dir(). '/' . 'testSetFileKey'; +// +// $view = new \OC_FilesystemView( '/tmp/' ); +// +// //$view = new \OC_FilesystemView( '/' . $this->userId . '/files_encryption/keyfiles' ); +// +// Encryption\Keymanager::setFileKey( $tmpPath, $key['key'], $view ); + + } + +// /** +// * @depends testGetPrivateKey +// */ +// function testGetPrivateKey_decrypt() { +// +// $key = Encryption\Keymanager::getPrivateKey( $this->view, $this->userId ); +// +// # TODO: replace call to Crypt with a mock object? +// $decrypted = Encryption\Crypt::symmetricDecryptFileContent( $key, $this->passphrase ); +// +// $this->assertEquals( 1704, strlen( $decrypted ) ); +// +// $this->assertEquals( '-----BEGIN PRIVATE KEY-----', substr( $decrypted, 0, 27 ) ); +// +// } + + function testGetUserKeys() { + + $keys = Encryption\Keymanager::getUserKeys( $this->view, $this->userId ); + + $this->assertEquals( 451, strlen( $keys['publicKey'] ) ); + $this->assertEquals( '-----BEGIN PUBLIC KEY-----', substr( $keys['publicKey'], 0, 26 ) ); + $this->assertEquals( 2296, strlen( $keys['privateKey'] ) ); + + } + + function testGetPublicKeys() { + + # TODO: write me + + } + + function testGetFileKey() { + +// Encryption\Keymanager::getFileKey( $this->view, $this->userId, $this->filePath ); + + } + +} diff --git a/apps/files_encryption/test/legacy-encrypted-text.txt b/apps/files_encryption/test/legacy-encrypted-text.txt new file mode 100644 index 0000000000..cb5bf50550 Binary files /dev/null and b/apps/files_encryption/test/legacy-encrypted-text.txt differ diff --git a/apps/files_encryption/test/proxy.php b/apps/files_encryption/test/proxy.php new file mode 100644 index 0000000000..709730f760 --- /dev/null +++ b/apps/files_encryption/test/proxy.php @@ -0,0 +1,220 @@ +, + * and Robin Appelman + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +// require_once "PHPUnit/Framework/TestCase.php"; +// require_once realpath( dirname(__FILE__).'/../../../lib/base.php' ); +// require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery.php' ); +// require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/Generator.php' ); +// require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/MockInterface.php' ); +// require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/Mock.php' ); +// require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/Container.php' ); +// require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/Configuration.php' ); +// require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/CompositeExpectation.php' ); +// require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/ExpectationDirector.php' ); +// require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/Expectation.php' ); +// require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/Exception.php' ); +// require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/CountValidator/CountValidatorAbstract.php' ); +// require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/CountValidator/Exception.php' ); +// require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/CountValidator/Exact.php' ); +// +// use \Mockery as m; +// use OCA\Encryption; + +// class Test_Util extends \PHPUnit_Framework_TestCase { +// +// public function setUp() { +// +// $this->proxy = new Encryption\Proxy(); +// +// $this->tmpFileName = "tmpFile-".time(); +// +// $this->privateKey = file_get_contents( realpath( dirname(__FILE__).'/data/admin.public.key' ) ); +// $this->publicKey = file_get_contents( realpath( dirname(__FILE__).'/data/admin.private.key' ) ); +// $this->encDataShort = file_get_contents( realpath( dirname(__FILE__).'/data/yoga-manchester-enc' ) ); +// $this->encDataShortKey = file_get_contents( realpath( dirname(__FILE__).'/data/yoga-manchester.key' ) ); +// +// $this->dataShort = file_get_contents( realpath( dirname(__FILE__).'/data/yoga-manchester' ) ); +// $this->dataLong = file_get_contents( realpath( dirname(__FILE__).'/../lib/crypt.php' ) ); +// $this->longDataPath = realpath( dirname(__FILE__).'/../lib/crypt.php' ); +// +// $this->data1 = file_get_contents( realpath( dirname(__FILE__).'/../../../data/admin/files/enc-test.txt' ) ); +// +// \OC_FileProxy::$enabled = false; +// $this->Encdata1 = file_get_contents( realpath( dirname(__FILE__).'/../../../data/admin/files/enc-test.txt' ) ); +// \OC_FileProxy::$enabled = true; +// +// $this->userId = 'admin'; +// $this->pass = 'admin'; +// +// $this->session = new Encryption\Session(); +// +// $this->session->setPrivateKey( +// '-----BEGIN PRIVATE KEY----- +// MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDiH3EA4EpFA7Fx +// s2dyyfL5jwXeYXrTqQJ6DqKgGn8VsbT3eu8R9KzM2XitVwZe8c8L52DvJ06o5vg0 +// GqPYxilFdOFJe/ggac5Tq8UmJiZS4EqYEMwxBIfIyWTxeGV06/0HOwnVAkqHMcBz +// 64qldtgi5O8kZMEM2/gKBgU0kMLJzM+8oEWhL1+gsUWQhxd8cKLXypS6iWgqFJrz +// f/X0hJsJR+gyYxNpahtnjzd/LxLAETrOMsl2tue+BAxmjbAM0aG0NEM0div+b59s +// 2uz/iWbxImp5pOdYVKcVW89D4XBMyGegR40trV2VwiuX1blKCfdjMsJhiaL9pymp +// ug1wzyQFAgMBAAECggEAK6c+PZkPPXuVCgpEcliiW6NM0r2m5K3AGKgypQ34csu3 +// z/8foCvIIFPrhCtEw5eTDQ1CHWlNOjY8vHJYJ0U6Onpx86nHIRrMBkMm8FJ1G5LJ +// U8oKYXwqaozWu/cuPwA//OFc6I5krOzh5n8WaRMkbrgbor8AtebRX74By0AXGrXe +// cswJI7zR96oFn4Dm7Pgvpg5Zhk1vFJ+w6QtH+4DDJ6PBvlZsRkGxYBLGVd/3qhAI +// sBAyjFlSzuP4eCRhHOhHC/e4gmAH9evFVXB88jFyRZm3K+jQ5W5CwrVRBCV2lph6 +// 2B6P7CBJN+IjGKMhy+75y13UvvKPv9IwH8Fzl2x1gQKBgQD8qQOr7a6KhSj16wQE +// jim2xqt9gQ2jH5No405NrKs/PFQQZnzD4YseQsiK//NUjOJiUhaT+L5jhIpzINHt +// RJpt3bGkEZmLyjdjgTpB3GwZdXa28DNK9VdXZ19qIl/ZH0qAjKmJCRahUDASMnVi +// M4Pkk9yx9ZIKkri4TcuMWqc0DQKBgQDlHKBTITZq/arYPD6Nl3NsoOdqVRqJrGay +// 0TjXAVbBXe46+z5lnMsqwXb79nx14hdmSEsZULrw/3f+MnQbdjMTYLFP24visZg9 +// MN8vAiALiiiR1a+Crz+DTA1Q8sGOMVCMqMDmD7QBys3ZuWxuapm0txAiIYUtsjJZ +// XN76T4nZ2QKBgQCHaT3igzwsWTmesxowJtEMeGWomeXpKx8h89EfqA8PkRGsyIDN +// qq+YxEoe1RZgljEuaLhZDdNcGsjo8woPk9kAUPTH7fbRCMuutK+4ZJ469s1tNkcH +// QX5SBcEJbOrZvv967ehe3VQXmJZq6kgnHVzuwKBjcC2ZJRGDFY6l5l/+cQKBgCqh +// +Adf/8NK7paMJ0urqfPFwSodKfICXZ3apswDWMRkmSbqh4La+Uc8dsqN5Dz/VEFZ +// JHhSeGbN8uMfOlG93eU2MehdPxtw1pZUWMNjjtj23XO9ooob2CKzbSrp8TBnZsi1 +// widNNr66oTFpeo7VUUK6acsgF6sYJJxSVr+XO1yJAoGAEhvitq8shNKcEY0xCipS +// k1kbgyS7KKB7opVxI5+ChEqyUDijS3Y9FZixrRIWE6i2uGu86UG+v2lbKvSbM4Qm +// xvbOcX9OVMnlRb7n8woOP10UMY+ZE2x+YEUXQTLtPYq7F66e1OfxltstMxLQA+3d +// Y1d5piFV8PXK3Fg2F+Cj5qg= +// -----END PRIVATE KEY----- +// ' +// , $this->userId +// ); +// +// \OC_User::setUserId( $this->userId ); +// +// } +// +// public function testpreFile_get_contents() { +// +// // This won't work for now because mocking of the static keymanager class isn't working :( +// +// // $mock = m::mock( 'alias:OCA\Encryption\Keymanager' ); +// // +// // $mock->shouldReceive( 'getFileKey' )->times(2)->andReturn( $this->encDataShort ); +// // +// // $encrypted = $this->proxy->postFile_get_contents( 'data/'.$this->tmpFileName, $this->encDataShortKey ); +// // +// // $this->assertNotEquals( $this->dataShort, $encrypted ); +// +// $decrypted = $this->proxy->postFile_get_contents( 'data/admin/files/enc-test.txt', $this->data1 ); +// +// } +// +// } + +// class Test_CryptProxy extends PHPUnit_Framework_TestCase { +// private $oldConfig; +// private $oldKey; +// +// public function setUp(){ +// $user=OC_User::getUser(); +// +// $this->oldConfig=OCP\Config::getAppValue('files_encryption','enable_encryption','true'); +// OCP\Config::setAppValue('files_encryption','enable_encryption','true'); +// $this->oldKey=isset($_SESSION['privateKey'])?$_SESSION['privateKey']:null; +// +// +// //set testing key +// $_SESSION['privateKey']=md5(time()); +// +// //clear all proxies and hooks so we can do clean testing +// OC_FileProxy::clearProxies(); +// OC_Hook::clear('OC_Filesystem'); +// +// //enable only the encryption hook +// OC_FileProxy::register(new OC_FileProxy_Encryption()); +// +// //set up temporary storage +// OC_Filesystem::clearMounts(); +// OC_Filesystem::mount('OC_Filestorage_Temporary',array(),'/'); +// +// OC_Filesystem::init('/'.$user.'/files'); +// +// //set up the users home folder in the temp storage +// $rootView=new OC_FilesystemView(''); +// $rootView->mkdir('/'.$user); +// $rootView->mkdir('/'.$user.'/files'); +// } +// +// public function tearDown(){ +// OCP\Config::setAppValue('files_encryption','enable_encryption',$this->oldConfig); +// if(!is_null($this->oldKey)){ +// $_SESSION['privateKey']=$this->oldKey; +// } +// } +// +// public function testSimple(){ +// $file=OC::$SERVERROOT.'/3rdparty/MDB2.php'; +// $original=file_get_contents($file); +// +// OC_Filesystem::file_put_contents('/file',$original); +// +// OC_FileProxy::$enabled=false; +// $stored=OC_Filesystem::file_get_contents('/file'); +// OC_FileProxy::$enabled=true; +// +// $fromFile=OC_Filesystem::file_get_contents('/file'); +// $this->assertNotEquals($original,$stored); +// $this->assertEquals(strlen($original),strlen($fromFile)); +// $this->assertEquals($original,$fromFile); +// +// } +// +// public function testView(){ +// $file=OC::$SERVERROOT.'/3rdparty/MDB2.php'; +// $original=file_get_contents($file); +// +// $rootView=new OC_FilesystemView(''); +// $view=new OC_FilesystemView('/'.OC_User::getUser()); +// $userDir='/'.OC_User::getUser().'/files'; +// +// $rootView->file_put_contents($userDir.'/file',$original); +// +// OC_FileProxy::$enabled=false; +// $stored=$rootView->file_get_contents($userDir.'/file'); +// OC_FileProxy::$enabled=true; +// +// $this->assertNotEquals($original,$stored); +// $fromFile=$rootView->file_get_contents($userDir.'/file'); +// $this->assertEquals($original,$fromFile); +// +// $fromFile=$view->file_get_contents('files/file'); +// $this->assertEquals($original,$fromFile); +// } +// +// public function testBinary(){ +// $file=__DIR__.'/binary'; +// $original=file_get_contents($file); +// +// OC_Filesystem::file_put_contents('/file',$original); +// +// OC_FileProxy::$enabled=false; +// $stored=OC_Filesystem::file_get_contents('/file'); +// OC_FileProxy::$enabled=true; +// +// $fromFile=OC_Filesystem::file_get_contents('/file'); +// $this->assertNotEquals($original,$stored); +// $this->assertEquals(strlen($original),strlen($fromFile)); +// $this->assertEquals($original,$fromFile); +// +// $file=__DIR__.'/zeros'; +// $original=file_get_contents($file); +// +// OC_Filesystem::file_put_contents('/file',$original); +// +// OC_FileProxy::$enabled=false; +// $stored=OC_Filesystem::file_get_contents('/file'); +// OC_FileProxy::$enabled=true; +// +// $fromFile=OC_Filesystem::file_get_contents('/file'); +// $this->assertNotEquals($original,$stored); +// $this->assertEquals(strlen($original),strlen($fromFile)); +// } +// } diff --git a/apps/files_encryption/test/stream.php b/apps/files_encryption/test/stream.php new file mode 100644 index 0000000000..ba82ac80ea --- /dev/null +++ b/apps/files_encryption/test/stream.php @@ -0,0 +1,226 @@ +// +// * This file is licensed under the Affero General Public License version 3 or +// * later. +// * See the COPYING-README file. +// */ +// +// namespace OCA\Encryption; +// +// class Test_Stream extends \PHPUnit_Framework_TestCase { +// +// function setUp() { +// +// \OC_Filesystem::mount( 'OC_Filestorage_Local', array(), '/' ); +// +// $this->empty = ''; +// +// $this->stream = new Stream(); +// +// $this->dataLong = file_get_contents( realpath( dirname(__FILE__).'/../lib/crypt.php' ) ); +// $this->dataShort = 'hats'; +// +// $this->emptyTmpFilePath = \OCP\Files::tmpFile(); +// +// $this->dataTmpFilePath = \OCP\Files::tmpFile(); +// +// file_put_contents( $this->dataTmpFilePath, "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Donec et mollis dolor. Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ut gravida lorem. Ut turpis felis, pulvinar a semper sed, adipiscing id dolor. Pellentesque auctor nisi id magna consequat sagittis. Curabitur dapibus enim sit amet elit pharetra tincidunt feugiat nisl imperdiet. Ut convallis libero in urna ultrices accumsan. Donec sed odio eros. Donec viverra mi quis quam pulvinar at malesuada arcu rhoncus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In rutrum accumsan ultricies. Mauris vitae nisi at sem facilisis semper ac in est." ); +// +// } +// +// function testStreamOpen() { +// +// $stream1 = new Stream(); +// +// $handle1 = $stream1->stream_open( $this->emptyTmpFilePath, 'wb', array(), $this->empty ); +// +// // Test that resource was returned successfully +// $this->assertTrue( $handle1 ); +// +// // Test that file has correct size +// $this->assertEquals( 0, $stream1->size ); +// +// // Test that path is correct +// $this->assertEquals( $this->emptyTmpFilePath, $stream1->rawPath ); +// +// $stream2 = new Stream(); +// +// $handle2 = $stream2->stream_open( 'crypt://' . $this->emptyTmpFilePath, 'wb', array(), $this->empty ); +// +// // Test that protocol identifier is removed from path +// $this->assertEquals( $this->emptyTmpFilePath, $stream2->rawPath ); +// +// // "Stat failed error" prevents this test from executing +// // $stream3 = new Stream(); +// // +// // $handle3 = $stream3->stream_open( $this->dataTmpFilePath, 'r', array(), $this->empty ); +// // +// // $this->assertEquals( 0, $stream3->size ); +// +// } +// +// function testStreamWrite() { +// +// $stream1 = new Stream(); +// +// $handle1 = $stream1->stream_open( $this->emptyTmpFilePath, 'r+b', array(), $this->empty ); +// +// # what about the keymanager? there is no key for the newly created temporary file! +// +// $stream1->stream_write( $this->dataShort ); +// +// } +// +// // function getStream( $id, $mode, $size ) { +// // +// // if ( $id === '' ) { +// // +// // $id = uniqid(); +// // } +// // +// // +// // if ( !isset( $this->tmpFiles[$id] ) ) { +// // +// // // If tempfile with given name does not already exist, create it +// // +// // $file = OCP\Files::tmpFile(); +// // +// // $this->tmpFiles[$id] = $file; +// // +// // } else { +// // +// // $file = $this->tmpFiles[$id]; +// // +// // } +// // +// // $stream = fopen( $file, $mode ); +// // +// // Stream::$sourceStreams[$id] = array( 'path' => 'dummy' . $id, 'stream' => $stream, 'size' => $size ); +// // +// // return fopen( 'crypt://streams/'.$id, $mode ); +// // +// // } +// // +// // function testStream( ){ +// // +// // $stream = $this->getStream( 'test1', 'w', strlen( 'foobar' ) ); +// // +// // fwrite( $stream, 'foobar' ); +// // +// // fclose( $stream ); +// // +// // +// // $stream = $this->getStream( 'test1', 'r', strlen( 'foobar' ) ); +// // +// // $data = fread( $stream, 6 ); +// // +// // fclose( $stream ); +// // +// // $this->assertEquals( 'foobar', $data ); +// // +// // +// // $file = OC::$SERVERROOT.'/3rdparty/MDB2.php'; +// // +// // $source = fopen( $file, 'r' ); +// // +// // $target = $this->getStream( 'test2', 'w', 0 ); +// // +// // OCP\Files::streamCopy( $source, $target ); +// // +// // fclose( $target ); +// // +// // fclose( $source ); +// // +// // +// // $stream = $this->getStream( 'test2', 'r', filesize( $file ) ); +// // +// // $data = stream_get_contents( $stream ); +// // +// // $original = file_get_contents( $file ); +// // +// // $this->assertEquals( strlen( $original ), strlen( $data ) ); +// // +// // $this->assertEquals( $original, $data ); +// // +// // } +// +// } +// +// // class Test_CryptStream extends PHPUnit_Framework_TestCase { +// // private $tmpFiles=array(); +// // +// // function testStream(){ +// // $stream=$this->getStream('test1','w',strlen('foobar')); +// // fwrite($stream,'foobar'); +// // fclose($stream); +// // +// // $stream=$this->getStream('test1','r',strlen('foobar')); +// // $data=fread($stream,6); +// // fclose($stream); +// // $this->assertEquals('foobar',$data); +// // +// // $file=OC::$SERVERROOT.'/3rdparty/MDB2.php'; +// // $source=fopen($file,'r'); +// // $target=$this->getStream('test2','w',0); +// // OCP\Files::streamCopy($source,$target); +// // fclose($target); +// // fclose($source); +// // +// // $stream=$this->getStream('test2','r',filesize($file)); +// // $data=stream_get_contents($stream); +// // $original=file_get_contents($file); +// // $this->assertEquals(strlen($original),strlen($data)); +// // $this->assertEquals($original,$data); +// // } +// // +// // /** +// // * get a cryptstream to a temporary file +// // * @param string $id +// // * @param string $mode +// // * @param int size +// // * @return resource +// // */ +// // function getStream($id,$mode,$size){ +// // if($id===''){ +// // $id=uniqid(); +// // } +// // if(!isset($this->tmpFiles[$id])){ +// // $file=OCP\Files::tmpFile(); +// // $this->tmpFiles[$id]=$file; +// // }else{ +// // $file=$this->tmpFiles[$id]; +// // } +// // $stream=fopen($file,$mode); +// // OC_CryptStream::$sourceStreams[$id]=array('path'=>'dummy'.$id,'stream'=>$stream,'size'=>$size); +// // return fopen('crypt://streams/'.$id,$mode); +// // } +// // +// // function testBinary(){ +// // $file=__DIR__.'/binary'; +// // $source=file_get_contents($file); +// // +// // $stream=$this->getStream('test','w',strlen($source)); +// // fwrite($stream,$source); +// // fclose($stream); +// // +// // $stream=$this->getStream('test','r',strlen($source)); +// // $data=stream_get_contents($stream); +// // fclose($stream); +// // $this->assertEquals(strlen($data),strlen($source)); +// // $this->assertEquals($source,$data); +// // +// // $file=__DIR__.'/zeros'; +// // $source=file_get_contents($file); +// // +// // $stream=$this->getStream('test2','w',strlen($source)); +// // fwrite($stream,$source); +// // fclose($stream); +// // +// // $stream=$this->getStream('test2','r',strlen($source)); +// // $data=stream_get_contents($stream); +// // fclose($stream); +// // $this->assertEquals(strlen($data),strlen($source)); +// // $this->assertEquals($source,$data); +// // } +// // } diff --git a/apps/files_encryption/test/util.php b/apps/files_encryption/test/util.php new file mode 100755 index 0000000000..a299ec67f5 --- /dev/null +++ b/apps/files_encryption/test/util.php @@ -0,0 +1,210 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +//require_once "PHPUnit/Framework/TestCase.php"; +require_once realpath( dirname(__FILE__).'/../../../lib/base.php' ); +require_once realpath( dirname(__FILE__).'/../lib/crypt.php' ); +require_once realpath( dirname(__FILE__).'/../lib/keymanager.php' ); +require_once realpath( dirname(__FILE__).'/../lib/proxy.php' ); +require_once realpath( dirname(__FILE__).'/../lib/stream.php' ); +require_once realpath( dirname(__FILE__).'/../lib/util.php' ); +require_once realpath( dirname(__FILE__).'/../appinfo/app.php' ); + +// Load mockery files +require_once 'Mockery/Loader.php'; +require_once 'Hamcrest/Hamcrest.php'; +$loader = new \Mockery\Loader; +$loader->register(); + +use \Mockery as m; +use OCA\Encryption; + +class Test_Enc_Util extends \PHPUnit_Framework_TestCase { + + function setUp() { + + \OC_Filesystem::mount( 'OC_Filestorage_Local', array(), '/' ); + + // set content for encrypting / decrypting in tests + $this->dataUrl = realpath( dirname(__FILE__).'/../lib/crypt.php' ); + $this->dataShort = 'hats'; + $this->dataLong = file_get_contents( realpath( dirname(__FILE__).'/../lib/crypt.php' ) ); + $this->legacyData = realpath( dirname(__FILE__).'/legacy-text.txt' ); + $this->legacyEncryptedData = realpath( dirname(__FILE__).'/legacy-encrypted-text.txt' ); + + $this->userId = 'admin'; + $this->pass = 'admin'; + + $keypair = Encryption\Crypt::createKeypair(); + + $this->genPublicKey = $keypair['publicKey']; + $this->genPrivateKey = $keypair['privateKey']; + + $this->publicKeyDir = '/' . 'public-keys'; + $this->encryptionDir = '/' . $this->userId . '/' . 'files_encryption'; + $this->keyfilesPath = $this->encryptionDir . '/' . 'keyfiles'; + $this->publicKeyPath = $this->publicKeyDir . '/' . $this->userId . '.public.key'; // e.g. data/public-keys/admin.public.key + $this->privateKeyPath = $this->encryptionDir . '/' . $this->userId . '.private.key'; // e.g. data/admin/admin.private.key + + $this->view = new OC_FilesystemView( '/admin' ); + + $this->mockView = m::mock('OC_FilesystemView'); + $this->util = new Encryption\Util( $this->mockView, $this->userId ); + + } + + function tearDown(){ + + m::close(); + + } + + /** + * @brief test that paths set during User construction are correct + */ + function testKeyPaths() { + + $mockView = m::mock('OC_FilesystemView'); + + $util = new Encryption\Util( $mockView, $this->userId ); + + $this->assertEquals( $this->publicKeyDir, $util->getPath( 'publicKeyDir' ) ); + $this->assertEquals( $this->encryptionDir, $util->getPath( 'encryptionDir' ) ); + $this->assertEquals( $this->keyfilesPath, $util->getPath( 'keyfilesPath' ) ); + $this->assertEquals( $this->publicKeyPath, $util->getPath( 'publicKeyPath' ) ); + $this->assertEquals( $this->privateKeyPath, $util->getPath( 'privateKeyPath' ) ); + + } + + /** + * @brief test setup of encryption directories when they don't yet exist + */ + function testSetupServerSideNotSetup() { + + $mockView = m::mock('OC_FilesystemView'); + + $mockView->shouldReceive( 'file_exists' )->times(4)->andReturn( false ); + $mockView->shouldReceive( 'mkdir' )->times(3)->andReturn( true ); + $mockView->shouldReceive( 'file_put_contents' )->withAnyArgs(); + + $util = new Encryption\Util( $mockView, $this->userId ); + + $this->assertEquals( true, $util->setupServerSide( $this->pass ) ); + + } + + /** + * @brief test setup of encryption directories when they already exist + */ + function testSetupServerSideIsSetup() { + + $mockView = m::mock('OC_FilesystemView'); + + $mockView->shouldReceive( 'file_exists' )->times(5)->andReturn( true ); + $mockView->shouldReceive( 'file_put_contents' )->withAnyArgs(); + + $util = new Encryption\Util( $mockView, $this->userId ); + + $this->assertEquals( true, $util->setupServerSide( $this->pass ) ); + + } + + /** + * @brief test checking whether account is ready for encryption, when it isn't ready + */ + function testReadyNotReady() { + + $mockView = m::mock('OC_FilesystemView'); + + $mockView->shouldReceive( 'file_exists' )->times(1)->andReturn( false ); + + $util = new Encryption\Util( $mockView, $this->userId ); + + $this->assertEquals( false, $util->ready() ); + + # TODO: Add more tests here to check that if any of the dirs are + # then false will be returned. Use strict ordering? + + } + + /** + * @brief test checking whether account is ready for encryption, when it is ready + */ + function testReadyIsReady() { + + $mockView = m::mock('OC_FilesystemView'); + + $mockView->shouldReceive( 'file_exists' )->times(3)->andReturn( true ); + + $util = new Encryption\Util( $mockView, $this->userId ); + + $this->assertEquals( true, $util->ready() ); + + # TODO: Add more tests here to check that if any of the dirs are + # then false will be returned. Use strict ordering? + + } + +// /** +// * @brief test decryption using legacy blowfish method +// * @depends testLegacyEncryptLong +// */ +// function testLegacyKeyRecryptKeyfileDecrypt( $recrypted ) { +// +// $decrypted = Encryption\Crypt::keyDecryptKeyfile( $recrypted['data'], $recrypted['key'], $this->genPrivateKey ); +// +// $this->assertEquals( $this->dataLong, $decrypted ); +// +// } + +// // Cannot use this test for now due to hidden dependencies in OC_FileCache +// function testIsLegacyEncryptedContent() { +// +// $keyfileContent = OCA\Encryption\Crypt::symmetricEncryptFileContent( $this->legacyEncryptedData, 'hat' ); +// +// $this->assertFalse( OCA\Encryption\Crypt::isLegacyEncryptedContent( $keyfileContent, '/files/admin/test.txt' ) ); +// +// OC_FileCache::put( '/admin/files/legacy-encrypted-test.txt', $this->legacyEncryptedData ); +// +// $this->assertTrue( OCA\Encryption\Crypt::isLegacyEncryptedContent( $this->legacyEncryptedData, '/files/admin/test.txt' ) ); +// +// } + +// // Cannot use this test for now due to need for different root in OC_Filesystem_view class +// function testGetLegacyKey() { +// +// $c = new \OCA\Encryption\Util( $view, false ); +// +// $bool = $c->getLegacyKey( 'admin' ); +// +// $this->assertTrue( $bool ); +// +// $this->assertTrue( $c->legacyKey ); +// +// $this->assertTrue( is_int( $c->legacyKey ) ); +// +// $this->assertTrue( strlen( $c->legacyKey ) == 20 ); +// +// } + +// // Cannot use this test for now due to need for different root in OC_Filesystem_view class +// function testLegacyDecrypt() { +// +// $c = new OCA\Encryption\Util( $this->view, false ); +// +// $bool = $c->getLegacyKey( 'admin' ); +// +// $encrypted = $c->legacyEncrypt( $this->data, $c->legacyKey ); +// +// $decrypted = $c->legacyDecrypt( $encrypted, $c->legacyKey ); +// +// $this->assertEquals( $decrypted, $this->data ); +// +// } + +} \ No newline at end of file diff --git a/apps/files_encryption/tests/zeros b/apps/files_encryption/test/zeros similarity index 100% rename from apps/files_encryption/tests/zeros rename to apps/files_encryption/test/zeros diff --git a/apps/files_encryption/tests/encryption.php b/apps/files_encryption/tests/encryption.php deleted file mode 100644 index 0e119f55be..0000000000 --- a/apps/files_encryption/tests/encryption.php +++ /dev/null @@ -1,72 +0,0 @@ - - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ - -class Test_Encryption extends UnitTestCase { - function testEncryption() { - $key=uniqid(); - $file=OC::$SERVERROOT.'/3rdparty/MDB2.php'; - $source=file_get_contents($file); //nice large text file - $encrypted=OC_Crypt::encrypt($source, $key); - $decrypted=OC_Crypt::decrypt($encrypted, $key); - $decrypted=rtrim($decrypted, "\0"); - $this->assertNotEqual($encrypted, $source); - $this->assertEqual($decrypted, $source); - - $chunk=substr($source, 0, 8192); - $encrypted=OC_Crypt::encrypt($chunk, $key); - $this->assertEqual(strlen($chunk), strlen($encrypted)); - $decrypted=OC_Crypt::decrypt($encrypted, $key); - $decrypted=rtrim($decrypted, "\0"); - $this->assertEqual($decrypted, $chunk); - - $encrypted=OC_Crypt::blockEncrypt($source, $key); - $decrypted=OC_Crypt::blockDecrypt($encrypted, $key); - $this->assertNotEqual($encrypted, $source); - $this->assertEqual($decrypted, $source); - - $tmpFileEncrypted=OCP\Files::tmpFile(); - OC_Crypt::encryptfile($file, $tmpFileEncrypted, $key); - $encrypted=file_get_contents($tmpFileEncrypted); - $decrypted=OC_Crypt::blockDecrypt($encrypted, $key); - $this->assertNotEqual($encrypted, $source); - $this->assertEqual($decrypted, $source); - - $tmpFileDecrypted=OCP\Files::tmpFile(); - OC_Crypt::decryptfile($tmpFileEncrypted, $tmpFileDecrypted, $key); - $decrypted=file_get_contents($tmpFileDecrypted); - $this->assertEqual($decrypted, $source); - - $file=OC::$SERVERROOT.'/core/img/weather-clear.png'; - $source=file_get_contents($file); //binary file - $encrypted=OC_Crypt::encrypt($source, $key); - $decrypted=OC_Crypt::decrypt($encrypted, $key); - $decrypted=rtrim($decrypted, "\0"); - $this->assertEqual($decrypted, $source); - - $encrypted=OC_Crypt::blockEncrypt($source, $key); - $decrypted=OC_Crypt::blockDecrypt($encrypted, $key); - $this->assertEqual($decrypted, $source); - - } - - function testBinary() { - $key=uniqid(); - - $file=__DIR__.'/binary'; - $source=file_get_contents($file); //binary file - $encrypted=OC_Crypt::encrypt($source, $key); - $decrypted=OC_Crypt::decrypt($encrypted, $key); - - $decrypted=rtrim($decrypted, "\0"); - $this->assertEqual($decrypted, $source); - - $encrypted=OC_Crypt::blockEncrypt($source, $key); - $decrypted=OC_Crypt::blockDecrypt($encrypted, $key, strlen($source)); - $this->assertEqual($decrypted, $source); - } -} diff --git a/apps/files_encryption/tests/proxy.php b/apps/files_encryption/tests/proxy.php deleted file mode 100644 index 5aa617e747..0000000000 --- a/apps/files_encryption/tests/proxy.php +++ /dev/null @@ -1,117 +0,0 @@ - - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ - -class Test_CryptProxy extends UnitTestCase { - private $oldConfig; - private $oldKey; - - public function setUp() { - $user=OC_User::getUser(); - - $this->oldConfig=OCP\Config::getAppValue('files_encryption','enable_encryption', 'true'); - OCP\Config::setAppValue('files_encryption', 'enable_encryption', 'true'); - $this->oldKey=isset($_SESSION['enckey'])?$_SESSION['enckey']:null; - - - //set testing key - $_SESSION['enckey']=md5(time()); - - //clear all proxies and hooks so we can do clean testing - OC_FileProxy::clearProxies(); - OC_Hook::clear('OC_Filesystem'); - - //enable only the encryption hook - OC_FileProxy::register(new OC_FileProxy_Encryption()); - - //set up temporary storage - OC_Filesystem::clearMounts(); - OC_Filesystem::mount('OC_Filestorage_Temporary', array(), '/'); - - OC_Filesystem::init('/'.$user.'/files'); - - //set up the users home folder in the temp storage - $rootView=new OC_FilesystemView(''); - $rootView->mkdir('/'.$user); - $rootView->mkdir('/'.$user.'/files'); - } - - public function tearDown() { - OCP\Config::setAppValue('files_encryption', 'enable_encryption', $this->oldConfig); - if ( ! is_null($this->oldKey)) { - $_SESSION['enckey']=$this->oldKey; - } - } - - public function testSimple() { - $file=OC::$SERVERROOT.'/3rdparty/MDB2.php'; - $original=file_get_contents($file); - - OC_Filesystem::file_put_contents('/file', $original); - - OC_FileProxy::$enabled=false; - $stored=OC_Filesystem::file_get_contents('/file'); - OC_FileProxy::$enabled=true; - - $fromFile=OC_Filesystem::file_get_contents('/file'); - $this->assertNotEqual($original, $stored); - $this->assertEqual(strlen($original), strlen($fromFile)); - $this->assertEqual($original, $fromFile); - - } - - public function testView() { - $file=OC::$SERVERROOT.'/3rdparty/MDB2.php'; - $original=file_get_contents($file); - - $rootView=new OC_FilesystemView(''); - $view=new OC_FilesystemView('/'.OC_User::getUser()); - $userDir='/'.OC_User::getUser().'/files'; - - $rootView->file_put_contents($userDir.'/file', $original); - - OC_FileProxy::$enabled=false; - $stored=$rootView->file_get_contents($userDir.'/file'); - OC_FileProxy::$enabled=true; - - $this->assertNotEqual($original, $stored); - $fromFile=$rootView->file_get_contents($userDir.'/file'); - $this->assertEqual($original, $fromFile); - - $fromFile=$view->file_get_contents('files/file'); - $this->assertEqual($original, $fromFile); - } - - public function testBinary() { - $file=__DIR__.'/binary'; - $original=file_get_contents($file); - - OC_Filesystem::file_put_contents('/file', $original); - - OC_FileProxy::$enabled=false; - $stored=OC_Filesystem::file_get_contents('/file'); - OC_FileProxy::$enabled=true; - - $fromFile=OC_Filesystem::file_get_contents('/file'); - $this->assertNotEqual($original, $stored); - $this->assertEqual(strlen($original), strlen($fromFile)); - $this->assertEqual($original, $fromFile); - - $file=__DIR__.'/zeros'; - $original=file_get_contents($file); - - OC_Filesystem::file_put_contents('/file', $original); - - OC_FileProxy::$enabled=false; - $stored=OC_Filesystem::file_get_contents('/file'); - OC_FileProxy::$enabled=true; - - $fromFile=OC_Filesystem::file_get_contents('/file'); - $this->assertNotEqual($original, $stored); - $this->assertEqual(strlen($original), strlen($fromFile)); - } -} diff --git a/apps/files_encryption/tests/stream.php b/apps/files_encryption/tests/stream.php deleted file mode 100644 index e4af17d47b..0000000000 --- a/apps/files_encryption/tests/stream.php +++ /dev/null @@ -1,85 +0,0 @@ - - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ - -class Test_CryptStream extends UnitTestCase { - private $tmpFiles=array(); - - function testStream() { - $stream=$this->getStream('test1', 'w', strlen('foobar')); - fwrite($stream, 'foobar'); - fclose($stream); - - $stream=$this->getStream('test1', 'r', strlen('foobar')); - $data=fread($stream, 6); - fclose($stream); - $this->assertEqual('foobar', $data); - - $file=OC::$SERVERROOT.'/3rdparty/MDB2.php'; - $source=fopen($file, 'r'); - $target=$this->getStream('test2', 'w', 0); - OCP\Files::streamCopy($source, $target); - fclose($target); - fclose($source); - - $stream=$this->getStream('test2', 'r', filesize($file)); - $data=stream_get_contents($stream); - $original=file_get_contents($file); - $this->assertEqual(strlen($original), strlen($data)); - $this->assertEqual($original, $data); - } - - /** - * get a cryptstream to a temporary file - * @param string $id - * @param string $mode - * @param int size - * @return resource - */ - function getStream($id, $mode, $size) { - if ($id==='') { - $id=uniqid(); - } - if ( ! isset($this->tmpFiles[$id])) { - $file=OCP\Files::tmpFile(); - $this->tmpFiles[$id]=$file; - } else { - $file=$this->tmpFiles[$id]; - } - $stream=fopen($file, $mode); - OC_CryptStream::$sourceStreams[$id]=array('path'=>'dummy'.$id, 'stream'=>$stream, 'size'=>$size); - return fopen('crypt://streams/'.$id, $mode); - } - - function testBinary() { - $file=__DIR__.'/binary'; - $source=file_get_contents($file); - - $stream=$this->getStream('test', 'w', strlen($source)); - fwrite($stream, $source); - fclose($stream); - - $stream=$this->getStream('test', 'r', strlen($source)); - $data=stream_get_contents($stream); - fclose($stream); - $this->assertEqual(strlen($data), strlen($source)); - $this->assertEqual($source, $data); - - $file=__DIR__.'/zeros'; - $source=file_get_contents($file); - - $stream=$this->getStream('test2', 'w', strlen($source)); - fwrite($stream, $source); - fclose($stream); - - $stream=$this->getStream('test2', 'r', strlen($source)); - $data=stream_get_contents($stream); - fclose($stream); - $this->assertEqual(strlen($data), strlen($source)); - $this->assertEqual($source, $data); - } -} diff --git a/apps/files_external/ajax/addRootCertificate.php b/apps/files_external/ajax/addRootCertificate.php index be60b415e1..2f67e801b2 100644 --- a/apps/files_external/ajax/addRootCertificate.php +++ b/apps/files_external/ajax/addRootCertificate.php @@ -12,8 +12,10 @@ $data = fread($fh, filesize($_FILES['rootcert_import']['tmp_name'])); fclose($fh); $filename = $_FILES['rootcert_import']['name']; -$view = new \OC_FilesystemView('/'.\OCP\User::getUser().'/files_external/uploads'); -if ( ! $view->file_exists('')) $view->mkdir(''); +$view = new \OC\Files\View('/'.\OCP\User::getUser().'/files_external/uploads'); +if (!$view->file_exists('')){ + $view->mkdir(''); +} $isValid = openssl_pkey_get_public($data); diff --git a/apps/files_external/appinfo/app.php b/apps/files_external/appinfo/app.php index 837d35c9c6..c58cfcd0f5 100644 --- a/apps/files_external/appinfo/app.php +++ b/apps/files_external/appinfo/app.php @@ -6,14 +6,14 @@ * See the COPYING-README file. */ -OC::$CLASSPATH['OC_FileStorage_StreamWrapper']='apps/files_external/lib/streamwrapper.php'; -OC::$CLASSPATH['OC_Filestorage_FTP']='apps/files_external/lib/ftp.php'; -OC::$CLASSPATH['OC_Filestorage_DAV']='apps/files_external/lib/webdav.php'; -OC::$CLASSPATH['OC_Filestorage_Google']='apps/files_external/lib/google.php'; -OC::$CLASSPATH['OC_Filestorage_SWIFT']='apps/files_external/lib/swift.php'; -OC::$CLASSPATH['OC_Filestorage_SMB']='apps/files_external/lib/smb.php'; -OC::$CLASSPATH['OC_Filestorage_AmazonS3']='apps/files_external/lib/amazons3.php'; -OC::$CLASSPATH['OC_Filestorage_Dropbox']='apps/files_external/lib/dropbox.php'; +OC::$CLASSPATH['OC\Files\Storage\StreamWrapper']='apps/files_external/lib/streamwrapper.php'; +OC::$CLASSPATH['OC\Files\Storage\FTP']='apps/files_external/lib/ftp.php'; +OC::$CLASSPATH['OC\Files\Storage\DAV']='apps/files_external/lib/webdav.php'; +OC::$CLASSPATH['OC\Files\Storage\Google']='apps/files_external/lib/google.php'; +OC::$CLASSPATH['OC\Files\Storage\SWIFT']='apps/files_external/lib/swift.php'; +OC::$CLASSPATH['OC\Files\Storage\SMB']='apps/files_external/lib/smb.php'; +OC::$CLASSPATH['OC\Files\Storage\AmazonS3']='apps/files_external/lib/amazons3.php'; +OC::$CLASSPATH['OC\Files\Storage\Dropbox']='apps/files_external/lib/dropbox.php'; OC::$CLASSPATH['OC_Mount_Config']='apps/files_external/lib/config.php'; OCP\App::registerAdmin('files_external', 'settings'); diff --git a/apps/files_external/appinfo/info.xml b/apps/files_external/appinfo/info.xml index 3da1913c5f..2c04216a9f 100644 --- a/apps/files_external/appinfo/info.xml +++ b/apps/files_external/appinfo/info.xml @@ -5,7 +5,7 @@ Mount external storage sources AGPL Robin Appelman, Michael Gapczynski - 4.9 + 4.91 true diff --git a/apps/files_external/js/dropbox.js b/apps/files_external/js/dropbox.js index c1e3864070..cd3c957e0a 100644 --- a/apps/files_external/js/dropbox.js +++ b/apps/files_external/js/dropbox.js @@ -1,6 +1,6 @@ $(document).ready(function() { - $('#externalStorage tbody tr.OC_Filestorage_Dropbox').each(function() { + $('#externalStorage tbody tr.\\\\OC\\\\Files\\\\Storage\\\\Dropbox').each(function() { var configured = $(this).find('[data-parameter="configured"]'); if ($(configured).val() == 'true') { $(this).find('.configuration input').attr('disabled', 'disabled'); @@ -36,9 +36,9 @@ $(document).ready(function() { } }); - $('#externalStorage tbody tr input').live('keyup', function() { + $('#externalStorage tbody').on('keyup', 'tr input', function() { var tr = $(this).parent().parent(); - if ($(tr).hasClass('OC_Filestorage_Dropbox') && $(tr).find('[data-parameter="configured"]').val() != 'true') { + if ($(tr).hasClass('\\\\OC\\\\Files\\\\Storage\\\\Dropbox') && $(tr).find('[data-parameter="configured"]').val() != 'true') { var config = $(tr).find('.configuration'); if ($(tr).find('.mountPoint input').val() != '' && $(config).find('[data-parameter="app_key"]').val() != '' && $(config).find('[data-parameter="app_secret"]').val() != '') { if ($(tr).find('.dropbox').length == 0) { @@ -52,7 +52,7 @@ $(document).ready(function() { } }); - $('.dropbox').live('click', function(event) { + $('.dropbox').on('click', function(event) { event.preventDefault(); var app_key = $(this).parent().find('[data-parameter="app_key"]').val(); var app_secret = $(this).parent().find('[data-parameter="app_secret"]').val(); diff --git a/apps/files_external/js/google.js b/apps/files_external/js/google.js index 0b3c314eb5..9b7f9514f1 100644 --- a/apps/files_external/js/google.js +++ b/apps/files_external/js/google.js @@ -1,6 +1,6 @@ $(document).ready(function() { - $('#externalStorage tbody tr.OC_Filestorage_Google').each(function() { + $('#externalStorage tbody tr.\\\\OC\\\\Files\\\\Storage\\\\Google').each(function() { var configured = $(this).find('[data-parameter="configured"]'); if ($(configured).val() == 'true') { $(this).find('.configuration') @@ -33,8 +33,8 @@ $(document).ready(function() { } }); - $('#externalStorage tbody tr').live('change', function() { - if ($(this).hasClass('OC_Filestorage_Google') && $(this).find('[data-parameter="configured"]').val() != 'true') { + $('#externalStorage tbody').on('change', 'tr', function() { + if ($(this).hasClass('\\\\OC\\\\Files\\\\Storage\\\\Google') && $(this).find('[data-parameter="configured"]').val() != 'true') { if ($(this).find('.mountPoint input').val() != '') { if ($(this).find('.google').length == 0) { $(this).find('.configuration').append('
'+t('files_external', 'Grant access')+''); @@ -43,9 +43,9 @@ $(document).ready(function() { } }); - $('#externalStorage tbody tr .mountPoint input').live('keyup', function() { + $('#externalStorage tbody').on('keyup', 'tr .mountPoint input', function() { var tr = $(this).parent().parent(); - if ($(tr).hasClass('OC_Filestorage_Google') && $(tr).find('[data-parameter="configured"]').val() != 'true' && $(tr).find('.google').length > 0) { + if ($(tr).hasClass('\\\\OC\\\\Files\\\\Storage\\\\Google') && $(tr).find('[data-parameter="configured"]').val() != 'true' && $(tr).find('.google').length > 0) { if ($(this).val() != '') { $(tr).find('.google').show(); } else { @@ -54,7 +54,7 @@ $(document).ready(function() { } }); - $('.google').live('click', function(event) { + $('.google').on('click', function(event) { event.preventDefault(); var tr = $(this).parent().parent(); var configured = $(this).parent().find('[data-parameter="configured"]'); diff --git a/apps/files_external/js/settings.js b/apps/files_external/js/settings.js index 0dc983ca8a..172ef097fb 100644 --- a/apps/files_external/js/settings.js +++ b/apps/files_external/js/settings.js @@ -71,7 +71,7 @@ OC.MountConfig={ $(document).ready(function() { $('.chzn-select').chosen(); - $('#selectBackend').live('change', function() { + $('#selectBackend').on('change', function() { var tr = $(this).parent().parent(); $('#externalStorage tbody').append($(tr).clone()); $('#externalStorage tbody tr').last().find('.mountPoint input').val(''); @@ -100,7 +100,7 @@ $(document).ready(function() { td.append(''); } }); - if (parameters['custom'] && $('#externalStorage tbody tr.'+backendClass).length == 1) { + if (parameters['custom'] && $('#externalStorage tbody tr.'+backendClass.replace(/\\/g, '\\\\')).length == 1) { OC.addScript('files_external', parameters['custom']); } return false; @@ -135,11 +135,11 @@ $(document).ready(function() { return defaultMountPoint+append; } - $('#externalStorage td').live('change', function() { + $('#externalStorage').on('change', 'td', function() { OC.MountConfig.saveStorage($(this).parent()); }); - $('td.remove>img').live('click', function() { + $('td.remove>img').on('click', function() { var tr = $(this).parent().parent(); var mountPoint = $(tr).find('.mountPoint input').val(); if ( ! mountPoint) { diff --git a/apps/files_external/l10n/fa.php b/apps/files_external/l10n/fa.php index b866201361..5acf3eac5a 100644 --- a/apps/files_external/l10n/fa.php +++ b/apps/files_external/l10n/fa.php @@ -1,5 +1,10 @@ "حافظه خارجی", +"Configuration" => "پیکربندی", +"Options" => "تنظیمات", +"Applicable" => "قابل اجرا", "Groups" => "گروه ها", "Users" => "کاربران", -"Delete" => "حذف" +"Delete" => "حذف", +"Enable User External Storage" => "فعال سازی حافظه خارجی کاربر" ); diff --git a/apps/files_external/l10n/fi_FI.php b/apps/files_external/l10n/fi_FI.php index d7b16e0d3e..8c7381db71 100644 --- a/apps/files_external/l10n/fi_FI.php +++ b/apps/files_external/l10n/fi_FI.php @@ -4,6 +4,8 @@ "Grant access" => "Salli pääsy", "Fill out all required fields" => "Täytä kaikki vaaditut kentät", "Error configuring Google Drive storage" => "Virhe Google Drive levyn asetuksia tehtäessä", +"Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Varoitus: \"smbclient\" ei ole asennettuna. CIFS-/SMB-jakojen liittäminen ei ole mahdollista. Pyydä järjestelmän ylläpitäjää asentamaan smbclient.", +"Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "Varoitus: PHP:n FTP-tuki ei ole käytössä tai sitä ei ole asennettu. FTP-jakojen liittäminen ei ole mahdollista. Pyydä järjestelmän ylläpitäjää ottamaan FTP-tuki käyttöön.", "External Storage" => "Erillinen tallennusväline", "Mount point" => "Liitospiste", "Backend" => "Taustaosa", diff --git a/apps/files_external/l10n/ko.php b/apps/files_external/l10n/ko.php index cb691cf5e3..47b75f74b8 100644 --- a/apps/files_external/l10n/ko.php +++ b/apps/files_external/l10n/ko.php @@ -5,8 +5,8 @@ "Fill out all required fields" => "모든 필수 항목을 입력하십시오", "Please provide a valid Dropbox app key and secret." => "올바른 Dropbox 앱 키와 암호를 입력하십시오.", "Error configuring Google Drive storage" => "Google 드라이브 저장소 설정 오류", -"Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "경고\"smbclient\"가 설치되지 않았습니다. CIFS/SMB 공유애 연결이 불가능 합니다.. 시스템 관리자에게 요청하여 설치하시기 바랍니다.", -"Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "경고PHP용 FTP 지원이 사용 불가능 하거나 설치되지 않았습니다. FTP 공유에 연결이 불가능 합니다. 시스템 관리자에게 요청하여 설치하시기 바랍니다. ", +"Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "경고: \"smbclient\"가 설치되지 않았습니다. CIFS/SMB 공유 자원에 연결할 수 없습니다. 시스템 관리자에게 설치를 요청하십시오.", +"Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "경고: PHP FTP 지원이 비활성화되어 있거나 설치되지 않았습니다. FTP 공유를 마운트할 수 없습니다. 시스템 관리자에게 설치를 요청하십시오.", "External Storage" => "외부 저장소", "Mount point" => "마운트 지점", "Backend" => "백엔드", diff --git a/apps/files_external/l10n/pt_BR.php b/apps/files_external/l10n/pt_BR.php index 26e927a423..8539395488 100644 --- a/apps/files_external/l10n/pt_BR.php +++ b/apps/files_external/l10n/pt_BR.php @@ -5,6 +5,8 @@ "Fill out all required fields" => "Preencha todos os campos obrigatórios", "Please provide a valid Dropbox app key and secret." => "Por favor forneça um app key e secret válido do Dropbox", "Error configuring Google Drive storage" => "Erro ao configurar armazenamento do Google Drive", +"Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Aviso: \"smbclient\" não está instalado. Não será possível montar compartilhamentos de CIFS/SMB. Por favor, peça ao seu administrador do sistema para instalá-lo.", +"Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "Aviso: O suporte para FTP do PHP não está ativado ou instalado. Não será possível montar compartilhamentos FTP. Por favor, peça ao seu administrador do sistema para instalá-lo.", "External Storage" => "Armazenamento Externo", "Mount point" => "Ponto de montagem", "Backend" => "Backend", diff --git a/apps/files_external/l10n/ro.php b/apps/files_external/l10n/ro.php index 6a15278680..ca2c9f7e5c 100644 --- a/apps/files_external/l10n/ro.php +++ b/apps/files_external/l10n/ro.php @@ -1,4 +1,12 @@ "Acces permis", +"Error configuring Dropbox storage" => "Eroare la configurarea mediului de stocare Dropbox", +"Grant access" => "Permite accesul", +"Fill out all required fields" => "Completează toate câmpurile necesare", +"Please provide a valid Dropbox app key and secret." => "Prezintă te rog o cheie de Dropbox validă și parola", +"Error configuring Google Drive storage" => "Eroare la configurarea mediului de stocare Google Drive", +"Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Atenție: \"smbclient\" nu este instalat. Montarea mediilor CIFS/SMB partajate nu este posibilă. Solicită administratorului sistemului tău să îl instaleaze.", +"Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "Atenție: suportul pentru FTP în PHP nu este activat sau instalat. Montarea mediilor FPT partajate nu este posibilă. Solicită administratorului sistemului tău să îl instaleze.", "External Storage" => "Stocare externă", "Mount point" => "Punctul de montare", "Backend" => "Backend", diff --git a/apps/files_external/l10n/sk_SK.php b/apps/files_external/l10n/sk_SK.php index 04d5e3c7ee..0b6878a542 100644 --- a/apps/files_external/l10n/sk_SK.php +++ b/apps/files_external/l10n/sk_SK.php @@ -5,6 +5,8 @@ "Fill out all required fields" => "Vyplňte všetky vyžadované kolónky", "Please provide a valid Dropbox app key and secret." => "Zadajte platný kľúč aplikácie a heslo Dropbox", "Error configuring Google Drive storage" => "Chyba pri konfigurácii úložiska Google drive", +"Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Upozornenie: \"smbclient\" nie je nainštalovaný. Nie je možné pripojenie oddielov CIFS/SMB. Požiadajte administrátora systému, nech ho nainštaluje.", +"Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "Upozornenie: Podpora FTP v PHP nie je povolená alebo nainštalovaná. Nie je možné pripojenie oddielov FTP. Požiadajte administrátora systému, nech ho nainštaluje.", "External Storage" => "Externé úložisko", "Mount point" => "Prípojný bod", "Backend" => "Backend", diff --git a/apps/files_external/l10n/th_TH.php b/apps/files_external/l10n/th_TH.php index 70ab8d3348..870995c8e7 100644 --- a/apps/files_external/l10n/th_TH.php +++ b/apps/files_external/l10n/th_TH.php @@ -5,6 +5,8 @@ "Fill out all required fields" => "กรอกข้อมูลในช่องข้อมูลที่จำเป็นต้องกรอกทั้งหมด", "Please provide a valid Dropbox app key and secret." => "กรุณากรอกรหัส app key ของ Dropbox และรหัสลับ", "Error configuring Google Drive storage" => "เกิดข้อผิดพลาดในการกำหนดค่าการจัดเก็บข้อมูลในพื้นที่ของ Google Drive", +"Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "คำเตือน: \"smbclient\" ยังไม่ได้ถูกติดตั้ง. การชี้ CIFS/SMB เพื่อแชร์ข้อมูลไม่สามารถกระทำได้ กรุณาสอบถามข้อมูลเพิ่มเติมจากผู้ดูแลระบบเพื่อติดตั้ง.", +"Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "คำเตือน: การสนับสนุนการใช้งาน FTP ในภาษา PHP ยังไม่ได้ถูกเปิดใช้งานหรือถูกติดตั้ง. การชี้ FTP เพื่อแชร์ข้อมูลไม่สามารถดำเนินการได้ กรุณาสอบถามข้อมูลเพิ่มเติมจากผู้ดูแลระบบเพื่อติดตั้ง", "External Storage" => "พื้นทีจัดเก็บข้อมูลจากภายนอก", "Mount point" => "จุดชี้ตำแหน่ง", "Backend" => "ด้านหลังระบบ", diff --git a/apps/files_external/lib/amazons3.php b/apps/files_external/lib/amazons3.php index e5ef4eb097..494885a1dd 100644 --- a/apps/files_external/lib/amazons3.php +++ b/apps/files_external/lib/amazons3.php @@ -1,39 +1,43 @@ . -*/ + * ownCloud + * + * @author Michael Gapczynski + * @copyright 2012 Michael Gapczynski mtgap@owncloud.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see . + */ + +namespace OC\Files\Storage; require_once 'aws-sdk/sdk.class.php'; -class OC_Filestorage_AmazonS3 extends OC_Filestorage_Common { +class AmazonS3 extends \OC\Files\Storage\Common { private $s3; private $bucket; private $objects = array(); + private $id; private static $tempFiles = array(); // TODO options: storage class, encryption server side, encrypt before upload? public function __construct($params) { - $this->s3 = new AmazonS3(array('key' => $params['key'], 'secret' => $params['secret'])); + $this->id = 'amazon::' . $params['key'] . md5($params['secret']); + $this->s3 = new \AmazonS3(array('key' => $params['key'], 'secret' => $params['secret'])); $this->bucket = $params['bucket']; } @@ -47,7 +51,7 @@ class OC_Filestorage_AmazonS3 extends OC_Filestorage_Common { return $response; // This object could be a folder, a '/' must be at the end of the path } else if (substr($path, -1) != '/') { - $response = $this->s3->get_object_metadata($this->bucket, $path.'/'); + $response = $this->s3->get_object_metadata($this->bucket, $path . '/'); if ($response) { $this->objects[$path] = $response; return $response; @@ -57,6 +61,10 @@ class OC_Filestorage_AmazonS3 extends OC_Filestorage_Common { return false; } + public function getId() { + return $this->id; + } + public function mkdir($path) { // Folders in Amazon S3 are 0 byte objects with a '/' at the end of the name if (substr($path, -1) != '/') { @@ -96,8 +104,8 @@ class OC_Filestorage_AmazonS3 extends OC_Filestorage_Common { foreach ($response->body->CommonPrefixes as $object) { $files[] = basename($object->Prefix); } - OC_FakeDirStream::$dirs['amazons3'.$path] = $files; - return opendir('fakedir://amazons3'.$path); + \OC\Files\Stream\Dir::register('amazons3' . $path, $files); + return opendir('fakedir://amazons3' . $path); } return false; } @@ -107,15 +115,10 @@ class OC_Filestorage_AmazonS3 extends OC_Filestorage_Common { $stat['size'] = $this->s3->get_bucket_filesize($this->bucket); $stat['atime'] = time(); $stat['mtime'] = $stat['atime']; - $stat['ctime'] = $stat['atime']; - } else { - $object = $this->getObject($path); - if ($object) { - $stat['size'] = $object['Size']; - $stat['atime'] = time(); - $stat['mtime'] = strtotime($object['LastModified']); - $stat['ctime'] = $stat['mtime']; - } + } else if ($object = $this->getObject($path)) { + $stat['size'] = $object['Size']; + $stat['atime'] = time(); + $stat['mtime'] = strtotime($object['LastModified']); } if (isset($stat)) { return $stat; @@ -166,7 +169,7 @@ class OC_Filestorage_AmazonS3 extends OC_Filestorage_Common { switch ($mode) { case 'r': case 'rb': - $tmpFile = OC_Helper::tmpFile(); + $tmpFile = \OC_Helper::tmpFile(); $handle = fopen($tmpFile, 'w'); $response = $this->s3->get_object($this->bucket, $path, array('fileDownload' => $handle)); if ($response->isOK()) { @@ -190,14 +193,14 @@ class OC_Filestorage_AmazonS3 extends OC_Filestorage_Common { } else { $ext = ''; } - $tmpFile = OC_Helper::tmpFile($ext); - OC_CloseStreamWrapper::$callBacks[$tmpFile] = array($this, 'writeBack'); + $tmpFile = \OC_Helper::tmpFile($ext); + \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack')); if ($this->file_exists($path)) { $source = $this->fopen($path, 'r'); file_put_contents($tmpFile, $source); } self::$tempFiles[$tmpFile] = $path; - return fopen('close://'.$tmpFile, $mode); + return fopen('close://' . $tmpFile, $mode); } return false; } @@ -206,8 +209,8 @@ class OC_Filestorage_AmazonS3 extends OC_Filestorage_Common { if (isset(self::$tempFiles[$tmpFile])) { $handle = fopen($tmpFile, 'r'); $response = $this->s3->create_object($this->bucket, - self::$tempFiles[$tmpFile], - array('fileUpload' => $handle)); + self::$tempFiles[$tmpFile], + array('fileUpload' => $handle)); if ($response->isOK()) { unlink($tmpFile); } diff --git a/apps/files_external/lib/config.php b/apps/files_external/lib/config.php index fd3dc2ca0d..6b0df21461 100755 --- a/apps/files_external/lib/config.php +++ b/apps/files_external/lib/config.php @@ -38,20 +38,20 @@ class OC_Mount_Config { * @return array */ public static function getBackends() { - - $backends['OC_Filestorage_Local']=array( + + $backends['\OC\Files\Storage\Local']=array( 'backend' => 'Local', 'configuration' => array( 'datadir' => 'Location')); - $backends['OC_Filestorage_AmazonS3']=array( + $backends['\OC\Files\Storage\AmazonS3']=array( 'backend' => 'Amazon S3', 'configuration' => array( 'key' => 'Key', 'secret' => '*Secret', 'bucket' => 'Bucket')); - $backends['OC_Filestorage_Dropbox']=array( + $backends['\OC\Files\Storage\Dropbox']=array( 'backend' => 'Dropbox', 'configuration' => array( 'configured' => '#configured', @@ -61,7 +61,7 @@ class OC_Mount_Config { 'token_secret' => '#token_secret'), 'custom' => 'dropbox'); - if(OC_Mount_Config::checkphpftp()) $backends['OC_Filestorage_FTP']=array( + if(OC_Mount_Config::checkphpftp()) $backends['\OC\Files\Storage\FTP']=array( 'backend' => 'FTP', 'configuration' => array( 'host' => 'URL', @@ -70,15 +70,15 @@ class OC_Mount_Config { 'root' => '&Root', 'secure' => '!Secure ftps://')); - $backends['OC_Filestorage_Google']=array( + $backends['\OC\Files\Storage\Google']=array( 'backend' => 'Google Drive', 'configuration' => array( 'configured' => '#configured', 'token' => '#token', 'token_secret' => '#token secret'), 'custom' => 'google'); - - $backends['OC_Filestorage_SWIFT']=array( + + $backends['\OC\Files\Storage\SWIFT']=array( 'backend' => 'OpenStack Swift', 'configuration' => array( 'host' => 'URL', @@ -86,8 +86,8 @@ class OC_Mount_Config { 'token' => '*Token', 'root' => '&Root', 'secure' => '!Secure ftps://')); - - if(OC_Mount_Config::checksmbclient()) $backends['OC_Filestorage_SMB']=array( + + if(OC_Mount_Config::checksmbclient()) $backends['\OC\Files\Storage\SMB']=array( 'backend' => 'SMB / CIFS', 'configuration' => array( 'host' => 'URL', @@ -95,8 +95,8 @@ class OC_Mount_Config { 'password' => '*Password', 'share' => 'Share', 'root' => '&Root')); - - $backends['OC_Filestorage_DAV']=array( + + $backends['\OC\Files\Storage\DAV']=array( 'backend' => 'ownCloud / WebDAV', 'configuration' => array( 'host' => 'URL', @@ -120,6 +120,10 @@ class OC_Mount_Config { if (isset($mountPoints[self::MOUNT_TYPE_GROUP])) { foreach ($mountPoints[self::MOUNT_TYPE_GROUP] as $group => $mounts) { foreach ($mounts as $mountPoint => $mount) { + // Update old classes to new namespace + if (strpos($mount['class'], 'OC_Filestorage_') !== false) { + $mount['class'] = '\OC\Files\Storage\\'.substr($mount['class'], 15); + } // Remove '/$user/files/' from mount point $mountPoint = substr($mountPoint, 13); // Merge the mount point into the current mount points @@ -139,6 +143,10 @@ class OC_Mount_Config { if (isset($mountPoints[self::MOUNT_TYPE_USER])) { foreach ($mountPoints[self::MOUNT_TYPE_USER] as $user => $mounts) { foreach ($mounts as $mountPoint => $mount) { + // Update old classes to new namespace + if (strpos($mount['class'], 'OC_Filestorage_') !== false) { + $mount['class'] = '\OC\Files\Storage\\'.substr($mount['class'], 15); + } // Remove '/$user/files/' from mount point $mountPoint = substr($mountPoint, 13); // Merge the mount point into the current mount points @@ -169,6 +177,10 @@ class OC_Mount_Config { $personal = array(); if (isset($mountPoints[self::MOUNT_TYPE_USER][$uid])) { foreach ($mountPoints[self::MOUNT_TYPE_USER][$uid] as $mountPoint => $mount) { + // Update old classes to new namespace + if (strpos($mount['class'], 'OC_Filestorage_') !== false) { + $mount['class'] = '\OC\Files\Storage\\'.substr($mount['class'], 15); + } // Remove '/uid/files/' from mount point $personal[substr($mountPoint, strlen($uid) + 8)] = array('class' => $mount['class'], 'backend' => $backends[$mount['class']]['backend'], @@ -178,22 +190,6 @@ class OC_Mount_Config { return $personal; } - /** - * Add directory for mount point to the filesystem - * @param OC_Fileview instance $view - * @param string path to mount point - */ - private static function addMountPointDirectory($view, $path) { - $dir = ''; - foreach ( explode('/', $path) as $pathPart) { - $dir = $dir.'/'.$pathPart; - if ( !$view->file_exists($dir)) { - $view->mkdir($dir); - } - } - } - - /** * Add a mount point to the filesystem * @param string Mount point @@ -213,36 +209,11 @@ class OC_Mount_Config { if ($isPersonal) { // Verify that the mount point applies for the current user // Prevent non-admin users from mounting local storage - if ($applicable != OCP\User::getUser() || $class == 'OC_Filestorage_Local') { + if ($applicable != OCP\User::getUser() || $class == '\OC\Files\Storage\Local') { return false; } - $view = new OC_FilesystemView('/'.OCP\User::getUser().'/files'); - self::addMountPointDirectory($view, ltrim($mountPoint, '/')); $mountPoint = '/'.$applicable.'/files/'.ltrim($mountPoint, '/'); } else { - $view = new OC_FilesystemView('/'); - switch ($mountType) { - case 'user': - if ($applicable == "all") { - $users = OCP\User::getUsers(); - foreach ( $users as $user ) { - $path = $user.'/files/'.ltrim($mountPoint, '/'); - self::addMountPointDirectory($view, $path); - } - } else { - $path = $applicable.'/files/'.ltrim($mountPoint, '/'); - self::addMountPointDirectory($view, $path); - } - break; - case 'group' : - $groupMembers = OC_Group::usersInGroups(array($applicable)); - foreach ( $groupMembers as $user ) { - $path = $user.'/files/'.ltrim($mountPoint, '/'); - self::addMountPointDirectory($view, $path); - } - break; - } - $mountPoint = '/$user/files/'.ltrim($mountPoint, '/'); } $mount = array($applicable => array($mountPoint => array('class' => $class, 'options' => $classOptions))); diff --git a/apps/files_external/lib/dropbox.php b/apps/files_external/lib/dropbox.php index 33ca14cab1..11644e4a2c 100755 --- a/apps/files_external/lib/dropbox.php +++ b/apps/files_external/lib/dropbox.php @@ -20,12 +20,15 @@ * License along with this library. If not, see . */ +namespace OC\Files\Storage; + require_once 'Dropbox/autoload.php'; -class OC_Filestorage_Dropbox extends OC_Filestorage_Common { +class Dropbox extends \OC\Files\Storage\Common { private $dropbox; private $root; + private $id; private $metaData = array(); private static $tempFiles = array(); @@ -37,13 +40,14 @@ class OC_Filestorage_Dropbox extends OC_Filestorage_Common { && isset($params['token']) && isset($params['token_secret']) ) { + $this->id = 'dropbox::'.$params['app_key'] . $params['token']. '/' . $params['root']; $this->root=isset($params['root'])?$params['root']:''; - $oauth = new Dropbox_OAuth_Curl($params['app_key'], $params['app_secret']); + $oauth = new \Dropbox_OAuth_Curl($params['app_key'], $params['app_secret']); $oauth->setToken($params['token'], $params['token_secret']); - $this->dropbox = new Dropbox_API($oauth, 'dropbox'); + $this->dropbox = new \Dropbox_API($oauth, 'dropbox'); $this->mkdir(''); } else { - throw new Exception('Creating OC_Filestorage_Dropbox storage failed'); + throw new \Exception('Creating \OC\Files\Storage\Dropbox storage failed'); } } @@ -55,8 +59,8 @@ class OC_Filestorage_Dropbox extends OC_Filestorage_Common { if ($list) { try { $response = $this->dropbox->getMetaData($path); - } catch (Exception $exception) { - OCP\Util::writeLog('files_external', $exception->getMessage(), OCP\Util::ERROR); + } catch (\Exception $exception) { + \OCP\Util::writeLog('files_external', $exception->getMessage(), \OCP\Util::ERROR); return false; } if ($response && isset($response['contents'])) { @@ -76,21 +80,25 @@ class OC_Filestorage_Dropbox extends OC_Filestorage_Common { $response = $this->dropbox->getMetaData($path, 'false'); $this->metaData[$path] = $response; return $response; - } catch (Exception $exception) { - OCP\Util::writeLog('files_external', $exception->getMessage(), OCP\Util::ERROR); + } catch (\Exception $exception) { + \OCP\Util::writeLog('files_external', $exception->getMessage(), \OCP\Util::ERROR); return false; } } } } + public function getId(){ + return $this->id; + } + public function mkdir($path) { $path = $this->root.$path; try { $this->dropbox->createFolder($path); return true; - } catch (Exception $exception) { - OCP\Util::writeLog('files_external', $exception->getMessage(), OCP\Util::ERROR); + } catch (\Exception $exception) { + \OCP\Util::writeLog('files_external', $exception->getMessage(), \OCP\Util::ERROR); return false; } } @@ -106,7 +114,7 @@ class OC_Filestorage_Dropbox extends OC_Filestorage_Common { foreach ($contents as $file) { $files[] = basename($file['path']); } - OC_FakeDirStream::$dirs['dropbox'.$path] = $files; + \OC\Files\Stream\Dir::register('dropbox'.$path, $files); return opendir('fakedir://dropbox'.$path); } return false; @@ -118,7 +126,6 @@ class OC_Filestorage_Dropbox extends OC_Filestorage_Common { $stat['size'] = $metaData['bytes']; $stat['atime'] = time(); $stat['mtime'] = (isset($metaData['modified'])) ? strtotime($metaData['modified']) : time(); - $stat['ctime'] = $stat['mtime']; return $stat; } return false; @@ -163,8 +170,8 @@ class OC_Filestorage_Dropbox extends OC_Filestorage_Common { try { $this->dropbox->delete($path); return true; - } catch (Exception $exception) { - OCP\Util::writeLog('files_external', $exception->getMessage(), OCP\Util::ERROR); + } catch (\Exception $exception) { + \OCP\Util::writeLog('files_external', $exception->getMessage(), \OCP\Util::ERROR); return false; } } @@ -175,8 +182,8 @@ class OC_Filestorage_Dropbox extends OC_Filestorage_Common { try { $this->dropbox->move($path1, $path2); return true; - } catch (Exception $exception) { - OCP\Util::writeLog('files_external', $exception->getMessage(), OCP\Util::ERROR); + } catch (\Exception $exception) { + \OCP\Util::writeLog('files_external', $exception->getMessage(), \OCP\Util::ERROR); return false; } } @@ -187,8 +194,8 @@ class OC_Filestorage_Dropbox extends OC_Filestorage_Common { try { $this->dropbox->copy($path1, $path2); return true; - } catch (Exception $exception) { - OCP\Util::writeLog('files_external', $exception->getMessage(), OCP\Util::ERROR); + } catch (\Exception $exception) { + \OCP\Util::writeLog('files_external', $exception->getMessage(), \OCP\Util::ERROR); return false; } } @@ -198,13 +205,13 @@ class OC_Filestorage_Dropbox extends OC_Filestorage_Common { switch ($mode) { case 'r': case 'rb': - $tmpFile = OC_Helper::tmpFile(); + $tmpFile = \OC_Helper::tmpFile(); try { $data = $this->dropbox->getFile($path); file_put_contents($tmpFile, $data); return fopen($tmpFile, 'r'); - } catch (Exception $exception) { - OCP\Util::writeLog('files_external', $exception->getMessage(), OCP\Util::ERROR); + } catch (\Exception $exception) { + \OCP\Util::writeLog('files_external', $exception->getMessage(), \OCP\Util::ERROR); return false; } case 'w': @@ -224,8 +231,8 @@ class OC_Filestorage_Dropbox extends OC_Filestorage_Common { } else { $ext = ''; } - $tmpFile = OC_Helper::tmpFile($ext); - OC_CloseStreamWrapper::$callBacks[$tmpFile] = array($this, 'writeBack'); + $tmpFile = \OC_Helper::tmpFile($ext); + \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack')); if ($this->file_exists($path)) { $source = $this->fopen($path, 'r'); file_put_contents($tmpFile, $source); @@ -242,8 +249,8 @@ class OC_Filestorage_Dropbox extends OC_Filestorage_Common { try { $this->dropbox->putFile(self::$tempFiles[$tmpFile], $handle); unlink($tmpFile); - } catch (Exception $exception) { - OCP\Util::writeLog('files_external', $exception->getMessage(), OCP\Util::ERROR); + } catch (\Exception $exception) { + \OCP\Util::writeLog('files_external', $exception->getMessage(), \OCP\Util::ERROR); } } } @@ -264,8 +271,8 @@ class OC_Filestorage_Dropbox extends OC_Filestorage_Common { try { $info = $this->dropbox->getAccountInfo(); return $info['quota_info']['quota'] - $info['quota_info']['normal']; - } catch (Exception $exception) { - OCP\Util::writeLog('files_external', $exception->getMessage(), OCP\Util::ERROR); + } catch (\Exception $exception) { + \OCP\Util::writeLog('files_external', $exception->getMessage(), \OCP\Util::ERROR); return false; } } diff --git a/apps/files_external/lib/ftp.php b/apps/files_external/lib/ftp.php index e796ae446b..9a27b63323 100644 --- a/apps/files_external/lib/ftp.php +++ b/apps/files_external/lib/ftp.php @@ -6,7 +6,9 @@ * See the COPYING-README file. */ -class OC_FileStorage_FTP extends OC_FileStorage_StreamWrapper{ +namespace OC\Files\Storage; + +class FTP extends \OC\Files\Storage\StreamWrapper{ private $password; private $user; private $host; @@ -38,9 +40,13 @@ class OC_FileStorage_FTP extends OC_FileStorage_StreamWrapper{ } } + public function getId(){ + return 'ftp::' . $this->user . '@' . $this->host . '/' . $this->root; + } + /** * construct the ftp url - * @param string path + * @param string $path * @return string */ public function constructUrl($path) { @@ -51,7 +57,8 @@ class OC_FileStorage_FTP extends OC_FileStorage_StreamWrapper{ $url.='://'.$this->user.':'.$this->password.'@'.$this->host.$this->root.$path; return $url; } - public function fopen($path, $mode) { + public function fopen($path,$mode) { + $this->init(); switch($mode) { case 'r': case 'rb': @@ -61,7 +68,7 @@ class OC_FileStorage_FTP extends OC_FileStorage_StreamWrapper{ case 'ab': //these are supported by the wrapper $context = stream_context_create(array('ftp' => array('overwrite' => true))); - return fopen($this->constructUrl($path), $mode, false, $context); + return fopen($this->constructUrl($path),$mode, false,$context); case 'r+': case 'w+': case 'wb+': @@ -77,16 +84,18 @@ class OC_FileStorage_FTP extends OC_FileStorage_StreamWrapper{ $ext=''; } $tmpFile=OCP\Files::tmpFile($ext); - OC_CloseStreamWrapper::$callBacks[$tmpFile]=array($this, 'writeBack'); + \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack')); if ($this->file_exists($path)) { $this->getFile($path, $tmpFile); } self::$tempFiles[$tmpFile]=$path; - return fopen('close://'.$tmpFile, $mode); + return fopen('close://'.$tmpFile,$mode); } + return false; } public function writeBack($tmpFile) { + $this->init(); if (isset(self::$tempFiles[$tmpFile])) { $this->uploadFile($tmpFile, self::$tempFiles[$tmpFile]); unlink($tmpFile); diff --git a/apps/files_external/lib/google.php b/apps/files_external/lib/google.php index c836a5a07c..7396c7e3f2 100644 --- a/apps/files_external/lib/google.php +++ b/apps/files_external/lib/google.php @@ -20,14 +20,17 @@ * License along with this library. If not, see . */ +namespace OC\Files\Storage; + require_once 'Google/common.inc.php'; -class OC_Filestorage_Google extends OC_Filestorage_Common { +class Google extends \OC\Files\Storage\Common { private $consumer; private $oauth_token; private $sig_method; private $entries; + private $id; private static $tempFiles = array(); @@ -38,12 +41,13 @@ class OC_Filestorage_Google extends OC_Filestorage_Common { ) { $consumer_key = isset($params['consumer_key']) ? $params['consumer_key'] : 'anonymous'; $consumer_secret = isset($params['consumer_secret']) ? $params['consumer_secret'] : 'anonymous'; - $this->consumer = new OAuthConsumer($consumer_key, $consumer_secret); - $this->oauth_token = new OAuthToken($params['token'], $params['token_secret']); - $this->sig_method = new OAuthSignatureMethod_HMAC_SHA1(); + $this->id = 'google::' . $params['token']; + $this->consumer = new \OAuthConsumer($consumer_key, $consumer_secret); + $this->oauth_token = new \OAuthToken($params['token'], $params['token_secret']); + $this->sig_method = new \OAuthSignatureMethod_HMAC_SHA1(); $this->entries = array(); } else { - throw new Exception('Creating OC_Filestorage_Google storage failed'); + throw new \Exception('Creating \OC\Files\Storage\Google storage failed'); } } @@ -68,7 +72,7 @@ class OC_Filestorage_Google extends OC_Filestorage_Common { $tempStr .= '&' . urlencode($key) . '=' . urlencode($value); } $uri = preg_replace('/&/', '?', $tempStr, 1); - $request = OAuthRequest::from_consumer_and_token($this->consumer, + $request = \OAuthRequest::from_consumer_and_token($this->consumer, $this->oauth_token, $httpMethod, $uri, @@ -110,7 +114,7 @@ class OC_Filestorage_Google extends OC_Filestorage_Common { curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); } if ($isDownload) { - $tmpFile = OC_Helper::tmpFile(); + $tmpFile = \OC_Helper::tmpFile(); $handle = fopen($tmpFile, 'w'); curl_setopt($curl, CURLOPT_FILE, $handle); } @@ -139,7 +143,7 @@ class OC_Filestorage_Google extends OC_Filestorage_Common { private function getFeed($feedUri, $httpMethod, $postData = null) { $result = $this->sendRequest($feedUri, $httpMethod, $postData); if ($result) { - $dom = new DOMDocument(); + $dom = new \DOMDocument(); $dom->loadXML($result); return $dom; } @@ -194,6 +198,9 @@ class OC_Filestorage_Google extends OC_Filestorage_Common { } } + public function getId(){ + return $this->id; + } public function mkdir($path) { $collection = dirname($path); @@ -266,7 +273,7 @@ class OC_Filestorage_Google extends OC_Filestorage_Common { $this->entries[$name] = $entry; } } - OC_FakeDirStream::$dirs['google'.$path] = $files; + \OC\Files\Stream\Dir::register('google'.$path, $files); return opendir('fakedir://google'.$path); } @@ -287,7 +294,6 @@ class OC_Filestorage_Google extends OC_Filestorage_Common { //$stat['atime'] = strtotime($entry->getElementsByTagNameNS('http://schemas.google.com/g/2005', // 'lastViewed')->item(0)->nodeValue); $stat['mtime'] = strtotime($entry->getElementsByTagName('updated')->item(0)->nodeValue); - $stat['ctime'] = strtotime($entry->getElementsByTagName('published')->item(0)->nodeValue); } } if (isset($stat)) { @@ -443,8 +449,8 @@ class OC_Filestorage_Google extends OC_Filestorage_Common { } else { $ext = ''; } - $tmpFile = OC_Helper::tmpFile($ext); - OC_CloseStreamWrapper::$callBacks[$tmpFile] = array($this, 'writeBack'); + $tmpFile = \OC_Helper::tmpFile($ext); + \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack')); if ($this->file_exists($path)) { $source = $this->fopen($path, 'r'); file_put_contents($tmpFile, $source); @@ -482,7 +488,7 @@ class OC_Filestorage_Google extends OC_Filestorage_Common { } if (isset($uploadUri) && $handle = fopen($path, 'r')) { $uploadUri .= '?convert=false'; - $mimetype = OC_Helper::getMimeType($path); + $mimetype = \OC_Helper::getMimeType($path); $size = filesize($path); $headers = array('X-Upload-Content-Type: ' => $mimetype, 'X-Upload-Content-Length: ' => $size); $postData = ''; @@ -590,4 +596,4 @@ class OC_Filestorage_Google extends OC_Filestorage_Common { } -} \ No newline at end of file +} diff --git a/apps/files_external/lib/smb.php b/apps/files_external/lib/smb.php index 071a9cd5f9..96778b0b2e 100644 --- a/apps/files_external/lib/smb.php +++ b/apps/files_external/lib/smb.php @@ -6,9 +6,11 @@ * See the COPYING-README file. */ +namespace OC\Files\Storage; + require_once 'smb4php/smb.php'; -class OC_FileStorage_SMB extends OC_FileStorage_StreamWrapper{ +class SMB extends \OC\Files\Storage\StreamWrapper{ private $password; private $user; private $host; @@ -30,14 +32,13 @@ class OC_FileStorage_SMB extends OC_FileStorage_StreamWrapper{ if ( ! $this->share || $this->share[0]!='/') { $this->share='/'.$this->share; } - if (substr($this->share, -1, 1)=='/') { - $this->share=substr($this->share, 0, -1); + if(substr($this->share, -1, 1)=='/') { + $this->share = substr($this->share,0,-1); } + } - //create the root folder if necesary - if ( ! $this->is_dir('')) { - $this->mkdir(''); - } + public function getId(){ + return 'smb::' . $this->user . '@' . $this->host . '/' . $this->share . '/' . $this->root; } public function constructUrl($path) { @@ -65,11 +66,13 @@ class OC_FileStorage_SMB extends OC_FileStorage_StreamWrapper{ /** * check if a file or folder has been updated since $time + * @param string $path * @param int $time * @return bool */ - public function hasUpdated($path, $time) { - if ( ! $path and $this->root=='/') { + public function hasUpdated($path,$time) { + $this->init(); + if(!$path and $this->root=='/') { // mtime doesn't work for shares, but giving the nature of the backend, // doing a full update is still just fast enough return true; diff --git a/apps/files_external/lib/streamwrapper.php b/apps/files_external/lib/streamwrapper.php index a386e33399..7c3ddcf8a2 100644 --- a/apps/files_external/lib/streamwrapper.php +++ b/apps/files_external/lib/streamwrapper.php @@ -6,16 +6,33 @@ * See the COPYING-README file. */ +namespace OC\Files\Storage; + +abstract class StreamWrapper extends \OC\Files\Storage\Common{ + private $ready = false; + + protected function init(){ + if($this->ready){ + return; + } + $this->ready = true; + + //create the root folder if necesary + if(!$this->is_dir('')) { + $this->mkdir(''); + } + } -abstract class OC_FileStorage_StreamWrapper extends OC_Filestorage_Common{ abstract public function constructUrl($path); public function mkdir($path) { + $this->init(); return mkdir($this->constructUrl($path)); } public function rmdir($path) { - if ($this->file_exists($path)) { + $this->init(); + if($this->file_exists($path)) { $succes = rmdir($this->constructUrl($path)); clearstatcache(); return $succes; @@ -25,10 +42,12 @@ abstract class OC_FileStorage_StreamWrapper extends OC_Filestorage_Common{ } public function opendir($path) { + $this->init(); return opendir($this->constructUrl($path)); } public function filetype($path) { + $this->init(); return filetype($this->constructUrl($path)); } @@ -41,46 +60,54 @@ abstract class OC_FileStorage_StreamWrapper extends OC_Filestorage_Common{ } public function file_exists($path) { + $this->init(); return file_exists($this->constructUrl($path)); } public function unlink($path) { + $this->init(); $succes = unlink($this->constructUrl($path)); clearstatcache(); return $succes; } - public function fopen($path, $mode) { - return fopen($this->constructUrl($path), $mode); + public function fopen($path,$mode) { + $this->init(); + return fopen($this->constructUrl($path),$mode); } public function free_space($path) { return 0; } - public function touch($path, $mtime = null) { - if (is_null($mtime)) { - $fh = $this->fopen($path, 'a'); - fwrite($fh, ''); + public function touch($path,$mtime=null) { + $this->init(); + if(is_null($mtime)) { + $fh = $this->fopen($path,'a'); + fwrite($fh,''); fclose($fh); } else { return false;//not supported } } - public function getFile($path, $target) { - return copy($this->constructUrl($path), $target); + public function getFile($path,$target) { + $this->init(); + return copy($this->constructUrl($path),$target); } - public function uploadFile($path, $target) { - return copy($path, $this->constructUrl($target)); + public function uploadFile($path,$target) { + $this->init(); + return copy($path,$this->constructUrl($target)); } - public function rename($path1, $path2) { - return rename($this->constructUrl($path1), $this->constructUrl($path2)); + public function rename($path1,$path2) { + $this->init(); + return rename($this->constructUrl($path1),$this->constructUrl($path2)); } public function stat($path) { + $this->init(); return stat($this->constructUrl($path)); } diff --git a/apps/files_external/lib/swift.php b/apps/files_external/lib/swift.php index a071dfdbb0..cbf2007052 100644 --- a/apps/files_external/lib/swift.php +++ b/apps/files_external/lib/swift.php @@ -6,24 +6,28 @@ * See the COPYING-README file. */ +namespace OC\Files\Storage; + require_once 'php-cloudfiles/cloudfiles.php'; -class OC_FileStorage_SWIFT extends OC_Filestorage_Common{ +class SWIFT extends \OC\Files\Storage\Common{ + private $id; private $host; private $root; private $user; private $token; private $secure; + private $ready = false; /** - * @var CF_Authentication auth + * @var \CF_Authentication auth */ private $auth; /** - * @var CF_Connection conn + * @var \CF_Connection conn */ private $conn; /** - * @var CF_Container rootContainer + * @var \CF_Container rootContainer */ private $rootContainer; @@ -35,18 +39,18 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{ /** * translate directory path to container name - * @param string path + * @param string $path * @return string */ private function getContainerName($path) { - $path=trim(trim($this->root, '/')."/".$path, '/.'); + $path=trim(trim($this->root, '/') . "/".$path, '/.'); return str_replace('/', '\\', $path); } /** * get container by path - * @param string path - * @return CF_Container + * @param string $path + * @return \CF_Container */ private function getContainer($path) { if ($path=='' or $path=='/') { @@ -59,15 +63,15 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{ $container=$this->conn->get_container($this->getContainerName($path)); $this->containers[$path]=$container; return $container; - } catch(NoSuchContainerException $e) { + } catch(\NoSuchContainerException $e) { return null; } } /** * create container - * @param string path - * @return CF_Container + * @param string $path + * @return \CF_Container */ private function createContainer($path) { if ($path=='' or $path=='/' or $path=='.') { @@ -89,8 +93,8 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{ /** * get object by path - * @param string path - * @return CF_Object + * @param string $path + * @return \CF_Object */ private function getObject($path) { if (isset($this->objects[$path])) { @@ -107,7 +111,7 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{ $obj=$container->get_object(basename($path)); $this->objects[$path]=$obj; return $obj; - } catch(NoSuchObjectException $e) { + } catch(\NoSuchObjectException $e) { return null; } } @@ -132,8 +136,8 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{ /** * create object - * @param string path - * @return CF_Object + * @param string $path + * @return \CF_Object */ private function createObject($path) { $container=$this->getContainer(dirname($path)); @@ -154,7 +158,7 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{ /** * check if container for path exists - * @param string path + * @param string $path * @return bool */ private function containerExists($path) { @@ -163,15 +167,15 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{ /** * get the list of emulated sub containers - * @param CF_Container container + * @param \CF_Container $container * @return array */ private function getSubContainers($container) { - $tmpFile=OCP\Files::tmpFile(); + $tmpFile=\OCP\Files::tmpFile(); $obj=$this->getSubContainerFile($container); try { $obj->save_to_filename($tmpFile); - } catch(Exception $e) { + } catch(\Exception $e) { return array(); } $obj->save_to_filename($tmpFile); @@ -185,15 +189,15 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{ /** * add an emulated sub container - * @param CF_Container container - * @param string name + * @param \CF_Container $container + * @param string $name * @return bool */ private function addSubContainer($container, $name) { if ( ! $name) { return false; } - $tmpFile=OCP\Files::tmpFile(); + $tmpFile=\OCP\Files::tmpFile(); $obj=$this->getSubContainerFile($container); try { $obj->save_to_filename($tmpFile); @@ -201,16 +205,15 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{ foreach ($containers as &$sub) { $sub=trim($sub); } - if (array_search($name, $containers)!==false) { + if(array_search($name, $containers) !== false) { unlink($tmpFile); return false; } else { $fh=fopen($tmpFile, 'a'); - fwrite($fh, $name."\n"); + fwrite($fh,$name . "\n"); } - } catch(Exception $e) { - $containers=array(); - file_put_contents($tmpFile, $name."\n"); + } catch(\Exception $e) { + file_put_contents($tmpFile, $name . "\n"); } $obj->load_from_filename($tmpFile); @@ -220,20 +223,20 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{ /** * remove an emulated sub container - * @param CF_Container container - * @param string name + * @param \CF_Container $container + * @param string $name * @return bool */ private function removeSubContainer($container, $name) { if ( ! $name) { return false; } - $tmpFile=OCP\Files::tmpFile(); + $tmpFile=\OCP\Files::tmpFile(); $obj=$this->getSubContainerFile($container); try { $obj->save_to_filename($tmpFile); $containers=file($tmpFile); - } catch (Exception $e) { + } catch (\Exception $e) { return false; } foreach ($containers as &$sub) { @@ -255,8 +258,8 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{ /** * ensure a subcontainer file exists and return it's object - * @param CF_Container container - * @return CF_Object + * @param \CF_Container $container + * @return \CF_Object */ private function getSubContainerFile($container) { try { @@ -283,10 +286,19 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{ if ( ! $this->root || $this->root[0]!='/') { $this->root='/'.$this->root; } - $this->auth = new CF_Authentication($this->user, $this->token, null, $this->host); + + } + + private function init(){ + if($this->ready){ + return; + } + $this->ready = true; + + $this->auth = new \CF_Authentication($this->user, $this->token, null, $this->host); $this->auth->authenticate(); - $this->conn = new CF_Connection($this->auth); + $this->conn = new \CF_Connection($this->auth); if ( ! $this->containerExists('/')) { $this->rootContainer=$this->createContainer('/'); @@ -295,8 +307,13 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{ } } + public function getId(){ + return $this->id; + } + public function mkdir($path) { + $this->init(); if ($this->containerExists($path)) { return false; } else { @@ -306,7 +323,8 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{ } public function rmdir($path) { - if ( ! $this->containerExists($path)) { + $this->init(); + if (!$this->containerExists($path)) { return false; } else { $this->emptyContainer($path); @@ -343,6 +361,7 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{ } public function opendir($path) { + $this->init(); $container=$this->getContainer($path); $files=$this->getObjects($container); $i=array_search(self::SUBCONTAINER_FILE, $files); @@ -352,11 +371,12 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{ $subContainers=$this->getSubContainers($container); $files=array_merge($files, $subContainers); $id=$this->getContainerName($path); - OC_FakeDirStream::$dirs[$id]=$files; + \OC\Files\Stream\Dir::register($id, $files); return opendir('fakedir://'.$id); } public function filetype($path) { + $this->init(); if ($this->containerExists($path)) { return 'dir'; } else { @@ -373,6 +393,7 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{ } public function file_exists($path) { + $this->init(); if ($this->is_dir($path)) { return true; } else { @@ -381,6 +402,7 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{ } public function file_get_contents($path) { + $this->init(); $obj=$this->getObject($path); if (is_null($obj)) { return false; @@ -389,6 +411,7 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{ } public function file_put_contents($path, $content) { + $this->init(); $obj=$this->getObject($path); if (is_null($obj)) { $container=$this->getContainer(dirname($path)); @@ -402,6 +425,7 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{ } public function unlink($path) { + $this->init(); if ($this->containerExists($path)) { return $this->rmdir($path); } @@ -415,6 +439,7 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{ } public function fopen($path, $mode) { + $this->init(); switch($mode) { case 'r': case 'rb': @@ -440,7 +465,7 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{ case 'c': case 'c+': $tmpFile=$this->getTmpFile($path); - OC_CloseStreamWrapper::$callBacks[$tmpFile]=array($this, 'writeBack'); + \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack')); self::$tempFiles[$tmpFile]=$path; return fopen('close://'.$tmpFile, $mode); } @@ -458,6 +483,7 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{ } public function touch($path, $mtime=null) { + $this->init(); $obj=$this->getObject($path); if (is_null($obj)) { return false; @@ -472,6 +498,7 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{ } public function rename($path1, $path2) { + $this->init(); $sourceContainer=$this->getContainer(dirname($path1)); $targetContainer=$this->getContainer(dirname($path2)); $result=$sourceContainer->move_object_to(basename($path1), $targetContainer, basename($path2)); @@ -484,6 +511,7 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{ } public function copy($path1, $path2) { + $this->init(); $sourceContainer=$this->getContainer(dirname($path1)); $targetContainer=$this->getContainer(dirname($path2)); $result=$sourceContainer->copy_object_to(basename($path1), $targetContainer, basename($path2)); @@ -495,6 +523,7 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{ } public function stat($path) { + $this->init(); $container=$this->getContainer($path); if ( ! is_null($container)) { return array( @@ -523,17 +552,19 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{ } private function getTmpFile($path) { + $this->init(); $obj=$this->getObject($path); if ( ! is_null($obj)) { - $tmpFile=OCP\Files::tmpFile(); + $tmpFile=\OCP\Files::tmpFile(); $obj->save_to_filename($tmpFile); return $tmpFile; } else { - return OCP\Files::tmpFile(); + return \OCP\Files::tmpFile(); } } private function fromTmpFile($tmpFile, $path) { + $this->init(); $obj=$this->getObject($path); if (is_null($obj)) { $obj=$this->createObject($path); @@ -544,7 +575,7 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{ /** * remove custom mtime metadata - * @param CF_Object obj + * @param \CF_Object $obj */ private function resetMTime($obj) { if (isset($obj->metadata['Mtime'])) { diff --git a/apps/files_external/lib/webdav.php b/apps/files_external/lib/webdav.php index 920aefc12d..2a953ac63f 100644 --- a/apps/files_external/lib/webdav.php +++ b/apps/files_external/lib/webdav.php @@ -6,14 +6,17 @@ * See the COPYING-README file. */ -class OC_FileStorage_DAV extends OC_Filestorage_Common{ +namespace OC\Files\Storage; + +class DAV extends \OC\Files\Storage\Common{ private $password; private $user; private $host; private $secure; private $root; + private $ready; /** - * @var Sabre_DAV_Client + * @var \Sabre_DAV_Client */ private $client; @@ -43,6 +46,13 @@ class OC_FileStorage_DAV extends OC_Filestorage_Common{ if (substr($this->root, -1, 1)!='/') { $this->root.='/'; } + } + + private function init(){ + if($this->ready){ + return; + } + $this->ready = true; $settings = array( 'baseUri' => $this->createBaseUri(), @@ -50,7 +60,7 @@ class OC_FileStorage_DAV extends OC_Filestorage_Common{ 'password' => $this->password, ); - $this->client = new Sabre_DAV_Client($settings); + $this->client = new \Sabre_DAV_Client($settings); $caview = \OCP\Files::getStorage('files_external'); if ($caview) { @@ -63,6 +73,10 @@ class OC_FileStorage_DAV extends OC_Filestorage_Common{ $this->mkdir(''); } + public function getId(){ + return 'webdav::' . $this->user . '@' . $this->host . '/' . $this->root; + } + private function createBaseUri() { $baseUri='http'; if ($this->secure) { @@ -73,40 +87,45 @@ class OC_FileStorage_DAV extends OC_Filestorage_Common{ } public function mkdir($path) { + $this->init(); $path=$this->cleanPath($path); return $this->simpleResponse('MKCOL', $path, null, 201); } public function rmdir($path) { + $this->init(); $path=$this->cleanPath($path); return $this->simpleResponse('DELETE', $path, null, 204); } public function opendir($path) { + $this->init(); $path=$this->cleanPath($path); try { $response=$this->client->propfind($path, array(), 1); $id=md5('webdav'.$this->root.$path); - OC_FakeDirStream::$dirs[$id]=array(); + $content = array(); $files=array_keys($response); array_shift($files);//the first entry is the current directory foreach ($files as $file) { $file = urldecode(basename($file)); - OC_FakeDirStream::$dirs[$id][]=$file; + $content[]=$file; } + \OC\Files\Stream\Dir::register($id, $content); return opendir('fakedir://'.$id); - } catch(Exception $e) { + } catch(\Exception $e) { return false; } } public function filetype($path) { + $this->init(); $path=$this->cleanPath($path); try { $response=$this->client->propfind($path, array('{DAV:}resourcetype')); $responseType=$response["{DAV:}resourcetype"]->resourceType; return (count($responseType)>0 and $responseType[0]=="{DAV:}collection")?'dir':'file'; - } catch(Exception $e) { + } catch(\Exception $e) { error_log($e->getMessage()); \OCP\Util::writeLog("webdav client", \OCP\Util::sanitizeHTML($e->getMessage()), \OCP\Util::ERROR); return false; @@ -122,20 +141,23 @@ class OC_FileStorage_DAV extends OC_Filestorage_Common{ } public function file_exists($path) { + $this->init(); $path=$this->cleanPath($path); try { $this->client->propfind($path, array('{DAV:}resourcetype')); return true;//no 404 exception - } catch(Exception $e) { + } catch(\Exception $e) { return false; } } public function unlink($path) { - return $this->simpleResponse('DELETE', $path, null, 204); + $this->init(); + return $this->simpleResponse('DELETE', $path, null ,204); } - public function fopen($path, $mode) { + public function fopen($path,$mode) { + $this->init(); $path=$this->cleanPath($path); switch($mode) { case 'r': @@ -172,9 +194,9 @@ class OC_FileStorage_DAV extends OC_Filestorage_Common{ } else { $ext=''; } - $tmpFile=OCP\Files::tmpFile($ext); - OC_CloseStreamWrapper::$callBacks[$tmpFile]=array($this, 'writeBack'); - if ($this->file_exists($path)) { + $tmpFile = \OCP\Files::tmpFile($ext); + \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack')); + if($this->file_exists($path)) { $this->getFile($path, $tmpFile); } self::$tempFiles[$tmpFile]=$path; @@ -190,6 +212,7 @@ class OC_FileStorage_DAV extends OC_Filestorage_Common{ } public function free_space($path) { + $this->init(); $path=$this->cleanPath($path); try { $response=$this->client->propfind($path, array('{DAV:}quota-available-bytes')); @@ -198,12 +221,13 @@ class OC_FileStorage_DAV extends OC_Filestorage_Common{ } else { return 0; } - } catch(Exception $e) { + } catch(\Exception $e) { return 0; } } public function touch($path, $mtime=null) { + $this->init(); if (is_null($mtime)) { $mtime=time(); } @@ -211,12 +235,14 @@ class OC_FileStorage_DAV extends OC_Filestorage_Common{ $this->client->proppatch($path, array('{DAV:}lastmodified' => $mtime)); } - public function getFile($path, $target) { - $source=$this->fopen($path, 'r'); - file_put_contents($target, $source); + public function getFile($path,$target) { + $this->init(); + $source=$this->fopen($path,'r'); + file_put_contents($target,$source); } - public function uploadFile($path, $target) { + public function uploadFile($path,$target) { + $this->init(); $source=fopen($path, 'r'); $curl = curl_init(); @@ -230,47 +256,46 @@ class OC_FileStorage_DAV extends OC_Filestorage_Common{ curl_close ($curl); } - public function rename($path1, $path2) { + public function rename($path1,$path2) { + $this->init(); $path1=$this->cleanPath($path1); $path2=$this->root.$this->cleanPath($path2); try { $this->client->request('MOVE', $path1, null, array('Destination'=>$path2)); return true; - } catch(Exception $e) { - echo $e; - echo 'fail'; + } catch(\Exception $e) { return false; } } - public function copy($path1, $path2) { + public function copy($path1,$path2) { + $this->init(); $path1=$this->cleanPath($path1); $path2=$this->root.$this->cleanPath($path2); try { $this->client->request('COPY', $path1, null, array('Destination'=>$path2)); return true; - } catch(Exception $e) { - echo $e; - echo 'fail'; + } catch(\Exception $e) { return false; } } public function stat($path) { + $this->init(); $path=$this->cleanPath($path); try { $response=$this->client->propfind($path, array('{DAV:}getlastmodified', '{DAV:}getcontentlength')); return array( 'mtime'=>strtotime($response['{DAV:}getlastmodified']), 'size'=>(int)isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0, - 'ctime'=>-1, ); - } catch(Exception $e) { + } catch(\Exception $e) { return array(); } } public function getMimeType($path) { + $this->init(); $path=$this->cleanPath($path); try { $response=$this->client->propfind($path, array('{DAV:}getcontenttype', '{DAV:}resourcetype')); @@ -283,7 +308,7 @@ class OC_FileStorage_DAV extends OC_Filestorage_Common{ } else { return false; } - } catch(Exception $e) { + } catch(\Exception $e) { return false; } } @@ -296,12 +321,12 @@ class OC_FileStorage_DAV extends OC_Filestorage_Common{ } } - private function simpleResponse($method, $path, $body, $expected) { + private function simpleResponse($method,$path,$body,$expected) { $path=$this->cleanPath($path); try { $response=$this->client->request($method, $path, $body); return $response['statusCode']==$expected; - } catch(Exception $e) { + } catch(\Exception $e) { return false; } } diff --git a/apps/files_external/personal.php b/apps/files_external/personal.php index 4215b28787..268d188023 100755 --- a/apps/files_external/personal.php +++ b/apps/files_external/personal.php @@ -24,7 +24,7 @@ OCP\Util::addScript('files_external', 'settings'); OCP\Util::addStyle('files_external', 'settings'); $backends = OC_Mount_Config::getBackends(); // Remove local storage -unset($backends['OC_Filestorage_Local']); +unset($backends['\OC\Files\Storage\Local']); $tmpl = new OCP\Template('files_external', 'settings'); $tmpl->assign('isAdminPage', false, false); $tmpl->assign('mounts', OC_Mount_Config::getPersonalMountPoints()); diff --git a/apps/files_external/tests/amazons3.php b/apps/files_external/tests/amazons3.php index 39f96fe8e5..6b3a942b5b 100644 --- a/apps/files_external/tests/amazons3.php +++ b/apps/files_external/tests/amazons3.php @@ -20,7 +20,9 @@ * License along with this library. If not, see . */ -class Test_Filestorage_AmazonS3 extends Test_FileStorage { +namespace Test\Files\Storage; + +class AmazonS3 extends Storage { private $config; private $id; @@ -32,12 +34,12 @@ class Test_Filestorage_AmazonS3 extends Test_FileStorage { $this->markTestSkipped('AmazonS3 backend not configured'); } $this->config['amazons3']['bucket'] = $id; // Make sure we have a new empty bucket to work in - $this->instance = new OC_Filestorage_AmazonS3($this->config['amazons3']); + $this->instance = new \OC\Files\Storage\AmazonS3($this->config['amazons3']); } public function tearDown() { if ($this->instance) { - $s3 = new AmazonS3(array('key' => $this->config['amazons3']['key'], + $s3 = new \AmazonS3(array('key' => $this->config['amazons3']['key'], 'secret' => $this->config['amazons3']['secret'])); if ($s3->delete_all_objects($this->id)) { $s3->delete_bucket($this->id); diff --git a/apps/files_external/tests/config.php b/apps/files_external/tests/config.php index ff16b1c1d8..65127175ad 100644 --- a/apps/files_external/tests/config.php +++ b/apps/files_external/tests/config.php @@ -8,7 +8,7 @@ return array( 'root'=>'/test', ), 'webdav'=>array( - 'run'=>false, + 'run'=>true, 'host'=>'localhost', 'user'=>'test', 'password'=>'test', @@ -30,7 +30,7 @@ return array( 'root'=>'/', ), 'smb'=>array( - 'run'=>false, + 'run'=>true, 'user'=>'test', 'password'=>'test', 'host'=>'localhost', diff --git a/apps/files_external/tests/dropbox.php b/apps/files_external/tests/dropbox.php index 304cb3ca38..e4e598b06b 100644 --- a/apps/files_external/tests/dropbox.php +++ b/apps/files_external/tests/dropbox.php @@ -6,7 +6,9 @@ * See the COPYING-README file. */ -class Test_Filestorage_Dropbox extends Test_FileStorage { +namespace Test\Files\Storage; + +class Dropbox extends Storage { private $config; public function setUp() { @@ -16,7 +18,7 @@ class Test_Filestorage_Dropbox extends Test_FileStorage { $this->markTestSkipped('Dropbox backend not configured'); } $this->config['dropbox']['root'] .= '/' . $id; //make sure we have an new empty folder to work in - $this->instance = new OC_Filestorage_Dropbox($this->config['dropbox']); + $this->instance = new \OC\Files\Storage\Dropbox($this->config['dropbox']); } public function tearDown() { diff --git a/apps/files_external/tests/ftp.php b/apps/files_external/tests/ftp.php index d0404b5f34..923b5e3968 100644 --- a/apps/files_external/tests/ftp.php +++ b/apps/files_external/tests/ftp.php @@ -6,7 +6,9 @@ * See the COPYING-README file. */ -class Test_Filestorage_FTP extends Test_FileStorage { +namespace Test\Files\Storage; + +class FTP extends Storage { private $config; public function setUp() { @@ -16,12 +18,12 @@ class Test_Filestorage_FTP extends Test_FileStorage { $this->markTestSkipped('FTP backend not configured'); } $this->config['ftp']['root'] .= '/' . $id; //make sure we have an new empty folder to work in - $this->instance = new OC_Filestorage_FTP($this->config['ftp']); + $this->instance = new \OC\Files\Storage\FTP($this->config['ftp']); } public function tearDown() { if ($this->instance) { - OCP\Files::rmdirr($this->instance->constructUrl('')); + \OCP\Files::rmdirr($this->instance->constructUrl('')); } } @@ -32,18 +34,18 @@ class Test_Filestorage_FTP extends Test_FileStorage { 'root' => '/', 'secure' => false ); $instance = new OC_Filestorage_FTP($config); - $this->assertEqual('ftp://ftp:ftp@localhost/', $instance->constructUrl('')); + $this->assertEquals('ftp://ftp:ftp@localhost/', $instance->constructUrl('')); $config['secure'] = true; $instance = new OC_Filestorage_FTP($config); - $this->assertEqual('ftps://ftp:ftp@localhost/', $instance->constructUrl('')); + $this->assertEquals('ftps://ftp:ftp@localhost/', $instance->constructUrl('')); $config['secure'] = 'false'; $instance = new OC_Filestorage_FTP($config); - $this->assertEqual('ftp://ftp:ftp@localhost/', $instance->constructUrl('')); + $this->assertEquals('ftp://ftp:ftp@localhost/', $instance->constructUrl('')); $config['secure'] = 'true'; $instance = new OC_Filestorage_FTP($config); - $this->assertEqual('ftps://ftp:ftp@localhost/', $instance->constructUrl('')); + $this->assertEquals('ftps://ftp:ftp@localhost/', $instance->constructUrl('')); } } diff --git a/apps/files_external/tests/google.php b/apps/files_external/tests/google.php index 379bf992ff..f344163a8b 100644 --- a/apps/files_external/tests/google.php +++ b/apps/files_external/tests/google.php @@ -20,8 +20,9 @@ * License along with this library. If not, see . */ -class Test_Filestorage_Google extends Test_FileStorage { +namespace Test\Files\Storage; +class Google extends Storage { private $config; public function setUp() { @@ -31,7 +32,7 @@ class Test_Filestorage_Google extends Test_FileStorage { $this->markTestSkipped('Google backend not configured'); } $this->config['google']['root'] .= '/' . $id; //make sure we have an new empty folder to work in - $this->instance = new OC_Filestorage_Google($this->config['google']); + $this->instance = new \OC\Files\Storage\Google($this->config['google']); } public function tearDown() { diff --git a/apps/files_external/tests/smb.php b/apps/files_external/tests/smb.php index 2d6268ef26..be3ea5a830 100644 --- a/apps/files_external/tests/smb.php +++ b/apps/files_external/tests/smb.php @@ -6,7 +6,10 @@ * See the COPYING-README file. */ -class Test_Filestorage_SMB extends Test_FileStorage { +namespace Test\Files\Storage; + +class SMB extends Storage { + private $config; public function setUp() { @@ -16,12 +19,12 @@ class Test_Filestorage_SMB extends Test_FileStorage { $this->markTestSkipped('Samba backend not configured'); } $this->config['smb']['root'] .= $id; //make sure we have an new empty folder to work in - $this->instance = new OC_Filestorage_SMB($this->config['smb']); + $this->instance = new \OC\Files\Storage\SMB($this->config['smb']); } public function tearDown() { if ($this->instance) { - OCP\Files::rmdirr($this->instance->constructUrl('')); + \OCP\Files::rmdirr($this->instance->constructUrl('')); } } } diff --git a/apps/files_external/tests/swift.php b/apps/files_external/tests/swift.php index 8b25db5099..5c78284024 100644 --- a/apps/files_external/tests/swift.php +++ b/apps/files_external/tests/swift.php @@ -6,7 +6,9 @@ * See the COPYING-README file. */ -class Test_Filestorage_SWIFT extends Test_FileStorage { +namespace Test\Files\Storage; + +class SWIFT extends Storage { private $config; public function setUp() { @@ -16,7 +18,7 @@ class Test_Filestorage_SWIFT extends Test_FileStorage { $this->markTestSkipped('OpenStack SWIFT backend not configured'); } $this->config['swift']['root'] .= '/' . $id; //make sure we have an new empty folder to work in - $this->instance = new OC_Filestorage_SWIFT($this->config['swift']); + $this->instance = new \OC\Files\Storage\SWIFT($this->config['swift']); } diff --git a/apps/files_external/tests/webdav.php b/apps/files_external/tests/webdav.php index dd938a0c93..1702898045 100644 --- a/apps/files_external/tests/webdav.php +++ b/apps/files_external/tests/webdav.php @@ -6,7 +6,10 @@ * See the COPYING-README file. */ -class Test_Filestorage_DAV extends Test_FileStorage { +namespace Test\Files\Storage; + +class DAV extends Storage { + private $config; public function setUp() { @@ -16,7 +19,7 @@ class Test_Filestorage_DAV extends Test_FileStorage { $this->markTestSkipped('WebDAV backend not configured'); } $this->config['webdav']['root'] .= '/' . $id; //make sure we have an new empty folder to work in - $this->instance = new OC_Filestorage_DAV($this->config['webdav']); + $this->instance = new \OC\Files\Storage\DAV($this->config['webdav']); } public function tearDown() { diff --git a/apps/files_sharing/appinfo/app.php b/apps/files_sharing/appinfo/app.php index 0104d0d017..d3e05cc62d 100644 --- a/apps/files_sharing/appinfo/app.php +++ b/apps/files_sharing/appinfo/app.php @@ -2,8 +2,11 @@ OC::$CLASSPATH['OC_Share_Backend_File'] = "apps/files_sharing/lib/share/file.php"; OC::$CLASSPATH['OC_Share_Backend_Folder'] = 'apps/files_sharing/lib/share/folder.php'; -OC::$CLASSPATH['OC_Filestorage_Shared'] = "apps/files_sharing/lib/sharedstorage.php"; -OCP\Util::connectHook('OC_Filesystem', 'setup', 'OC_Filestorage_Shared', 'setup'); +OC::$CLASSPATH['OC\Files\Storage\Shared'] = "apps/files_sharing/lib/sharedstorage.php"; +OC::$CLASSPATH['OC\Files\Cache\Shared_Cache'] = 'apps/files_sharing/lib/cache.php'; +OC::$CLASSPATH['OC\Files\Cache\Shared_Permissions'] = 'apps/files_sharing/lib/permissions.php'; +OC::$CLASSPATH['OC\Files\Cache\Shared_Watcher'] = 'apps/files_sharing/lib/watcher.php'; +OCP\Util::connectHook('OC_Filesystem', 'setup', '\OC\Files\Storage\Shared', 'setup'); OCP\Share::registerBackend('file', 'OC_Share_Backend_File'); OCP\Share::registerBackend('folder', 'OC_Share_Backend_Folder', 'file'); -OCP\Util::addScript('files_sharing', 'share'); \ No newline at end of file +OCP\Util::addScript('files_sharing', 'share'); diff --git a/apps/files_sharing/appinfo/info.xml b/apps/files_sharing/appinfo/info.xml index a44d0338bb..1f24a4dde8 100644 --- a/apps/files_sharing/appinfo/info.xml +++ b/apps/files_sharing/appinfo/info.xml @@ -5,7 +5,7 @@ File sharing between users AGPL Michael Gapczynski - 4.9 + 4.91 true diff --git a/apps/files_sharing/appinfo/update.php b/apps/files_sharing/appinfo/update.php index e998626f4a..1d22b32b50 100644 --- a/apps/files_sharing/appinfo/update.php +++ b/apps/files_sharing/appinfo/update.php @@ -9,10 +9,12 @@ if (version_compare($installedVersion, '0.3', '<')) { OC_User::useBackend(new OC_User_Database()); OC_Group::useBackend(new OC_Group_Database()); OC_App::loadApps(array('authentication')); + $rootView = new \OC\Files\View(''); while ($row = $result->fetchRow()) { - $itemSource = OC_FileCache::getId($row['source'], ''); + $meta = $rootView->getFileInfo($$row['source']); + $itemSource = $meta['fileid']; if ($itemSource != -1) { - $file = OC_FileCache::get($row['source'], ''); + $file = $meta; if ($file['mimetype'] == 'httpd/unix-directory') { $itemType = 'folder'; } else { @@ -68,6 +70,6 @@ if (version_compare($installedVersion, '0.3.3', '<')) { OC_App::loadApps(array('authentication')); $users = OC_User::getUsers(); foreach ($users as $user) { - OC_FileCache::delete('Shared', '/'.$user.'/files/'); +// OC_FileCache::delete('Shared', '/'.$user.'/files/'); } -} \ No newline at end of file +} diff --git a/apps/files_sharing/js/share.js b/apps/files_sharing/js/share.js index a46d017980..eb5a6e8cb7 100644 --- a/apps/files_sharing/js/share.js +++ b/apps/files_sharing/js/share.js @@ -1,6 +1,8 @@ $(document).ready(function() { - if (typeof OC.Share !== 'undefined' && typeof FileActions !== 'undefined' && !publicListView) { + var disableSharing = $('#disableSharing').data('status'); + + if (typeof OC.Share !== 'undefined' && typeof FileActions !== 'undefined' && !disableSharing) { FileActions.register('all', 'Share', OC.PERMISSION_READ, OC.imagePath('core', 'actions/share'), function(filename) { if ($('#dir').val() == '/') { diff --git a/apps/files_sharing/l10n/fa.php b/apps/files_sharing/l10n/fa.php index 06e1862e8b..4313acae1a 100644 --- a/apps/files_sharing/l10n/fa.php +++ b/apps/files_sharing/l10n/fa.php @@ -1,6 +1,9 @@ "اندازه", -"Modified" => "تاریخ", -"Delete all" => "حذف همه", -"Delete" => "حذف" +"Password" => "گذرواژه", +"Submit" => "ثبت", +"%s shared the folder %s with you" => "%sپوشه %s را با شما به اشتراک گذاشت", +"%s shared the file %s with you" => "%sفایل %s را با شما به اشتراک گذاشت", +"Download" => "دانلود", +"No preview available for" => "هیچگونه پیش نمایشی موجود نیست", +"web services under your control" => "سرویس های تحت وب در کنترل شما" ); diff --git a/apps/files_sharing/l10n/lb.php b/apps/files_sharing/l10n/lb.php new file mode 100644 index 0000000000..8aba5806aa --- /dev/null +++ b/apps/files_sharing/l10n/lb.php @@ -0,0 +1,3 @@ + "Passwuert" +); diff --git a/apps/files_sharing/l10n/lv.php b/apps/files_sharing/l10n/lv.php new file mode 100644 index 0000000000..4d2eed23a2 --- /dev/null +++ b/apps/files_sharing/l10n/lv.php @@ -0,0 +1,3 @@ + "Lejupielādēt" +); diff --git a/apps/files_sharing/l10n/sr.php b/apps/files_sharing/l10n/sr.php new file mode 100644 index 0000000000..7a922b8900 --- /dev/null +++ b/apps/files_sharing/l10n/sr.php @@ -0,0 +1,3 @@ + "Пошаљи" +); diff --git a/apps/files_sharing/l10n/zh_TW.php b/apps/files_sharing/l10n/zh_TW.php index fa4f8075c6..f1d28731a7 100644 --- a/apps/files_sharing/l10n/zh_TW.php +++ b/apps/files_sharing/l10n/zh_TW.php @@ -4,5 +4,6 @@ "%s shared the folder %s with you" => "%s 分享了資料夾 %s 給您", "%s shared the file %s with you" => "%s 分享了檔案 %s 給您", "Download" => "下載", -"No preview available for" => "無法預覽" +"No preview available for" => "無法預覽", +"web services under your control" => "在您掌控之下的網路服務" ); diff --git a/apps/files_sharing/lib/cache.php b/apps/files_sharing/lib/cache.php new file mode 100644 index 0000000000..9655e44787 --- /dev/null +++ b/apps/files_sharing/lib/cache.php @@ -0,0 +1,258 @@ +. + */ + +namespace OC\Files\Cache; + +/** + * Metadata cache for shared files + * + * don't use this class directly if you need to get metadata, use \OC\Files\Filesystem::getFileInfo instead + */ +class Shared_Cache extends Cache { + + private $files = array(); + + public function __construct($storage) { + + } + + /** + * @brief Get the source cache of a shared file or folder + * @param string $target Shared target file path + * @return \OC\Files\Cache\Cache + */ + private function getSourceCache($target) { + $source = \OC_Share_Backend_File::getSource($target); + if (isset($source['path'])) { + $source['path'] = '/' . $source['uid_owner'] . '/' . $source['path']; + \OC\Files\Filesystem::initMountPoints($source['uid_owner']); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source['path']); + if ($storage) { + $this->files[$target] = $internalPath; + $cache = $storage->getCache(); + $this->storageId = $storage->getId(); + $this->numericId = $cache->getNumericStorageId(); + return $cache; + } + } + return false; + } + + /** + * get the stored metadata of a file or folder + * + * @param string/int $file + * @return array + */ + public function get($file) { + if ($file == '') { + return \OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_File::FORMAT_FILE_APP_ROOT); + } else if (is_string($file)) { + if ($cache = $this->getSourceCache($file)) { + return $cache->get($this->files[$file]); + } + } else { + $query = \OC_DB::prepare( + 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted` + FROM `*PREFIX*filecache` WHERE `fileid` = ?'); + $result = $query->execute(array($file)); + $data = $result->fetchRow(); + $data['fileid'] = (int)$data['fileid']; + $data['size'] = (int)$data['size']; + $data['mtime'] = (int)$data['mtime']; + $data['encrypted'] = (bool)$data['encrypted']; + $data['mimetype'] = $this->getMimetype($data['mimetype']); + $data['mimepart'] = $this->getMimetype($data['mimepart']); + return $data; + } + return false; + } + + /** + * get the metadata of all files stored in $folder + * + * @param string $folder + * @return array + */ + public function getFolderContents($folder) { + if ($folder == '') { + $files = \OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_File::FORMAT_GET_FOLDER_CONTENTS); + foreach ($files as &$file) { + $file['mimetype'] = $this->getMimetype($file['mimetype']); + $file['mimepart'] = $this->getMimetype($file['mimepart']); + } + return $files; + } else { + if ($cache = $this->getSourceCache($folder)) { + return $cache->getFolderContents($this->files[$folder]); + } + } + return false; + } + + /** + * store meta data for a file or folder + * + * @param string $file + * @param array $data + * + * @return int file id + */ + public function put($file, array $data) { + if ($cache = $this->getSourceCache($file)) { + return $cache->put($this->files[$file], $data); + } + return false; + } + + /** + * get the file id for a file + * + * @param string $file + * @return int + */ + public function getId($file) { + if ($cache = $this->getSourceCache($file)) { + return $cache->getId($this->files[$file]); + } + return -1; + } + + /** + * check if a file is available in the cache + * + * @param string $file + * @return bool + */ + public function inCache($file) { + if ($file == '') { + return true; + } + return parent::inCache($file); + } + + /** + * remove a file or folder from the cache + * + * @param string $file + */ + public function remove($file) { + if ($cache = $this->getSourceCache($file)) { + $cache->remove($this->files[$file]); + } + } + + /** + * Move a file or folder in the cache + * + * @param string $source + * @param string $target + */ + public function move($source, $target) { + if ($cache = $this->getSourceCache($source)) { + $targetPath = \OC_Share_Backend_File::getSourcePath(dirname($target)); + if ($targetPath) { + $targetPath .= '/' . basename($target); + $cache->move($this->files[$source], $targetPath); + } + + } + } + + /** + * remove all entries for files that are stored on the storage from the cache + */ + public function clear() { + // Not a valid action for Shared Cache + } + + /** + * @param string $file + * + * @return int, Cache::NOT_FOUND, Cache::PARTIAL, Cache::SHALLOW or Cache::COMPLETE + */ + public function getStatus($file) { + if ($file == '') { + return self::COMPLETE; + } + if ($cache = $this->getSourceCache($file)) { + return $cache->getStatus($this->files[$file]); + } + return self::NOT_FOUND; + } + + /** + * search for files matching $pattern + * + * @param string $pattern + * @return array of file data + */ + public function search($pattern) { + // TODO + } + + /** + * search for files by mimetype + * + * @param string $part1 + * @param string $part2 + * @return array + */ + public function searchByMime($mimetype) { + if (strpos($mimetype, '/')) { + $where = '`mimetype` = ?'; + } else { + $where = '`mimepart` = ?'; + } + $mimetype = $this->getMimetypeId($mimetype); + $ids = $this->getAll(); + $placeholders = join(',', array_fill(0, count($ids), '?')); + $query = \OC_DB::prepare(' + SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted` + FROM `*PREFIX*filecache` WHERE ' . $where . ' AND `fileid` IN (' . $placeholders . ')' + ); + $result = $query->execute(array_merge(array($mimetype), $ids)); + return $result->fetchAll(); + } + + /** + * get the size of a folder and set it in the cache + * + * @param string $path + * @return int + */ + public function calculateFolderSize($path) { + if ($cache = $this->getSourceCache($path)) { + return $cache->calculateFolderSize($this->files[$path]); + } + return 0; + } + + /** + * get all file ids on the files on the storage + * + * @return int[] + */ + public function getAll() { + return \OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_File::FORMAT_GET_ALL); + } + +} diff --git a/apps/files_sharing/lib/permissions.php b/apps/files_sharing/lib/permissions.php new file mode 100644 index 0000000000..2b068ff935 --- /dev/null +++ b/apps/files_sharing/lib/permissions.php @@ -0,0 +1,85 @@ +. +*/ +namespace OC\Files\Cache; + +class Shared_Permissions extends Permissions { + + /** + * get the permissions for a single file + * + * @param int $fileId + * @param string $user + * @return int (-1 if file no permissions set) + */ + public function get($fileId, $user) { + if ($fileId == -1) { + return \OCP\PERMISSION_READ; + } + $source = \OCP\Share::getItemSharedWithBySource('file', $fileId, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE, null, true); + if ($source) { + return $source['permissions']; + } else { + return -1; + } + } + + /** + * set the permissions of a file + * + * @param int $fileId + * @param string $user + * @param int $permissions + */ + public function set($fileId, $user, $permissions) { + // Not a valid action for Shared Permissions + } + + /** + * get the permissions of multiply files + * + * @param int[] $fileIds + * @param string $user + * @return int[] + */ + public function getMultiple($fileIds, $user) { + if (count($fileIds) === 0) { + return array(); + } + foreach ($fileIds as $fileId) { + $filePermissions[$fileId] = self::get($fileId, $user); + } + return $filePermissions; + } + + /** + * remove the permissions for a file + * + * @param int $fileId + * @param string $user + */ + public function remove($fileId, $user) { + // Not a valid action for Shared Permissions + } + + public function removeMultiple($fileIds, $user) { + // Not a valid action for Shared Permissions + } +} diff --git a/apps/files_sharing/lib/share/file.php b/apps/files_sharing/lib/share/file.php index ac58523683..6d3c55a008 100644 --- a/apps/files_sharing/lib/share/file.php +++ b/apps/files_sharing/lib/share/file.php @@ -22,16 +22,18 @@ class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent { const FORMAT_SHARED_STORAGE = 0; - const FORMAT_FILE_APP = 1; + const FORMAT_GET_FOLDER_CONTENTS = 1; const FORMAT_FILE_APP_ROOT = 2; const FORMAT_OPENDIR = 3; + const FORMAT_GET_ALL = 4; private $path; public function isValidSource($itemSource, $uidOwner) { - $path = OC_FileCache::getPath($itemSource, $uidOwner); - if ($path) { - $this->path = $path; + $query = \OC_DB::prepare('SELECT `name` FROM `*PREFIX*filecache` WHERE `fileid` = ?'); + $result = $query->execute(array($itemSource)); + if ($row = $result->fetchRow()) { + $this->path = $row['name']; return true; } return false; @@ -70,37 +72,21 @@ class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent { public function formatItems($items, $format, $parameters = null) { if ($format == self::FORMAT_SHARED_STORAGE) { // Only 1 item should come through for this format call - return array('path' => $items[key($items)]['path'], 'permissions' => $items[key($items)]['permissions']); - } else if ($format == self::FORMAT_FILE_APP) { - if (isset($parameters['mimetype_filter']) && $parameters['mimetype_filter']) { - $mimetype_filter = $parameters['mimetype_filter']; - } + return array('path' => $items[key($items)]['path'], 'permissions' => $items[key($items)]['permissions'], 'uid_owner' => $items[key($items)]['uid_owner']); + } else if ($format == self::FORMAT_GET_FOLDER_CONTENTS) { $files = array(); foreach ($items as $item) { - if (isset($mimetype_filter) - && strpos($item['mimetype'], $mimetype_filter) !== 0 - && $item['mimetype'] != 'httpd/unix-directory') { - continue; - } $file = array(); - $file['id'] = $item['file_source']; + $file['fileid'] = $item['file_source']; + $file['storage'] = $item['storage']; $file['path'] = $item['file_target']; + $file['parent'] = $item['file_parent']; $file['name'] = basename($item['file_target']); - $file['ctime'] = $item['ctime']; - $file['mtime'] = $item['mtime']; $file['mimetype'] = $item['mimetype']; + $file['mimepart'] = $item['mimepart']; $file['size'] = $item['size']; + $file['mtime'] = $item['mtime']; $file['encrypted'] = $item['encrypted']; - $file['versioned'] = $item['versioned']; - $file['directory'] = $parameters['folder']; - $file['type'] = ($item['mimetype'] == 'httpd/unix-directory') ? 'dir' : 'file'; - $file['permissions'] = $item['permissions']; - if ($file['type'] == 'file') { - // Remove Create permission if type is file - $file['permissions'] &= ~OCP\PERMISSION_CREATE; - } - // NOTE: Temporary fix to allow unsharing of files in root of Shared directory - $file['permissions'] |= OCP\PERMISSION_DELETE; $files[] = $file; } return $files; @@ -111,17 +97,48 @@ class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent { if ($item['mtime'] > $mtime) { $mtime = $item['mtime']; } - $size += $item['size']; + $size += (int)$item['size']; } - return array(0 => array('id' => -1, 'name' => 'Shared', 'mtime' => $mtime, 'mimetype' => 'httpd/unix-directory', 'size' => $size, 'writable' => false, 'type' => 'dir', 'directory' => '', 'permissions' => OCP\PERMISSION_READ)); + return array('fileid' => -1, 'name' => 'Shared', 'mtime' => $mtime, 'mimetype' => 'httpd/unix-directory', 'size' => $size); } else if ($format == self::FORMAT_OPENDIR) { $files = array(); foreach ($items as $item) { $files[] = basename($item['file_target']); } return $files; + } else if ($format == self::FORMAT_GET_ALL) { + $ids = array(); + foreach ($items as $item) { + $ids[] = $item['file_source']; + } + return $ids; } return array(); } + public static function getSource($target) { + if ($target == '') { + return false; + } + $target = '/'.$target; + $target = rtrim($target, '/'); + $pos = strpos($target, '/', 1); + // Get shared folder name + if ($pos !== false) { + $folder = substr($target, 0, $pos); + $source = \OCP\Share::getItemSharedWith('folder', $folder, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE); + if ($source) { + $source['path'] = $source['path'].substr($target, strlen($folder)); + return $source; + } + } else { + $source = \OCP\Share::getItemSharedWith('file', $target, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE); + if ($source) { + return $source; + } + } + \OCP\Util::writeLog('files_sharing', 'File source not found for: '.$target, \OCP\Util::ERROR); + return false; + } + } diff --git a/apps/files_sharing/lib/share/folder.php b/apps/files_sharing/lib/share/folder.php index d414fcf10f..11c8c6b1e8 100644 --- a/apps/files_sharing/lib/share/folder.php +++ b/apps/files_sharing/lib/share/folder.php @@ -21,47 +21,26 @@ class OC_Share_Backend_Folder extends OC_Share_Backend_File implements OCP\Share_Backend_Collection { - public function formatItems($items, $format, $parameters = null) { - if ($format == self::FORMAT_SHARED_STORAGE) { - // Only 1 item should come through for this format call - return array('path' => $items[key($items)]['path'], 'permissions' => $items[key($items)]['permissions']); - } else if ($format == self::FORMAT_FILE_APP && isset($parameters['folder'])) { - // Only 1 item should come through for this format call - $folder = $items[key($items)]; - if (isset($parameters['mimetype_filter'])) { - $mimetype_filter = $parameters['mimetype_filter']; - } else { - $mimetype_filter = ''; - } - $path = $folder['path'].substr($parameters['folder'], 7 + strlen($folder['file_target'])); - $files = OC_FileCache::getFolderContent($path, '', $mimetype_filter); - foreach ($files as &$file) { - $file['directory'] = $parameters['folder']; - $file['type'] = ($file['mimetype'] == 'httpd/unix-directory') ? 'dir' : 'file'; - $file['permissions'] = $folder['permissions']; - if ($file['type'] == 'file') { - // Remove Create permission if type is file - $file['permissions'] &= ~OCP\PERMISSION_CREATE; - } - } - return $files; - } - return array(); - } - public function getChildren($itemSource) { $children = array(); $parents = array($itemSource); + $query = \OC_DB::prepare('SELECT `id` FROM `*PREFIX*mimetypes` WHERE `mimetype` = ?'); + $result = $query->execute(array('httpd/unix-directory')); + if ($row = $result->fetchRow()) { + $mimetype = $row['id']; + } else { + $mimetype = -1; + } while (!empty($parents)) { $parents = "'".implode("','", $parents)."'"; - $query = OC_DB::prepare('SELECT `id`, `name`, `mimetype` FROM `*PREFIX*fscache` WHERE `parent` IN ('.$parents.')'); + $query = OC_DB::prepare('SELECT `fileid`, `name`, `mimetype` FROM `*PREFIX*filecache` WHERE `parent` IN ('.$parents.')'); $result = $query->execute(); $parents = array(); while ($file = $result->fetchRow()) { - $children[] = array('source' => $file['id'], 'file_path' => $file['name']); + $children[] = array('source' => $file['fileid'], 'file_path' => $file['name']); // If a child folder is found look inside it - if ($file['mimetype'] == 'httpd/unix-directory') { - $parents[] = $file['id']; + if ($file['mimetype'] == $mimetype) { + $parents[] = $file['fileid']; } } } diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php index 50db9166fe..ea28ca69b9 100644 --- a/apps/files_sharing/lib/sharedstorage.php +++ b/apps/files_sharing/lib/sharedstorage.php @@ -20,10 +20,12 @@ * */ +namespace OC\Files\Storage; + /** * Convert target path to source path and pass the function call to the correct storage provider */ -class OC_Filestorage_Shared extends OC_Filestorage_Common { +class Shared extends \OC\Files\Storage\Common { private $sharedFolder; private $files = array(); @@ -32,54 +34,36 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { $this->sharedFolder = $arguments['sharedFolder']; } + public function getId(){ + return 'shared::' . $this->sharedFolder; + } + /** - * @brief Get the source file path and the permissions granted for a shared file + * @brief Get the source file path, permissions, and owner for a shared file * @param string Shared target file path - * @return Returns array with the keys path and permissions or false if not found + * @return Returns array with the keys path, permissions, and owner or false if not found */ private function getFile($target) { - $target = '/'.$target; - $target = rtrim($target, '/'); - if (isset($this->files[$target])) { - return $this->files[$target]; - } else { - $pos = strpos($target, '/', 1); - // Get shared folder name - if ($pos !== false) { - $folder = substr($target, 0, $pos); - if (isset($this->files[$folder])) { - $file = $this->files[$folder]; - } else { - $file = OCP\Share::getItemSharedWith('folder', $folder, OC_Share_Backend_File::FORMAT_SHARED_STORAGE); - } - if ($file) { - $this->files[$target]['path'] = $file['path'].substr($target, strlen($folder)); - $this->files[$target]['permissions'] = $file['permissions']; - return $this->files[$target]; - } - } else { - $file = OCP\Share::getItemSharedWith('file', $target, OC_Share_Backend_File::FORMAT_SHARED_STORAGE); - if ($file) { - $this->files[$target] = $file; - return $this->files[$target]; - } + if (!isset($this->files[$target])) { + $source = \OC_Share_Backend_File::getSource($target); + if ($source) { + $source['path'] = '/'.$source['uid_owner'].'/'.$source['path']; } - OCP\Util::writeLog('files_sharing', 'File source not found for: '.$target, OCP\Util::ERROR); - return false; + $this->files[$target] = $source; } + return $this->files[$target]; } /** * @brief Get the source file path for a shared file * @param string Shared target file path - * @return Returns source file path or false if not found + * @return string source file path or false if not found */ private function getSourcePath($target) { - $file = $this->getFile($target); - if (isset($file['path'])) { - $uid = substr($file['path'], 1, strpos($file['path'], '/', 1) - 1); - OC_Filesystem::mount('OC_Filestorage_Local', array('datadir' => OC_User::getHome($uid)), $uid); - return $file['path']; + $source = $this->getFile($target); + if ($source) { + \OC\Files\Filesystem::initMountPoints($source['uid_owner']); + return $source['path']; } return false; } @@ -87,61 +71,42 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { /** * @brief Get the permissions granted for a shared file * @param string Shared target file path - * @return Returns CRUDS permissions granted or false if not found + * @return int CRUDS permissions granted or false if not found */ - private function getPermissions($target) { - $file = $this->getFile($target); - if (isset($file['permissions'])) { - return $file['permissions']; + public function getPermissions($target) { + $source = $this->getFile($target); + if ($source) { + return $source['permissions']; } return false; } - /** - * @brief Get the internal path to pass to the storage filesystem call - * @param string Source file path - * @return Source file path with mount point stripped out - */ - private function getInternalPath($path) { - $mountPoint = OC_Filesystem::getMountPoint($path); - $internalPath = substr($path, strlen($mountPoint)); - return $internalPath; - } - - public function getOwner($target) { - $shared_item = OCP\Share::getItemSharedWith('folder', $target, OC_Share_Backend_File::FORMAT_SHARED_STORAGE); - if ($shared_item) { - return $shared_item[0]["uid_owner"]; - } - return null; - } - public function mkdir($path) { if ($path == '' || $path == '/' || !$this->isCreatable(dirname($path))) { return false; } else if ($source = $this->getSourcePath($path)) { - $storage = OC_Filesystem::getStorage($source); - return $storage->mkdir($this->getInternalPath($source)); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); + return $storage->mkdir($internalPath); } return false; } public function rmdir($path) { if (($source = $this->getSourcePath($path)) && $this->isDeletable($path)) { - $storage = OC_Filesystem::getStorage($source); - return $storage->rmdir($this->getInternalPath($source)); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); + return $storage->rmdir($internalPath); } return false; } public function opendir($path) { if ($path == '' || $path == '/') { - $files = OCP\Share::getItemsSharedWith('file', OC_Share_Backend_Folder::FORMAT_OPENDIR); - OC_FakeDirStream::$dirs['shared'] = $files; + $files = \OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_Folder::FORMAT_OPENDIR); + \OC\Files\Stream\Dir::register('shared', $files); return opendir('fakedir://shared'); } else if ($source = $this->getSourcePath($path)) { - $storage = OC_Filesystem::getStorage($source); - return $storage->opendir($this->getInternalPath($source)); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); + return $storage->opendir($internalPath); } return false; } @@ -150,16 +115,16 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { if ($path == '' || $path == '/') { return true; } else if ($source = $this->getSourcePath($path)) { - $storage = OC_Filesystem::getStorage($source); - return $storage->is_dir($this->getInternalPath($source)); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); + return $storage->is_dir($internalPath); } return false; } public function is_file($path) { if ($source = $this->getSourcePath($path)) { - $storage = OC_Filesystem::getStorage($source); - return $storage->is_file($this->getInternalPath($source)); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); + return $storage->is_file($internalPath); } return false; } @@ -168,11 +133,10 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { if ($path == '' || $path == '/') { $stat['size'] = $this->filesize($path); $stat['mtime'] = $this->filemtime($path); - $stat['ctime'] = $this->filectime($path); return $stat; } else if ($source = $this->getSourcePath($path)) { - $storage = OC_Filesystem::getStorage($source); - return $storage->stat($this->getInternalPath($source)); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); + return $storage->stat($internalPath); } return false; } @@ -181,8 +145,8 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { if ($path == '' || $path == '/') { return 'dir'; } else if ($source = $this->getSourcePath($path)) { - $storage = OC_Filesystem::getStorage($source); - return $storage->filetype($this->getInternalPath($source)); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); + return $storage->filetype($internalPath); } return false; } @@ -191,8 +155,8 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { if ($path == '' || $path == '/' || $this->is_dir($path)) { return 0; } else if ($source = $this->getSourcePath($path)) { - $storage = OC_Filesystem::getStorage($source); - return $storage->filesize($this->getInternalPath($source)); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); + return $storage->filesize($internalPath); } return false; } @@ -201,7 +165,7 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { if ($path == '') { return false; } - return ($this->getPermissions($path) & OCP\PERMISSION_CREATE); + return ($this->getPermissions($path) & \OCP\PERMISSION_CREATE); } public function isReadable($path) { @@ -212,54 +176,33 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { if ($path == '') { return false; } - return ($this->getPermissions($path) & OCP\PERMISSION_UPDATE); + return ($this->getPermissions($path) & \OCP\PERMISSION_UPDATE); } public function isDeletable($path) { if ($path == '') { return true; } - return ($this->getPermissions($path) & OCP\PERMISSION_DELETE); + return ($this->getPermissions($path) & \OCP\PERMISSION_DELETE); } public function isSharable($path) { if ($path == '') { return false; } - return ($this->getPermissions($path) & OCP\PERMISSION_SHARE); + return ($this->getPermissions($path) & \OCP\PERMISSION_SHARE); } public function file_exists($path) { if ($path == '' || $path == '/') { return true; } else if ($source = $this->getSourcePath($path)) { - $storage = OC_Filesystem::getStorage($source); - return $storage->file_exists($this->getInternalPath($source)); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); + return $storage->file_exists($internalPath); } return false; } - public function filectime($path) { - if ($path == '' || $path == '/') { - $ctime = 0; - if ($dh = $this->opendir($path)) { - while (($filename = readdir($dh)) !== false) { - $tempctime = $this->filectime($filename); - if ($tempctime < $ctime) { - $ctime = $tempctime; - } - } - } - return $ctime; - } else { - $source = $this->getSourcePath($path); - if ($source) { - $storage = OC_Filesystem::getStorage($source); - return $storage->filectime($this->getInternalPath($source)); - } - } - } - public function filemtime($path) { if ($path == '' || $path == '/') { $mtime = 0; @@ -275,8 +218,8 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { } else { $source = $this->getSourcePath($path); if ($source) { - $storage = OC_Filesystem::getStorage($source); - return $storage->filemtime($this->getInternalPath($source)); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); + return $storage->filemtime($internalPath); } } } @@ -288,9 +231,9 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { 'target' => $this->sharedFolder.$path, 'source' => $source, ); - OCP\Util::emitHook('OC_Filestorage_Shared', 'file_get_contents', $info); - $storage = OC_Filesystem::getStorage($source); - return $storage->file_get_contents($this->getInternalPath($source)); + \OCP\Util::emitHook('\OC\Files\Storage\Shared', 'file_get_contents', $info); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); + return $storage->file_get_contents($internalPath); } } @@ -304,9 +247,9 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { 'target' => $this->sharedFolder.$path, 'source' => $source, ); - OCP\Util::emitHook('OC_Filestorage_Shared', 'file_put_contents', $info); - $storage = OC_Filesystem::getStorage($source); - $result = $storage->file_put_contents($this->getInternalPath($source), $data); + \OCP\Util::emitHook('\OC\Files\Storage\Shared', 'file_put_contents', $info); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); + $result = $storage->file_put_contents($internalPath, $data); return $result; } return false; @@ -316,8 +259,8 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { // Delete the file if DELETE permission is granted if ($source = $this->getSourcePath($path)) { if ($this->isDeletable($path)) { - $storage = OC_Filesystem::getStorage($source); - return $storage->unlink($this->getInternalPath($source)); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); + return $storage->unlink($internalPath); } else if (dirname($path) == '/' || dirname($path) == '.') { // Unshare the file from the user if in the root of the Shared folder if ($this->is_dir($path)) { @@ -325,7 +268,7 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { } else { $itemType = 'file'; } - return OCP\Share::unshareFromSelf($itemType, $path); + return \OCP\Share::unshareFromSelf($itemType, $path); } } return false; @@ -340,8 +283,9 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { if (dirname($path1) == dirname($path2)) { // Rename the file if UPDATE permission is granted if ($this->isUpdatable($path1)) { - $storage = OC_Filesystem::getStorage($oldSource); - return $storage->rename($this->getInternalPath($oldSource), $this->getInternalPath($newSource)); + list($storage, $oldInternalPath) = \OC\Files\Filesystem::resolvePath($oldSource); + list( , $newInternalPath) = \OC\Files\Filesystem::resolvePath($newSource); + return $storage->rename($oldInternalPath, $newInternalPath); } } else { // Move the file if DELETE and CREATE permissions are granted @@ -355,8 +299,9 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { return $this->unlink($path1); } } else { - $storage = OC_Filesystem::getStorage($oldSource); - return $storage->rename($this->getInternalPath($oldSource), $this->getInternalPath($newSource)); + list($storage, $oldInternalPath) = \OC\Files\Filesystem::resolvePath($oldSource); + list( , $newInternalPath) = \OC\Files\Filesystem::resolvePath($newSource); + return $storage->rename($oldInternalPath, $newInternalPath); } } } @@ -369,7 +314,7 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { if ($this->isCreatable(dirname($path2))) { $source = $this->fopen($path1, 'r'); $target = $this->fopen($path2, 'w'); - return OC_Helper::streamCopy($source, $target); + return \OC_Helper::streamCopy($source, $target); } return false; } @@ -400,9 +345,9 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { 'source' => $source, 'mode' => $mode, ); - OCP\Util::emitHook('OC_Filestorage_Shared', 'fopen', $info); - $storage = OC_Filesystem::getStorage($source); - return $storage->fopen($this->getInternalPath($source), $mode); + \OCP\Util::emitHook('\OC\Files\Storage\Shared', 'fopen', $info); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); + return $storage->fopen($internalPath, $mode); } return false; } @@ -412,47 +357,88 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { return 'httpd/unix-directory'; } if ($source = $this->getSourcePath($path)) { - $storage = OC_Filesystem::getStorage($source); - return $storage->getMimeType($this->getInternalPath($source)); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); + return $storage->getMimeType($internalPath); } return false; } public function free_space($path) { + if ($path == '') { + return -1; + } $source = $this->getSourcePath($path); if ($source) { - $storage = OC_Filesystem::getStorage($source); - return $storage->free_space($this->getInternalPath($source)); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); + return $storage->free_space($internalPath); } } public function getLocalFile($path) { if ($source = $this->getSourcePath($path)) { - $storage = OC_Filesystem::getStorage($source); - return $storage->getLocalFile($this->getInternalPath($source)); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); + return $storage->getLocalFile($internalPath); } return false; } public function touch($path, $mtime = null) { if ($source = $this->getSourcePath($path)) { - $storage = OC_Filesystem::getStorage($source); - return $storage->touch($this->getInternalPath($source), $mtime); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); + return $storage->touch($internalPath, $mtime); } return false; } public static function setup($options) { - $user_dir = $options['user_dir']; - OC_Filesystem::mount('OC_Filestorage_Shared', array('sharedFolder' => '/Shared'), $user_dir.'/Shared/'); + if (\OCP\Share::getItemsSharedWith('file')) { + $user_dir = $options['user_dir']; + \OC\Files\Filesystem::mount('\OC\Files\Storage\Shared', array('sharedFolder' => '/Shared'), $user_dir.'/Shared/'); + } } - /** - * check if a file or folder has been updated since $time - * @param int $time - * @return bool - */ public function hasUpdated($path, $time) { - //TODO + if ($path == '') { + return false; + } + return $this->filemtime($path) > $time; + } + + public function getCache($path = '') { + return new \OC\Files\Cache\Shared_Cache($this); + } + + public function getScanner($path = '') { + return new \OC\Files\Cache\Scanner($this); + } + + public function getPermissionsCache($path = '') { + return new \OC\Files\Cache\Shared_Permissions($this); + } + + public function getWatcher($path = '') { + return new \OC\Files\Cache\Shared_Watcher($this); + } + + public function getOwner($path) { + if ($path == '') { + return false; + } + $source = $this->getFile($path); + if ($source) { + return $source['uid_owner']; + } return false; } + + public function getETag($path) { + if ($path == '') { + return parent::getETag($path); + } + if ($source = $this->getSourcePath($path)) { + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); + return $storage->getETag($internalPath); + } + return null; + } + } diff --git a/apps/files_sharing/lib/watcher.php b/apps/files_sharing/lib/watcher.php new file mode 100644 index 0000000000..e67d1ee908 --- /dev/null +++ b/apps/files_sharing/lib/watcher.php @@ -0,0 +1,51 @@ +. +*/ + +namespace OC\Files\Cache; + +/** + * check the storage backends for updates and change the cache accordingly + */ +class Shared_Watcher extends Watcher { + + /** + * check $path for updates + * + * @param string $path + */ + public function checkUpdate($path) { + if ($path != '') { + parent::checkUpdate($path); + } + } + + /** + * remove deleted files in $path from the cache + * + * @param string $path + */ + public function cleanFolder($path) { + if ($path != '') { + parent::cleanFolder($path); + } + } + +} \ No newline at end of file diff --git a/apps/files_sharing/public.php b/apps/files_sharing/public.php index efd977a1b6..a3e0ec192a 100644 --- a/apps/files_sharing/public.php +++ b/apps/files_sharing/public.php @@ -9,9 +9,10 @@ if (isset($_GET['token'])) { unset($_GET['file']); $qry = \OC_DB::prepare('SELECT `source` FROM `*PREFIX*sharing` WHERE `target` = ?', 1); $filepath = $qry->execute(array($_GET['token']))->fetchOne(); - if(isset($filepath)) { - $info = OC_FileCache_Cached::get($filepath, ''); - if(strtolower($info['mimetype']) == 'httpd/unix-directory') { + if (isset($filepath)) { + $rootView = new \OC\Files\View(''); + $info = $rootView->getFileInfo($filepath, ''); + if (strtolower($info['mimetype']) == 'httpd/unix-directory') { $_GET['dir'] = $filepath; } else { $_GET['file'] = $filepath; @@ -25,7 +26,7 @@ if (isset($_GET['token'])) { function getID($path) { // use the share table from the db to find the item source if the file was reshared because shared files //are not stored in the file cache. - if (substr(OC_Filesystem::getMountPoint($path), -7, 6) == "Shared") { + if (substr(\OC\Files\Filesystem::getMountPoint($path), -7, 6) == "Shared") { $path_parts = explode('/', $path, 5); $user = $path_parts[1]; $intPath = '/'.$path_parts[4]; @@ -37,16 +38,19 @@ function getID($path) { $row = $result->fetchRow(); $fileSource = $row['item_source']; } else { - $fileSource = OC_Filecache::getId($path, ''); + $rootView = new \OC\Files\View(''); + $meta = $rootView->getFileInfo($path); + $fileSource = $meta['fileid']; } return $fileSource; } + // Enf of backward compatibility /** * lookup file path and owner by fetching it from the fscache - * needed becaus OC_FileCache::getPath($id, $user) already requires the user + * needed because OC_FileCache::getPath($id, $user) already requires the user * @param int $id * @return array */ @@ -86,41 +90,43 @@ if (isset($_GET['t'])) { OC_Util::setupFS($fileOwner); } } -} else if (isset($_GET['file']) || isset($_GET['dir'])) { - OCP\Util::writeLog('share', 'Missing token, trying fallback file/dir links', \OCP\Util::DEBUG); - if (isset($_GET['dir'])) { - $type = 'folder'; - $path = $_GET['dir']; - if(strlen($path)>1 and substr($path, -1, 1)==='/') { - $path=substr($path, 0, -1); +} else { + if (isset($_GET['file']) || isset($_GET['dir'])) { + OCP\Util::writeLog('share', 'Missing token, trying fallback file/dir links', \OCP\Util::DEBUG); + if (isset($_GET['dir'])) { + $type = 'folder'; + $path = $_GET['dir']; + if (strlen($path) > 1 and substr($path, -1, 1) === '/') { + $path = substr($path, 0, -1); + } + $baseDir = $path; + $dir = $baseDir; + } else { + $type = 'file'; + $path = $_GET['file']; + if (strlen($path) > 1 and substr($path, -1, 1) === '/') { + $path = substr($path, 0, -1); + } } - $baseDir = $path; - $dir = $baseDir; - } else { - $type = 'file'; - $path = $_GET['file']; - if(strlen($path)>1 and substr($path, -1, 1)==='/') { - $path=substr($path, 0, -1); - } - } - $shareOwner = substr($path, 1, strpos($path, '/', 1) - 1); + $shareOwner = substr($path, 1, strpos($path, '/', 1) - 1); - if (OCP\User::userExists($shareOwner)) { - OC_Util::setupFS($shareOwner); - $fileSource = getId($path); - if ($fileSource != -1 ) { - $linkItem = OCP\Share::getItemSharedWithByLink($type, $fileSource, $shareOwner); - $pathAndUser['path'] = $path; - $path_parts = explode('/', $path, 5); - $pathAndUser['user'] = $path_parts[1]; - $fileOwner = $path_parts[1]; + if (OCP\User::userExists($shareOwner)) { + OC_Util::setupFS($shareOwner); + $fileSource = getId($path); + if ($fileSource != -1) { + $linkItem = OCP\Share::getItemSharedWithByLink($type, $fileSource, $shareOwner); + $pathAndUser['path'] = $path; + $path_parts = explode('/', $path, 5); + $pathAndUser['user'] = $path_parts[1]; + $fileOwner = $path_parts[1]; + } } } } if ($linkItem) { if (!isset($linkItem['item_type'])) { - OCP\Util::writeLog('share', 'No item type set for share id: '.$linkItem['id'], \OCP\Util::ERROR); + OCP\Util::writeLog('share', 'No item type set for share id: ' . $linkItem['id'], \OCP\Util::ERROR); header('HTTP/1.0 404 Not Found'); $tmpl = new OCP\Template('', '404', 'guest'); $tmpl->printPage(); @@ -128,11 +134,13 @@ if ($linkItem) { } if (isset($linkItem['share_with'])) { // Authenticate share_with - $url = OCP\Util::linkToPublic('files').'&t='.$token; + $url = OCP\Util::linkToPublic('files') . '&t=' . $token; if (isset($_GET['file'])) { - $url .= '&file='.urlencode($_GET['file']); - } else if (isset($_GET['dir'])) { - $url .= '&dir='.urlencode($_GET['dir']); + $url .= '&file=' . urlencode($_GET['file']); + } else { + if (isset($_GET['dir'])) { + $url .= '&dir=' . urlencode($_GET['dir']); + } } if (isset($_POST['password'])) { $password = $_POST['password']; @@ -173,13 +181,13 @@ if ($linkItem) { } } } - $basePath = substr($pathAndUser['path'], strlen('/'.$fileOwner.'/files')); + $basePath = substr($pathAndUser['path'], strlen('/' . $fileOwner . '/files')); $path = $basePath; if (isset($_GET['path'])) { $path .= $_GET['path']; } - if (!$path || !OC_Filesystem::isValidPath($path) || !OC_Filesystem::file_exists($path)) { - OCP\Util::writeLog('share', 'Invalid path '.$path.' for share id '.$linkItem['id'], \OCP\Util::ERROR); + if (!$path || !\OC\Files\Filesystem::isValidPath($path) || !\OC\Files\Filesystem::file_exists($path)) { + OCP\Util::writeLog('share', 'Invalid path ' . $path . ' for share id ' . $linkItem['id'], \OCP\Util::ERROR); header('HTTP/1.0 404 Not Found'); $tmpl = new OCP\Template('', '404', 'guest'); $tmpl->printPage(); @@ -189,13 +197,15 @@ if ($linkItem) { $file = basename($path); // Download the file if (isset($_GET['download'])) { - if (isset($_GET['path']) && $_GET['path'] !== '' ) { - if ( isset($_GET['files']) ) { // download selected files + if (isset($_GET['path']) && $_GET['path'] !== '') { + if (isset($_GET['files'])) { // download selected files OC_Files::get($path, $_GET['files'], $_SERVER['REQUEST_METHOD'] == 'HEAD' ? true : false); - } else if (isset($_GET['path']) && $_GET['path'] != '' ) { // download a file from a shared directory - OC_Files::get($dir, $file, $_SERVER['REQUEST_METHOD'] == 'HEAD' ? true : false); - } else { // download the whole shared directory - OC_Files::get($dir, $file, $_SERVER['REQUEST_METHOD'] == 'HEAD' ? true : false); + } else { + if (isset($_GET['path']) && $_GET['path'] != '') { // download a file from a shared directory + OC_Files::get($dir, $file, $_SERVER['REQUEST_METHOD'] == 'HEAD' ? true : false); + } else { // download the whole shared directory + OC_Files::get($dir, $file, $_SERVER['REQUEST_METHOD'] == 'HEAD' ? true : false); + } } } else { // download a single shared file OC_Files::get($dir, $file, $_SERVER['REQUEST_METHOD'] == 'HEAD' ? true : false); @@ -207,9 +217,10 @@ if ($linkItem) { OCP\Util::addScript('files', 'fileactions'); $tmpl = new OCP\Template('files_sharing', 'public', 'base'); $tmpl->assign('uidOwner', $shareOwner); + $tmpl->assign('displayName', \OCP\User::getDisplayName($shareOwner)); $tmpl->assign('dir', $dir); $tmpl->assign('filename', $file); - $tmpl->assign('mimetype', OC_Filesystem::getMimeType($path)); + $tmpl->assign('mimetype', \OC\Files\Filesystem::getMimeType($path)); if (isset($_GET['path'])) { $getPath = $_GET['path']; } else { @@ -220,10 +231,11 @@ if ($linkItem) { .(isset($_GET['dir'])?'&dir='.$_GET['dir']:'') .(isset($_GET['file'])?'&file='.$_GET['file']:''); // Show file list - if (OC_Filesystem::is_dir($path)) { + if (\OC\Files\Filesystem::is_dir($path)) { OCP\Util::addStyle('files', 'files'); OCP\Util::addScript('files', 'files'); OCP\Util::addScript('files', 'filelist'); + OCP\Util::addscript('files', 'keyboardshortcuts'); $files = array(); $rootLength = strlen($basePath) + 1; foreach (OC_Files::getDirectoryContent($path) as $i) { @@ -231,9 +243,9 @@ if ($linkItem) { if ($i['type'] == 'file') { $fileinfo = pathinfo($i['name']); $i['basename'] = $fileinfo['filename']; - $i['extension'] = isset($fileinfo['extension']) ? ('.'.$fileinfo['extension']) : ''; + $i['extension'] = isset($fileinfo['extension']) ? ('.' . $fileinfo['extension']) : ''; } - $i['directory'] = '/'.substr($i['directory'], $rootLength); + $i['directory'] = '/' . substr($i['directory'], $rootLength); if ($i['directory'] == '/') { $i['directory'] = ''; } @@ -250,14 +262,142 @@ if ($linkItem) { //add subdir breadcrumbs foreach (explode('/', urldecode($getPath)) as $i) { if ($i != '') { - $pathtohere .= '/'.$i; + $pathtohere .= '/' . $i; $breadcrumb[] = array('dir' => $pathtohere, 'name' => $i); + $path = $linkItem['path']; + if (isset($_GET['path'])) { + $path .= $_GET['path']; + $dir .= $_GET['path']; + if (!\OC\Files\Filesystem::file_exists($path)) { + header('HTTP/1.0 404 Not Found'); + $tmpl = new OCP\Template('', '404', 'guest'); + $tmpl->printPage(); + exit(); + } + } + + $list = new OCP\Template('files', 'part.list', ''); + $list->assign('files', $files, false); + $list->assign('publicListView', true); + $list->assign('baseURL', OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&path=', false); + $list->assign('downloadURL', OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&download&path=', false); + $breadcrumbNav = new OCP\Template('files', 'part.breadcrumb', ''); + $breadcrumbNav->assign('breadcrumb', $breadcrumb, false); + $breadcrumbNav->assign('baseURL', OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&path=', false); + $folder = new OCP\Template('files', 'index', ''); + $folder->assign('fileList', $list->fetchPage(), false); + $folder->assign('breadcrumb', $breadcrumbNav->fetchPage(), false); + $folder->assign('isCreatable', false); + $folder->assign('permissions', 0); + $folder->assign('files', $files); + $folder->assign('uploadMaxFilesize', 0); + $folder->assign('uploadMaxHumanFilesize', 0); + $folder->assign('allowZipDownload', intval(OCP\Config::getSystemValue('allowZipDownload', true))); + $tmpl->assign('folder', $folder->fetchPage(), false); + $tmpl->assign('allowZipDownload', intval(OCP\Config::getSystemValue('allowZipDownload', true))); + $tmpl->assign('downloadURL', OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&download&path=' . urlencode($getPath)); + } else { + // Show file preview if viewer is available + if ($type == 'file') { + $tmpl->assign('downloadURL', OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&download'); + } else { + OCP\Util::addStyle('files_sharing', 'public'); + OCP\Util::addScript('files_sharing', 'public'); + OCP\Util::addScript('files', 'fileactions'); + $tmpl = new OCP\Template('files_sharing', 'public', 'base'); + $tmpl->assign('owner', $uidOwner); + // Show file list + if (\OC\Files\Filesystem::is_dir($path)) { + OCP\Util::addStyle('files', 'files'); + OCP\Util::addScript('files', 'files'); + OCP\Util::addScript('files', 'filelist'); + $files = array(); + $rootLength = strlen($baseDir) + 1; + foreach (OC_Files::getDirectoryContent($path) as $i) { + $i['date'] = OCP\Util::formatDate($i['mtime']); + if ($i['type'] == 'file') { + $fileinfo = pathinfo($i['name']); + $i['basename'] = $fileinfo['filename']; + $i['extension'] = isset($fileinfo['extension']) ? ('.' . $fileinfo['extension']) : ''; + } + $i['directory'] = '/' . substr('/' . $uidOwner . '/files' . $i['directory'], $rootLength); + if ($i['directory'] == '/') { + $i['directory'] = ''; + } + $i['permissions'] = OCP\PERMISSION_READ; + $files[] = $i; + } + // Make breadcrumb + $breadcrumb = array(); + $pathtohere = ''; + $count = 1; + foreach (explode('/', $dir) as $i) { + if ($i != '') { + if ($i != $baseDir) { + $pathtohere .= '/' . $i; + } + if (strlen($pathtohere) < strlen($_GET['dir'])) { + continue; + } + $breadcrumb[] = array('dir' => str_replace($_GET['dir'], "", $pathtohere, $count), 'name' => $i); + } + } + $list = new OCP\Template('files', 'part.list', ''); + $list->assign('files', $files, false); + $list->assign('publicListView', true); + $list->assign('baseURL', OCP\Util::linkToPublic('files') . '&dir=' . urlencode($_GET['dir']) . '&path=', false); + $list->assign('downloadURL', OCP\Util::linkToPublic('files') . '&download&dir=' . urlencode($_GET['dir']) . '&path=', false); + $breadcrumbNav = new OCP\Template('files', 'part.breadcrumb', ''); + $breadcrumbNav->assign('breadcrumb', $breadcrumb, false); + $breadcrumbNav->assign('baseURL', OCP\Util::linkToPublic('files') . '&dir=' . urlencode($_GET['dir']) . '&path=', false); + $folder = new OCP\Template('files', 'index', ''); + $folder->assign('fileList', $list->fetchPage(), false); + $folder->assign('breadcrumb', $breadcrumbNav->fetchPage(), false); + $folder->assign('dir', basename($dir)); + $folder->assign('isCreatable', false); + $folder->assign('permissions', 0); + $folder->assign('files', $files); + $folder->assign('uploadMaxFilesize', 0); + $folder->assign('uploadMaxHumanFilesize', 0); + $folder->assign('allowZipDownload', intval(OCP\Config::getSystemValue('allowZipDownload', true))); + $tmpl->assign('folder', $folder->fetchPage(), false); + $tmpl->assign('uidOwner', $uidOwner); + $tmpl->assign('dir', basename($dir)); + $tmpl->assign('filename', basename($path)); + $tmpl->assign('mimetype', \OC\Files\Filesystem::getMimeType($path)); + $tmpl->assign('allowZipDownload', intval(OCP\Config::getSystemValue('allowZipDownload', true))); + if (isset($_GET['path'])) { + $getPath = $_GET['path']; + } else { + $getPath = ''; + } + $tmpl->assign('downloadURL', OCP\Util::linkToPublic('files') . '&download&dir=' . urlencode($_GET['dir']) . '&path=' . urlencode($getPath), false); + } else { + // Show file preview if viewer is available + $tmpl->assign('uidOwner', $uidOwner); + $tmpl->assign('dir', dirname($path)); + $tmpl->assign('filename', basename($path)); + $tmpl->assign('mimetype', \OC\Files\Filesystem::getMimeType($path)); + if ($type == 'file') { + $tmpl->assign('downloadURL', OCP\Util::linkToPublic('files') . '&file=' . urlencode($_GET['file']) . '&download', false); + } else { + if (isset($_GET['path'])) { + $getPath = $_GET['path']; + } else { + $getPath = ''; + } + $tmpl->assign('downloadURL', OCP\Util::linkToPublic('files') . '&download&dir=' . urlencode($_GET['dir']) . '&path=' . urlencode($getPath), false); + } + } + $tmpl->printPage(); + } } + $tmpl->printPage(); } $list = new OCP\Template('files', 'part.list', ''); $list->assign('files', $files, false); - $list->assign('publicListView', true); + $list->assign('disableSharing', true); $list->assign('baseURL', OCP\Util::linkToPublic('files').$urlLinkIdentifiers.'&path=', false); $list->assign('downloadURL', OCP\Util::linkToPublic('files').$urlLinkIdentifiers.'&download&path=', false); $breadcrumbNav = new OCP\Template('files', 'part.breadcrumb', '' ); @@ -278,21 +418,11 @@ if ($linkItem) { $tmpl->assign('downloadURL', OCP\Util::linkToPublic('files') .$urlLinkIdentifiers.'&download&path='.urlencode($getPath)); } else { - // Show file preview if viewer is available - if ($type == 'file') { - $tmpl->assign('downloadURL', OCP\Util::linkToPublic('files') - .$urlLinkIdentifiers.'&download'); - } else { - $tmpl->assign('downloadURL', OCP\Util::linkToPublic('files') - .$urlLinkIdentifiers.'&download&path='.urlencode($getPath)); - } + OCP\Util::writeLog('share', 'could not resolve linkItem', \OCP\Util::DEBUG); } - $tmpl->printPage(); } - exit(); -} else { - OCP\Util::writeLog('share', 'could not resolve linkItem', \OCP\Util::DEBUG); } header('HTTP/1.0 404 Not Found'); $tmpl = new OCP\Template('', '404', 'guest'); $tmpl->printPage(); + diff --git a/apps/files_sharing/templates/public.php b/apps/files_sharing/templates/public.php index 647e1e08a3..71fca09ed6 100644 --- a/apps/files_sharing/templates/public.php +++ b/apps/files_sharing/templates/public.php @@ -1,11 +1,3 @@ - @@ -14,9 +6,9 @@ ownCloud
- t('%s shared the folder %s with you', array($_['uidOwner'], $_['filename'])) ?> + t('%s shared the folder %s with you', array($_['displayName'], $_['filename'])) ?> - t('%s shared the file %s with you', array($_['uidOwner'], $_['filename'])) ?> + t('%s shared the file %s with you', array($_['displayName'], $_['filename'])) ?> Download" />t('Download')?> diff --git a/apps/files_trashbin/ajax/undelete.php b/apps/files_trashbin/ajax/undelete.php new file mode 100644 index 0000000000..ee1c64aaaf --- /dev/null +++ b/apps/files_trashbin/ajax/undelete.php @@ -0,0 +1,44 @@ + array("message" => "Couldn't restore ".rtrim($filelist,', '), "success" => $success, "error" => $error))); +} else { + OCP\JSON::success(array("data" => array("success" => $success))); +} + diff --git a/apps/files_trashbin/appinfo/app.php b/apps/files_trashbin/appinfo/app.php new file mode 100644 index 0000000000..3741d42c78 --- /dev/null +++ b/apps/files_trashbin/appinfo/app.php @@ -0,0 +1,7 @@ + + + + *dbname* + true + false + + utf8 + + + + *dbprefix*files_trash + + + + + id + text + + true + 50 + + + + user + text + + true + 50 + + + + timestamp + text + + true + 12 + + + + location + text + + true + 200 + + + + type + text + + true + 4 + + + + mime + text + + true + 30 + + + + id_index + + id + ascending + + + + + timestamp_index + + timestamp + ascending + + + + + user_index + + user + ascending + + + + + +
+ +
diff --git a/apps/files_trashbin/appinfo/info.xml b/apps/files_trashbin/appinfo/info.xml new file mode 100644 index 0000000000..9b48612636 --- /dev/null +++ b/apps/files_trashbin/appinfo/info.xml @@ -0,0 +1,14 @@ + + + files_trashbin + Trash + Trash bin + AGPL + Bjoern Schiessle + true + 4.9 + + + + + diff --git a/apps/files_trashbin/appinfo/version b/apps/files_trashbin/appinfo/version new file mode 100644 index 0000000000..49d59571fb --- /dev/null +++ b/apps/files_trashbin/appinfo/version @@ -0,0 +1 @@ +0.1 diff --git a/apps/files_trashbin/download.php b/apps/files_trashbin/download.php new file mode 100644 index 0000000000..665697dca5 --- /dev/null +++ b/apps/files_trashbin/download.php @@ -0,0 +1,51 @@ +. +* +*/ + +// Check if we are a user +OCP\User::checkLoggedIn(); + +$filename = $_GET["file"]; + +$view = new OC_FilesystemView('/'.\OCP\User::getUser().'/files_trashbin'); + +if(!$view->file_exists($filename)) { + header("HTTP/1.0 404 Not Found"); + $tmpl = new OCP\Template( '', '404', 'guest' ); + $tmpl->assign('file', $filename); + $tmpl->printPage(); + exit; +} + +$ftype=$view->getMimeType( $filename ); + +header('Content-Type:'.$ftype);if ( preg_match( "/MSIE/", $_SERVER["HTTP_USER_AGENT"] ) ) { + header( 'Content-Disposition: attachment; filename="' . rawurlencode( basename($filename) ) . '"' ); +} else { + header( 'Content-Disposition: attachment; filename*=UTF-8\'\'' . rawurlencode( basename($filename) ) + . '; filename="' . rawurlencode( basename($filename) ) . '"' ); +} +OCP\Response::disableCaching(); +header('Content-Length: '. $view->filesize($filename)); + +OC_Util::obEnd(); +$view->readfile( $filename ); diff --git a/apps/files_trashbin/index.php b/apps/files_trashbin/index.php new file mode 100644 index 0000000000..46a601cfdd --- /dev/null +++ b/apps/files_trashbin/index.php @@ -0,0 +1,100 @@ +getAbsolutePath($dir); + $dirContent = opendir($fullpath); + $i = 0; + while($entryName = readdir($dirContent)) { + if ( $entryName != '.' && $entryName != '..' ) { + $pos = strpos($dir.'/', '/', 1); + $tmp = substr($dir, 0, $pos); + $pos = strrpos($tmp, '.d'); + $timestamp = substr($tmp,$pos+2); + $result[] = array( + 'id' => $entryName, + 'timestamp' => $timestamp, + 'mime' => $view->getMimeType($dir.'/'.$entryName), + 'type' => $view->is_dir($dir.'/'.$entryName) ? 'dir' : 'file', + 'location' => $dir, + ); + } + } + closedir($fullpath); + +} else { + $dirlisting = false; + $query = \OC_DB::prepare('SELECT id,location,timestamp,type,mime FROM *PREFIX*files_trash WHERE user=?'); + $result = $query->execute(array($user))->fetchAll(); +} + +$files = array(); +foreach ($result as $r) { + $i = array(); + $i['name'] = $r['id']; + $i['date'] = OCP\Util::formatDate($r['timestamp']); + $i['timestamp'] = $r['timestamp']; + $i['mimetype'] = $r['mime']; + $i['type'] = $r['type']; + if ($i['type'] == 'file') { + $fileinfo = pathinfo($r['id']); + $i['basename'] = $fileinfo['filename']; + $i['extension'] = isset($fileinfo['extension']) ? ('.'.$fileinfo['extension']) : ''; + } + $i['directory'] = $r['location']; + if ($i['directory'] == '/') { + $i['directory'] = ''; + } + $i['permissions'] = OCP\PERMISSION_READ; + $files[] = $i; +} + +// Make breadcrumb +$breadcrumb = array(array('dir' => '', 'name' => 'Trash')); +$pathtohere = ''; +foreach (explode('/', $dir) as $i) { + if ($i != '') { + if ( preg_match('/^(.+)\.d[0-9]+$/', $i, $match) ) { + $name = $match[1]; + } else { + $name = $i; + } + $pathtohere .= '/' . $i; + $breadcrumb[] = array('dir' => $pathtohere, 'name' => $name); + } +} + +$breadcrumbNav = new OCP\Template('files', 'part.breadcrumb', ''); +$breadcrumbNav->assign('breadcrumb', $breadcrumb, false); +$breadcrumbNav->assign('baseURL', OCP\Util::linkTo('files_trashbin', 'index.php') . '?dir=', false); + +$list = new OCP\Template('files_trashbin', 'part.list', ''); +$list->assign('files', $files, false); +$list->assign('baseURL', OCP\Util::linkTo('files_trashbin', 'index.php'). '?dir='.$dir, false); +$list->assign('downloadURL', OCP\Util::linkTo('files_trashbin', 'download.php') . '?file='.$dir, false); +$list->assign('disableSharing', true); +$list->assign('dirlisting', $dirlisting); +$list->assign('disableDownloadActions', true); +$tmpl->assign('breadcrumb', $breadcrumbNav->fetchPage(), false); +$tmpl->assign('fileList', $list->fetchPage(), false); +$tmpl->assign('files', $files); +$tmpl->assign('dir', OC_Filesystem::normalizePath($view->getAbsolutePath())); + +$tmpl->printPage(); diff --git a/apps/files_trashbin/js/disableDefaultActions.js b/apps/files_trashbin/js/disableDefaultActions.js new file mode 100644 index 0000000000..56b95407dd --- /dev/null +++ b/apps/files_trashbin/js/disableDefaultActions.js @@ -0,0 +1,3 @@ +/* disable download and sharing actions */ +var disableDownloadActions = true; +var disableSharing = true; diff --git a/apps/files_trashbin/js/trash.js b/apps/files_trashbin/js/trash.js new file mode 100644 index 0000000000..f1241fce51 --- /dev/null +++ b/apps/files_trashbin/js/trash.js @@ -0,0 +1,158 @@ + +$(document).ready(function() { + + if (typeof FileActions !== 'undefined') { + FileActions.register('all', 'Restore', OC.PERMISSION_READ, OC.imagePath('core', 'actions/undelete.png'), function(filename) { + var tr=$('tr').filterAttr('data-file', filename); + var spinner = ''; + var undeleteAction = $('tr').filterAttr('data-file',filename).children("td.date"); + undeleteAction[0].innerHTML = undeleteAction[0].innerHTML+spinner; + $.post(OC.filePath('files_trashbin','ajax','undelete.php'), + {files:tr.attr('data-file'), dirlisting:tr.attr('data-dirlisting') }, + function(result){ + for (var i = 0; i < result.data.success.length; i++) { + var row = document.getElementById(result.data.success[i].filename); + row.parentNode.removeChild(row); + } + if (result.status != 'success') { + OC.dialogs.alert(result.data.message, 'Error'); + } + }); + + }); + }; + + // Sets the select_all checkbox behaviour : + $('#select_all').click(function() { + if($(this).attr('checked')){ + // Check all + $('td.filename input:checkbox').attr('checked', true); + $('td.filename input:checkbox').parent().parent().addClass('selected'); + }else{ + // Uncheck all + $('td.filename input:checkbox').attr('checked', false); + $('td.filename input:checkbox').parent().parent().removeClass('selected'); + } + processSelection(); + }); + + $('td.filename input:checkbox').live('change',function(event) { + if (event.shiftKey) { + var last = $(lastChecked).parent().parent().prevAll().length; + var first = $(this).parent().parent().prevAll().length; + var start = Math.min(first, last); + var end = Math.max(first, last); + var rows = $(this).parent().parent().parent().children('tr'); + for (var i = start; i < end; i++) { + $(rows).each(function(index) { + if (index == i) { + var checkbox = $(this).children().children('input:checkbox'); + $(checkbox).attr('checked', 'checked'); + $(checkbox).parent().parent().addClass('selected'); + } + }); + } + } + var selectedCount=$('td.filename input:checkbox:checked').length; + $(this).parent().parent().toggleClass('selected'); + if(!$(this).attr('checked')){ + $('#select_all').attr('checked',false); + }else{ + if(selectedCount==$('td.filename input:checkbox').length){ + $('#select_all').attr('checked',true); + } + } + processSelection(); + }); + + $('.undelete').click('click',function(event) { + var spinner = ''; + var files=getSelectedFiles('file'); + var fileslist=files.join(';'); + var dirlisting=getSelectedFiles('dirlisting')[0]; + + for (var i in files) { + var undeleteAction = $('tr').filterAttr('data-file',files[i]).children("td.date"); + undeleteAction[0].innerHTML = undeleteAction[0].innerHTML+spinner; + } + + $.post(OC.filePath('files_trashbin','ajax','undelete.php'), + {files:fileslist, dirlisting:dirlisting}, + function(result){ + for (var i = 0; i < result.data.success.length; i++) { + var row = document.getElementById(result.data.success[i].filename); + row.parentNode.removeChild(row); + } + if (result.status != 'success') { + OC.dialogs.alert(result.data.message, 'Error'); + } + }); + }); + + +}); + +function processSelection(){ + var selected=getSelectedFiles(); + var selectedFiles=selected.filter(function(el){return el.type=='file'}); + var selectedFolders=selected.filter(function(el){return el.type=='dir'}); + if(selectedFiles.length==0 && selectedFolders.length==0) { + $('#headerName>span.name').text(t('files','Name')); + $('#modified').text(t('files','Deleted')); + $('table').removeClass('multiselect'); + $('.selectedActions').hide(); + } + else { + $('.selectedActions').show(); + var selection=''; + if(selectedFolders.length>0){ + if(selectedFolders.length==1){ + selection+=t('files','1 folder'); + }else{ + selection+=t('files','{count} folders',{count: selectedFolders.length}); + } + if(selectedFiles.length>0){ + selection+=' & '; + } + } + if(selectedFiles.length>0){ + if(selectedFiles.length==1){ + selection+=t('files','1 file'); + }else{ + selection+=t('files','{count} files',{count: selectedFiles.length}); + } + } + $('#headerName>span.name').text(selection); + $('#modified').text(''); + $('table').addClass('multiselect'); + } +} + +/** + * @brief get a list of selected files + * @param string property (option) the property of the file requested + * @return array + * + * possible values for property: name, mime, size and type + * if property is set, an array with that property for each file is returnd + * if it's ommited an array of objects with all properties is returned + */ +function getSelectedFiles(property){ + var elements=$('td.filename input:checkbox:checked').parent().parent(); + var files=[]; + elements.each(function(i,element){ + var file={ + name:$(element).attr('data-filename'), + file:$(element).attr('data-file'), + timestamp:$(element).attr('data-timestamp'), + type:$(element).attr('data-type'), + dirlisting:$(element).attr('data-dirlisting') + }; + if(property){ + files.push(file[property]); + }else{ + files.push(file); + } + }); + return files; +} \ No newline at end of file diff --git a/apps/files_trashbin/l10n/.gitkeep b/apps/files_trashbin/l10n/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/files_trashbin/l10n/ar.php b/apps/files_trashbin/l10n/ar.php new file mode 100644 index 0000000000..e38130fe2d --- /dev/null +++ b/apps/files_trashbin/l10n/ar.php @@ -0,0 +1,3 @@ + "اسم" +); diff --git a/apps/files_trashbin/l10n/bg_BG.php b/apps/files_trashbin/l10n/bg_BG.php new file mode 100644 index 0000000000..681c1dc580 --- /dev/null +++ b/apps/files_trashbin/l10n/bg_BG.php @@ -0,0 +1,3 @@ + "Име" +); diff --git a/apps/files_trashbin/l10n/bn_BD.php b/apps/files_trashbin/l10n/bn_BD.php new file mode 100644 index 0000000000..c669eff7e1 --- /dev/null +++ b/apps/files_trashbin/l10n/bn_BD.php @@ -0,0 +1,7 @@ + "রাম", +"1 folder" => "১টি ফোল্ডার", +"{count} folders" => "{count} টি ফোল্ডার", +"1 file" => "১টি ফাইল", +"{count} files" => "{count} টি ফাইল" +); diff --git a/apps/files_trashbin/l10n/ca.php b/apps/files_trashbin/l10n/ca.php new file mode 100644 index 0000000000..3af33c8a31 --- /dev/null +++ b/apps/files_trashbin/l10n/ca.php @@ -0,0 +1,11 @@ + "executa l'operació de restauració", +"Name" => "Nom", +"Deleted" => "Eliminat", +"1 folder" => "1 carpeta", +"{count} folders" => "{count} carpetes", +"1 file" => "1 fitxer", +"{count} files" => "{count} fitxers", +"Nothing in here. Your trash bin is empty!" => "La paperera està buida!", +"Restore" => "Recupera" +); diff --git a/apps/files_trashbin/l10n/cs_CZ.php b/apps/files_trashbin/l10n/cs_CZ.php new file mode 100644 index 0000000000..caaaea3743 --- /dev/null +++ b/apps/files_trashbin/l10n/cs_CZ.php @@ -0,0 +1,11 @@ + "provést obnovu", +"Name" => "Název", +"Deleted" => "Smazáno", +"1 folder" => "1 složka", +"{count} folders" => "{count} složky", +"1 file" => "1 soubor", +"{count} files" => "{count} soubory", +"Nothing in here. Your trash bin is empty!" => "Žádný obsah. Váš koš je prázdný.", +"Restore" => "Obnovit" +); diff --git a/apps/files_trashbin/l10n/da.php b/apps/files_trashbin/l10n/da.php new file mode 100644 index 0000000000..3343b6fc8f --- /dev/null +++ b/apps/files_trashbin/l10n/da.php @@ -0,0 +1,8 @@ + "Navn", +"1 folder" => "1 mappe", +"{count} folders" => "{count} mapper", +"1 file" => "1 fil", +"{count} files" => "{count} filer", +"Restore" => "Gendan" +); diff --git a/apps/files_trashbin/l10n/de.php b/apps/files_trashbin/l10n/de.php new file mode 100644 index 0000000000..45dfb9d605 --- /dev/null +++ b/apps/files_trashbin/l10n/de.php @@ -0,0 +1,11 @@ + "Wiederherstellung ausführen", +"Name" => "Name", +"Deleted" => "gelöscht", +"1 folder" => "1 Ordner", +"{count} folders" => "{count} Ordner", +"1 file" => "1 Datei", +"{count} files" => "{count} Dateien", +"Nothing in here. Your trash bin is empty!" => "Nichts zu löschen, der Papierkorb ist leer!", +"Restore" => "Wiederherstellen" +); diff --git a/apps/files_trashbin/l10n/de_DE.php b/apps/files_trashbin/l10n/de_DE.php new file mode 100644 index 0000000000..45e30d85a3 --- /dev/null +++ b/apps/files_trashbin/l10n/de_DE.php @@ -0,0 +1,11 @@ + "Führe die Wiederherstellung aus", +"Name" => "Name", +"Deleted" => "Gelöscht", +"1 folder" => "1 Ordner", +"{count} folders" => "{count} Ordner", +"1 file" => "1 Datei", +"{count} files" => "{count} Dateien", +"Nothing in here. Your trash bin is empty!" => "Nichts zu löschen, Ihr Papierkorb ist leer!", +"Restore" => "Wiederherstellen" +); diff --git a/apps/files_trashbin/l10n/el.php b/apps/files_trashbin/l10n/el.php new file mode 100644 index 0000000000..83e359890e --- /dev/null +++ b/apps/files_trashbin/l10n/el.php @@ -0,0 +1,8 @@ + "Όνομα", +"1 folder" => "1 φάκελος", +"{count} folders" => "{count} φάκελοι", +"1 file" => "1 αρχείο", +"{count} files" => "{count} αρχεία", +"Restore" => "Επαναφορά" +); diff --git a/apps/files_trashbin/l10n/eo.php b/apps/files_trashbin/l10n/eo.php new file mode 100644 index 0000000000..f357e3c10c --- /dev/null +++ b/apps/files_trashbin/l10n/eo.php @@ -0,0 +1,8 @@ + "Nomo", +"1 folder" => "1 dosierujo", +"{count} folders" => "{count} dosierujoj", +"1 file" => "1 dosiero", +"{count} files" => "{count} dosierujoj", +"Restore" => "Restaŭri" +); diff --git a/apps/files_trashbin/l10n/es.php b/apps/files_trashbin/l10n/es.php new file mode 100644 index 0000000000..798322cab2 --- /dev/null +++ b/apps/files_trashbin/l10n/es.php @@ -0,0 +1,8 @@ + "Nombre", +"1 folder" => "1 carpeta", +"{count} folders" => "{count} carpetas", +"1 file" => "1 archivo", +"{count} files" => "{count} archivos", +"Restore" => "Recuperar" +); diff --git a/apps/files_trashbin/l10n/es_AR.php b/apps/files_trashbin/l10n/es_AR.php new file mode 100644 index 0000000000..d2c5f30428 --- /dev/null +++ b/apps/files_trashbin/l10n/es_AR.php @@ -0,0 +1,8 @@ + "Nombre", +"1 folder" => "1 directorio", +"{count} folders" => "{count} directorios", +"1 file" => "1 archivo", +"{count} files" => "{count} archivos", +"Restore" => "Recuperar" +); diff --git a/apps/files_trashbin/l10n/et_EE.php b/apps/files_trashbin/l10n/et_EE.php new file mode 100644 index 0000000000..4f46f38802 --- /dev/null +++ b/apps/files_trashbin/l10n/et_EE.php @@ -0,0 +1,7 @@ + "Nimi", +"1 folder" => "1 kaust", +"{count} folders" => "{count} kausta", +"1 file" => "1 fail", +"{count} files" => "{count} faili" +); diff --git a/apps/files_trashbin/l10n/eu.php b/apps/files_trashbin/l10n/eu.php new file mode 100644 index 0000000000..a1e3ca53e6 --- /dev/null +++ b/apps/files_trashbin/l10n/eu.php @@ -0,0 +1,8 @@ + "Izena", +"1 folder" => "karpeta bat", +"{count} folders" => "{count} karpeta", +"1 file" => "fitxategi bat", +"{count} files" => "{count} fitxategi", +"Restore" => "Berrezarri" +); diff --git a/apps/files_trashbin/l10n/fa.php b/apps/files_trashbin/l10n/fa.php new file mode 100644 index 0000000000..487d165798 --- /dev/null +++ b/apps/files_trashbin/l10n/fa.php @@ -0,0 +1,8 @@ + "نام", +"1 folder" => "1 پوشه", +"{count} folders" => "{ شمار} پوشه ها", +"1 file" => "1 پرونده", +"{count} files" => "{ شمار } فایل ها", +"Restore" => "بازیابی" +); diff --git a/apps/files_trashbin/l10n/fi_FI.php b/apps/files_trashbin/l10n/fi_FI.php new file mode 100644 index 0000000000..de25027f9a --- /dev/null +++ b/apps/files_trashbin/l10n/fi_FI.php @@ -0,0 +1,11 @@ + "suorita palautustoiminto", +"Name" => "Nimi", +"Deleted" => "Poistettu", +"1 folder" => "1 kansio", +"{count} folders" => "{count} kansiota", +"1 file" => "1 tiedosto", +"{count} files" => "{count} tiedostoa", +"Nothing in here. Your trash bin is empty!" => "Tyhjää täynnä! Roskakorissa ei ole mitään.", +"Restore" => "Palauta" +); diff --git a/apps/files_trashbin/l10n/fr.php b/apps/files_trashbin/l10n/fr.php new file mode 100644 index 0000000000..51ade82d90 --- /dev/null +++ b/apps/files_trashbin/l10n/fr.php @@ -0,0 +1,11 @@ + "effectuer l'opération de restauration", +"Name" => "Nom", +"Deleted" => "Effacé", +"1 folder" => "1 dossier", +"{count} folders" => "{count} dossiers", +"1 file" => "1 fichier", +"{count} files" => "{count} fichiers", +"Nothing in here. Your trash bin is empty!" => "Il n'y a rien ici. Votre corbeille est vide !", +"Restore" => "Restaurer" +); diff --git a/apps/files_trashbin/l10n/gl.php b/apps/files_trashbin/l10n/gl.php new file mode 100644 index 0000000000..bdc3187b20 --- /dev/null +++ b/apps/files_trashbin/l10n/gl.php @@ -0,0 +1,8 @@ + "Nome", +"1 folder" => "1 cartafol", +"{count} folders" => "{count} cartafoles", +"1 file" => "1 ficheiro", +"{count} files" => "{count} ficheiros", +"Restore" => "Restablecer" +); diff --git a/apps/files_trashbin/l10n/he.php b/apps/files_trashbin/l10n/he.php new file mode 100644 index 0000000000..d026add5d7 --- /dev/null +++ b/apps/files_trashbin/l10n/he.php @@ -0,0 +1,7 @@ + "שם", +"1 folder" => "תיקייה אחת", +"{count} folders" => "{count} תיקיות", +"1 file" => "קובץ אחד", +"{count} files" => "{count} קבצים" +); diff --git a/apps/files_trashbin/l10n/hr.php b/apps/files_trashbin/l10n/hr.php new file mode 100644 index 0000000000..52255c7429 --- /dev/null +++ b/apps/files_trashbin/l10n/hr.php @@ -0,0 +1,3 @@ + "Ime" +); diff --git a/apps/files_trashbin/l10n/hu_HU.php b/apps/files_trashbin/l10n/hu_HU.php new file mode 100644 index 0000000000..c4e2b5e212 --- /dev/null +++ b/apps/files_trashbin/l10n/hu_HU.php @@ -0,0 +1,8 @@ + "Név", +"1 folder" => "1 mappa", +"{count} folders" => "{count} mappa", +"1 file" => "1 fájl", +"{count} files" => "{count} fájl", +"Restore" => "Visszaállítás" +); diff --git a/apps/files_trashbin/l10n/ia.php b/apps/files_trashbin/l10n/ia.php new file mode 100644 index 0000000000..c2581f3de1 --- /dev/null +++ b/apps/files_trashbin/l10n/ia.php @@ -0,0 +1,3 @@ + "Nomine" +); diff --git a/apps/files_trashbin/l10n/id.php b/apps/files_trashbin/l10n/id.php new file mode 100644 index 0000000000..1a14d8b7c2 --- /dev/null +++ b/apps/files_trashbin/l10n/id.php @@ -0,0 +1,3 @@ + "nama" +); diff --git a/apps/files_trashbin/l10n/is.php b/apps/files_trashbin/l10n/is.php new file mode 100644 index 0000000000..416f641a8e --- /dev/null +++ b/apps/files_trashbin/l10n/is.php @@ -0,0 +1,7 @@ + "Nafn", +"1 folder" => "1 mappa", +"{count} folders" => "{count} möppur", +"1 file" => "1 skrá", +"{count} files" => "{count} skrár" +); diff --git a/apps/files_trashbin/l10n/it.php b/apps/files_trashbin/l10n/it.php new file mode 100644 index 0000000000..7def431a42 --- /dev/null +++ b/apps/files_trashbin/l10n/it.php @@ -0,0 +1,11 @@ + "esegui operazione di ripristino", +"Name" => "Nome", +"Deleted" => "Eliminati", +"1 folder" => "1 cartella", +"{count} folders" => "{count} cartelle", +"1 file" => "1 file", +"{count} files" => "{count} file", +"Nothing in here. Your trash bin is empty!" => "Qui non c'è niente. Il tuo cestino è vuoto.", +"Restore" => "Ripristina" +); diff --git a/apps/files_trashbin/l10n/ja_JP.php b/apps/files_trashbin/l10n/ja_JP.php new file mode 100644 index 0000000000..0b4e1954e7 --- /dev/null +++ b/apps/files_trashbin/l10n/ja_JP.php @@ -0,0 +1,11 @@ + "復元操作を実行する", +"Name" => "名前", +"Deleted" => "削除済み", +"1 folder" => "1 フォルダ", +"{count} folders" => "{count} フォルダ", +"1 file" => "1 ファイル", +"{count} files" => "{count} ファイル", +"Nothing in here. Your trash bin is empty!" => "ここには何もありません。ゴミ箱は空です!", +"Restore" => "復元" +); diff --git a/apps/files_trashbin/l10n/ka_GE.php b/apps/files_trashbin/l10n/ka_GE.php new file mode 100644 index 0000000000..43dba38f5c --- /dev/null +++ b/apps/files_trashbin/l10n/ka_GE.php @@ -0,0 +1,7 @@ + "სახელი", +"1 folder" => "1 საქაღალდე", +"{count} folders" => "{count} საქაღალდე", +"1 file" => "1 ფაილი", +"{count} files" => "{count} ფაილი" +); diff --git a/apps/files_trashbin/l10n/ko.php b/apps/files_trashbin/l10n/ko.php new file mode 100644 index 0000000000..61acd1276a --- /dev/null +++ b/apps/files_trashbin/l10n/ko.php @@ -0,0 +1,8 @@ + "이름", +"1 folder" => "폴더 1개", +"{count} folders" => "폴더 {count}개", +"1 file" => "파일 1개", +"{count} files" => "파일 {count}개", +"Restore" => "복원" +); diff --git a/apps/files_trashbin/l10n/ku_IQ.php b/apps/files_trashbin/l10n/ku_IQ.php new file mode 100644 index 0000000000..cbdbe4644d --- /dev/null +++ b/apps/files_trashbin/l10n/ku_IQ.php @@ -0,0 +1,3 @@ + "ناو" +); diff --git a/apps/files_trashbin/l10n/lb.php b/apps/files_trashbin/l10n/lb.php new file mode 100644 index 0000000000..d1bd751866 --- /dev/null +++ b/apps/files_trashbin/l10n/lb.php @@ -0,0 +1,3 @@ + "Numm" +); diff --git a/apps/files_trashbin/l10n/lt_LT.php b/apps/files_trashbin/l10n/lt_LT.php new file mode 100644 index 0000000000..4933e97202 --- /dev/null +++ b/apps/files_trashbin/l10n/lt_LT.php @@ -0,0 +1,7 @@ + "Pavadinimas", +"1 folder" => "1 aplankalas", +"{count} folders" => "{count} aplankalai", +"1 file" => "1 failas", +"{count} files" => "{count} failai" +); diff --git a/apps/files_trashbin/l10n/lv.php b/apps/files_trashbin/l10n/lv.php new file mode 100644 index 0000000000..017a8d285c --- /dev/null +++ b/apps/files_trashbin/l10n/lv.php @@ -0,0 +1,11 @@ + "veikt atjaunošanu", +"Name" => "Nosaukums", +"Deleted" => "Dzēsts", +"1 folder" => "1 mape", +"{count} folders" => "{count} mapes", +"1 file" => "1 datne", +"{count} files" => "{count} datnes", +"Nothing in here. Your trash bin is empty!" => "Šeit nekā nav. Jūsu miskaste ir tukša!", +"Restore" => "Atjaunot" +); diff --git a/apps/files_trashbin/l10n/mk.php b/apps/files_trashbin/l10n/mk.php new file mode 100644 index 0000000000..b983c341e8 --- /dev/null +++ b/apps/files_trashbin/l10n/mk.php @@ -0,0 +1,7 @@ + "Име", +"1 folder" => "1 папка", +"{count} folders" => "{count} папки", +"1 file" => "1 датотека", +"{count} files" => "{count} датотеки" +); diff --git a/apps/files_trashbin/l10n/ms_MY.php b/apps/files_trashbin/l10n/ms_MY.php new file mode 100644 index 0000000000..73e97b496e --- /dev/null +++ b/apps/files_trashbin/l10n/ms_MY.php @@ -0,0 +1,3 @@ + "Nama" +); diff --git a/apps/files_trashbin/l10n/nb_NO.php b/apps/files_trashbin/l10n/nb_NO.php new file mode 100644 index 0000000000..49364753d1 --- /dev/null +++ b/apps/files_trashbin/l10n/nb_NO.php @@ -0,0 +1,7 @@ + "Navn", +"1 folder" => "1 mappe", +"{count} folders" => "{count} mapper", +"1 file" => "1 fil", +"{count} files" => "{count} filer" +); diff --git a/apps/files_trashbin/l10n/nl.php b/apps/files_trashbin/l10n/nl.php new file mode 100644 index 0000000000..4efa6ecf66 --- /dev/null +++ b/apps/files_trashbin/l10n/nl.php @@ -0,0 +1,11 @@ + "uitvoeren restore operatie", +"Name" => "Naam", +"Deleted" => "Verwijderd", +"1 folder" => "1 map", +"{count} folders" => "{count} mappen", +"1 file" => "1 bestand", +"{count} files" => "{count} bestanden", +"Nothing in here. Your trash bin is empty!" => "Niets te vinden. Uw prullenbak is leeg!", +"Restore" => "Herstellen" +); diff --git a/apps/files_trashbin/l10n/nn_NO.php b/apps/files_trashbin/l10n/nn_NO.php new file mode 100644 index 0000000000..be60dabdf0 --- /dev/null +++ b/apps/files_trashbin/l10n/nn_NO.php @@ -0,0 +1,3 @@ + "Namn" +); diff --git a/apps/files_trashbin/l10n/oc.php b/apps/files_trashbin/l10n/oc.php new file mode 100644 index 0000000000..2c705193c1 --- /dev/null +++ b/apps/files_trashbin/l10n/oc.php @@ -0,0 +1,3 @@ + "Nom" +); diff --git a/apps/files_trashbin/l10n/pl.php b/apps/files_trashbin/l10n/pl.php new file mode 100644 index 0000000000..d2ada4c946 --- /dev/null +++ b/apps/files_trashbin/l10n/pl.php @@ -0,0 +1,8 @@ + "Nazwa", +"1 folder" => "1 folder", +"{count} folders" => "{count} foldery", +"1 file" => "1 plik", +"{count} files" => "{count} pliki", +"Restore" => "Przywróć" +); diff --git a/apps/files_trashbin/l10n/pt_BR.php b/apps/files_trashbin/l10n/pt_BR.php new file mode 100644 index 0000000000..db5737d923 --- /dev/null +++ b/apps/files_trashbin/l10n/pt_BR.php @@ -0,0 +1,11 @@ + "realizar operação de restauração", +"Name" => "Nome", +"Deleted" => "Excluído", +"1 folder" => "1 pasta", +"{count} folders" => "{count} pastas", +"1 file" => "1 arquivo", +"{count} files" => "{count} arquivos", +"Nothing in here. Your trash bin is empty!" => "Nada aqui. Sua lixeira está vazia!", +"Restore" => "Restaurar" +); diff --git a/apps/files_trashbin/l10n/pt_PT.php b/apps/files_trashbin/l10n/pt_PT.php new file mode 100644 index 0000000000..79930315b0 --- /dev/null +++ b/apps/files_trashbin/l10n/pt_PT.php @@ -0,0 +1,11 @@ + "Restaurar", +"Name" => "Nome", +"Deleted" => "Apagado", +"1 folder" => "1 pasta", +"{count} folders" => "{count} pastas", +"1 file" => "1 ficheiro", +"{count} files" => "{count} ficheiros", +"Nothing in here. Your trash bin is empty!" => "Não ha ficheiros. O lixo está vazio", +"Restore" => "Restaurar" +); diff --git a/apps/files_trashbin/l10n/ro.php b/apps/files_trashbin/l10n/ro.php new file mode 100644 index 0000000000..6ece51e02c --- /dev/null +++ b/apps/files_trashbin/l10n/ro.php @@ -0,0 +1,7 @@ + "Nume", +"1 folder" => "1 folder", +"{count} folders" => "{count} foldare", +"1 file" => "1 fisier", +"{count} files" => "{count} fisiere" +); diff --git a/apps/files_trashbin/l10n/ru.php b/apps/files_trashbin/l10n/ru.php new file mode 100644 index 0000000000..23d739a2ff --- /dev/null +++ b/apps/files_trashbin/l10n/ru.php @@ -0,0 +1,7 @@ + "Имя", +"1 folder" => "1 папка", +"{count} folders" => "{count} папок", +"1 file" => "1 файл", +"{count} files" => "{count} файлов" +); diff --git a/apps/files_trashbin/l10n/ru_RU.php b/apps/files_trashbin/l10n/ru_RU.php new file mode 100644 index 0000000000..8ef2658cf2 --- /dev/null +++ b/apps/files_trashbin/l10n/ru_RU.php @@ -0,0 +1,7 @@ + "Имя", +"1 folder" => "1 папка", +"{count} folders" => "{количество} папок", +"1 file" => "1 файл", +"{count} files" => "{количество} файлов" +); diff --git a/apps/files_trashbin/l10n/si_LK.php b/apps/files_trashbin/l10n/si_LK.php new file mode 100644 index 0000000000..cb351afaec --- /dev/null +++ b/apps/files_trashbin/l10n/si_LK.php @@ -0,0 +1,5 @@ + "නම", +"1 folder" => "1 ෆොල්ඩරයක්", +"1 file" => "1 ගොනුවක්" +); diff --git a/apps/files_trashbin/l10n/sk_SK.php b/apps/files_trashbin/l10n/sk_SK.php new file mode 100644 index 0000000000..854c17ad48 --- /dev/null +++ b/apps/files_trashbin/l10n/sk_SK.php @@ -0,0 +1,8 @@ + "Meno", +"1 folder" => "1 priečinok", +"{count} folders" => "{count} priečinkov", +"1 file" => "1 súbor", +"{count} files" => "{count} súborov", +"Restore" => "Obnoviť" +); diff --git a/apps/files_trashbin/l10n/sl.php b/apps/files_trashbin/l10n/sl.php new file mode 100644 index 0000000000..2579f95c86 --- /dev/null +++ b/apps/files_trashbin/l10n/sl.php @@ -0,0 +1,7 @@ + "Ime", +"1 folder" => "1 mapa", +"{count} folders" => "{count} map", +"1 file" => "1 datoteka", +"{count} files" => "{count} datotek" +); diff --git a/apps/files_trashbin/l10n/sr.php b/apps/files_trashbin/l10n/sr.php new file mode 100644 index 0000000000..36659e7080 --- /dev/null +++ b/apps/files_trashbin/l10n/sr.php @@ -0,0 +1,11 @@ + "врати у претходно стање", +"Name" => "Име", +"Deleted" => "Обрисано", +"1 folder" => "1 фасцикла", +"{count} folders" => "{count} фасцикле/и", +"1 file" => "1 датотека", +"{count} files" => "{count} датотеке/а", +"Nothing in here. Your trash bin is empty!" => "Овде нема ништа. Корпа за отпатке је празна.", +"Restore" => "Врати" +); diff --git a/apps/files_trashbin/l10n/sr@latin.php b/apps/files_trashbin/l10n/sr@latin.php new file mode 100644 index 0000000000..52255c7429 --- /dev/null +++ b/apps/files_trashbin/l10n/sr@latin.php @@ -0,0 +1,3 @@ + "Ime" +); diff --git a/apps/files_trashbin/l10n/sv.php b/apps/files_trashbin/l10n/sv.php new file mode 100644 index 0000000000..ca4dba0496 --- /dev/null +++ b/apps/files_trashbin/l10n/sv.php @@ -0,0 +1,10 @@ + "Namn", +"Deleted" => "Raderad", +"1 folder" => "1 mapp", +"{count} folders" => "{count} mappar", +"1 file" => "1 fil", +"{count} files" => "{count} filer", +"Nothing in here. Your trash bin is empty!" => "Ingenting här. Din papperskorg är tom!", +"Restore" => "Återskapa" +); diff --git a/apps/files_trashbin/l10n/ta_LK.php b/apps/files_trashbin/l10n/ta_LK.php new file mode 100644 index 0000000000..a436e2344a --- /dev/null +++ b/apps/files_trashbin/l10n/ta_LK.php @@ -0,0 +1,7 @@ + "பெயர்", +"1 folder" => "1 கோப்புறை", +"{count} folders" => "{எண்ணிக்கை} கோப்புறைகள்", +"1 file" => "1 கோப்பு", +"{count} files" => "{எண்ணிக்கை} கோப்புகள்" +); diff --git a/apps/files_trashbin/l10n/th_TH.php b/apps/files_trashbin/l10n/th_TH.php new file mode 100644 index 0000000000..8a031fb0d7 --- /dev/null +++ b/apps/files_trashbin/l10n/th_TH.php @@ -0,0 +1,11 @@ + "ดำเนินการคืนค่า", +"Name" => "ชื่อ", +"Deleted" => "ลบแล้ว", +"1 folder" => "1 โฟลเดอร์", +"{count} folders" => "{count} โฟลเดอร์", +"1 file" => "1 ไฟล์", +"{count} files" => "{count} ไฟล์", +"Nothing in here. Your trash bin is empty!" => "ไม่มีอะไรอยู่ในนี้ ถังขยะของคุณยังว่างอยู่", +"Restore" => "คืนค่า" +); diff --git a/apps/files_trashbin/l10n/tr.php b/apps/files_trashbin/l10n/tr.php new file mode 100644 index 0000000000..5b7064ecea --- /dev/null +++ b/apps/files_trashbin/l10n/tr.php @@ -0,0 +1,7 @@ + "İsim", +"1 folder" => "1 dizin", +"{count} folders" => "{count} dizin", +"1 file" => "1 dosya", +"{count} files" => "{count} dosya" +); diff --git a/apps/files_trashbin/l10n/uk.php b/apps/files_trashbin/l10n/uk.php new file mode 100644 index 0000000000..14c6931255 --- /dev/null +++ b/apps/files_trashbin/l10n/uk.php @@ -0,0 +1,7 @@ + "Ім'я", +"1 folder" => "1 папка", +"{count} folders" => "{count} папок", +"1 file" => "1 файл", +"{count} files" => "{count} файлів" +); diff --git a/apps/files_trashbin/l10n/vi.php b/apps/files_trashbin/l10n/vi.php new file mode 100644 index 0000000000..2c51c69aaf --- /dev/null +++ b/apps/files_trashbin/l10n/vi.php @@ -0,0 +1,7 @@ + "Tên", +"1 folder" => "1 thư mục", +"{count} folders" => "{count} thư mục", +"1 file" => "1 tập tin", +"{count} files" => "{count} tập tin" +); diff --git a/apps/files_trashbin/l10n/zh_CN.GB2312.php b/apps/files_trashbin/l10n/zh_CN.GB2312.php new file mode 100644 index 0000000000..2c6a7891e9 --- /dev/null +++ b/apps/files_trashbin/l10n/zh_CN.GB2312.php @@ -0,0 +1,7 @@ + "名称", +"1 folder" => "1 个文件夹", +"{count} folders" => "{count} 个文件夹", +"1 file" => "1 个文件", +"{count} files" => "{count} 个文件" +); diff --git a/apps/files_trashbin/l10n/zh_CN.php b/apps/files_trashbin/l10n/zh_CN.php new file mode 100644 index 0000000000..0060b1f31d --- /dev/null +++ b/apps/files_trashbin/l10n/zh_CN.php @@ -0,0 +1,7 @@ + "名称", +"1 folder" => "1个文件夹", +"{count} folders" => "{count} 个文件夹", +"1 file" => "1 个文件", +"{count} files" => "{count} 个文件" +); diff --git a/apps/files_trashbin/l10n/zh_TW.php b/apps/files_trashbin/l10n/zh_TW.php new file mode 100644 index 0000000000..be61d9b0b6 --- /dev/null +++ b/apps/files_trashbin/l10n/zh_TW.php @@ -0,0 +1,7 @@ + "名稱", +"1 folder" => "1 個資料夾", +"{count} folders" => "{count} 個資料夾", +"1 file" => "1 個檔案", +"{count} files" => "{count} 個檔案" +); diff --git a/apps/files_trashbin/lib/hooks.php b/apps/files_trashbin/lib/hooks.php new file mode 100644 index 0000000000..d3bee105b5 --- /dev/null +++ b/apps/files_trashbin/lib/hooks.php @@ -0,0 +1,45 @@ +. + * + */ + +/** + * This class contains all hooks. + */ + +namespace OCA_Trash; + +class Hooks { + + /** + * @brief Copy files to trash bin + * @param array + * + * This function is connected to the delete signal of OC_Filesystem + * to copy the file to the trash bin + */ + public static function remove_hook($params) { + + if ( \OCP\App::isEnabled('files_trashbin') ) { + $path = $params['path']; + Trashbin::move2trash($path); + } + } +} diff --git a/apps/files_trashbin/lib/trash.php b/apps/files_trashbin/lib/trash.php new file mode 100644 index 0000000000..a7eff3d44e --- /dev/null +++ b/apps/files_trashbin/lib/trash.php @@ -0,0 +1,264 @@ +. + * + */ + +namespace OCA_Trash; + +class Trashbin { + + const DEFAULT_RETENTION_OBLIGATION=180; // how long do we keep files in the trash bin if no other value is defined in the config file (unit: days) + /** + * move file to the trash bin + * + * @param $file_path path to the deleted file/directory relative to the files root directory + */ + public static function move2trash($file_path) { + $user = \OCP\User::getUser(); + $view = new \OC_FilesystemView('/'. $user); + if (!$view->is_dir('files_trashbin')) { + $view->mkdir('files_trashbin'); + $view->mkdir("versions_trashbin"); + } + + $path_parts = pathinfo($file_path); + + $deleted = $path_parts['basename']; + $location = $path_parts['dirname']; + $timestamp = time(); + $mime = $view->getMimeType('files'.$file_path); + + if ( $view->is_dir('files'.$file_path) ) { + $type = 'dir'; + } else { + $type = 'file'; + } + + self::copy_recursive($file_path, 'files_trashbin/'.$deleted.'.d'.$timestamp, $view); + + if ( $view->file_exists('files_trashbin/'.$deleted.'.d'.$timestamp) ) { + $query = \OC_DB::prepare("INSERT INTO *PREFIX*files_trash (id,timestamp,location,type,mime,user) VALUES (?,?,?,?,?,?)"); + $result = $query->execute(array($deleted, $timestamp, $location, $type, $mime, $user)); + if ( !$result ) { // if file couldn't be added to the database than also don't store it in the trash bin. + $view->deleteAll('files_trashbin/'.$deleted.'.d'.$timestamp); + \OC_Log::write('files_trashbin', 'trash bin database couldn\'t be updated', \OC_log::ERROR); + return; + } + + if ( \OCP\App::isEnabled('files_versions') ) { + if ( $view->is_dir('files_versions'.$file_path) ) { + $view->rename('files_versions'.$file_path, 'versions_trashbin/'. $deleted.'.d'.$timestamp); + } else if ( $versions = \OCA_Versions\Storage::getVersions($file_path) ) { + foreach ($versions as $v) { + $view->rename('files_versions'.$v['path'].'.v'.$v['version'], 'versions_trashbin/'. $deleted.'.v'.$v['version'].'.d'.$timestamp); + } + } + } + } else { + \OC_Log::write('files_trashbin', 'Couldn\'t move '.$file_path.' to the trash bin' , \OC_log::ERROR); + } + + self::expire(); + } + + + /** + * restore files from trash bin + * @param $file path to the deleted file + * @param $filename name of the file + * @param $timestamp time when the file was deleted + */ + public static function restore($file, $filename, $timestamp) { + + $user = \OCP\User::getUser(); + $view = new \OC_FilesystemView('/'.$user); + + if ( $timestamp ) { + $query = \OC_DB::prepare('SELECT location,type FROM *PREFIX*files_trash WHERE user=? AND id=? AND timestamp=?'); + $result = $query->execute(array($user,$filename,$timestamp))->fetchAll(); + if ( count($result) != 1 ) { + \OC_Log::write('files_trashbin', 'trash bin database inconsistent!', \OC_Log::ERROR); + return false; + } + + // if location no longer exists, restore file in the root directory + $location = $result[0]['location']; + if ( $result[0]['location'] != '/' && + (!$view->is_dir('files'.$result[0]['location']) || + !$view->isUpdatable('files'.$result[0]['location'])) ) { + $location = ''; + } + } else { + $path_parts = pathinfo($filename); + $result[] = array( + 'location' => $path_parts['dirname'], + 'type' => $view->is_dir('/files_trashbin/'.$file) ? 'dir' : 'files', + ); + $location = ''; + } + + $source = \OC_Filesystem::normalizePath('files_trashbin/'.$file); + $target = \OC_Filesystem::normalizePath('files/'.$location.'/'.$filename); + + // we need a extension in case a file/dir with the same name already exists + $ext = self::getUniqueExtension($location, $filename, $view); + $mtime = $view->filemtime($source); + if( $view->rename($source, $target.$ext) ) { + $view->touch($target.$ext, $mtime); + // if versioning app is enabled, copy versions from the trash bin back to the original location + if ( \OCP\App::isEnabled('files_versions') ) { + if ( $result[0]['type'] == 'dir' ) { + $view->rename(\OC_Filesystem::normalizePath('versions_trashbin/'. $file), \OC_Filesystem::normalizePath('files_versions/'.$location.'/'.$filename.$ext)); + } else if ( $versions = self::getVersionsFromTrash($file, $timestamp) ) { + foreach ($versions as $v) { + if ($timestamp ) { + $view->rename('versions_trashbin/'.$filename.'.v'.$v.'.d'.$timestamp, 'files_versions/'.$location.'/'.$filename.$ext.'.v'.$v); + } else { + $view->rename('versions_trashbin/'.$file.'.v'.$v, 'files_versions/'.$location.'/'.$filename.$ext.'.v'.$v); + } + } + } + } + + if ( $timestamp ) { + $query = \OC_DB::prepare('DELETE FROM *PREFIX*files_trash WHERE user=? AND id=? AND timestamp=?'); + $query->execute(array($user,$filename,$timestamp)); + } + + return true; + } else { + \OC_Log::write('files_trashbin', 'Couldn\'t restore file from trash bin, '.$filename , \OC_log::ERROR); + } + + return false; + } + + /** + * clean up the trash bin + */ + private static function expire() { + + $view = new \OC_FilesystemView('/'.\OCP\User::getUser()); + $user = \OCP\User::getUser(); + + $query = \OC_DB::prepare('SELECT location,type,id,timestamp FROM *PREFIX*files_trash WHERE user=?'); + $result = $query->execute(array($user))->fetchAll(); + + $retention_obligation = \OC_Config::getValue('trashbin_retention_obligation', self::DEFAULT_RETENTION_OBLIGATION); + + $limit = time() - ($retention_obligation * 86400); + + foreach ( $result as $r ) { + $timestamp = $r['timestamp']; + $filename = $r['id']; + if ( $r['timestamp'] < $limit ) { + $view->unlink('files_trashbin/'.$filename.'.d'.$timestamp); + if ($r['type'] == 'dir') { + $view->unlink('versions_trashbin/'.$filename.'.d'.$timestamp); + } else if ( $versions = self::getVersionsFromTrash($filename, $timestamp) ) { + foreach ($versions as $v) { + $view->unlink('versions_trashbin/'.$filename.'.v'.$v.'.d'.$timestamp); + } + } + } + } + + $query = \OC_DB::prepare('DELETE FROM *PREFIX*files_trash WHERE user=? AND timestampexecute(array($user,$limit)); + } + + /** + * recursive copy to copy a whole directory + * + * @param $source source path, relative to the users files directory + * @param $destination destination path relative to the users root directoy + * @param $view file view for the users root directory + */ + private static function copy_recursive( $source, $destination, $view ) { + if ( $view->is_dir( 'files'.$source ) ) { + $view->mkdir( $destination ); + $view->touch($destination, $view->filemtime('files'.$source)); + foreach ( \OC_Files::getDirectoryContent($source) as $i ) { + $pathDir = $source.'/'.$i['name']; + if ( $view->is_dir('files'.$pathDir) ) { + self::copy_recursive($pathDir, $destination.'/'.$i['name'], $view); + } else { + $view->copy( 'files'.$pathDir, $destination . '/' . $i['name'] ); + $view->touch($destination . '/' . $i['name'], $view->filemtime('files'.$pathDir)); + } + } + } else { + $view->copy( 'files'.$source, $destination ); + $view->touch($destination, $view->filemtime('files'.$source)); + } + } + + /** + * find all versions which belong to the file we want to restore + * @param $filename name of the file which should be restored + * @param $timestamp timestamp when the file was deleted + */ + private static function getVersionsFromTrash($filename, $timestamp) { + $view = new \OC_FilesystemView('/'.\OCP\User::getUser().'/versions_trashbin'); + $versionsName = \OCP\Config::getSystemValue('datadirectory').$view->getAbsolutePath($filename); + $versions = array(); + + if ($timestamp ) { + // fetch for old versions + $matches = glob( $versionsName.'.v*.d'.$timestamp ); + $offset = -strlen($timestamp)-2; + } else { + $matches = glob( $versionsName.'.v*' ); + } + + foreach( $matches as $ma ) { + if ( $timestamp ) { + $parts = explode( '.v', substr($ma, 0, $offset) ); + $versions[] = ( end( $parts ) ); + } else { + $parts = explode( '.v', $ma ); + $versions[] = ( end( $parts ) ); + } + } + return $versions; + } + + /** + * find unique extension for restored file if a file with the same name already exists + * @param $location where the file should be restored + * @param $filename name of the file + * @param $view filesystem view relative to users root directory + * @return string with unique extension + */ + private static function getUniqueExtension($location, $filename, $view) { + $ext = ''; + if ( $view->file_exists('files'.$location.'/'.$filename) ) { + $tmpext = '.restored'; + $ext = $tmpext; + $i = 1; + while ( $view->file_exists('files'.$location.'/'.$filename.$ext) ) { + $ext = $tmpext.$i; + $i++; + } + } + return $ext; + } + +} diff --git a/apps/files_trashbin/templates/index.php b/apps/files_trashbin/templates/index.php new file mode 100644 index 0000000000..c3e51b4bec --- /dev/null +++ b/apps/files_trashbin/templates/index.php @@ -0,0 +1,34 @@ + +
+ +
+
+
+ + +
t('Nothing in here. Your trash bin is empty!')?>
+ + + + + + + + + + + + +
+ + t( 'Name' ); ?> + + + <?php echo $l->t( 'Restore' ); ?>" /> + t('Restore')?> + + + + t( 'Deleted' ); ?> +
diff --git a/apps/files_trashbin/templates/part.list.php b/apps/files_trashbin/templates/part.list.php new file mode 100644 index 0000000000..fe8a71f44e --- /dev/null +++ b/apps/files_trashbin/templates/part.list.php @@ -0,0 +1,76 @@ + +200) $relative_date_color = 200; + $name = str_replace('+', '%20', urlencode($file['name'])); + $name = str_replace('%2F', '/', $name); + $directory = str_replace('+', '%20', urlencode($file['directory'])); + $directory = str_replace('%2F', '/', $directory); ?> + ' + + id="" + data-file="" + data-timestamp='' + data-dirlisting=1 + + id="" + data-file="" + data-timestamp='' + data-dirlisting=0 + > + + style="background-image:url()" + + style="background-image:url()" + + > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Versions AGPL Frank Karlitschek - 4.9 + 4.91 true Versioning of files diff --git a/apps/files_versions/l10n/fa.php b/apps/files_versions/l10n/fa.php index 98dd415969..9b618fdd32 100644 --- a/apps/files_versions/l10n/fa.php +++ b/apps/files_versions/l10n/fa.php @@ -1,3 +1,4 @@ "انقضای تمامی نسخه‌ها" +"History" => "تاریخچه", +"Enable" => "فعال" ); diff --git a/apps/files_versions/l10n/lb.php b/apps/files_versions/l10n/lb.php new file mode 100644 index 0000000000..3aa625ffc9 --- /dev/null +++ b/apps/files_versions/l10n/lb.php @@ -0,0 +1,5 @@ + "Historique", +"Files Versioning" => "Fichier's Versionéierung ", +"Enable" => "Aschalten" +); diff --git a/apps/files_versions/l10n/lv.php b/apps/files_versions/l10n/lv.php new file mode 100644 index 0000000000..e363693e45 --- /dev/null +++ b/apps/files_versions/l10n/lv.php @@ -0,0 +1,3 @@ + "Aktivēt" +); diff --git a/apps/files_versions/l10n/sr.php b/apps/files_versions/l10n/sr.php new file mode 100644 index 0000000000..0195f84567 --- /dev/null +++ b/apps/files_versions/l10n/sr.php @@ -0,0 +1,5 @@ + "Историја", +"Files Versioning" => "Прављење верзија датотека", +"Enable" => "Омогући" +); diff --git a/apps/files_versions/lib/hooks.php b/apps/files_versions/lib/hooks.php index 5fb9dc3c3c..5cefc53289 100644 --- a/apps/files_versions/lib/hooks.php +++ b/apps/files_versions/lib/hooks.php @@ -21,9 +21,9 @@ class Hooks { if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { - $versions = new Storage( new \OC_FilesystemView('') ); + $versions = new Storage( new \OC\Files\View('') ); - $path = $params[\OC_Filesystem::signal_param_path]; + $path = $params[\OC\Files\Filesystem::signal_param_path]; if($path<>'') $versions->store( $path ); @@ -39,15 +39,15 @@ class Hooks { * cleanup the versions directory if the actual file gets deleted */ public static function remove_hook($params) { - if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { - - $versions = new Storage( new \OC_FilesystemView('') ); - - $path = $params[\OC_Filesystem::signal_param_path]; - - if($path<>'') $versions->delete( $path ); - - } + if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { + + $versions = new Storage( new \OC_FilesystemView('') ); + + $path = $params[\OC\Files\Filesystem::signal_param_path]; + + if($path<>'') $versions->delete( $path ); + + } } /** @@ -58,15 +58,15 @@ class Hooks { * of the stored versions along the actual file */ public static function rename_hook($params) { - if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { - - $versions = new Storage( new \OC_FilesystemView('') ); - + if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { + + $versions = new Storage( new \OC_FilesystemView('') ); + $oldpath = $params['oldpath']; - $newpath = $params['newpath']; - - if($oldpath<>'' && $newpath<>'') $versions->rename( $oldpath, $newpath ); - + $newpath = $params['newpath']; + + if($oldpath<>'' && $newpath<>'') $versions->rename( $oldpath, $newpath ); + } } diff --git a/apps/files_versions/lib/versions.php b/apps/files_versions/lib/versions.php index 48be5e223a..003d548d2b 100644 --- a/apps/files_versions/lib/versions.php +++ b/apps/files_versions/lib/versions.php @@ -23,15 +23,15 @@ class Storage { private static $max_versions_per_interval = array( 1 => array('intervalEndsAfter' => 10, //first 10sec, one version every 2sec 'step' => 2), - 2 => array('intervalEndsAfter' => 60, //next minute, one version every 10sec + 2 => array('intervalEndsAfter' => 60, //next minute, one version every 10sec 'step' => 10), 3 => array('intervalEndsAfter' => 3600, //next hour, one version every minute 'step' => 60), 4 => array('intervalEndsAfter' => 86400, //next 24h, one version every hour 'step' => 3600), - 5 => array('intervalEndsAfter' => 2592000, //next 30days, one version per day + 5 => array('intervalEndsAfter' => 2592000, //next 30days, one version per day 'step' => 86400), - 6 => array('intervalEndsAfter' => -1, //until the end one version per week + 6 => array('intervalEndsAfter' => -1, //until the end one version per week 'step' => 604800), ); @@ -58,8 +58,8 @@ class Storage { public function store($filename) { if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { list($uid, $filename) = self::getUidAndFilename($filename); - $files_view = new \OC_FilesystemView('/'.$uid .'/files'); - $users_view = new \OC_FilesystemView('/'.$uid); + $files_view = new \OC\Files\View('/'.\OCP\User::getUser() .'/files'); + $users_view = new \OC\Files\View('/'.\OCP\User::getUser()); //check if source file already exist as version to avoid recursions. // todo does this check work? @@ -86,8 +86,8 @@ class Storage { // store a new version of a file $result = $users_view->copy('files'.$filename, 'files_versions'.$filename.'.v'.$users_view->filemtime('files'.$filename)); - if ( ($versionsSize = \OCP\Config::getAppValue('files_versions', 'size')) === null ) { - $versionsSize = self::calculateSize($uid); + if ( ($versionsSize = \OCP\Config::getAppValue('files_versions', 'size')) === null ) { + $versionsSize = self::calculateSize($uid); } $versionsSize += $users_view->filesize('files'.$filename); @@ -105,42 +105,42 @@ class Storage { * Delete versions of a file */ public static function delete($filename) { - list($uid, $filename) = self::getUidAndFilename($filename); + list($uid, $filename) = self::getUidAndFilename($filename); $versions_fileview = new \OC_FilesystemView('/'.$uid .'/files_versions'); - - $abs_path = \OCP\Config::getSystemValue('datadirectory').$versions_fileview->getAbsolutePath('').$filename.'.v'; - if( ($versions = self::getVersions($filename)) ) { - if ( ($versionsSize = \OCP\Config::getAppValue('files_versions', 'size')) === null ) { - $versionsSize = self::calculateSize($uid); - } - foreach ($versions as $v) { - unlink($abs_path . $v['version']); - $versionsSize -= $v['size']; - } - \OCP\Config::setAppValue('files_versions', 'size', $versionsSize); + + $abs_path = \OCP\Config::getSystemValue('datadirectory').$versions_fileview->getAbsolutePath('').$filename.'.v'; + if( ($versions = self::getVersions($filename)) ) { + if ( ($versionsSize = \OCP\Config::getAppValue('files_versions', 'size')) === null ) { + $versionsSize = self::calculateSize($uid); + } + foreach ($versions as $v) { + unlink($abs_path . $v['version']); + $versionsSize -= $v['size']; + } + \OCP\Config::setAppValue('files_versions', 'size', $versionsSize); } } - /** - * rename versions of a file - */ - public static function rename($oldpath, $newpath) { + /** + * rename versions of a file + */ + public static function rename($oldpath, $newpath) { list($uid, $oldpath) = self::getUidAndFilename($oldpath); - list($uidn, $newpath) = self::getUidAndFilename($newpath); + list($uidn, $newpath) = self::getUidAndFilename($newpath); $versions_view = new \OC_FilesystemView('/'.$uid .'/files_versions'); $files_view = new \OC_FilesystemView('/'.$uid .'/files'); - $abs_newpath = \OCP\Config::getSystemValue('datadirectory').$versions_view->getAbsolutePath('').$newpath; - + $abs_newpath = \OCP\Config::getSystemValue('datadirectory').$versions_view->getAbsolutePath('').$newpath; + if ( $files_view->is_dir($oldpath) && $versions_view->is_dir($oldpath) ) { $versions_view->rename($oldpath, $newpath); - } else if ( ($versions = Storage::getVersions($oldpath)) ) { - $info=pathinfo($abs_newpath); - if(!file_exists($info['dirname'])) mkdir($info['dirname'], 0750, true); - $versions = Storage::getVersions($oldpath); + } else if ( ($versions = Storage::getVersions($oldpath)) ) { + $info=pathinfo($abs_newpath); + if(!file_exists($info['dirname'])) mkdir($info['dirname'], 0750, true); + $versions = Storage::getVersions($oldpath); foreach ($versions as $v) { - $versions_view->rename($oldpath.'.v'.$v['version'], $newpath.'.v'.$v['version']); - } - } + $versions_view->rename($oldpath.'.v'.$v['version'], $newpath.'.v'.$v['version']); + } + } } /** @@ -150,7 +150,7 @@ class Storage { if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { list($uid, $filename) = self::getUidAndFilename($filename); - $users_view = new \OC_FilesystemView('/'.$uid); + $users_view = new \OC\Files\View('/'.$uid); $versionCreated = false; //first create a new version @@ -184,7 +184,7 @@ class Storage { public static function getVersions( $filename, $count = 0 ) { if( \OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true' ) { list($uid, $filename) = self::getUidAndFilename($filename); - $versions_fileview = new \OC_FilesystemView('/'.$uid.'/files_versions'); + $versions_fileview = new \OC\Files\View('/' . \OCP\User::getUser() . '/files_versions'); $versionsName = \OCP\Config::getSystemValue('datadirectory').$versions_fileview->getAbsolutePath($filename); $versions = array(); @@ -202,7 +202,7 @@ class Storage { $key = $version.'#'.$filename; $versions[$key]['cur'] = 0; $versions[$key]['version'] = $version; - $versions[$key]['path'] = $filename; + $versions[$key]['path'] = $filename; $versions[$key]['size'] = $versions_fileview->filesize($filename.'.v'.$version); // if file with modified date exists, flag it in array as currently enabled version @@ -236,29 +236,29 @@ class Storage { } - /** - * @brief get the size of all stored versions from a given user - * @param $uid id from the user - * @return size of vesions - */ - private static function calculateSize($uid) { - if( \OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true' ) { - $versions_fileview = new \OC_FilesystemView('/'.$uid.'/files_versions'); - $versionsRoot = \OCP\Config::getSystemValue('datadirectory').$versions_fileview->getAbsolutePath(''); - - $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($versionsRoot), \RecursiveIteratorIterator::CHILD_FIRST); - + /** + * @brief get the size of all stored versions from a given user + * @param $uid id from the user + * @return size of vesions + */ + private static function calculateSize($uid) { + if( \OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true' ) { + $versions_fileview = new \OC_FilesystemView('/'.$uid.'/files_versions'); + $versionsRoot = \OCP\Config::getSystemValue('datadirectory').$versions_fileview->getAbsolutePath(''); + + $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($versionsRoot), \RecursiveIteratorIterator::CHILD_FIRST); + $size = 0; - - foreach ($iterator as $path) { - if ( preg_match('/^.+\.v(\d+)$/', $path, $match) ) { + + foreach ($iterator as $path) { + if ( preg_match('/^.+\.v(\d+)$/', $path, $match) ) { $relpath = substr($path, strlen($versionsRoot)-1); - $size += $versions_fileview->filesize($relpath); - } + $size += $versions_fileview->filesize($relpath); + } } - return $size; - } + return $size; + } } /** @@ -267,11 +267,11 @@ class Storage { * @return array with contains two arrays 'all' which contains all versions sorted by age and 'by_file' which contains all versions sorted by filename */ private static function getAllVersions($uid) { - if( \OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true' ) { + if( \OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true' ) { $versions_fileview = new \OC_FilesystemView('/'.$uid.'/files_versions'); $versionsRoot = \OCP\Config::getSystemValue('datadirectory').$versions_fileview->getAbsolutePath(''); - $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($versionsRoot), \RecursiveIteratorIterator::CHILD_FIRST); + $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($versionsRoot), \RecursiveIteratorIterator::CHILD_FIRST); $versions = array(); @@ -280,7 +280,7 @@ class Storage { $relpath = substr($path, strlen($versionsRoot)-1); $versions[$match[1].'#'.$relpath] = array('path' => $relpath, 'timestamp' => $match[1]); } - } + } ksort($versions); @@ -288,20 +288,20 @@ class Storage { $result = array(); - foreach( $versions as $key => $value ) { + foreach( $versions as $key => $value ) { $i++; $size = $versions_fileview->filesize($value['path']); $filename = substr($value['path'], 0, -strlen($value['timestamp'])-2); - + $result['all'][$key]['version'] = $value['timestamp']; - $result['all'][$key]['path'] = $filename; + $result['all'][$key]['path'] = $filename; $result['all'][$key]['size'] = $size; $filename = substr($value['path'], 0, -strlen($value['timestamp'])-2); $result['by_file'][$filename][$key]['version'] = $value['timestamp']; - $result['by_file'][$filename][$key]['path'] = $filename; + $result['by_file'][$filename][$key]['path'] = $filename; $result['by_file'][$filename][$key]['size'] = $size; - + } return $result; @@ -322,7 +322,7 @@ class Storage { $quota = \OCP\Util::computerFileSize(\OC_Appconfig::getValue('files', 'default_quota')); } if ( $quota == null ) { - $quota = \OC_Filesystem::free_space('/'); + $quota = \OC\Files\Filesystem::free_space('/'); } // make sure that we have the current size of the version history @@ -332,7 +332,7 @@ class Storage { } } - // calculate available space for version history + // calculate available space for version history $rootInfo = \OC_FileCache::get('', '/'. $uid . '/files'); $free = $quota-$rootInfo['size']; // remaining free space for user if ( $free > 0 ) { @@ -394,7 +394,7 @@ class Storage { $nextVersion = $prevTimestamp - $step; if ( Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'] == -1 ) { $nextInterval = -1; - } else { + } else { $nextInterval = $time - Storage::$max_versions_per_interval[$interval]['intervalEndsAfter']; } $newInterval = true; // we changed the interval -> check same version with new interval diff --git a/apps/user_ldap/ajax/deleteConfiguration.php b/apps/user_ldap/ajax/deleteConfiguration.php new file mode 100644 index 0000000000..b7d633a049 --- /dev/null +++ b/apps/user_ldap/ajax/deleteConfiguration.php @@ -0,0 +1,35 @@ +. + * + */ + +// Check user and app status +OCP\JSON::checkAdminUser(); +OCP\JSON::checkAppEnabled('user_ldap'); +OCP\JSON::callCheck(); + +$prefix = $_POST['ldap_serverconfig_chooser']; +if(\OCA\user_ldap\lib\Helper::deleteServerConfiguration($prefix)){ + OCP\JSON::success(); +} else { + $l=OC_L10N::get('user_ldap'); + OCP\JSON::error(array('message' => $l->t('Failed to delete the server configuration'))); +} \ No newline at end of file diff --git a/apps/user_ldap/ajax/getConfiguration.php b/apps/user_ldap/ajax/getConfiguration.php new file mode 100644 index 0000000000..dfae68d2dc --- /dev/null +++ b/apps/user_ldap/ajax/getConfiguration.php @@ -0,0 +1,31 @@ +. + * + */ + +// Check user and app status +OCP\JSON::checkAdminUser(); +OCP\JSON::checkAppEnabled('user_ldap'); +OCP\JSON::callCheck(); + +$prefix = $_POST['ldap_serverconfig_chooser']; +$connection = new \OCA\user_ldap\lib\Connection($prefix); +OCP\JSON::success(array('configuration' => $connection->getConfiguration())); \ No newline at end of file diff --git a/apps/user_ldap/ajax/getNewServerConfigPrefix.php b/apps/user_ldap/ajax/getNewServerConfigPrefix.php new file mode 100644 index 0000000000..17e78f8707 --- /dev/null +++ b/apps/user_ldap/ajax/getNewServerConfigPrefix.php @@ -0,0 +1,34 @@ +. + * + */ + +// Check user and app status +OCP\JSON::checkAdminUser(); +OCP\JSON::checkAppEnabled('user_ldap'); +OCP\JSON::callCheck(); + +$serverConnections = \OCA\user_ldap\lib\Helper::getServerConfigurationPrefixes(); +sort($serverConnections); +$lk = array_pop($serverConnections); +$ln = intval(str_replace('s', '', $lk)); +$nk = 's'.str_pad($ln+1, 2, '0', STR_PAD_LEFT); +OCP\JSON::success(array('configPrefix' => $nk)); \ No newline at end of file diff --git a/apps/user_ldap/ajax/setConfiguration.php b/apps/user_ldap/ajax/setConfiguration.php new file mode 100644 index 0000000000..206487c7e0 --- /dev/null +++ b/apps/user_ldap/ajax/setConfiguration.php @@ -0,0 +1,33 @@ +. + * + */ + +// Check user and app status +OCP\JSON::checkAdminUser(); +OCP\JSON::checkAppEnabled('user_ldap'); +OCP\JSON::callCheck(); + +$prefix = $_POST['ldap_serverconfig_chooser']; +$connection = new \OCA\user_ldap\lib\Connection($prefix); +$connection->setConfiguration($_POST); +$connection->saveConfiguration(); +OCP\JSON::success(); \ No newline at end of file diff --git a/apps/user_ldap/ajax/testConfiguration.php b/apps/user_ldap/ajax/testConfiguration.php index a82f7e4c17..f8038e3146 100644 --- a/apps/user_ldap/ajax/testConfiguration.php +++ b/apps/user_ldap/ajax/testConfiguration.php @@ -4,7 +4,7 @@ * ownCloud - user_ldap * * @author Arthur Schiwon - * @copyright 2012 Arthur Schiwon blizzz@owncloud.com + * @copyright 2012, 2013 Arthur Schiwon blizzz@owncloud.com * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -26,14 +26,16 @@ OCP\JSON::checkAdminUser(); OCP\JSON::checkAppEnabled('user_ldap'); OCP\JSON::callCheck(); -$connection = new \OCA\user_ldap\lib\Connection(null); +$l=OC_L10N::get('user_ldap'); + +$connection = new \OCA\user_ldap\lib\Connection('', null); if($connection->setConfiguration($_POST)) { //Configuration is okay if($connection->bind()) { - OCP\JSON::success(array('message' => 'The configuration is valid and the connection could be established!')); + OCP\JSON::success(array('message' => $l->t('The configuration is valid and the connection could be established!'))); } else { - OCP\JSON::error(array('message' => 'The configuration is valid, but the Bind failed. Please check the server settings and credentials.')); + OCP\JSON::error(array('message' => $l->t('The configuration is valid, but the Bind failed. Please check the server settings and credentials.'))); } } else { - OCP\JSON::error(array('message' => 'The configuration is invalid. Please look in the ownCloud log for further details.')); + OCP\JSON::error(array('message' => $l->t('The configuration is invalid. Please look in the ownCloud log for further details.'))); } diff --git a/apps/user_ldap/appinfo/app.php b/apps/user_ldap/appinfo/app.php index ce3079da0b..dec87684c9 100644 --- a/apps/user_ldap/appinfo/app.php +++ b/apps/user_ldap/appinfo/app.php @@ -23,15 +23,23 @@ OCP\App::registerAdmin('user_ldap', 'settings'); -$connector = new OCA\user_ldap\lib\Connection('user_ldap'); -$userBackend = new OCA\user_ldap\USER_LDAP(); -$userBackend->setConnector($connector); -$groupBackend = new OCA\user_ldap\GROUP_LDAP(); -$groupBackend->setConnector($connector); +$configPrefixes = OCA\user_ldap\lib\Helper::getServerConfigurationPrefixes(true); +if(count($configPrefixes) == 1) { + $connector = new OCA\user_ldap\lib\Connection($configPrefixes[0]); + $userBackend = new OCA\user_ldap\USER_LDAP(); + $userBackend->setConnector($connector); + $groupBackend = new OCA\user_ldap\GROUP_LDAP(); + $groupBackend->setConnector($connector); +} else { + $userBackend = new OCA\user_ldap\User_Proxy($configPrefixes); + $groupBackend = new OCA\user_ldap\Group_Proxy($configPrefixes); +} -// register user backend -OC_User::useBackend($userBackend); -OC_Group::useBackend($groupBackend); +if(count($configPrefixes) > 0) { + // register user backend + OC_User::useBackend($userBackend); + OC_Group::useBackend($groupBackend); +} // add settings page to navigation $entry = array( diff --git a/apps/user_ldap/appinfo/info.xml b/apps/user_ldap/appinfo/info.xml index a760577527..53269edfb3 100644 --- a/apps/user_ldap/appinfo/info.xml +++ b/apps/user_ldap/appinfo/info.xml @@ -7,7 +7,7 @@ This app is not compatible to the WebDAV user backend. AGPL Dominik Schmidt and Arthur Schiwon - 4.9 + 4.91 true diff --git a/apps/user_ldap/appinfo/update.php b/apps/user_ldap/appinfo/update.php index 9b54ba18b6..f9681e38e6 100644 --- a/apps/user_ldap/appinfo/update.php +++ b/apps/user_ldap/appinfo/update.php @@ -5,7 +5,7 @@ //ATTENTION //Upgrade from ownCloud 3 (LDAP backend 0.1) to ownCloud 4.5 (LDAP backend 0.3) is not supported!! //You must do upgrade to ownCloud 4.0 first! -//The upgrade stuff in the section from 0.1 to 0.2 is just to minimize the bad efffects. +//The upgrade stuff in the section from 0.1 to 0.2 is just to minimize the bad effects. //settings $pw = OCP\Config::getAppValue('user_ldap', 'ldap_password'); @@ -22,12 +22,10 @@ if($state == 'unset') { OCP\Config::setSystemValue('ldapIgnoreNamingRules', false); } -// ### SUPPORTED upgrade path starts here ### - //from version 0.2 to 0.3 (0.2.0.x dev version) $objects = array('user', 'group'); -$connector = new \OCA\user_ldap\lib\Connection('user_ldap'); +$connector = new \OCA\user_ldap\lib\Connection(); $userBE = new \OCA\user_ldap\USER_LDAP(); $userBE->setConnector($connector); $groupBE = new \OCA\user_ldap\GROUP_LDAP(); @@ -80,3 +78,13 @@ function escapeDN($dn) { return $dn; } + + +// SUPPORTED UPGRADE FROM Version 0.3 (ownCloud 4.5) to 0.4 (ownCloud 5) + +if(!isset($connector)) { + $connector = new \OCA\user_ldap\lib\Connection(); +} +//it is required, that connections do have ldap_configuration_active setting stored in the database +$connector->getConfiguration(); +$connector->saveConfiguration(); \ No newline at end of file diff --git a/apps/user_ldap/appinfo/version b/apps/user_ldap/appinfo/version index b1a5f4781d..705e30728e 100644 --- a/apps/user_ldap/appinfo/version +++ b/apps/user_ldap/appinfo/version @@ -1 +1 @@ -0.3.0.1 \ No newline at end of file +0.3.9.0 \ No newline at end of file diff --git a/apps/user_ldap/group_ldap.php b/apps/user_ldap/group_ldap.php index 6343731008..02ceecaea0 100644 --- a/apps/user_ldap/group_ldap.php +++ b/apps/user_ldap/group_ldap.php @@ -171,7 +171,6 @@ class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface { return array(); } - $search = empty($search) ? '*' : '*'.$search.'*'; $groupUsers = array(); $isMemberUid = (strtolower($this->connection->ldapGroupMemberAssocAttr) == 'memberuid'); foreach($members as $member) { @@ -179,7 +178,7 @@ class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface { //we got uids, need to get their DNs to 'tranlsate' them to usernames $filter = $this->combineFilterWithAnd(array( \OCP\Util::mb_str_replace('%uid', $member, $this->connection>ldapLoginFilter, 'UTF-8'), - $this->connection->ldapUserDisplayName.'='.$search + $this->getFilterPartForUserSearch($search) )); $ldap_users = $this->fetchListOfUsers($filter, 'dn'); if(count($ldap_users) < 1) { @@ -188,8 +187,8 @@ class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface { $groupUsers[] = $this->dn2username($ldap_users[0]); } else { //we got DNs, check if we need to filter by search or we can give back all of them - if($search != '*') { - if(!$this->readAttribute($member, $this->connection->ldapUserDisplayName, $this->connection->ldapUserDisplayName.'='.$search)) { + if(!empty($search)) { + if(!$this->readAttribute($member, $this->connection->ldapUserDisplayName, $this->getFilterPartForUserSearch($search))) { continue; } } @@ -230,10 +229,9 @@ class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface { if($limit <= 0) { $limit = null; } - $search = empty($search) ? '*' : '*'.$search.'*'; $filter = $this->combineFilterWithAnd(array( $this->connection->ldapGroupFilter, - $this->connection->ldapGroupDisplayName.'='.$search + $this->getFilterPartForGroupSearch($search) )); \OCP\Util::writeLog('user_ldap', 'getGroups Filter '.$filter, \OCP\Util::DEBUG); $ldap_groups = $this->fetchListOfGroups($filter, array($this->connection->ldapGroupDisplayName, 'dn'), $limit, $offset); diff --git a/apps/user_ldap/group_proxy.php b/apps/user_ldap/group_proxy.php new file mode 100644 index 0000000000..5aa1aef0e0 --- /dev/null +++ b/apps/user_ldap/group_proxy.php @@ -0,0 +1,178 @@ +. + * + */ + +namespace OCA\user_ldap; + +class Group_Proxy extends lib\Proxy implements \OCP\GroupInterface { + private $backends = array(); + private $refBackend = null; + + /** + * @brief Constructor + * @param $serverConfigPrefixes array containing the config Prefixes + */ + public function __construct($serverConfigPrefixes) { + parent::__construct(); + foreach($serverConfigPrefixes as $configPrefix) { + $this->backends[$configPrefix] = new \OCA\user_ldap\GROUP_LDAP(); + $connector = $this->getConnector($configPrefix); + $this->backends[$configPrefix]->setConnector($connector); + if(is_null($this->refBackend)) { + $this->refBackend = &$this->backends[$configPrefix]; + } + } + } + + /** + * @brief Tries the backends one after the other until a positive result is returned from the specified method + * @param $gid string, the gid connected to the request + * @param $method string, the method of the group backend that shall be called + * @param $parameters an array of parameters to be passed + * @return mixed, the result of the method or false + */ + protected function walkBackends($gid, $method, $parameters) { + $cacheKey = $this->getGroupCacheKey($gid); + foreach($this->backends as $configPrefix => $backend) { + if($result = call_user_func_array(array($backend, $method), $parameters)) { + $this->writeToCache($cacheKey, $configPrefix); + return $result; + } + } + return false; + } + + /** + * @brief Asks the backend connected to the server that supposely takes care of the gid from the request. + * @param $gid string, the gid connected to the request + * @param $method string, the method of the group backend that shall be called + * @param $parameters an array of parameters to be passed + * @return mixed, the result of the method or false + */ + protected function callOnLastSeenOn($gid, $method, $parameters) { + $cacheKey = $this->getGroupCacheKey($gid);; + $prefix = $this->getFromCache($cacheKey); + //in case the uid has been found in the past, try this stored connection first + if(!is_null($prefix)) { + if(isset($this->backends[$prefix])) { + $result = call_user_func_array(array($this->backends[$prefix], $method), $parameters); + if(!$result) { + //not found here, reset cache to null + $this->writeToCache($cacheKey, null); + } + return $result; + } + } + return false; + } + + /** + * @brief is user in group? + * @param $uid uid of the user + * @param $gid gid of the group + * @returns true/false + * + * Checks whether the user is member of a group or not. + */ + public function inGroup($uid, $gid) { + return $this->handleRequest($gid, 'inGroup', array($uid, $gid)); + } + + /** + * @brief Get all groups a user belongs to + * @param $uid Name of the user + * @returns array with group names + * + * This function fetches all groups a user belongs to. It does not check + * if the user exists at all. + */ + public function getUserGroups($uid) { + $groups = array(); + + foreach($this->backends as $backend) { + $backendGroups = $backend->getUserGroups($uid); + if (is_array($backendGroups)) { + $groups = array_merge($groups, $backendGroups); + } + } + + return $groups; + } + + /** + * @brief get a list of all users in a group + * @returns array with user ids + */ + public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) { + $users = array(); + + foreach($this->backends as $backend) { + $backendUsers = $backend->usersInGroup($gid, $search, $limit, $offset); + if (is_array($backendUsers)) { + $users = array_merge($users, $backendUsers); + } + } + + return $users; + } + + /** + * @brief get a list of all groups + * @returns array with group names + * + * Returns a list with all groups + */ + public function getGroups($search = '', $limit = -1, $offset = 0) { + $groups = array(); + + foreach($this->backends as $backend) { + $backendGroups = $backend->getGroups($search, $limit, $offset); + if (is_array($backendGroups)) { + $groups = array_merge($groups, $backendGroups); + } + } + + return $groups; + } + + /** + * check if a group exists + * @param string $gid + * @return bool + */ + public function groupExists($gid) { + return $this->handleRequest($gid, 'groupExists', array($gid)); + } + + /** + * @brief Check if backend implements actions + * @param $actions bitwise-or'ed actions + * @returns boolean + * + * Returns the supported actions as int to be + * compared with OC_USER_BACKEND_CREATE_USER etc. + */ + public function implementsActions($actions) { + //it's the same across all our user backends obviously + return $this->refBackend->implementsActions($actions); + } +} \ No newline at end of file diff --git a/apps/user_ldap/js/settings.js b/apps/user_ldap/js/settings.js index 7063eead96..e34849ec88 100644 --- a/apps/user_ldap/js/settings.js +++ b/apps/user_ldap/js/settings.js @@ -1,6 +1,114 @@ +var LdapConfiguration = { + refreshConfig: function() { + if($('#ldap_serverconfig_chooser option').length < 2) { + LdapConfiguration.addConfiguration(true); + return; + } + $.post( + OC.filePath('user_ldap','ajax','getConfiguration.php'), + $('#ldap_serverconfig_chooser').serialize(), + function (result) { + if(result.status == 'success') { + $.each(result.configuration, function(configkey, configvalue) { + elementID = '#'+configkey; + + //deal with Checkboxes + if($(elementID).is('input[type=checkbox]')) { + if(configvalue == 1) { + $(elementID).attr('checked', 'checked'); + } else { + $(elementID).removeAttr('checked'); + } + return; + } + + //On Textareas, Multi-Line Settings come as array + if($(elementID).is('textarea') && $.isArray(configvalue)) { + configvalue = configvalue.join("\n"); + } + + // assign the value + $('#'+configkey).val(configvalue); + }); + } + } + ); + }, + + resetDefaults: function() { + $('#ldap').find('input[type=text], input[type=number], input[type=password], textarea, select').each(function() { + if($(this).attr('id') == 'ldap_serverconfig_chooser') { + return; + } + $(this).val($(this).attr('data-default')); + }); + $('#ldap').find('input[type=checkbox]').each(function() { + if($(this).attr('data-default') == 1) { + $(this).attr('checked', 'checked'); + } else { + $(this).removeAttr('checked'); + } + }); + }, + + deleteConfiguration: function() { + $.post( + OC.filePath('user_ldap','ajax','deleteConfiguration.php'), + $('#ldap_serverconfig_chooser').serialize(), + function (result) { + if(result.status == 'success') { + $('#ldap_serverconfig_chooser option:selected').remove(); + $('#ldap_serverconfig_chooser option:first').select(); + LdapConfiguration.refreshConfig(); + } else { + OC.dialogs.alert( + result.message, + t('user_ldap', 'Deletion failed') + ); + } + } + ); + }, + + addConfiguration: function(doNotAsk) { + $.post( + OC.filePath('user_ldap','ajax','getNewServerConfigPrefix.php'), + function (result) { + if(result.status == 'success') { + if(doNotAsk) { + LdapConfiguration.resetDefaults(); + } else { + OC.dialogs.confirm( + t('user_ldap', 'Take over settings from recent server configuration?'), + t('user_ldap', 'Keep settings?'), + function(keep) { + if(!keep) { + LdapConfiguration.resetDefaults(); + } + } + ); + } + $('#ldap_serverconfig_chooser option:selected').removeAttr('selected'); + var html = ''; + $('#ldap_serverconfig_chooser option:last').before(html); + } else { + OC.dialogs.alert( + result.message, + t('user_ldap', 'Cannot add server configuration') + ); + } + } + ); + } +} + $(document).ready(function() { + $('#ldapAdvancedAccordion').accordion({ heightStyle: 'content', animate: 'easeInOutCirc'}); $('#ldapSettings').tabs(); + $('#ldap_submit').button(); $('#ldap_action_test_connection').button(); + $('#ldap_action_delete_configuration').button(); + LdapConfiguration.refreshConfig(); $('#ldap_action_test_connection').click(function(event){ event.preventDefault(); $.post( @@ -10,15 +118,60 @@ $(document).ready(function() { if (result.status == 'success') { OC.dialogs.alert( result.message, - 'Connection test succeeded' + t('user_ldap', 'Connection test succeeded') ); } else { OC.dialogs.alert( result.message, - 'Connection test failed' + t('user_ldap', 'Connection test failed') ); } } ); }); + + $('#ldap_action_delete_configuration').click(function(event) { + event.preventDefault(); + OC.dialogs.confirm( + t('user_ldap', 'Do you really want to delete the current Server Configuration?'), + t('user_ldap', 'Confirm Deletion'), + function(deleteConfiguration) { + if(deleteConfiguration) { + LdapConfiguration.deleteConfiguration(); + } + } + ); + }); + + $('#ldap_submit').click(function(event) { + event.preventDefault(); + $.post( + OC.filePath('user_ldap','ajax','setConfiguration.php'), + $('#ldap').serialize(), + function (result) { + bgcolor = $('#ldap_submit').css('background'); + if (result.status == 'success') { + //the dealing with colors is a but ugly, but the jQuery version in use has issues with rgba colors + $('#ldap_submit').css('background', '#fff'); + $('#ldap_submit').effect('highlight', {'color':'#A8FA87'}, 5000, function() { + $('#ldap_submit').css('background', bgcolor); + }); + } else { + $('#ldap_submit').css('background', '#fff'); + $('#ldap_submit').effect('highlight', {'color':'#E97'}, 5000, function() { + $('#ldap_submit').css('background', bgcolor); + }); + } + } + ); + }); + + $('#ldap_serverconfig_chooser').change(function(event) { + value = $('#ldap_serverconfig_chooser option:selected:first').attr('value'); + if(value == 'NEW') { + LdapConfiguration.addConfiguration(false); + } else { + LdapConfiguration.refreshConfig(); + } + }); }); \ No newline at end of file diff --git a/apps/user_ldap/l10n/ar.php b/apps/user_ldap/l10n/ar.php index ced0b4293b..4d7b7ac4ad 100644 --- a/apps/user_ldap/l10n/ar.php +++ b/apps/user_ldap/l10n/ar.php @@ -1,3 +1,5 @@ "كلمة المرور" +"Deletion failed" => "فشل الحذف", +"Password" => "كلمة المرور", +"Help" => "المساعدة" ); diff --git a/apps/user_ldap/l10n/bn_BD.php b/apps/user_ldap/l10n/bn_BD.php index 094b20cad2..6c347eab87 100644 --- a/apps/user_ldap/l10n/bn_BD.php +++ b/apps/user_ldap/l10n/bn_BD.php @@ -17,21 +17,21 @@ "Defines the filter to apply, when retrieving groups." => "গোষ্ঠীসমূহ উদ্ধার করার সময় প্রয়োগের জন্য ছাঁকনী নির্ধারণ করবে।", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "কোন স্থান ধারক ব্যতীত, উদাহরণঃ\"objectClass=posixGroup\"।", "Port" => "পোর্ট", -"Base User Tree" => "ভিত্তি ব্যবহারকারি বৃক্ষাকারে", -"Base Group Tree" => "ভিত্তি গোষ্ঠী বৃক্ষাকারে", -"Group-Member association" => "গোষ্ঠী-সদস্য সংস্থাপন", "Use TLS" => "TLS ব্যবহার কর", "Do not use it for SSL connections, it will fail." => "SSL সংযোগের জন্য এটি ব্যবহার করবেন না, তাহলে ব্যর্থ হবেনই।", "Case insensitve LDAP server (Windows)" => "বর্ণ অসংবেদী LDAP সার্ভার (উইন্ডোজ)", "Turn off SSL certificate validation." => "SSL সনদপত্র যাচাইকরণ বন্ধ রাক।", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "শুধুমাত্র যদি এই বিকল্পটি ব্যবহার করেই সংযোগ কার্যকরী হয় তবে আপনার ownCloud সার্ভারে LDAP সার্ভারের SSL সনদপত্রটি আমদানি করুন।", "Not recommended, use for testing only." => "অনুমোদিত নয়, শুধুমাত্র পরীক্ষামূলক ব্যবহারের জন্য।", +"in seconds. A change empties the cache." => "সেকেন্ডে। কোন পরিবর্তন ক্যাসে খালি করবে।", "User Display Name Field" => "ব্যবহারকারীর প্রদর্শিতব্য নামের ক্ষেত্র", "The LDAP attribute to use to generate the user`s ownCloud name." => "ব্যবহারকারীর ownCloud নাম তৈরি করার জন্য ব্যভহৃত LDAP বৈশিষ্ট্য।", +"Base User Tree" => "ভিত্তি ব্যবহারকারি বৃক্ষাকারে", "Group Display Name Field" => "গোষ্ঠীর প্রদর্শিতব্য নামের ক্ষেত্র", "The LDAP attribute to use to generate the groups`s ownCloud name." => "গোষ্ঠীর ownCloud নাম তৈরি করার জন্য ব্যভহৃত LDAP বৈশিষ্ট্য।", +"Base Group Tree" => "ভিত্তি গোষ্ঠী বৃক্ষাকারে", +"Group-Member association" => "গোষ্ঠী-সদস্য সংস্থাপন", "in bytes" => "বাইটে", -"in seconds. A change empties the cache." => "সেকেন্ডে। কোন পরিবর্তন ক্যাসে খালি করবে।", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "ব্যবহারকারী নামের জন্য ফাঁকা রাখুন (পূর্বনির্ধারিত)। অন্যথায়, LDAP/AD বৈশিষ্ট্য নির্ধারণ করুন।", "Help" => "সহায়িকা" ); diff --git a/apps/user_ldap/l10n/ca.php b/apps/user_ldap/l10n/ca.php index c11eb5bdc5..7cfbac6c74 100644 --- a/apps/user_ldap/l10n/ca.php +++ b/apps/user_ldap/l10n/ca.php @@ -1,8 +1,24 @@ "Ha fallat en eliminar la configuració del servidor", +"The configuration is valid and the connection could be established!" => "La configuració és vàlida i s'ha pogut establir la comunicació!", +"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "La configuració és vàlida, però ha fallat el Bind. Comproveu les credencials i l'arranjament del servidor.", +"The configuration is invalid. Please look in the ownCloud log for further details." => "La configuració no és vàlida. Per més detalls mireu al registre d'ownCloud.", +"Deletion failed" => "Eliminació fallida", +"Take over settings from recent server configuration?" => "Voleu prendre l'arranjament de la configuració actual del servidor?", +"Keep settings?" => "Voleu mantenir la configuració?", +"Cannot add server configuration" => "No es pot afegir la configuració del servidor", +"Connection test succeeded" => "La prova de connexió ha reeixit", +"Connection test failed" => "La prova de connexió ha fallat", +"Do you really want to delete the current Server Configuration?" => "Voleu eliminar la configuració actual del servidor?", +"Confirm Deletion" => "Confirma l'eliminació", "Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Avís: Les aplicacions user_ldap i user_webdavauth són incompatibles. Podeu experimentar comportaments no desitjats. Demaneu a l'administrador del sistema que en desactivi una.", +"Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Avís: El mòdul PHP LDAP no està instal·lat, el dorsal no funcionarà. Demaneu a l'administrador del sistema que l'instal·li.", +"Server configuration" => "Configuració del servidor", +"Add Server Configuration" => "Afegeix la configuració del servidor", "Host" => "Màquina", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Podeu ometre el protocol, excepte si requeriu SSL. Llavors comenceu amb ldaps://", "Base DN" => "DN Base", +"One Base DN per line" => "Una DN Base per línia", "You can specify Base DN for users and groups in the Advanced tab" => "Podeu especificar DN Base per usuaris i grups a la pestanya Avançat", "User DN" => "DN Usuari", "The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "La DN de l'usuari client amb la que s'haurà de fer, per exemple uid=agent,dc=exemple,dc=com. Per un accés anònim, deixeu la DN i la contrasenya en blanc.", @@ -17,22 +33,34 @@ "Group Filter" => "Filtre de grup", "Defines the filter to apply, when retrieving groups." => "Defineix el filtre a aplicar quan es mostren grups.", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "sense cap paràmetre de substitució, per exemple \"objectClass=grupPosix\".", +"Configuration Active" => "Configuració activa", +"When unchecked, this configuration will be skipped." => "Si està desmarcat, aquesta configuració s'ometrà.", "Port" => "Port", -"Base User Tree" => "Arbre base d'usuaris", -"Base Group Tree" => "Arbre base de grups", -"Group-Member association" => "Associació membres-grup", +"Backup (Replica) Host" => "Màquina de còpia de serguretat (rèplica)", +"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Afegiu una màquina de còpia de seguretat opcional. Ha de ser una rèplica del servidor LDAP/AD principal.", +"Backup (Replica) Port" => "Port de la còpia de seguretat (rèplica)", +"Disable Main Server" => "Desactiva el servidor principal", +"When switched on, ownCloud will only connect to the replica server." => "Quan està connectat, ownCloud només es connecta al servidor de la rèplica.", "Use TLS" => "Usa TLS", "Do not use it for SSL connections, it will fail." => "No ho useu en connexions SSL, fallarà.", "Case insensitve LDAP server (Windows)" => "Servidor LDAP sense distinció entre majúscules i minúscules (Windows)", "Turn off SSL certificate validation." => "Desactiva la validació de certificat SSL.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Si la connexió només funciona amb aquesta opció, importeu el certificat SSL del servidor LDAP en el vostre servidor ownCloud.", "Not recommended, use for testing only." => "No recomanat, ús només per proves.", +"in seconds. A change empties the cache." => "en segons. Un canvi buidarà la memòria de cau.", "User Display Name Field" => "Camp per mostrar el nom d'usuari", "The LDAP attribute to use to generate the user`s ownCloud name." => "Atribut LDAP a usar per generar el nom d'usuari ownCloud.", +"Base User Tree" => "Arbre base d'usuaris", +"One User Base DN per line" => "Una DN Base d'Usuari per línia", +"User Search Attributes" => "Atributs de cerca d'usuari", +"Optional; one attribute per line" => "Opcional; Un atribut per línia", "Group Display Name Field" => "Camp per mostrar el nom del grup", "The LDAP attribute to use to generate the groups`s ownCloud name." => "Atribut LDAP a usar per generar el nom de grup ownCloud.", +"Base Group Tree" => "Arbre base de grups", +"One Group Base DN per line" => "Una DN Base de Grup per línia", +"Group Search Attributes" => "Atributs de cerca de grup", +"Group-Member association" => "Associació membres-grup", "in bytes" => "en bytes", -"in seconds. A change empties the cache." => "en segons. Un canvi buidarà la memòria de cau.", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Deixeu-ho buit pel nom d'usuari (per defecte). Altrament, especifiqueu un atribut LDAP/AD.", "Help" => "Ajuda" ); diff --git a/apps/user_ldap/l10n/cs_CZ.php b/apps/user_ldap/l10n/cs_CZ.php index 9b307e54dd..0aace1f741 100644 --- a/apps/user_ldap/l10n/cs_CZ.php +++ b/apps/user_ldap/l10n/cs_CZ.php @@ -1,8 +1,24 @@ "Selhalo smazání konfigurace serveru", +"The configuration is valid and the connection could be established!" => "Nastavení je v pořádku a spojení bylo navázáno.", +"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "Konfigurace je v pořádku, ale spojení selhalo. Zkontrolujte, prosím, nastavení serveru a přihlašovací údaje.", +"The configuration is invalid. Please look in the ownCloud log for further details." => "Nastavení je neplatné. Zkontrolujte, prosím, záznam ownCloud pro další podrobnosti.", +"Deletion failed" => "Mazání selhalo.", +"Take over settings from recent server configuration?" => "Převzít nastavení z nedávného nastavení serveru?", +"Keep settings?" => "Ponechat nastavení?", +"Cannot add server configuration" => "Nelze přidat nastavení serveru", +"Connection test succeeded" => "Test spojení byl úspěšný", +"Connection test failed" => "Test spojení selhal", +"Do you really want to delete the current Server Configuration?" => "Opravdu si přejete smazat současné nastavení serveru?", +"Confirm Deletion" => "Potvrdit smazání", "Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Varování: Aplikace user_ldap a user_webdavauth nejsou kompatibilní. Může nastávat neočekávané chování. Požádejte, prosím, správce systému aby jednu z nich zakázal.", +"Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Varování: není nainstalován LDAP modul pro PHP, podpůrná vrstva nebude fungovat. Požádejte, prosím, správce systému aby jej nainstaloval.", +"Server configuration" => "Nastavení serveru", +"Add Server Configuration" => "Přidat nastavení serveru", "Host" => "Počítač", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Můžete vynechat protokol, vyjma pokud požadujete SSL. Tehdy začněte s ldaps://", "Base DN" => "Základní DN", +"One Base DN per line" => "Jedna základní DN na řádku", "You can specify Base DN for users and groups in the Advanced tab" => "V rozšířeném nastavení můžete určit základní DN pro uživatele a skupiny", "User DN" => "Uživatelské DN", "The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN klentského uživatele ke kterému tvoříte vazbu, např. uid=agent,dc=example,dc=com. Pro anonymní přístup ponechte údaje DN and Heslo prázdné.", @@ -17,22 +33,37 @@ "Group Filter" => "Filtr skupin", "Defines the filter to apply, when retrieving groups." => "Určuje použitý filtr, pro získávaní skupin.", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "bez zástupných znaků, např. \"objectClass=posixGroup\".", +"Connection Settings" => "Nastavení spojení", +"Configuration Active" => "Nastavení aktivní", +"When unchecked, this configuration will be skipped." => "Pokud není zaškrtnuto, bude nastavení přeskočeno.", "Port" => "Port", -"Base User Tree" => "Základní uživatelský strom", -"Base Group Tree" => "Základní skupinový strom", -"Group-Member association" => "Asociace člena skupiny", +"Backup (Replica) Host" => "Záložní (kopie) hostitel", +"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Zadejte volitelného záložního hostitele. Musí to být kopie hlavního serveru LDAP/AD.", +"Backup (Replica) Port" => "Záložní (kopie) port", +"Disable Main Server" => "Zakázat hlavní serveru", +"When switched on, ownCloud will only connect to the replica server." => "Při zapnutí se ownCloud připojí pouze k záložnímu serveru", "Use TLS" => "Použít TLS", "Do not use it for SSL connections, it will fail." => "Nepoužívejte pro připojení pomocí SSL, připojení selže.", "Case insensitve LDAP server (Windows)" => "LDAP server nerozlišující velikost znaků (Windows)", "Turn off SSL certificate validation." => "Vypnout ověřování SSL certifikátu.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Pokud připojení pracuje pouze s touto možností, tak importujte SSL certifikát SSL serveru do Vašeho serveru ownCloud", "Not recommended, use for testing only." => "Není doporučeno, pouze pro testovací účely.", +"in seconds. A change empties the cache." => "ve vteřinách. Změna vyprázdní vyrovnávací paměť.", +"Directory Settings" => "Nastavení adresáře", "User Display Name Field" => "Pole pro zobrazované jméno uživatele", "The LDAP attribute to use to generate the user`s ownCloud name." => "Atribut LDAP použitý k vytvoření jména uživatele ownCloud", +"Base User Tree" => "Základní uživatelský strom", +"One User Base DN per line" => "Jedna uživatelská základní DN na řádku", +"User Search Attributes" => "Atributy vyhledávání uživatelů", +"Optional; one attribute per line" => "Volitelné, atribut na řádku", "Group Display Name Field" => "Pole pro zobrazení jména skupiny", "The LDAP attribute to use to generate the groups`s ownCloud name." => "Atribut LDAP použitý k vytvoření jména skupiny ownCloud", +"Base Group Tree" => "Základní skupinový strom", +"One Group Base DN per line" => "Jedna skupinová základní DN na řádku", +"Group Search Attributes" => "Atributy vyhledávání skupin", +"Group-Member association" => "Asociace člena skupiny", +"Special Attributes" => "Speciální atributy", "in bytes" => "v bajtech", -"in seconds. A change empties the cache." => "ve vteřinách. Změna vyprázdní vyrovnávací paměť.", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Ponechte prázdné pro uživatelské jméno (výchozí). Jinak uveďte LDAP/AD parametr.", "Help" => "Nápověda" ); diff --git a/apps/user_ldap/l10n/da.php b/apps/user_ldap/l10n/da.php index b11b56f9b6..dd7fb8a1a0 100644 --- a/apps/user_ldap/l10n/da.php +++ b/apps/user_ldap/l10n/da.php @@ -1,4 +1,5 @@ "Fejl ved sletning", "Host" => "Host", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Du kan udelade protokollen, medmindre du skal bruge SSL. Start i så fald med ldaps://", "Base DN" => "Base DN", @@ -12,14 +13,14 @@ "Group Filter" => "Gruppe Filter", "Defines the filter to apply, when retrieving groups." => "Definere filteret der bruges når der indlæses grupper.", "Port" => "Port", -"Base User Tree" => "Base Bruger Træ", -"Base Group Tree" => "Base Group Tree", -"Group-Member association" => "Group-Member association", "Use TLS" => "Brug TLS", "Do not use it for SSL connections, it will fail." => "Brug ikke til SSL forbindelser, da den vil fejle.", "Turn off SSL certificate validation." => "Deaktiver SSL certifikat validering", "Not recommended, use for testing only." => "Anbefales ikke, brug kun for at teste.", "User Display Name Field" => "User Display Name Field", +"Base User Tree" => "Base Bruger Træ", +"Base Group Tree" => "Base Group Tree", +"Group-Member association" => "Group-Member association", "in bytes" => "i bytes", "Help" => "Hjælp" ); diff --git a/apps/user_ldap/l10n/de.php b/apps/user_ldap/l10n/de.php index 89bda8af97..df680465c9 100644 --- a/apps/user_ldap/l10n/de.php +++ b/apps/user_ldap/l10n/de.php @@ -1,8 +1,12 @@ "Löschen fehlgeschlagen", +"Keep settings?" => "Einstellungen beibehalten?", "Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Warnung: Die Anwendungen user_ldap und user_webdavauth sind inkompatibel. Es kann demzufolge zu unerwarteten Verhalten kommen. Bitte Deinen Systemadministator eine der beiden Anwendungen zu deaktivieren.", +"Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Warnung: Da das PHP-Modul für LDAP nicht installiert ist, wird das Backend nicht funktionieren. Bitten Sie Ihren Systemadministrator das Modul zu installieren.", "Host" => "Host", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Du kannst das Protokoll auslassen, außer wenn Du SSL benötigst. Beginne dann mit ldaps://", "Base DN" => "Basis-DN", +"One Base DN per line" => "Ein Base DN pro Zeile", "You can specify Base DN for users and groups in the Advanced tab" => "Du kannst Basis-DN für Benutzer und Gruppen in dem \"Erweitert\"-Reiter konfigurieren", "User DN" => "Benutzer-DN", "The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "Der DN des Benutzers für LDAP-Bind, z.B.: uid=agent,dc=example,dc=com. Für anonymen Zugriff lasse DN und Passwort leer.", @@ -18,21 +22,23 @@ "Defines the filter to apply, when retrieving groups." => "Definiert den Filter für die Anfrage der Gruppen.", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "ohne Platzhalter, z.B.: \"objectClass=posixGroup\"", "Port" => "Port", -"Base User Tree" => "Basis-Benutzerbaum", -"Base Group Tree" => "Basis-Gruppenbaum", -"Group-Member association" => "Assoziation zwischen Gruppe und Benutzer", "Use TLS" => "Nutze TLS", "Do not use it for SSL connections, it will fail." => "Verwende dies nicht für SSL-Verbindungen, es wird fehlschlagen.", "Case insensitve LDAP server (Windows)" => "LDAP-Server (Windows: Groß- und Kleinschreibung bleibt unbeachtet)", "Turn off SSL certificate validation." => "Schalte die SSL-Zertifikatsprüfung aus.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Falls die Verbindung es erfordert, muss das SSL-Zertifikat des LDAP-Server importiert werden.", "Not recommended, use for testing only." => "Nicht empfohlen, nur zu Testzwecken.", +"in seconds. A change empties the cache." => "in Sekunden. Eine Änderung leert den Cache.", "User Display Name Field" => "Feld für den Anzeigenamen des Benutzers", "The LDAP attribute to use to generate the user`s ownCloud name." => "Das LDAP-Attribut für die Generierung des Benutzernamens in ownCloud. ", +"Base User Tree" => "Basis-Benutzerbaum", +"One User Base DN per line" => "Ein Benutzer Base DN pro Zeile", "Group Display Name Field" => "Feld für den Anzeigenamen der Gruppe", "The LDAP attribute to use to generate the groups`s ownCloud name." => "Das LDAP-Attribut für die Generierung des Gruppennamens in ownCloud. ", +"Base Group Tree" => "Basis-Gruppenbaum", +"One Group Base DN per line" => "Ein Gruppen Base DN pro Zeile", +"Group-Member association" => "Assoziation zwischen Gruppe und Benutzer", "in bytes" => "in Bytes", -"in seconds. A change empties the cache." => "in Sekunden. Eine Änderung leert den Cache.", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Ohne Eingabe wird der Benutzername (Standard) verwendet. Anderenfall trage ein LDAP/AD-Attribut ein.", "Help" => "Hilfe" ); diff --git a/apps/user_ldap/l10n/de_DE.php b/apps/user_ldap/l10n/de_DE.php index 82877b5ca1..4859b22357 100644 --- a/apps/user_ldap/l10n/de_DE.php +++ b/apps/user_ldap/l10n/de_DE.php @@ -1,8 +1,23 @@ "Das Löschen der Server-Konfiguration schlug fehl", +"The configuration is valid and the connection could be established!" => "Die Konfiguration ist valide und eine Verbindung konnte hergestellt werden!", +"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "Die Konfiguration ist valide, aber das Herstellen einer Verbindung schlug fehl. Bitte überprüfen Sie die Server-Einstellungen und Zertifikate.", +"The configuration is invalid. Please look in the ownCloud log for further details." => "Die Konfiguration ist nicht valide. Weitere Details können Sie im ownCloud-Log nachlesen.", +"Deletion failed" => "Löschen fehlgeschlagen", +"Keep settings?" => "Einstellungen behalten?", +"Cannot add server configuration" => "Das Hinzufügen der Server-Konfiguration schlug fehl", +"Connection test succeeded" => "Verbindungs-Test erfolgreich", +"Connection test failed" => "Verbindungs-Test fehlgeschlagen", +"Do you really want to delete the current Server Configuration?" => "Möchten Sie wirklich die Server-Konfiguration löschen?", +"Confirm Deletion" => "Löschung bestätigen", "Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Warnung: Die Anwendungen user_ldap und user_webdavauth sind inkompatibel. Es kann demzufolge zu unerwarteten Verhalten kommen. Bitten Sie Ihren Systemadministator eine der beiden Anwendungen zu deaktivieren.", +"Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Warnung: Da das PHP-Modul für LDAP ist nicht installiert, das Backend wird nicht funktionieren. Bitten Sie Ihren Systemadministrator das Modul zu installieren.", +"Server configuration" => "Server-Konfiguration", +"Add Server Configuration" => "Server-Konfiguration hinzufügen", "Host" => "Host", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Sie können das Protokoll auslassen, außer wenn Sie SSL benötigen. Beginnen Sie dann mit ldaps://", "Base DN" => "Basis-DN", +"One Base DN per line" => "Ein Base DN pro Zeile", "You can specify Base DN for users and groups in the Advanced tab" => "Sie können Basis-DN für Benutzer und Gruppen in dem \"Erweitert\"-Reiter konfigurieren", "User DN" => "Benutzer-DN", "The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "Der DN des Benutzers für LDAP-Bind, z.B.: uid=agent,dc=example,dc=com. Für anonymen Zugriff lassen Sie DN und Passwort leer.", @@ -17,22 +32,29 @@ "Group Filter" => "Gruppen-Filter", "Defines the filter to apply, when retrieving groups." => "Definiert den Filter für die Anfrage der Gruppen.", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "ohne Platzhalter, z.B.: \"objectClass=posixGroup\"", +"Configuration Active" => "Konfiguration aktiv", +"When unchecked, this configuration will be skipped." => "Wenn nicht angehakt, wird diese Konfiguration übersprungen.", "Port" => "Port", -"Base User Tree" => "Basis-Benutzerbaum", -"Base Group Tree" => "Basis-Gruppenbaum", -"Group-Member association" => "Assoziation zwischen Gruppe und Benutzer", +"Backup (Replica) Host" => "Back-Up (Replikation) Host", +"Backup (Replica) Port" => "Back-Up (Replikation) Port", "Use TLS" => "Nutze TLS", "Do not use it for SSL connections, it will fail." => "Verwenden Sie dies nicht für SSL-Verbindungen, es wird fehlschlagen.", "Case insensitve LDAP server (Windows)" => "LDAP-Server (Windows: Groß- und Kleinschreibung bleibt unbeachtet)", "Turn off SSL certificate validation." => "Schalten Sie die SSL-Zertifikatsprüfung aus.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Falls die Verbindung es erfordert, muss das SSL-Zertifikat des LDAP-Server importiert werden.", "Not recommended, use for testing only." => "Nicht empfohlen, nur zu Testzwecken.", +"in seconds. A change empties the cache." => "in Sekunden. Eine Änderung leert den Cache.", "User Display Name Field" => "Feld für den Anzeigenamen des Benutzers", "The LDAP attribute to use to generate the user`s ownCloud name." => "Das LDAP-Attribut für die Generierung des Benutzernamens in ownCloud. ", +"Base User Tree" => "Basis-Benutzerbaum", +"One User Base DN per line" => "Ein Benutzer Base DN pro Zeile", +"Optional; one attribute per line" => "Optional; Ein Attribut pro Zeile", "Group Display Name Field" => "Feld für den Anzeigenamen der Gruppe", "The LDAP attribute to use to generate the groups`s ownCloud name." => "Das LDAP-Attribut für die Generierung des Gruppennamens in ownCloud. ", +"Base Group Tree" => "Basis-Gruppenbaum", +"One Group Base DN per line" => "Ein Gruppen Base DN pro Zeile", +"Group-Member association" => "Assoziation zwischen Gruppe und Benutzer", "in bytes" => "in Bytes", -"in seconds. A change empties the cache." => "in Sekunden. Eine Änderung leert den Cache.", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Ohne Eingabe wird der Benutzername (Standard) verwendet. Anderenfall trage ein LDAP/AD-Attribut ein.", "Help" => "Hilfe" ); diff --git a/apps/user_ldap/l10n/el.php b/apps/user_ldap/l10n/el.php index 1f75a687a5..3951c94dfa 100644 --- a/apps/user_ldap/l10n/el.php +++ b/apps/user_ldap/l10n/el.php @@ -1,4 +1,5 @@ "Η διαγραφή απέτυχε", "Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Προσοχή: Οι εφαρμογές user_ldap και user_webdavauth είναι ασύμβατες. Μπορεί να αντιμετωπίσετε απρόβλεπτη συμπεριφορά. Παρακαλώ ζητήστε από τον διαχειριστή συστήματος να απενεργοποιήσει μία από αυτές.", "Host" => "Διακομιστής", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Μπορείτε να παραλείψετε το πρωτόκολλο, εκτός αν απαιτείται SSL. Σε αυτή την περίπτωση ξεκινήστε με ldaps://", @@ -18,21 +19,21 @@ "Defines the filter to apply, when retrieving groups." => "Καθορίζει το φίλτρο που θα ισχύει κατά την ανάκτηση ομάδων.", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "χωρίς κάποια μεταβλητή, π.χ. \"objectClass=ΟμάδαPosix\".", "Port" => "Θύρα", -"Base User Tree" => "Base User Tree", -"Base Group Tree" => "Base Group Tree", -"Group-Member association" => "Group-Member association", "Use TLS" => "Χρήση TLS", "Do not use it for SSL connections, it will fail." => "Μην χρησιμοποιείτε για συνδέσεις SSL, θα αποτύχει.", "Case insensitve LDAP server (Windows)" => "LDAP server (Windows) με διάκριση πεζών-ΚΕΦΑΛΑΙΩΝ", "Turn off SSL certificate validation." => "Απενεργοποίηση επικύρωσης πιστοποιητικού SSL.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Εάν η σύνδεση δουλεύει μόνο με αυτή την επιλογή, εισάγετε το LDAP SSL πιστοποιητικό του διακομιστή στον ownCloud server σας.", "Not recommended, use for testing only." => "Δεν προτείνεται, χρήση μόνο για δοκιμές.", +"in seconds. A change empties the cache." => "σε δευτερόλεπτα. Μια αλλαγή αδειάζει την μνήμη cache.", "User Display Name Field" => "Πεδίο Ονόματος Χρήστη", "The LDAP attribute to use to generate the user`s ownCloud name." => "Η ιδιότητα LDAP που θα χρησιμοποιείται για τη δημιουργία του ονόματος χρήστη του ownCloud.", +"Base User Tree" => "Base User Tree", "Group Display Name Field" => "Group Display Name Field", "The LDAP attribute to use to generate the groups`s ownCloud name." => "Η ιδιότητα LDAP που θα χρησιμοποιείται για τη δημιουργία του ονόματος ομάδας του ownCloud.", +"Base Group Tree" => "Base Group Tree", +"Group-Member association" => "Group-Member association", "in bytes" => "σε bytes", -"in seconds. A change empties the cache." => "σε δευτερόλεπτα. Μια αλλαγή αδειάζει την μνήμη cache.", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Αφήστε το κενό για το όνομα χρήστη (προεπιλογή). Διαφορετικά, συμπληρώστε μία ιδιότητα LDAP/AD.", "Help" => "Βοήθεια" ); diff --git a/apps/user_ldap/l10n/eo.php b/apps/user_ldap/l10n/eo.php index ef8aff8a39..2a2b70603c 100644 --- a/apps/user_ldap/l10n/eo.php +++ b/apps/user_ldap/l10n/eo.php @@ -1,7 +1,8 @@ "Forigo malsukcesis", "Host" => "Gastigo", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Vi povas neglekti la protokolon, escepte se vi bezonas SSL-on. Tiuokaze, komencu per ldaps://", -"Base DN" => "Baz-DN", +"Base DN" => "Bazo-DN", "User DN" => "Uzanto-DN", "Password" => "Pasvorto", "For anonymous access, leave DN and Password empty." => "Por sennoman aliron, lasu DN-on kaj Pasvorton malplenaj.", @@ -15,21 +16,21 @@ "Defines the filter to apply, when retrieving groups." => "Ĝi difinas la filtrilon aplikotan, kiam veniĝas grupoj.", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "sen ajna referencilo, ekz.: \"objectClass=posixGroup\".", "Port" => "Pordo", -"Base User Tree" => "Baza uzantarbo", -"Base Group Tree" => "Baza gruparbo", -"Group-Member association" => "Asocio de grupo kaj membro", "Use TLS" => "Uzi TLS-on", "Do not use it for SSL connections, it will fail." => "Ne uzu ĝin por SSL-konektoj, ĝi malsukcesos.", "Case insensitve LDAP server (Windows)" => "LDAP-servilo blinda je litergrandeco (Vindozo)", "Turn off SSL certificate validation." => "Malkapabligi validkontrolon de SSL-atestiloj.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Se la konekto nur funkcias kun ĉi tiu malnepro, enportu la SSL-atestilo de la LDAP-servilo en via ownCloud-servilo.", "Not recommended, use for testing only." => "Ne rekomendata, uzu ĝin nur por testoj.", +"in seconds. A change empties the cache." => "sekunde. Ajna ŝanĝo malplenigas la kaŝmemoron.", "User Display Name Field" => "Kampo de vidignomo de uzanto", "The LDAP attribute to use to generate the user`s ownCloud name." => "La atributo de LDAP uzota por generi la ownCloud-an nomon de la uzanto.", +"Base User Tree" => "Baza uzantarbo", "Group Display Name Field" => "Kampo de vidignomo de grupo", "The LDAP attribute to use to generate the groups`s ownCloud name." => "La atributo de LDAP uzota por generi la ownCloud-an nomon de la grupo.", +"Base Group Tree" => "Baza gruparbo", +"Group-Member association" => "Asocio de grupo kaj membro", "in bytes" => "duumoke", -"in seconds. A change empties the cache." => "sekunde. Ajna ŝanĝo malplenigas la kaŝmemoron.", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Lasu malplena por uzantonomo (defaŭlto). Alie, specifu LDAP/AD-atributon.", "Help" => "Helpo" ); diff --git a/apps/user_ldap/l10n/es.php b/apps/user_ldap/l10n/es.php index 48e7b24734..a6d1d9d260 100644 --- a/apps/user_ldap/l10n/es.php +++ b/apps/user_ldap/l10n/es.php @@ -1,8 +1,22 @@ "No se pudo borrar la configuración del servidor", +"The configuration is valid and the connection could be established!" => "La configuración es válida y la conexión puede establecerse!", +"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "La configuración es válida, pero falló el Enlace. Por favor, compruebe la configuración del servidor y las credenciales.", +"The configuration is invalid. Please look in the ownCloud log for further details." => "La configuración no es válida. Por favor, busque en el log de ownCloud para más detalles.", +"Deletion failed" => "Falló el borrado", +"Keep settings?" => "Mantener la configuración?", +"Cannot add server configuration" => "No se puede añadir la configuración del servidor", +"Connection test succeeded" => "La prueba de conexión fue exitosa", +"Connection test failed" => "La prueba de conexión falló", +"Do you really want to delete the current Server Configuration?" => "¿Realmente desea eliminar la configuración actual del servidor?", +"Confirm Deletion" => "Confirmar eliminación", "Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Advertencia: Los Apps user_ldap y user_webdavauth son incompatibles. Puede que experimente un comportamiento inesperado. Pregunte al administrador del sistema para desactivar uno de ellos.", -"Host" => "Servidor", +"Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Advertencia: El módulo LDAP de PHP no está instalado, el sistema no funcionará. Por favor consulte al administrador del sistema para instalarlo.", +"Server configuration" => "Configuración del Servidor", +"Host" => "Máquina", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Puede omitir el protocolo, excepto si requiere SSL. En ese caso, empiece con ldaps://", "Base DN" => "DN base", +"One Base DN per line" => "Un DN Base por línea", "You can specify Base DN for users and groups in the Advanced tab" => "Puede especificar el DN base para usuarios y grupos en la pestaña Avanzado", "User DN" => "DN usuario", "The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "El DN del usuario cliente con el que se hará la asociación, p.ej. uid=agente,dc=ejemplo,dc=com. Para acceso anónimo, deje DN y contraseña vacíos.", @@ -18,21 +32,23 @@ "Defines the filter to apply, when retrieving groups." => "Define el filtro a aplicar, cuando se obtienen grupos.", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "Con cualquier placeholder, ej: \"objectClass=posixGroup\".", "Port" => "Puerto", -"Base User Tree" => "Árbol base de usuario", -"Base Group Tree" => "Árbol base de grupo", -"Group-Member association" => "Asociación Grupo-Miembro", "Use TLS" => "Usar TLS", "Do not use it for SSL connections, it will fail." => "No usarlo para SSL, habrá error.", "Case insensitve LDAP server (Windows)" => "Servidor de LDAP sensible a mayúsculas/minúsculas (Windows)", "Turn off SSL certificate validation." => "Apagar la validación por certificado SSL.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Si la conexión sólo funciona con esta opción, importe el certificado SSL del servidor LDAP en su servidor ownCloud.", "Not recommended, use for testing only." => "No recomendado, sólo para pruebas.", +"in seconds. A change empties the cache." => "en segundos. Un cambio vacía la cache.", "User Display Name Field" => "Campo de nombre de usuario a mostrar", "The LDAP attribute to use to generate the user`s ownCloud name." => "El atributo LDAP a usar para generar el nombre de usuario de ownCloud.", +"Base User Tree" => "Árbol base de usuario", +"One User Base DN per line" => "Un DN Base de Usuario por línea", "Group Display Name Field" => "Campo de nombre de grupo a mostrar", "The LDAP attribute to use to generate the groups`s ownCloud name." => "El atributo LDAP a usar para generar el nombre de los grupos de ownCloud.", +"Base Group Tree" => "Árbol base de grupo", +"One Group Base DN per line" => "Un DN Base de Grupo por línea", +"Group-Member association" => "Asociación Grupo-Miembro", "in bytes" => "en bytes", -"in seconds. A change empties the cache." => "en segundos. Un cambio vacía la cache.", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Vacío para el nombre de usuario (por defecto). En otro caso, especifique un atributo LDAP/AD.", "Help" => "Ayuda" ); diff --git a/apps/user_ldap/l10n/es_AR.php b/apps/user_ldap/l10n/es_AR.php index 331bf8699f..dce2321e6b 100644 --- a/apps/user_ldap/l10n/es_AR.php +++ b/apps/user_ldap/l10n/es_AR.php @@ -1,8 +1,11 @@ "Error al borrar", "Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Advertencia: Los Apps user_ldap y user_webdavauth son incompatibles. Puede que experimente un comportamiento inesperado. Pregunte al administrador del sistema para desactivar uno de ellos.", +"Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Atención: El módulo PHP LDAP no está instalado, este elemento no va a funcionar. Por favor, pedile al administrador que lo instale.", "Host" => "Servidor", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Podés omitir el protocolo, excepto si SSL es requerido. En ese caso, empezá con ldaps://", "Base DN" => "DN base", +"One Base DN per line" => "Una DN base por línea", "You can specify Base DN for users and groups in the Advanced tab" => "Podés especificar el DN base para usuarios y grupos en la pestaña \"Avanzado\"", "User DN" => "DN usuario", "The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "El DN del usuario cliente con el que se hará la asociación, p.ej. uid=agente,dc=ejemplo,dc=com. Para acceso anónimo, dejá DN y contraseña vacíos.", @@ -18,21 +21,23 @@ "Defines the filter to apply, when retrieving groups." => "Define el filtro a aplicar cuando se obtienen grupos.", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "Sin ninguna plantilla, p. ej.: \"objectClass=posixGroup\".", "Port" => "Puerto", -"Base User Tree" => "Árbol base de usuario", -"Base Group Tree" => "Árbol base de grupo", -"Group-Member association" => "Asociación Grupo-Miembro", "Use TLS" => "Usar TLS", "Do not use it for SSL connections, it will fail." => "No usarlo para SSL, dará error.", "Case insensitve LDAP server (Windows)" => "Servidor de LDAP sensible a mayúsculas/minúsculas (Windows)", "Turn off SSL certificate validation." => "Desactivar la validación por certificado SSL.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Si la conexión sólo funciona con esta opción, importá el certificado SSL del servidor LDAP en tu servidor ownCloud.", "Not recommended, use for testing only." => "No recomendado, sólo para pruebas.", +"in seconds. A change empties the cache." => "en segundos. Cambiarlo vacía la cache.", "User Display Name Field" => "Campo de nombre de usuario a mostrar", "The LDAP attribute to use to generate the user`s ownCloud name." => "El atributo LDAP a usar para generar el nombre de usuario de ownCloud.", +"Base User Tree" => "Árbol base de usuario", +"One User Base DN per line" => "Una DN base de usuario por línea", "Group Display Name Field" => "Campo de nombre de grupo a mostrar", "The LDAP attribute to use to generate the groups`s ownCloud name." => "El atributo LDAP a usar para generar el nombre de los grupos de ownCloud.", +"Base Group Tree" => "Árbol base de grupo", +"One Group Base DN per line" => "Una DN base de grupo por línea", +"Group-Member association" => "Asociación Grupo-Miembro", "in bytes" => "en bytes", -"in seconds. A change empties the cache." => "en segundos. Cambiarlo vacía la cache.", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Vacío para el nombre de usuario (por defecto). En otro caso, especificá un atributo LDAP/AD.", "Help" => "Ayuda" ); diff --git a/apps/user_ldap/l10n/et_EE.php b/apps/user_ldap/l10n/et_EE.php index 9752d73c1c..ba03a8a809 100644 --- a/apps/user_ldap/l10n/et_EE.php +++ b/apps/user_ldap/l10n/et_EE.php @@ -1,4 +1,5 @@ "Kustutamine ebaõnnestus", "Host" => "Host", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Sa ei saa protokolli ära jätta, välja arvatud siis, kui sa nõuad SSL-ühendust. Sel juhul alusta eesliitega ldaps://", "Base DN" => "Baas DN", @@ -17,21 +18,21 @@ "Defines the filter to apply, when retrieving groups." => "Määrab gruppe hankides filtri, mida rakendatakse.", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "ilma ühegi kohatäitjata, nt. \"objectClass=posixGroup\".", "Port" => "Port", -"Base User Tree" => "Baaskasutaja puu", -"Base Group Tree" => "Baasgrupi puu", -"Group-Member association" => "Grupiliikme seotus", "Use TLS" => "Kasutaja TLS", "Do not use it for SSL connections, it will fail." => "Ära kasuta seda SSL ühenduse jaoks, see ei toimi.", "Case insensitve LDAP server (Windows)" => "Mittetõstutundlik LDAP server (Windows)", "Turn off SSL certificate validation." => "Lülita SSL sertifikaadi kontrollimine välja.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Kui ühendus toimib ainult selle valikuga, siis impordi LDAP serveri SSL sertifikaat oma ownCloud serverisse.", "Not recommended, use for testing only." => "Pole soovitatav, kasuta ainult testimiseks.", +"in seconds. A change empties the cache." => "sekundites. Muudatus tühjendab vahemälu.", "User Display Name Field" => "Kasutaja näidatava nime väli", "The LDAP attribute to use to generate the user`s ownCloud name." => "LDAP omadus, mida kasutatakse kasutaja ownCloudi nime loomiseks.", +"Base User Tree" => "Baaskasutaja puu", "Group Display Name Field" => "Grupi näidatava nime väli", "The LDAP attribute to use to generate the groups`s ownCloud name." => "LDAP omadus, mida kasutatakse ownCloudi grupi nime loomiseks.", +"Base Group Tree" => "Baasgrupi puu", +"Group-Member association" => "Grupiliikme seotus", "in bytes" => "baitides", -"in seconds. A change empties the cache." => "sekundites. Muudatus tühjendab vahemälu.", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Kasutajanime (vaikeväärtus) kasutamiseks jäta tühjaks. Vastasel juhul määra LDAP/AD omadus.", "Help" => "Abiinfo" ); diff --git a/apps/user_ldap/l10n/eu.php b/apps/user_ldap/l10n/eu.php index 7290dabbef..2aad2363ce 100644 --- a/apps/user_ldap/l10n/eu.php +++ b/apps/user_ldap/l10n/eu.php @@ -1,8 +1,11 @@ "Ezabaketak huts egin du", "Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Abisua: user_ldap eta user_webdavauth aplikazioak bateraezinak dira. Portaera berezia izan dezakezu. Mesedez eskatu zure sistema kudeatzaileari bietako bat desgaitzeko.", +"Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Abisua: PHPk behar duen LDAP modulua ez dago instalaturik, motorrak ez du funtzionatuko. Mesedez eskatu zure sistema kudeatzaileari instala dezan.", "Host" => "Hostalaria", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Protokoloa ez da beharrezkoa, SSL behar baldin ez baduzu. Honela bada hasi ldaps://", "Base DN" => "Oinarrizko DN", +"One Base DN per line" => "DN Oinarri bat lerroko", "You can specify Base DN for users and groups in the Advanced tab" => "Erabiltzaile eta taldeentzako Oinarrizko DN zehaztu dezakezu Aurreratu fitxan", "User DN" => "Erabiltzaile DN", "The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "Lotura egingo den bezero erabiltzailearen DNa, adb. uid=agent,dc=example,dc=com. Sarrera anonimoak gaitzeko utzi DN eta Pasahitza hutsik.", @@ -18,21 +21,23 @@ "Defines the filter to apply, when retrieving groups." => "Taldeak jasotzen direnean ezarriko den iragazkia zehazten du.", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "txantiloirik gabe, adb. \"objectClass=posixGroup\".", "Port" => "Portua", -"Base User Tree" => "Oinarrizko Erabiltzaile Zuhaitza", -"Base Group Tree" => "Oinarrizko Talde Zuhaitza", -"Group-Member association" => "Talde-Kide elkarketak", "Use TLS" => "Erabili TLS", "Do not use it for SSL connections, it will fail." => "Ez erabili SSL konexioetan, huts egingo du.", "Case insensitve LDAP server (Windows)" => "Maiuskulak eta minuskulak ezberditzen ez dituen LDAP zerbitzaria (windows)", "Turn off SSL certificate validation." => "Ezgaitu SSL ziurtagirien egiaztapena.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Konexioa aukera hau ezinbestekoa badu, inportatu LDAP zerbitzariaren SSL ziurtagiria zure ownCloud zerbitzarian.", "Not recommended, use for testing only." => "Ez da aholkatzen, erabili bakarrik frogak egiteko.", +"in seconds. A change empties the cache." => "segundutan. Aldaketak katxea husten du.", "User Display Name Field" => "Erabiltzaileen bistaratzeko izena duen eremua", "The LDAP attribute to use to generate the user`s ownCloud name." => "ownCloud erabiltzailearen izena sortzeko erabiliko den LDAP atributua", +"Base User Tree" => "Oinarrizko Erabiltzaile Zuhaitza", +"One User Base DN per line" => "Erabiltzaile DN Oinarri bat lerroko", "Group Display Name Field" => "Taldeen bistaratzeko izena duen eremua", "The LDAP attribute to use to generate the groups`s ownCloud name." => "ownCloud taldearen izena sortzeko erabiliko den LDAP atributua", +"Base Group Tree" => "Oinarrizko Talde Zuhaitza", +"One Group Base DN per line" => "Talde DN Oinarri bat lerroko", +"Group-Member association" => "Talde-Kide elkarketak", "in bytes" => "bytetan", -"in seconds. A change empties the cache." => "segundutan. Aldaketak katxea husten du.", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Utzi hutsik erabiltzaile izenarako (lehentsia). Bestela zehaztu LDAP/AD atributua.", "Help" => "Laguntza" ); diff --git a/apps/user_ldap/l10n/fa.php b/apps/user_ldap/l10n/fa.php index 4432422116..e3955d3f32 100644 --- a/apps/user_ldap/l10n/fa.php +++ b/apps/user_ldap/l10n/fa.php @@ -1,5 +1,7 @@ "حذف کردن انجام نشد", "Host" => "میزبانی", "Password" => "رمز عبور", +"Port" => "درگاه", "Help" => "راه‌نما" ); diff --git a/apps/user_ldap/l10n/fi_FI.php b/apps/user_ldap/l10n/fi_FI.php index 24195649a6..4f8fd3f2d1 100644 --- a/apps/user_ldap/l10n/fi_FI.php +++ b/apps/user_ldap/l10n/fi_FI.php @@ -1,4 +1,5 @@ "Poisto epäonnistui", "Host" => "Isäntä", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Voit jättää protokollan määrittämättä, paitsi kun vaadit SSL:ää. Aloita silloin ldaps://", "Base DN" => "Oletus DN", @@ -17,21 +18,21 @@ "Defines the filter to apply, when retrieving groups." => "Määrittelee käytettävän suodattimen, kun ryhmiä haetaan. ", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "ilman paikanvaraustermiä, ts. \"objectClass=posixGroup\".", "Port" => "Portti", -"Base User Tree" => "Oletuskäyttäjäpuu", -"Base Group Tree" => "Ryhmien juuri", -"Group-Member association" => "Ryhmän ja jäsenen assosiaatio (yhteys)", "Use TLS" => "Käytä TLS:ää", "Do not use it for SSL connections, it will fail." => "Älä käytä SSL-yhteyttä varten, se epäonnistuu. ", "Case insensitve LDAP server (Windows)" => "Kirjainkoosta piittamaton LDAP-palvelin (Windows)", "Turn off SSL certificate validation." => "Poista käytöstä SSL-varmenteen vahvistus", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Jos yhteys toimii vain tällä valinnalla, siirrä LDAP-palvelimen SSL-varmenne ownCloud-palvelimellesi.", "Not recommended, use for testing only." => "Ei suositella, käytä vain testausta varten.", +"in seconds. A change empties the cache." => "sekunneissa. Muutos tyhjentää välimuistin.", "User Display Name Field" => "Käyttäjän näytettävän nimen kenttä", "The LDAP attribute to use to generate the user`s ownCloud name." => "LDAP-attribuutti, jota käytetään käyttäjän ownCloud-käyttäjänimenä ", +"Base User Tree" => "Oletuskäyttäjäpuu", "Group Display Name Field" => "Ryhmän \"näytettävä nimi\"-kenttä", "The LDAP attribute to use to generate the groups`s ownCloud name." => "LDAP-attribuutti, jota käytetään luomaan ryhmän ownCloud-nimi", +"Base Group Tree" => "Ryhmien juuri", +"Group-Member association" => "Ryhmän ja jäsenen assosiaatio (yhteys)", "in bytes" => "tavuissa", -"in seconds. A change empties the cache." => "sekunneissa. Muutos tyhjentää välimuistin.", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Jätä tyhjäksi käyttäjänimi (oletusasetus). Muutoin anna LDAP/AD-atribuutti.", "Help" => "Ohje" ); diff --git a/apps/user_ldap/l10n/fr.php b/apps/user_ldap/l10n/fr.php index dd2fb08091..a2879b4fa0 100644 --- a/apps/user_ldap/l10n/fr.php +++ b/apps/user_ldap/l10n/fr.php @@ -1,11 +1,27 @@ "Échec de la suppression de la configuration du serveur", +"The configuration is valid and the connection could be established!" => "La configuration est valide est la connexion peut être établie !", +"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "La configuration est valide, mais le lien ne peut être établi. Veuillez vérifier les paramètres du serveur ainsi que vos identifiants de connexion.", +"The configuration is invalid. Please look in the ownCloud log for further details." => "La configuration est invalide. Veuillez vous référer aux fichiers de journaux ownCloud pour plus d'information.", +"Deletion failed" => "La suppression a échoué", +"Take over settings from recent server configuration?" => "Récupérer les paramètres depuis une configuration récente du serveur ?", +"Keep settings?" => "Garder ces paramètres ?", +"Cannot add server configuration" => "Impossible d'ajouter la configuration du serveur.", +"Connection test succeeded" => "Test de connexion réussi", +"Connection test failed" => "Le test de connexion a échoué", +"Do you really want to delete the current Server Configuration?" => "Êtes-vous vraiment sûr de vouloir effacer la configuration actuelle du serveur ?", +"Confirm Deletion" => "Confirmer la suppression", "Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Avertissement: Les applications user_ldap et user_webdavauth sont incompatibles. Des disfonctionnements peuvent survenir. Contactez votre administrateur système pour qu'il désactive l'une d'elles.", +"Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Attention : Le module php LDAP n'est pas installé, par conséquent cette extension ne pourra fonctionner. Veuillez contacter votre administrateur système afin qu'il l'installe.", +"Server configuration" => "Configuration du serveur", +"Add Server Configuration" => "Ajouter une configuration du serveur", "Host" => "Hôte", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Vous pouvez omettre le protocole, sauf si vous avez besoin de SSL. Dans ce cas préfixez avec ldaps://", "Base DN" => "DN Racine", -"You can specify Base DN for users and groups in the Advanced tab" => "Vous pouvez détailler les DN Racines de vos utilisateurs et groupes dans l'onglet Avancé", +"One Base DN per line" => "Un DN racine par ligne", +"You can specify Base DN for users and groups in the Advanced tab" => "Vous pouvez spécifier les DN Racines de vos utilisateurs et groupes via l'onglet Avancé", "User DN" => "DN Utilisateur (Autorisé à consulter l'annuaire)", -"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "Le DN de l'utilisateur client avec lequel la liaison doit se faire, par exemple uid=agent,dc=example,dc=com. Pour l'accès anonyme, laisser le DN et le mot de passe vides.", +"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN de l'utilisateur client pour lequel la liaison doit se faire, par exemple uid=agent,dc=example,dc=com. Pour un accès anonyme, laisser le DN et le mot de passe vides.", "Password" => "Mot de passe", "For anonymous access, leave DN and Password empty." => "Pour un accès anonyme, laisser le DN Utilisateur et le mot de passe vides.", "User Login Filter" => "Modèle d'authentification utilisateurs", @@ -17,22 +33,34 @@ "Group Filter" => "Filtre de groupes", "Defines the filter to apply, when retrieving groups." => "Définit le filtre à appliquer lors de la récupération des groupes.", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "sans élément de substitution, par exemple \"objectClass=posixGroup\".", +"Configuration Active" => "Configuration active", +"When unchecked, this configuration will be skipped." => "Lorsque non cochée, la configuration sera ignorée.", "Port" => "Port", -"Base User Tree" => "DN racine de l'arbre utilisateurs", -"Base Group Tree" => "DN racine de l'arbre groupes", -"Group-Member association" => "Association groupe-membre", +"Backup (Replica) Host" => "Serveur de backup (réplique)", +"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Fournir un serveur de backup optionnel. Il doit s'agir d'une réplique du serveur LDAP/AD principal.", +"Backup (Replica) Port" => "Port du serveur de backup (réplique)", +"Disable Main Server" => "Désactiver le serveur principal", +"When switched on, ownCloud will only connect to the replica server." => "Lorsqu'activé, ownCloud ne se connectera qu'au serveur répliqué.", "Use TLS" => "Utiliser TLS", "Do not use it for SSL connections, it will fail." => "Ne pas utiliser pour les connexions SSL, car cela échouera.", "Case insensitve LDAP server (Windows)" => "Serveur LDAP insensible à la casse (Windows)", "Turn off SSL certificate validation." => "Désactiver la validation du certificat SSL.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Si la connexion ne fonctionne qu'avec cette option, importez le certificat SSL du serveur LDAP dans le serveur ownCloud.", "Not recommended, use for testing only." => "Non recommandé, utilisation pour tests uniquement.", +"in seconds. A change empties the cache." => "en secondes. Tout changement vide le cache.", "User Display Name Field" => "Champ \"nom d'affichage\" de l'utilisateur", "The LDAP attribute to use to generate the user`s ownCloud name." => "L'attribut LDAP utilisé pour générer les noms d'utilisateurs d'ownCloud.", +"Base User Tree" => "DN racine de l'arbre utilisateurs", +"One User Base DN per line" => "Un DN racine utilisateur par ligne", +"User Search Attributes" => "Recherche des attributs utilisateur", +"Optional; one attribute per line" => "Optionnel, un attribut par ligne", "Group Display Name Field" => "Champ \"nom d'affichage\" du groupe", "The LDAP attribute to use to generate the groups`s ownCloud name." => "L'attribut LDAP utilisé pour générer les noms de groupes d'ownCloud.", +"Base Group Tree" => "DN racine de l'arbre groupes", +"One Group Base DN per line" => "Un DN racine groupe par ligne", +"Group Search Attributes" => "Recherche des attributs du groupe", +"Group-Member association" => "Association groupe-membre", "in bytes" => "en octets", -"in seconds. A change empties the cache." => "en secondes. Tout changement vide le cache.", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Laisser vide ", "Help" => "Aide" ); diff --git a/apps/user_ldap/l10n/gl.php b/apps/user_ldap/l10n/gl.php index d60521c4a0..a2531a40a8 100644 --- a/apps/user_ldap/l10n/gl.php +++ b/apps/user_ldap/l10n/gl.php @@ -1,4 +1,5 @@ "Fallou o borrado", "Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Aviso: Os aplicativos user_ldap e user_webdavauth son incompatíbeis. Pode acontecer un comportamento estraño. Consulte co administrador do sistema para desactivar un deles.", "Host" => "Servidor", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Pode omitir o protocolo agás que precise de SSL. Nese caso comece con ldaps://", @@ -18,21 +19,21 @@ "Defines the filter to apply, when retrieving groups." => "Define o filtro a aplicar cando se recompilan os grupos.", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "sen ningunha marca de posición, como p.ex «objectClass=grupoPosix».", "Port" => "Porto", -"Base User Tree" => "Base da árbore de usuarios", -"Base Group Tree" => "Base da árbore de grupo", -"Group-Member association" => "Asociación de grupos e membros", "Use TLS" => "Usar TLS", "Do not use it for SSL connections, it will fail." => "Non empregalo para conexións SSL: fallará.", "Case insensitve LDAP server (Windows)" => "Servidor LDAP que non distingue entre maiúsculas e minúsculas (Windows)", "Turn off SSL certificate validation." => "Desactiva a validación do certificado SSL.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Se a conexión só funciona con esta opción importa o certificado SSL do servidor LDAP no seu servidor ownCloud.", "Not recommended, use for testing only." => "Non se recomenda. Só para probas.", +"in seconds. A change empties the cache." => "en segundos. Calquera cambio baleira a caché.", "User Display Name Field" => "Campo de mostra do nome de usuario", "The LDAP attribute to use to generate the user`s ownCloud name." => "O atributo LDAP a empregar para xerar o nome de usuario de ownCloud.", +"Base User Tree" => "Base da árbore de usuarios", "Group Display Name Field" => "Campo de mostra do nome de grupo", "The LDAP attribute to use to generate the groups`s ownCloud name." => "O atributo LDAP úsase para xerar os nomes dos grupos de ownCloud.", +"Base Group Tree" => "Base da árbore de grupo", +"Group-Member association" => "Asociación de grupos e membros", "in bytes" => "en bytes", -"in seconds. A change empties the cache." => "en segundos. Calquera cambio baleira a caché.", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Deixar baleiro para o nome de usuario (predeterminado). Noutro caso, especifique un atributo LDAP/AD.", "Help" => "Axuda" ); diff --git a/apps/user_ldap/l10n/he.php b/apps/user_ldap/l10n/he.php index d33ecaadf0..5c563b7b6f 100644 --- a/apps/user_ldap/l10n/he.php +++ b/apps/user_ldap/l10n/he.php @@ -1,4 +1,5 @@ "מחיקה נכשלה", "Host" => "מארח", "User DN" => "DN משתמש", "Password" => "סיסמא", @@ -6,7 +7,7 @@ "User Login Filter" => "סנן כניסת משתמש", "User List Filter" => "סנן רשימת משתמשים", "Group Filter" => "סנן קבוצה", -"in bytes" => "בבתים", "in seconds. A change empties the cache." => "בשניות. שינוי מרוקן את המטמון.", +"in bytes" => "בבתים", "Help" => "עזרה" ); diff --git a/apps/user_ldap/l10n/hr.php b/apps/user_ldap/l10n/hr.php new file mode 100644 index 0000000000..9150331506 --- /dev/null +++ b/apps/user_ldap/l10n/hr.php @@ -0,0 +1,3 @@ + "Pomoć" +); diff --git a/apps/user_ldap/l10n/hu_HU.php b/apps/user_ldap/l10n/hu_HU.php index aae29d057e..64de16fa65 100644 --- a/apps/user_ldap/l10n/hu_HU.php +++ b/apps/user_ldap/l10n/hu_HU.php @@ -1,10 +1,14 @@ "A törlés nem sikerült", "Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Figyelem: a user_ldap és user_webdavauth alkalmazások nem kompatibilisek. Együttes használatuk váratlan eredményekhez vezethet. Kérje meg a rendszergazdát, hogy a kettő közül kapcsolja ki az egyiket.", +"Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Figyelmeztetés: Az LDAP PHP modul nincs telepítve, ezért ez az alrendszer nem fog működni. Kérje meg a rendszergazdát, hogy telepítse!", "Host" => "Kiszolgáló", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "A protokoll előtag elhagyható, kivéve, ha SSL-t kíván használni. Ebben az esetben kezdje így: ldaps://", "Base DN" => "DN-gyökér", +"One Base DN per line" => "Soronként egy DN-gyökér", "You can specify Base DN for users and groups in the Advanced tab" => "A Haladó fülre kattintva külön DN-gyökér állítható be a felhasználók és a csoportok számára", "User DN" => "A kapcsolódó felhasználó DN-je", +"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "Annak a felhasználónak a DN-je, akinek a nevében bejelentkezve kapcsolódunk a kiszolgálóhoz, pl. uid=agent,dc=example,dc=com. Bejelentkezés nélküli eléréshez ne töltse ki a DN és Jelszó mezőket!", "Password" => "Jelszó", "For anonymous access, leave DN and Password empty." => "Bejelentkezés nélküli eléréshez ne töltse ki a DN és Jelszó mezőket!", "User Login Filter" => "Szűrő a bejelentkezéshez", @@ -17,21 +21,23 @@ "Defines the filter to apply, when retrieving groups." => "Ez a szűrő érvényes a csoportok listázásakor.", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "itt ne használjunk változót, pl. \"objectClass=posixGroup\".", "Port" => "Port", -"Base User Tree" => "A felhasználói fa gyökere", -"Base Group Tree" => "A csoportfa gyökere", -"Group-Member association" => "A csoporttagság attribútuma", "Use TLS" => "Használjunk TLS-t", "Do not use it for SSL connections, it will fail." => "Ne használjuk SSL-kapcsolat esetén, mert nem fog működni!", "Case insensitve LDAP server (Windows)" => "Az LDAP-kiszolgáló nem tesz különbséget a kis- és nagybetűk között (Windows)", "Turn off SSL certificate validation." => "Ne ellenőrizzük az SSL-tanúsítvány érvényességét", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Ha a kapcsolat csak ezzel a beállítással működik, akkor importálja az LDAP-kiszolgáló SSL tanúsítványát az ownCloud kiszolgálóra!", "Not recommended, use for testing only." => "Nem javasolt, csak tesztelésre érdemes használni.", +"in seconds. A change empties the cache." => "másodpercben. A változtatás törli a cache tartalmát.", "User Display Name Field" => "A felhasználónév mezője", "The LDAP attribute to use to generate the user`s ownCloud name." => "Ebből az LDAP attribútumból képződik a felhasználó elnevezése, ami megjelenik az ownCloudban.", +"Base User Tree" => "A felhasználói fa gyökere", +"One User Base DN per line" => "Soronként egy felhasználói fa gyökerét adhatjuk meg", "Group Display Name Field" => "A csoport nevének mezője", "The LDAP attribute to use to generate the groups`s ownCloud name." => "Ebből az LDAP attribútumból képződik a csoport elnevezése, ami megjelenik az ownCloudban.", +"Base Group Tree" => "A csoportfa gyökere", +"One Group Base DN per line" => "Soronként egy csoportfa gyökerét adhatjuk meg", +"Group-Member association" => "A csoporttagság attribútuma", "in bytes" => "bájtban", -"in seconds. A change empties the cache." => "másodpercben. A változtatás törli a cache tartalmát.", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Hagyja üresen, ha a felhasználónevet kívánja használni. Ellenkező esetben adjon meg egy LDAP/AD attribútumot!", "Help" => "Súgó" ); diff --git a/apps/user_ldap/l10n/ia.php b/apps/user_ldap/l10n/ia.php new file mode 100644 index 0000000000..3586bf5a2e --- /dev/null +++ b/apps/user_ldap/l10n/ia.php @@ -0,0 +1,3 @@ + "Adjuta" +); diff --git a/apps/user_ldap/l10n/id.php b/apps/user_ldap/l10n/id.php index 56619634ba..33e8cc70e9 100644 --- a/apps/user_ldap/l10n/id.php +++ b/apps/user_ldap/l10n/id.php @@ -1,4 +1,5 @@ "penghapusan gagal", "Host" => "host", "Password" => "kata kunci", "User Login Filter" => "gunakan saringan login", @@ -8,7 +9,7 @@ "Do not use it for SSL connections, it will fail." => "jangan gunakan untuk koneksi SSL, itu akan gagal.", "Turn off SSL certificate validation." => "matikan validasi sertivikat SSL", "Not recommended, use for testing only." => "tidak disarankan, gunakan hanya untuk pengujian.", -"in bytes" => "dalam bytes", "in seconds. A change empties the cache." => "dalam detik. perubahan mengosongkan cache", +"in bytes" => "dalam bytes", "Help" => "bantuan" ); diff --git a/apps/user_ldap/l10n/it.php b/apps/user_ldap/l10n/it.php index b09819d79e..0220aa958c 100644 --- a/apps/user_ldap/l10n/it.php +++ b/apps/user_ldap/l10n/it.php @@ -1,8 +1,24 @@ "Eliminazione della configurazione del server non riuscita", +"The configuration is valid and the connection could be established!" => "La configurazione è valida e la connessione può essere stabilita.", +"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "La configurazione è valida, ma il Bind non è riuscito. Controlla le impostazioni del server e le credenziali.", +"The configuration is invalid. Please look in the ownCloud log for further details." => "La configurazione non è valida. Controlla il log di ownCloud per ulteriori dettagli.", +"Deletion failed" => "Eliminazione non riuscita", +"Take over settings from recent server configuration?" => "Vuoi recuperare le impostazioni dalla configurazione recente del server?", +"Keep settings?" => "Vuoi mantenere le impostazioni?", +"Cannot add server configuration" => "Impossibile aggiungere la configurazione del server", +"Connection test succeeded" => "Prova di connessione riuscita", +"Connection test failed" => "Prova di connessione non riuscita", +"Do you really want to delete the current Server Configuration?" => "Vuoi davvero eliminare la configurazione attuale del server?", +"Confirm Deletion" => "Conferma l'eliminazione", "Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Avviso: le applicazioni user_ldap e user_webdavauth sono incompatibili. Potresti riscontrare un comportamento inatteso. Chiedi al tuo amministratore di sistema di disabilitarne uno.", +"Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Avviso: il modulo PHP LDAP non è installato, il motore non funzionerà. Chiedi al tuo amministratore di sistema di installarlo.", +"Server configuration" => "Configurazione del server", +"Add Server Configuration" => "Aggiungi configurazione del server", "Host" => "Host", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "È possibile omettere il protocollo, ad eccezione se è necessario SSL. Quindi inizia con ldaps://", "Base DN" => "DN base", +"One Base DN per line" => "Un DN base per riga", "You can specify Base DN for users and groups in the Advanced tab" => "Puoi specificare una DN base per gli utenti ed i gruppi nella scheda Avanzate", "User DN" => "DN utente", "The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "Il DN per il client dell'utente con cui deve essere associato, ad esempio uid=agent,dc=example,dc=com. Per l'accesso anonimo, lasciare vuoti i campi DN e Password", @@ -17,22 +33,37 @@ "Group Filter" => "Filtro per il gruppo", "Defines the filter to apply, when retrieving groups." => "Specifica quale filtro utilizzare durante il recupero dei gruppi.", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "senza nessun segnaposto, per esempio \"objectClass=posixGroup\".", +"Connection Settings" => "Impostazioni di connessione", +"Configuration Active" => "Configurazione attiva", +"When unchecked, this configuration will be skipped." => "Se deselezionata, questa configurazione sarà saltata.", "Port" => "Porta", -"Base User Tree" => "Struttura base dell'utente", -"Base Group Tree" => "Struttura base del gruppo", -"Group-Member association" => "Associazione gruppo-utente ", +"Backup (Replica) Host" => "Host di backup (Replica)", +"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Fornisci un host di backup opzionale. Deve essere una replica del server AD/LDAP principale.", +"Backup (Replica) Port" => "Porta di backup (Replica)", +"Disable Main Server" => "Disabilita server principale", +"When switched on, ownCloud will only connect to the replica server." => "Se abilitata, ownCloud si collegherà solo al server di replica.", "Use TLS" => "Usa TLS", "Do not use it for SSL connections, it will fail." => "Non utilizzare per le connessioni SSL, fallirà.", "Case insensitve LDAP server (Windows)" => "Case insensitve LDAP server (Windows)", "Turn off SSL certificate validation." => "Disattiva il controllo del certificato SSL.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Se la connessione funziona esclusivamente con questa opzione, importa il certificato SSL del server LDAP nel tuo server ownCloud.", "Not recommended, use for testing only." => "Non consigliato, utilizzare solo per test.", +"in seconds. A change empties the cache." => "in secondi. Il cambio svuota la cache.", +"Directory Settings" => "Impostazioni delle cartelle", "User Display Name Field" => "Campo per la visualizzazione del nome utente", "The LDAP attribute to use to generate the user`s ownCloud name." => "L'attributo LDAP da usare per generare il nome dell'utente ownCloud.", +"Base User Tree" => "Struttura base dell'utente", +"One User Base DN per line" => "Un DN base utente per riga", +"User Search Attributes" => "Attributi di ricerca utente", +"Optional; one attribute per line" => "Opzionale; un attributo per riga", "Group Display Name Field" => "Campo per la visualizzazione del nome del gruppo", "The LDAP attribute to use to generate the groups`s ownCloud name." => "L'attributo LDAP da usare per generare il nome del gruppo ownCloud.", +"Base Group Tree" => "Struttura base del gruppo", +"One Group Base DN per line" => "Un DN base gruppo per riga", +"Group Search Attributes" => "Attributi di ricerca gruppo", +"Group-Member association" => "Associazione gruppo-utente ", +"Special Attributes" => "Attributi speciali", "in bytes" => "in byte", -"in seconds. A change empties the cache." => "in secondi. Il cambio svuota la cache.", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Lascia vuoto per il nome utente (predefinito). Altrimenti, specifica un attributo LDAP/AD.", "Help" => "Aiuto" ); diff --git a/apps/user_ldap/l10n/ja_JP.php b/apps/user_ldap/l10n/ja_JP.php index 9dff8e7bd6..7706357cbf 100644 --- a/apps/user_ldap/l10n/ja_JP.php +++ b/apps/user_ldap/l10n/ja_JP.php @@ -1,8 +1,24 @@ "サーバ設定の削除に失敗しました", +"The configuration is valid and the connection could be established!" => "設定は有効であり、接続を確立しました!", +"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "設定は有効ですが、接続に失敗しました。サーバ設定と資格情報を確認して下さい。", +"The configuration is invalid. Please look in the ownCloud log for further details." => "設定は無効です。詳細は ownCloud のログを見て下さい。", +"Deletion failed" => "削除に失敗しました", +"Take over settings from recent server configuration?" => "最近のサーバ設定から設定を引き継ぎますか?", +"Keep settings?" => "設定を保持しますか?", +"Cannot add server configuration" => "サーバ設定を追加できません", +"Connection test succeeded" => "接続テストに成功しました", +"Connection test failed" => "接続テストに失敗しました", +"Do you really want to delete the current Server Configuration?" => "現在のサーバ設定を本当に削除してもよろしいですか?", +"Confirm Deletion" => "削除の確認", "Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "警告: user_ldap と user_webdavauth のアプリには互換性がありません。予期せぬ動作をする可能姓があります。システム管理者にどちらかを無効にするよう問い合わせてください。", +"Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "警告: PHP LDAP モジュールがインストールされていません。バックエンドが正しく動作しません。システム管理者にインストールするよう問い合わせてください。", +"Server configuration" => "サーバ設定", +"Add Server Configuration" => "サーバ設定を追加", "Host" => "ホスト", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "SSL通信しない場合には、プロトコル名を省略することができます。そうでない場合には、ldaps:// から始めてください。", "Base DN" => "ベースDN", +"One Base DN per line" => "1行に1つのベースDN", "You can specify Base DN for users and groups in the Advanced tab" => "拡張タブでユーザとグループのベースDNを指定することができます。", "User DN" => "ユーザDN", "The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "クライアントユーザーのDNは、特定のものに結びつけることはしません。 例えば uid=agent,dc=example,dc=com. だと匿名アクセスの場合、DNとパスワードは空のままです。", @@ -17,22 +33,37 @@ "Group Filter" => "グループフィルタ", "Defines the filter to apply, when retrieving groups." => "グループを取得するときに適用するフィルターを定義する。", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "プレースホルダーを利用しないでください。例 \"objectClass=posixGroup\"", +"Connection Settings" => "接続設定", +"Configuration Active" => "設定はアクティブです", +"When unchecked, this configuration will be skipped." => "チェックを外すと、この設定はスキップされます。", "Port" => "ポート", -"Base User Tree" => "ベースユーザツリー", -"Base Group Tree" => "ベースグループツリー", -"Group-Member association" => "グループとメンバーの関連付け", +"Backup (Replica) Host" => "バックアップ(レプリカ)ホスト", +"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "バックアップホストをオプションで指定することができます。メインのLDAP/ADサーバのレプリカである必要があります。", +"Backup (Replica) Port" => "バックアップ(レプリカ)ポート", +"Disable Main Server" => "メインサーバを無効にする", +"When switched on, ownCloud will only connect to the replica server." => "有効にすると、ownCloudはレプリカサーバにのみ接続します。", "Use TLS" => "TLSを利用", "Do not use it for SSL connections, it will fail." => "SSL接続に利用しないでください、失敗します。", "Case insensitve LDAP server (Windows)" => "大文字/小文字を区別しないLDAPサーバ(Windows)", "Turn off SSL certificate validation." => "SSL証明書の確認を無効にする。", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "接続がこのオプションでのみ動作する場合は、LDAPサーバのSSL証明書をownCloudサーバにインポートしてください。", "Not recommended, use for testing only." => "推奨しません、テスト目的でのみ利用してください。", +"in seconds. A change empties the cache." => "秒。変更後にキャッシュがクリアされます。", +"Directory Settings" => "ディレクトリ設定", "User Display Name Field" => "ユーザ表示名のフィールド", "The LDAP attribute to use to generate the user`s ownCloud name." => "ユーザのownCloud名の生成に利用するLDAP属性。", +"Base User Tree" => "ベースユーザツリー", +"One User Base DN per line" => "1行に1つのユーザベースDN", +"User Search Attributes" => "ユーザ検索属性", +"Optional; one attribute per line" => "オプション:1行に1属性", "Group Display Name Field" => "グループ表示名のフィールド", "The LDAP attribute to use to generate the groups`s ownCloud name." => "グループのownCloud名の生成に利用するLDAP属性。", +"Base Group Tree" => "ベースグループツリー", +"One Group Base DN per line" => "1行に1つのグループベースDN", +"Group Search Attributes" => "グループ検索属性", +"Group-Member association" => "グループとメンバーの関連付け", +"Special Attributes" => "特殊属性", "in bytes" => "バイト", -"in seconds. A change empties the cache." => "秒。変更後にキャッシュがクリアされます。", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "ユーザ名を空のままにしてください(デフォルト)。そうでない場合は、LDAPもしくはADの属性を指定してください。", "Help" => "ヘルプ" ); diff --git a/apps/user_ldap/l10n/ka_GE.php b/apps/user_ldap/l10n/ka_GE.php new file mode 100644 index 0000000000..b31767fe93 --- /dev/null +++ b/apps/user_ldap/l10n/ka_GE.php @@ -0,0 +1,4 @@ + "წაშლის ველი", +"Help" => "დახმარება" +); diff --git a/apps/user_ldap/l10n/ko.php b/apps/user_ldap/l10n/ko.php index c0d09b5c3c..9ff8ff99d0 100644 --- a/apps/user_ldap/l10n/ko.php +++ b/apps/user_ldap/l10n/ko.php @@ -1,8 +1,11 @@ Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "경고user_ldap 앱과 user_webdavauth 앱은 호환되지 않습니다. 오동작을 일으킬 수 있으므로, 시스템 관리자에게 요청하여, 둘 중 하나를 비활성화 하시기 바랍니다.", +"Deletion failed" => "삭제 실패", +"Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "경고: user_ldap 앱과 user_webdavauth 앱은 호환되지 않습니다. 오동작을 일으킬 수 있으므로, 시스템 관리자에게 요청하여 둘 중 하나만 사용하도록 하십시오.", +"Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "경고: PHP LDAP 모듈이 비활성화되어 있거나 설치되어 있지 않습니다. 백엔드를 사용할 수 없습니다. 시스템 관리자에게 설치를 요청하십시오.", "Host" => "호스트", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "SSL을 사용하는 경우가 아니라면 프로토콜을 입력하지 않아도 됩니다. SSL을 사용하려면 ldaps://를 입력하십시오.", "Base DN" => "기본 DN", +"One Base DN per line" => "기본 DN을 한 줄에 하나씩 입력하십시오", "You can specify Base DN for users and groups in the Advanced tab" => "고급 탭에서 사용자 및 그룹에 대한 기본 DN을 지정할 수 있습니다.", "User DN" => "사용자 DN", "The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "바인딩 작업을 수행할 클라이언트 사용자 DN입니다. 예를 들어서 uid=agent,dc=example,dc=com입니다. 익명 접근을 허용하려면 DN과 암호를 비워 두십시오.", @@ -18,21 +21,23 @@ "Defines the filter to apply, when retrieving groups." => "그룹을 검색할 때 적용할 필터를 정의합니다.", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "자리 비움자를 사용할 수 없습니다. 예제: \"objectClass=posixGroup\"", "Port" => "포트", -"Base User Tree" => "기본 사용자 트리", -"Base Group Tree" => "기본 그룹 트리", -"Group-Member association" => "그룹-회원 연결", "Use TLS" => "TLS 사용", "Do not use it for SSL connections, it will fail." => "SSL 연결 시 사용하는 경우 연결되지 않습니다.", "Case insensitve LDAP server (Windows)" => "서버에서 대소문자를 구분하지 않음 (Windows)", "Turn off SSL certificate validation." => "SSL 인증서 유효성 검사를 해제합니다.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "이 옵션을 사용해야 연결할 수 있는 경우에는 LDAP 서버의 SSL 인증서를 ownCloud로 가져올 수 있습니다.", "Not recommended, use for testing only." => "추천하지 않음, 테스트로만 사용하십시오.", +"in seconds. A change empties the cache." => "초. 항목 변경 시 캐시가 갱신됩니다.", "User Display Name Field" => "사용자의 표시 이름 필드", "The LDAP attribute to use to generate the user`s ownCloud name." => "LDAP 속성은 사용자의 ownCloud 이름을 생성하기 위해 사용합니다.", +"Base User Tree" => "기본 사용자 트리", +"One User Base DN per line" => "사용자 DN을 한 줄에 하나씩 입력하십시오", "Group Display Name Field" => "그룹의 표시 이름 필드", "The LDAP attribute to use to generate the groups`s ownCloud name." => "LDAP 속성은 그룹의 ownCloud 이름을 생성하기 위해 사용합니다.", +"Base Group Tree" => "기본 그룹 트리", +"One Group Base DN per line" => "그룹 기본 DN을 한 줄에 하나씩 입력하십시오", +"Group-Member association" => "그룹-회원 연결", "in bytes" => "바이트", -"in seconds. A change empties the cache." => "초. 항목 변경 시 캐시가 갱신됩니다.", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "사용자 이름을 사용하려면 비워 두십시오(기본값). 기타 경우 LDAP/AD 속성을 지정하십시오.", "Help" => "도움말" ); diff --git a/apps/user_ldap/l10n/ku_IQ.php b/apps/user_ldap/l10n/ku_IQ.php new file mode 100644 index 0000000000..1ae808ddd9 --- /dev/null +++ b/apps/user_ldap/l10n/ku_IQ.php @@ -0,0 +1,3 @@ + "یارمەتی" +); diff --git a/apps/user_ldap/l10n/lb.php b/apps/user_ldap/l10n/lb.php new file mode 100644 index 0000000000..39ed627ce2 --- /dev/null +++ b/apps/user_ldap/l10n/lb.php @@ -0,0 +1,5 @@ + "Konnt net läschen", +"Password" => "Passwuert", +"Help" => "Hëllef" +); diff --git a/apps/user_ldap/l10n/lt_LT.php b/apps/user_ldap/l10n/lt_LT.php index 809ed571bd..aa21dd2d3c 100644 --- a/apps/user_ldap/l10n/lt_LT.php +++ b/apps/user_ldap/l10n/lt_LT.php @@ -1,4 +1,5 @@ "Ištrinti nepavyko", "Password" => "Slaptažodis", "Group Filter" => "Grupės filtras", "Port" => "Prievadas", diff --git a/apps/user_ldap/l10n/lv.php b/apps/user_ldap/l10n/lv.php new file mode 100644 index 0000000000..52353472e4 --- /dev/null +++ b/apps/user_ldap/l10n/lv.php @@ -0,0 +1,3 @@ + "Palīdzība" +); diff --git a/apps/user_ldap/l10n/mk.php b/apps/user_ldap/l10n/mk.php index 70a62e7176..7d34ff4949 100644 --- a/apps/user_ldap/l10n/mk.php +++ b/apps/user_ldap/l10n/mk.php @@ -1,5 +1,7 @@ "Бришењето е неуспешно", "Host" => "Домаќин", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Може да го скокнете протколот освен ако не ви треба SSL. Тогаш ставете ldaps://", -"Password" => "Лозинка" +"Password" => "Лозинка", +"Help" => "Помош" ); diff --git a/apps/user_ldap/l10n/ms_MY.php b/apps/user_ldap/l10n/ms_MY.php new file mode 100644 index 0000000000..17a6cbe2cb --- /dev/null +++ b/apps/user_ldap/l10n/ms_MY.php @@ -0,0 +1,4 @@ + "Pemadaman gagal", +"Help" => "Bantuan" +); diff --git a/apps/user_ldap/l10n/nb_NO.php b/apps/user_ldap/l10n/nb_NO.php index a5f4657d04..295166b0a5 100644 --- a/apps/user_ldap/l10n/nb_NO.php +++ b/apps/user_ldap/l10n/nb_NO.php @@ -1,11 +1,12 @@ "Sletting feilet", "Password" => "Passord", "Group Filter" => "Gruppefilter", "Port" => "Port", "Use TLS" => "Bruk TLS", "Do not use it for SSL connections, it will fail." => "Ikke bruk for SSL tilkoblinger, dette vil ikke fungere.", "Not recommended, use for testing only." => "Ikke anbefalt, bruk kun for testing", -"in bytes" => "i bytes", "in seconds. A change empties the cache." => "i sekunder. En endring tømmer bufferen.", +"in bytes" => "i bytes", "Help" => "Hjelp" ); diff --git a/apps/user_ldap/l10n/nl.php b/apps/user_ldap/l10n/nl.php index 5e2ddf8959..cc5e85fc30 100644 --- a/apps/user_ldap/l10n/nl.php +++ b/apps/user_ldap/l10n/nl.php @@ -1,10 +1,25 @@ "Verwijderen serverconfiguratie mislukt", +"The configuration is valid and the connection could be established!" => "De configuratie is geldig en de verbinding is geslaagd!", +"The configuration is invalid. Please look in the ownCloud log for further details." => "De configuratie is ongeldig. Controleer de ownCloud log voor meer details.", +"Deletion failed" => "Verwijderen mislukt", +"Take over settings from recent server configuration?" => "Overnemen instellingen van de recente serverconfiguratie?", +"Keep settings?" => "Instellingen bewaren?", +"Cannot add server configuration" => "Kon de serverconfiguratie niet toevoegen", +"Connection test succeeded" => "Verbindingstest geslaagd", +"Connection test failed" => "Verbindingstest mislukt", +"Do you really want to delete the current Server Configuration?" => "Wilt u werkelijk de huidige Serverconfiguratie verwijderen?", +"Confirm Deletion" => "Bevestig verwijderen", "Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Waarschuwing: De Apps user_ldap en user_webdavauth zijn incompatible. U kunt onverwacht gedrag ervaren. Vraag uw beheerder om een van beide apps de deactiveren.", +"Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Waarschuwing: De PHP LDAP module is niet geïnstalleerd, het backend zal niet werken. Vraag uw systeembeheerder om de module te installeren.", +"Server configuration" => "Serverconfiguratie", +"Add Server Configuration" => "Toevoegen serverconfiguratie", "Host" => "Host", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Je kunt het protocol weglaten, tenzij je SSL vereist. Start in dat geval met ldaps://", -"Base DN" => "Basis DN", -"You can specify Base DN for users and groups in the Advanced tab" => "Je kunt het standaard DN voor gebruikers en groepen specificeren in het tab Geavanceerd.", -"User DN" => "Gebruikers DN", +"Base DN" => "Base DN", +"One Base DN per line" => "Een Base DN per regel", +"You can specify Base DN for users and groups in the Advanced tab" => "Je kunt het Base DN voor gebruikers en groepen specificeren in het tab Geavanceerd.", +"User DN" => "User DN", "The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "De DN van de client gebruiker waarmee de verbinding zal worden gemaakt, bijv. uid=agent,dc=example,dc=com. Voor anonieme toegang laat je het DN en het wachtwoord leeg.", "Password" => "Wachtwoord", "For anonymous access, leave DN and Password empty." => "Voor anonieme toegang, laat de DN en het wachtwoord leeg.", @@ -17,22 +32,30 @@ "Group Filter" => "Groep Filter", "Defines the filter to apply, when retrieving groups." => "Definiëerd de toe te passen filter voor het ophalen van groepen.", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "zonder een placeholder, bijv. \"objectClass=posixGroup\"", +"Configuration Active" => "Configuratie actief", "Port" => "Poort", -"Base User Tree" => "Basis Gebruikers Structuur", -"Base Group Tree" => "Basis Groupen Structuur", -"Group-Member association" => "Groepslid associatie", +"Backup (Replica) Host" => "Backup (Replica) Host", +"Backup (Replica) Port" => "Backup (Replica) Poort", +"Disable Main Server" => "Deactiveren hoofdserver", +"When switched on, ownCloud will only connect to the replica server." => "Wanneer ingeschakeld, zal ownCloud allen verbinden met de replicaserver.", "Use TLS" => "Gebruik TLS", "Do not use it for SSL connections, it will fail." => "Gebruik niet voor SSL connecties, deze mislukken.", "Case insensitve LDAP server (Windows)" => "Niet-hoofdlettergevoelige LDAP server (Windows)", "Turn off SSL certificate validation." => "Schakel SSL certificaat validatie uit.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Als de connectie alleen werkt met deze optie, importeer dan het LDAP server SSL certificaat naar je ownCloud server.", "Not recommended, use for testing only." => "Niet aangeraden, gebruik alleen voor test doeleinden.", +"in seconds. A change empties the cache." => "in seconden. Een verandering maakt de cache leeg.", "User Display Name Field" => "Gebruikers Schermnaam Veld", "The LDAP attribute to use to generate the user`s ownCloud name." => "Het te gebruiken LDAP attribuut voor het genereren van de ownCloud naam voor de gebruikers.", +"Base User Tree" => "Basis Gebruikers Structuur", +"One User Base DN per line" => "Een User Base DN per regel", +"Optional; one attribute per line" => "Optioneel; één attribuut per regel", "Group Display Name Field" => "Groep Schermnaam Veld", "The LDAP attribute to use to generate the groups`s ownCloud name." => "Het te gebruiken LDAP attribuut voor het genereren van de ownCloud naam voor de groepen.", +"Base Group Tree" => "Basis Groupen Structuur", +"One Group Base DN per line" => "Een Group Base DN per regel", +"Group-Member association" => "Groepslid associatie", "in bytes" => "in bytes", -"in seconds. A change empties the cache." => "in seconden. Een verandering maakt de cache leeg.", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Laat leeg voor de gebruikersnaam (standaard). Of, specificeer een LDAP/AD attribuut.", "Help" => "Help" ); diff --git a/apps/user_ldap/l10n/nn_NO.php b/apps/user_ldap/l10n/nn_NO.php new file mode 100644 index 0000000000..54d1f158f6 --- /dev/null +++ b/apps/user_ldap/l10n/nn_NO.php @@ -0,0 +1,3 @@ + "Hjelp" +); diff --git a/apps/user_ldap/l10n/oc.php b/apps/user_ldap/l10n/oc.php new file mode 100644 index 0000000000..a128638172 --- /dev/null +++ b/apps/user_ldap/l10n/oc.php @@ -0,0 +1,4 @@ + "Fracàs d'escafatge", +"Help" => "Ajuda" +); diff --git a/apps/user_ldap/l10n/pl.php b/apps/user_ldap/l10n/pl.php index 55110b8a83..83a8d1615a 100644 --- a/apps/user_ldap/l10n/pl.php +++ b/apps/user_ldap/l10n/pl.php @@ -1,4 +1,5 @@ "Skasowanie nie powiodło się", "Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Ostrzeżenie: Aplikacje user_ldap i user_webdavauth nie są kompatybilne. Mogą powodować nieoczekiwane zachowanie. Poproś administratora o wyłączenie jednej z nich.", "Host" => "Host", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Można pominąć protokół, z wyjątkiem wymaganego protokołu SSL. Następnie uruchom z ldaps://", @@ -18,21 +19,21 @@ "Defines the filter to apply, when retrieving groups." => "Definiuje filtry do zastosowania, podczas pobierania grup.", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "bez żadnych symboli zastępczych np. \"objectClass=posixGroup\".", "Port" => "Port", -"Base User Tree" => "Drzewo bazy użytkowników", -"Base Group Tree" => "Drzewo bazy grup", -"Group-Member association" => "Członek grupy stowarzyszenia", "Use TLS" => "Użyj TLS", "Do not use it for SSL connections, it will fail." => "Nie używaj SSL dla połączeń, jeśli się nie powiedzie.", "Case insensitve LDAP server (Windows)" => "Wielkość liter serwera LDAP (Windows)", "Turn off SSL certificate validation." => "Wyłączyć sprawdzanie poprawności certyfikatu SSL.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Jeśli połączenie działa tylko z tą opcją, zaimportuj certyfikat SSL serwera LDAP w serwerze ownCloud.", "Not recommended, use for testing only." => "Niezalecane, użyj tylko testowo.", +"in seconds. A change empties the cache." => "w sekundach. Zmiana opróżnia pamięć podręczną.", "User Display Name Field" => "Pole wyświetlanej nazwy użytkownika", "The LDAP attribute to use to generate the user`s ownCloud name." => "Atrybut LDAP służy do generowania nazwy użytkownika ownCloud.", +"Base User Tree" => "Drzewo bazy użytkowników", "Group Display Name Field" => "Pole wyświetlanej nazwy grupy", "The LDAP attribute to use to generate the groups`s ownCloud name." => "Atrybut LDAP służy do generowania nazwy grup ownCloud.", +"Base Group Tree" => "Drzewo bazy grup", +"Group-Member association" => "Członek grupy stowarzyszenia", "in bytes" => "w bajtach", -"in seconds. A change empties the cache." => "w sekundach. Zmiana opróżnia pamięć podręczną.", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Pozostaw puste dla user name (domyślnie). W przeciwnym razie podaj atrybut LDAP/AD.", "Help" => "Pomoc" ); diff --git a/apps/user_ldap/l10n/pt_BR.php b/apps/user_ldap/l10n/pt_BR.php index 18eed6d014..79e56eeb65 100644 --- a/apps/user_ldap/l10n/pt_BR.php +++ b/apps/user_ldap/l10n/pt_BR.php @@ -1,4 +1,5 @@ "Remoção falhou", "Host" => "Host", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Você pode omitir o protocolo, exceto quando requerer SSL. Então inicie com ldaps://", "Base DN" => "DN Base", @@ -17,21 +18,21 @@ "Defines the filter to apply, when retrieving groups." => "Define o filtro a aplicar ao obter grupos.", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "sem nenhum espaço reservado, ex. \"objectClass=posixGroup\"", "Port" => "Porta", -"Base User Tree" => "Árvore de Usuário Base", -"Base Group Tree" => "Árvore de Grupo Base", -"Group-Member association" => "Associação Grupo-Membro", "Use TLS" => "Usar TLS", "Do not use it for SSL connections, it will fail." => "Não use-o para conexões SSL, pois falhará.", "Case insensitve LDAP server (Windows)" => "Servidor LDAP sensível à caixa alta (Windows)", "Turn off SSL certificate validation." => "Desligar validação de certificado SSL.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Se a conexão só funciona com essa opção, importe o certificado SSL do servidor LDAP no seu servidor ownCloud.", "Not recommended, use for testing only." => "Não recomendado, use somente para testes.", +"in seconds. A change empties the cache." => "em segundos. Uma mudança esvaziará o cache.", "User Display Name Field" => "Campo Nome de Exibição de Usuário", "The LDAP attribute to use to generate the user`s ownCloud name." => "O atributo LDAP para usar para gerar nome ownCloud do usuário.", +"Base User Tree" => "Árvore de Usuário Base", "Group Display Name Field" => "Campo Nome de Exibição de Grupo", "The LDAP attribute to use to generate the groups`s ownCloud name." => "O atributo LDAP para usar para gerar nome ownCloud do grupo.", +"Base Group Tree" => "Árvore de Grupo Base", +"Group-Member association" => "Associação Grupo-Membro", "in bytes" => "em bytes", -"in seconds. A change empties the cache." => "em segundos. Uma mudança esvaziará o cache.", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Deixe vazio para nome de usuário (padrão). Caso contrário, especifique um atributo LDAP/AD.", "Help" => "Ajuda" ); diff --git a/apps/user_ldap/l10n/pt_PT.php b/apps/user_ldap/l10n/pt_PT.php index 1640f03749..21735b497c 100644 --- a/apps/user_ldap/l10n/pt_PT.php +++ b/apps/user_ldap/l10n/pt_PT.php @@ -1,8 +1,24 @@ "Erro ao eliminar as configurações do servidor", +"The configuration is valid and the connection could be established!" => "A configuração está correcta e foi possível estabelecer a ligação!", +"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "A configuração está correcta, mas não foi possível estabelecer o \"laço\", por favor, verifique as configurações do servidor e as credenciais.", +"The configuration is invalid. Please look in the ownCloud log for further details." => "A configuração é inválida. Por favor, veja o log do ownCloud para mais detalhes.", +"Deletion failed" => "Erro ao apagar", +"Take over settings from recent server configuration?" => "Assumir as configurações da configuração do servidor mais recente?", +"Keep settings?" => "Manter as definições?", +"Cannot add server configuration" => "Não foi possível adicionar as configurações do servidor.", +"Connection test succeeded" => "Teste de conecção passado com sucesso.", +"Connection test failed" => "Erro no teste de conecção.", +"Do you really want to delete the current Server Configuration?" => "Deseja realmente apagar as configurações de servidor actuais?", +"Confirm Deletion" => "Confirmar a operação de apagar", "Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Aviso: A aplicação user_ldap e user_webdavauth são incompativeis. A aplicação pode tornar-se instável. Por favor, peça ao seu administrador para desactivar uma das aplicações.", +"Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Aviso: O módulo PHP LDAP não está instalado, logo não irá funcionar. Por favor peça ao administrador para o instalar.", +"Server configuration" => "Configurações do servidor", +"Add Server Configuration" => "Adicionar configurações do servidor", "Host" => "Anfitrião", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Pode omitir o protocolo, excepto se necessitar de SSL. Neste caso, comece com ldaps://", "Base DN" => "DN base", +"One Base DN per line" => "Uma base DN por linho", "You can specify Base DN for users and groups in the Advanced tab" => "Pode especificar o ND Base para utilizadores e grupos no separador Avançado", "User DN" => "DN do utilizador", "The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "O DN to cliente ", @@ -17,22 +33,34 @@ "Group Filter" => "Filtrar por grupo", "Defines the filter to apply, when retrieving groups." => "Defina o filtro a aplicar, ao recuperar grupos.", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "Sem nenhuma variável. Exemplo: \"objectClass=posixGroup\".", +"Configuration Active" => "Configuração activa", +"When unchecked, this configuration will be skipped." => "Se não estiver marcada, esta definição não será tida em conta.", "Port" => "Porto", -"Base User Tree" => "Base da árvore de utilizadores.", -"Base Group Tree" => "Base da árvore de grupos.", -"Group-Member association" => "Associar utilizador ao grupo.", +"Backup (Replica) Host" => "Servidor de Backup (Réplica)", +"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Forneça um servidor (anfitrião) de backup. Deve ser uma réplica do servidor principal de LDAP/AD ", +"Backup (Replica) Port" => "Porta do servidor de backup (Replica)", +"Disable Main Server" => "Desactivar servidor principal", +"When switched on, ownCloud will only connect to the replica server." => "Se estiver ligado, o ownCloud vai somente ligar-se a este servidor de réplicas.", "Use TLS" => "Usar TLS", "Do not use it for SSL connections, it will fail." => "Não use para ligações SSL, irá falhar.", "Case insensitve LDAP server (Windows)" => "Servidor LDAP (Windows) não sensível a maiúsculas.", "Turn off SSL certificate validation." => "Desligar a validação de certificado SSL.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Se a ligação apenas funcionar com está opção, importe o certificado SSL do servidor LDAP para o seu servidor do ownCloud.", "Not recommended, use for testing only." => "Não recomendado, utilizado apenas para testes!", +"in seconds. A change empties the cache." => "em segundos. Uma alteração esvazia a cache.", "User Display Name Field" => "Mostrador do nome de utilizador.", "The LDAP attribute to use to generate the user`s ownCloud name." => "Atributo LDAP para gerar o nome de utilizador do ownCloud.", +"Base User Tree" => "Base da árvore de utilizadores.", +"One User Base DN per line" => "Uma base de utilizador DN por linha", +"User Search Attributes" => "Utilizar atributos de pesquisa", +"Optional; one attribute per line" => "Opcional; Um atributo por linha", "Group Display Name Field" => "Mostrador do nome do grupo.", "The LDAP attribute to use to generate the groups`s ownCloud name." => "Atributo LDAP para gerar o nome do grupo do ownCloud.", +"Base Group Tree" => "Base da árvore de grupos.", +"One Group Base DN per line" => "Uma base de grupo DN por linha", +"Group Search Attributes" => "Atributos de pesquisa de grupo", +"Group-Member association" => "Associar utilizador ao grupo.", "in bytes" => "em bytes", -"in seconds. A change empties the cache." => "em segundos. Uma alteração esvazia a cache.", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Deixe vazio para nome de utilizador (padrão). De outro modo, especifique um atributo LDAP/AD.", "Help" => "Ajuda" ); diff --git a/apps/user_ldap/l10n/ro.php b/apps/user_ldap/l10n/ro.php index 3ab336cfff..3e7e750042 100644 --- a/apps/user_ldap/l10n/ro.php +++ b/apps/user_ldap/l10n/ro.php @@ -1,8 +1,11 @@ "Ștergerea a eșuat", "Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Atentie: Apps user_ldap si user_webdavauth sunt incompatibile. Este posibil sa experimentati un comportament neasteptat. Vă rugăm să întrebați administratorul de sistem pentru a dezactiva una dintre ele.", +"Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Atenție Modulul PHP LDAP nu este instalat, infrastructura nu va funcționa. Contactează administratorul sistemului pentru al instala.", "Host" => "Gazdă", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Puteți omite protocolul, decât dacă folosiți SSL. Atunci se începe cu ldaps://", "Base DN" => "DN de bază", +"One Base DN per line" => "Un Base DN pe linie", "You can specify Base DN for users and groups in the Advanced tab" => "Puteți să specificați DN de bază pentru utilizatori și grupuri în fila Avansat", "User DN" => "DN al utilizatorului", "The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN-ul clientului utilizator cu care se va efectua conectarea, d.e. uid=agent,dc=example,dc=com. Pentru acces anonim, lăsăți DN și Parolă libere.", @@ -18,21 +21,23 @@ "Defines the filter to apply, when retrieving groups." => "Definește filtrele care se aplică, când se preiau grupurile.", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "fără substituenți, d.e. \"objectClass=posixGroup\"", "Port" => "Portul", -"Base User Tree" => "Arborele de bază al Utilizatorilor", -"Base Group Tree" => "Arborele de bază al Grupurilor", -"Group-Member association" => "Asocierea Grup-Membru", "Use TLS" => "Utilizează TLS", "Do not use it for SSL connections, it will fail." => "A nu se utiliza pentru conexiuni SSL, va eșua.", "Case insensitve LDAP server (Windows)" => "Server LDAP insensibil la majuscule (Windows)", "Turn off SSL certificate validation." => "Oprește validarea certificatelor SSL ", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Dacă conexiunea lucrează doar cu această opțiune, importează certificatul SSL al serverului LDAP în serverul ownCloud.", "Not recommended, use for testing only." => "Nu este recomandat, a se utiliza doar pentru testare.", +"in seconds. A change empties the cache." => "în secunde. O schimbare curăță memoria tampon.", "User Display Name Field" => "Câmpul cu numele vizibil al utilizatorului", "The LDAP attribute to use to generate the user`s ownCloud name." => "Atributul LDAP folosit pentru a genera numele de utilizator din ownCloud.", +"Base User Tree" => "Arborele de bază al Utilizatorilor", +"One User Base DN per line" => "Un User Base DN pe linie", "Group Display Name Field" => "Câmpul cu numele grupului", "The LDAP attribute to use to generate the groups`s ownCloud name." => "Atributul LDAP folosit pentru a genera numele grupurilor din ownCloud", +"Base Group Tree" => "Arborele de bază al Grupurilor", +"One Group Base DN per line" => "Un Group Base DN pe linie", +"Group-Member association" => "Asocierea Grup-Membru", "in bytes" => "în octeți", -"in seconds. A change empties the cache." => "în secunde. O schimbare curăță memoria tampon.", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Lăsați gol pentru numele de utilizator (implicit). În caz contrar, specificați un atribut LDAP / AD.", "Help" => "Ajutor" ); diff --git a/apps/user_ldap/l10n/ru.php b/apps/user_ldap/l10n/ru.php index 42fba32f43..45f6c171bf 100644 --- a/apps/user_ldap/l10n/ru.php +++ b/apps/user_ldap/l10n/ru.php @@ -1,4 +1,5 @@ "Удаление не удалось", "Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Внимание:Приложения user_ldap и user_webdavauth несовместимы. Вы можете столкнуться с неожиданным поведением. Пожалуйста, обратитесь к системному администратору, чтобы отключить одно из них.", "Host" => "Сервер", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Можно опустить протокол, за исключением того, когда вам требуется SSL. Тогда начните с ldaps :/ /", @@ -18,21 +19,21 @@ "Defines the filter to apply, when retrieving groups." => "Определяет фильтр для применения при получении группы.", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "без заполнения, например \"objectClass=posixGroup\".", "Port" => "Порт", -"Base User Tree" => "База пользовательского дерева", -"Base Group Tree" => "База группового дерева", -"Group-Member association" => "Ассоциация Группа-Участник", "Use TLS" => "Использовать TLS", "Do not use it for SSL connections, it will fail." => "Не используйте для соединений SSL", "Case insensitve LDAP server (Windows)" => "Нечувствительный к регистру сервер LDAP (Windows)", "Turn off SSL certificate validation." => "Отключить проверку сертификата SSL.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Если соединение работает только с этой опцией, импортируйте на ваш сервер ownCloud сертификат SSL сервера LDAP.", "Not recommended, use for testing only." => "Не рекомендуется, используйте только для тестирования.", +"in seconds. A change empties the cache." => "в секундах. Изменение очистит кэш.", "User Display Name Field" => "Поле отображаемого имени пользователя", "The LDAP attribute to use to generate the user`s ownCloud name." => "Атрибут LDAP для генерации имени пользователя ownCloud.", +"Base User Tree" => "База пользовательского дерева", "Group Display Name Field" => "Поле отображаемого имени группы", "The LDAP attribute to use to generate the groups`s ownCloud name." => "Атрибут LDAP для генерации имени группы ownCloud.", +"Base Group Tree" => "База группового дерева", +"Group-Member association" => "Ассоциация Группа-Участник", "in bytes" => "в байтах", -"in seconds. A change empties the cache." => "в секундах. Изменение очистит кэш.", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Оставьте имя пользователя пустым (по умолчанию). Иначе укажите атрибут LDAP/AD.", "Help" => "Помощь" ); diff --git a/apps/user_ldap/l10n/ru_RU.php b/apps/user_ldap/l10n/ru_RU.php index 64ba1176f6..f62d2cd4ea 100644 --- a/apps/user_ldap/l10n/ru_RU.php +++ b/apps/user_ldap/l10n/ru_RU.php @@ -1,8 +1,11 @@ "Удаление не удалось", "Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Предупреждение: Приложения user_ldap и user_webdavauth несовместимы. Вы можете столкнуться с неожиданным поведением системы. Пожалуйста, обратитесь к системному администратору для отключения одного из них.", +"Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Предупреждение: Модуль PHP LDAP не установлен, бэкэнд не будет работать. Пожалуйста, обратитесь к Вашему системному администратору, чтобы установить его.", "Host" => "Хост", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Вы можете пропустить протокол, если Вам не требуется SSL. Затем начните с ldaps://", "Base DN" => "База DN", +"One Base DN per line" => "Одно базовое DN на линию", "You can specify Base DN for users and groups in the Advanced tab" => "Вы можете задать Base DN для пользователей и групп во вкладке «Дополнительно»", "User DN" => "DN пользователя", "The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN клиентского пользователя, с которого должна осуществляться привязка, например, uid=agent,dc=example,dc=com. Для анонимного доступа оставьте поля DN и Пароль пустыми.", @@ -18,21 +21,23 @@ "Defines the filter to apply, when retrieving groups." => "Задает фильтр, применяемый при получении групп.", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "без каких-либо заполнителей, например, \"objectClass=posixGroup\".", "Port" => "Порт", -"Base User Tree" => "Базовое дерево пользователей", -"Base Group Tree" => "Базовое дерево групп", -"Group-Member association" => "Связь член-группа", "Use TLS" => "Использовать TLS", "Do not use it for SSL connections, it will fail." => "Не используйте это SSL-соединений, это не будет выполнено.", "Case insensitve LDAP server (Windows)" => "Нечувствительный к регистру LDAP-сервер (Windows)", "Turn off SSL certificate validation." => "Выключить проверку сертификата SSL.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Если соединение работает только с этой опцией, импортируйте SSL-сертификат LDAP сервера в ваш ownCloud сервер.", "Not recommended, use for testing only." => "Не рекомендовано, используйте только для тестирования.", +"in seconds. A change empties the cache." => "в секундах. Изменение очищает кэш.", "User Display Name Field" => "Поле, отображаемое как имя пользователя", "The LDAP attribute to use to generate the user`s ownCloud name." => "Атрибут LDAP, используемый для создания имени пользователя в ownCloud.", +"Base User Tree" => "Базовое дерево пользователей", +"One User Base DN per line" => "Одно пользовательское базовое DN на линию", "Group Display Name Field" => "Поле, отображаемое как имя группы", "The LDAP attribute to use to generate the groups`s ownCloud name." => "Атрибут LDAP, используемый для создания группового имени в ownCloud.", +"Base Group Tree" => "Базовое дерево групп", +"One Group Base DN per line" => "Одно групповое базовое DN на линию", +"Group-Member association" => "Связь член-группа", "in bytes" => "в байтах", -"in seconds. A change empties the cache." => "в секундах. Изменение очищает кэш.", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Оставьте пустым под имя пользователя (по умолчанию). В противном случае задайте LDAP/AD атрибут.", "Help" => "Помощь" ); diff --git a/apps/user_ldap/l10n/si_LK.php b/apps/user_ldap/l10n/si_LK.php index fc8099e25e..50124e4d54 100644 --- a/apps/user_ldap/l10n/si_LK.php +++ b/apps/user_ldap/l10n/si_LK.php @@ -1,4 +1,5 @@ "මකාදැමීම අසාර්ථකයි", "Host" => "සත්කාරකය", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "SSL අවශ්‍යය වන විට පමණක් හැර, අන් අවස්ථාවන්හිදී ප්‍රොටොකෝලය අත් හැරිය හැක. භාවිතා කරන විට ldaps:// ලෙස ආරම්භ කරන්න", "Password" => "මුර පදය", diff --git a/apps/user_ldap/l10n/sk_SK.php b/apps/user_ldap/l10n/sk_SK.php index 2b340c8573..3d7dbd6269 100644 --- a/apps/user_ldap/l10n/sk_SK.php +++ b/apps/user_ldap/l10n/sk_SK.php @@ -1,7 +1,11 @@ "Odstránenie zlyhalo", +"Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Upozornenie: Aplikácie user_ldap a user_webdavauth nie sú kompatibilné. Môže nastávať neočakávané správanie. Požiadajte správcu systému aby jednu z nich zakázal.", +"Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Upozornenie: nie je nainštalovaný LDAP modul pre PHP, backend vrstva nebude fungovať. Požádejte správcu systému aby ho nainštaloval.", "Host" => "Hostiteľ", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Môžete vynechať protokol, s výnimkou požadovania SSL. Vtedy začnite s ldaps://", "Base DN" => "Základné DN", +"One Base DN per line" => "Jedno základné DN na riadok", "You can specify Base DN for users and groups in the Advanced tab" => "V rozšírenom nastavení môžete zadať základné DN pre používateľov a skupiny", "User DN" => "Používateľské DN", "The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN klientského používateľa, ku ktorému tvoríte väzbu, napr. uid=agent,dc=example,dc=com. Pre anonymný prístup ponechajte údaje DN a Heslo prázdne.", @@ -17,21 +21,23 @@ "Defines the filter to apply, when retrieving groups." => "Definuje použitý filter, pre získanie skupín.", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "bez zástupných znakov, napr. \"objectClass=posixGroup\"", "Port" => "Port", -"Base User Tree" => "Základný používateľský strom", -"Base Group Tree" => "Základný skupinový strom", -"Group-Member association" => "Asociácia člena skupiny", "Use TLS" => "Použi TLS", "Do not use it for SSL connections, it will fail." => "Nepoužívajte pre pripojenie SSL, pripojenie zlyhá.", "Case insensitve LDAP server (Windows)" => "LDAP server nerozlišuje veľkosť znakov (Windows)", "Turn off SSL certificate validation." => "Vypnúť overovanie SSL certifikátu.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Ak pripojenie pracuje len s touto možnosťou, tak importujte SSL certifikát LDAP serveru do vášho servera ownCloud.", "Not recommended, use for testing only." => "Nie je doporučované, len pre testovacie účely.", +"in seconds. A change empties the cache." => "v sekundách. Zmena vyprázdni vyrovnávaciu pamäť.", "User Display Name Field" => "Pole pre zobrazenia mena používateľa", "The LDAP attribute to use to generate the user`s ownCloud name." => "Atribút LDAP použitý na vygenerovanie mena používateľa ownCloud ", +"Base User Tree" => "Základný používateľský strom", +"One User Base DN per line" => "Jedna používateľská základná DN na riadok", "Group Display Name Field" => "Pole pre zobrazenie mena skupiny", "The LDAP attribute to use to generate the groups`s ownCloud name." => "Atribút LDAP použitý na vygenerovanie mena skupiny ownCloud ", +"Base Group Tree" => "Základný skupinový strom", +"One Group Base DN per line" => "Jedna skupinová základná DN na riadok", +"Group-Member association" => "Asociácia člena skupiny", "in bytes" => "v bajtoch", -"in seconds. A change empties the cache." => "v sekundách. Zmena vyprázdni vyrovnávaciu pamäť.", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Nechajte prázdne pre používateľské meno (predvolené). Inak uveďte atribút LDAP/AD.", "Help" => "Pomoc" ); diff --git a/apps/user_ldap/l10n/sl.php b/apps/user_ldap/l10n/sl.php index 247f2bfdcb..133d7ee911 100644 --- a/apps/user_ldap/l10n/sl.php +++ b/apps/user_ldap/l10n/sl.php @@ -1,4 +1,5 @@ "Brisanje je spodletelo.", "Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Opozorilo: Aplikaciji user_ldap in user_webdavauth nista združljivi. Morda boste opazili nepričakovano obnašanje sistema. Prosimo, prosite vašega skrbnika, da eno od aplikacij onemogoči.", "Host" => "Gostitelj", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Protokol je lahko izpuščen, če ni posebej zahtevan SSL. V tem primeru se mora naslov začeti z ldaps://", @@ -18,21 +19,21 @@ "Defines the filter to apply, when retrieving groups." => "Določi filter za uporabo med pridobivanjem skupin.", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "Brez katerekoli vsebnika, npr. \"objectClass=posixGroup\".", "Port" => "Vrata", -"Base User Tree" => "Osnovno uporabniško drevo", -"Base Group Tree" => "Osnovno drevo skupine", -"Group-Member association" => "Povezava člana skupine", "Use TLS" => "Uporabi TLS", "Do not use it for SSL connections, it will fail." => "Uporaba SSL za povezave bo spodletela.", "Case insensitve LDAP server (Windows)" => "Strežnik LDAP ne upošteva velikosti črk (Windows)", "Turn off SSL certificate validation." => "Onemogoči potrditev veljavnosti potrdila SSL.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "V primeru, da povezava deluje le s to možnostjo, uvozite potrdilo SSL iz strežnika LDAP na vaš strežnik ownCloud.", "Not recommended, use for testing only." => "Dejanje ni priporočeno; uporabljeno naj bo le za preizkušanje delovanja.", +"in seconds. A change empties the cache." => "v sekundah. Sprememba izprazni predpomnilnik.", "User Display Name Field" => "Polje za uporabnikovo prikazano ime", "The LDAP attribute to use to generate the user`s ownCloud name." => "Atribut LDAP, uporabljen pri ustvarjanju uporabniških imen ownCloud.", +"Base User Tree" => "Osnovno uporabniško drevo", "Group Display Name Field" => "Polje za prikazano ime skupine", "The LDAP attribute to use to generate the groups`s ownCloud name." => "Atribut LDAP, uporabljen pri ustvarjanju imen skupin ownCloud.", +"Base Group Tree" => "Osnovno drevo skupine", +"Group-Member association" => "Povezava člana skupine", "in bytes" => "v bajtih", -"in seconds. A change empties the cache." => "v sekundah. Sprememba izprazni predpomnilnik.", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Pustite prazno za uporabniško ime (privzeto). V nasprotnem primeru navedite atribut LDAP/AD.", "Help" => "Pomoč" ); diff --git a/apps/user_ldap/l10n/sr.php b/apps/user_ldap/l10n/sr.php new file mode 100644 index 0000000000..f16e59273c --- /dev/null +++ b/apps/user_ldap/l10n/sr.php @@ -0,0 +1,4 @@ + "Брисање није успело", +"Help" => "Помоћ" +); diff --git a/apps/user_ldap/l10n/sr@latin.php b/apps/user_ldap/l10n/sr@latin.php new file mode 100644 index 0000000000..9150331506 --- /dev/null +++ b/apps/user_ldap/l10n/sr@latin.php @@ -0,0 +1,3 @@ + "Pomoć" +); diff --git a/apps/user_ldap/l10n/sv.php b/apps/user_ldap/l10n/sv.php index 1e36ff91ba..b1da09ad3e 100644 --- a/apps/user_ldap/l10n/sv.php +++ b/apps/user_ldap/l10n/sv.php @@ -1,8 +1,22 @@ "Misslyckades med att radera serverinställningen", +"The configuration is valid and the connection could be established!" => "Inställningen är giltig och anslutningen kunde upprättas!", +"The configuration is invalid. Please look in the ownCloud log for further details." => "Inställningen är ogiltig. Vänligen se ownCloud-loggen för fler detaljer.", +"Deletion failed" => "Raderingen misslyckades", +"Keep settings?" => "Behåll inställningarna?", +"Cannot add server configuration" => "Kunde inte lägga till serverinställning", +"Connection test succeeded" => "Anslutningstestet lyckades", +"Connection test failed" => "Anslutningstestet misslyckades", +"Do you really want to delete the current Server Configuration?" => "Vill du verkligen radera den nuvarande serverinställningen?", +"Confirm Deletion" => "Bekräfta radering", "Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Varning: Apps user_ldap och user_webdavauth är inkompatibla. Oväntade problem kan uppstå. Be din systemadministratör att inaktivera en av dom.", +"Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Varning: PHP LDAP - modulen är inte installerad, serversidan kommer inte att fungera. Kontakta din systemadministratör för installation.", +"Server configuration" => "Serverinställning", +"Add Server Configuration" => "Lägg till serverinställning", "Host" => "Server", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Du behöver inte ange protokoll förutom om du använder SSL. Starta då med ldaps://", "Base DN" => "Start DN", +"One Base DN per line" => "Ett Start DN per rad", "You can specify Base DN for users and groups in the Advanced tab" => "Du kan ange start DN för användare och grupper under fliken Avancerat", "User DN" => "Användare DN", "The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN för användaren som skall användas, t.ex. uid=agent, dc=example, dc=com. För anonym åtkomst, lämna DN och lösenord tomt.", @@ -18,21 +32,24 @@ "Defines the filter to apply, when retrieving groups." => "Definierar filter att tillämpa vid listning av grupper.", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "utan platshållare, t.ex. \"objectClass=posixGroup\".", "Port" => "Port", -"Base User Tree" => "Bas för användare i katalogtjänst", -"Base Group Tree" => "Bas för grupper i katalogtjänst", -"Group-Member association" => "Attribut för gruppmedlemmar", +"Disable Main Server" => "Inaktivera huvudserver", "Use TLS" => "Använd TLS", "Do not use it for SSL connections, it will fail." => "Använd inte för SSL-anslutningar, det kommer inte att fungera.", "Case insensitve LDAP server (Windows)" => "LDAP-servern är okänslig för gemener och versaler (Windows)", "Turn off SSL certificate validation." => "Stäng av verifiering av SSL-certifikat.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Om anslutningen bara fungerar med det här alternativet, importera LDAP-serverns SSL-certifikat i din ownCloud-server.", "Not recommended, use for testing only." => "Rekommenderas inte, använd bara för test. ", +"in seconds. A change empties the cache." => "i sekunder. En förändring tömmer cache.", "User Display Name Field" => "Attribut för användarnamn", "The LDAP attribute to use to generate the user`s ownCloud name." => "Attribut som används för att generera användarnamn i ownCloud.", +"Base User Tree" => "Bas för användare i katalogtjänst", +"One User Base DN per line" => "En Användare start DN per rad", "Group Display Name Field" => "Attribut för gruppnamn", "The LDAP attribute to use to generate the groups`s ownCloud name." => "Attribut som används för att generera gruppnamn i ownCloud.", +"Base Group Tree" => "Bas för grupper i katalogtjänst", +"One Group Base DN per line" => "En Grupp start DN per rad", +"Group-Member association" => "Attribut för gruppmedlemmar", "in bytes" => "i bytes", -"in seconds. A change empties the cache." => "i sekunder. En förändring tömmer cache.", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Lämnas tomt för användarnamn (standard). Ange annars ett LDAP/AD-attribut.", "Help" => "Hjälp" ); diff --git a/apps/user_ldap/l10n/ta_LK.php b/apps/user_ldap/l10n/ta_LK.php index 2028becaf9..d617f49700 100644 --- a/apps/user_ldap/l10n/ta_LK.php +++ b/apps/user_ldap/l10n/ta_LK.php @@ -1,4 +1,5 @@ "நீக்கம் தோல்வியடைந்தது", "Host" => "ஓம்புனர்", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "நீங்கள் SSL சேவையை தவிர உடன்படு வரைமுறையை தவிர்க்க முடியும். பிறகு ldaps:.// உடன் ஆரம்பிக்கவும்", "Base DN" => "தள DN", @@ -7,21 +8,21 @@ "Password" => "கடவுச்சொல்", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "எந்த ஒதுக்கீடும் இல்லாமல், உதாரணம். \"objectClass=posixGroup\".", "Port" => "துறை ", -"Base User Tree" => "தள பயனாளர் மரம்", -"Base Group Tree" => "தள குழு மரம்", -"Group-Member association" => "குழு உறுப்பினர் சங்கம்", "Use TLS" => "TLS ஐ பயன்படுத்தவும்", "Do not use it for SSL connections, it will fail." => "SSL இணைப்பிற்கு பயன்படுத்தவேண்டாம், அது தோல்வியடையும்.", "Case insensitve LDAP server (Windows)" => "உணர்ச்சியான LDAP சேவையகம் (சாளரங்கள்)", "Turn off SSL certificate validation." => "SSL சான்றிதழின் செல்லுபடியை நிறுத்திவிடவும்", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "இந்த தெரிவுகளில் மட்டும் இணைப்பு வேலைசெய்தால், உங்களுடைய owncloud சேவையகத்திலிருந்து LDAP சேவையகத்தின் SSL சான்றிதழை இறக்குமதி செய்யவும்", "Not recommended, use for testing only." => "பரிந்துரைக்கப்படவில்லை, சோதனைக்காக மட்டும் பயன்படுத்தவும்.", +"in seconds. A change empties the cache." => "செக்கன்களில். ஒரு மாற்றம் இடைமாற்றுநினைவகத்தை வெற்றிடமாக்கும்.", "User Display Name Field" => "பயனாளர் காட்சிப்பெயர் புலம்", "The LDAP attribute to use to generate the user`s ownCloud name." => "பயனாளரின் ownCloud பெயரை உருவாக்க LDAP பண்புக்கூறை பயன்படுத்தவும்.", +"Base User Tree" => "தள பயனாளர் மரம்", "Group Display Name Field" => "குழுவின் காட்சி பெயர் புலம் ", "The LDAP attribute to use to generate the groups`s ownCloud name." => "ownCloud குழுக்களின் பெயர்களை உருவாக்க LDAP பண்புக்கூறை பயன்படுத்தவும்.", +"Base Group Tree" => "தள குழு மரம்", +"Group-Member association" => "குழு உறுப்பினர் சங்கம்", "in bytes" => "bytes களில் ", -"in seconds. A change empties the cache." => "செக்கன்களில். ஒரு மாற்றம் இடைமாற்றுநினைவகத்தை வெற்றிடமாக்கும்.", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "பயனாளர் பெயரிற்கு வெற்றிடமாக விடவும் (பொது இருப்பு). இல்லாவிடின் LDAP/AD பண்புக்கூறை குறிப்பிடவும்.", "Help" => "உதவி" ); diff --git a/apps/user_ldap/l10n/th_TH.php b/apps/user_ldap/l10n/th_TH.php index acc7a4936b..07dbc835b3 100644 --- a/apps/user_ldap/l10n/th_TH.php +++ b/apps/user_ldap/l10n/th_TH.php @@ -1,7 +1,23 @@ "การลบการกำหนดค่าเซิร์ฟเวอร์ล้มเหลว", +"The configuration is valid and the connection could be established!" => "การกำหนดค่าถูกต้องและการเชื่อมต่อสามารถเชื่อมต่อได้!", +"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "การกำหนดค่าถูกต้อง, แต่การผูกข้อมูลล้มเหลว, กรุณาตรวจสอบการตั้งค่าเซิร์ฟเวอร์และข้อมูลการเข้าใช้งาน", +"The configuration is invalid. Please look in the ownCloud log for further details." => "การกำหนดค่าไม่ถูกต้อง กรุณาดูรายละเอียดจากบันทึกการเปลี่ยนแปลงของ ownCloud สำหรับรายละเอียดเพิ่มเติม", +"Deletion failed" => "การลบทิ้งล้มเหลว", +"Keep settings?" => "รักษาการตั้งค่าไว้?", +"Cannot add server configuration" => "ไม่สามารถเพิ่มค่ากำหนดเซิร์ฟเวอร์ได้", +"Connection test succeeded" => "ทดสอบการเชื่อมต่อสำเร็จ", +"Connection test failed" => "ทดสอบการเชื่อมต่อล้มเหลว", +"Do you really want to delete the current Server Configuration?" => "คุณแน่ใจแล้วหรือว่าต้องการลบการกำหนดค่าเซิร์ฟเวอร์ปัจจุบันทิ้งไป?", +"Confirm Deletion" => "ยืนยันการลบทิ้ง", +"Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "คำเตือน: แอปฯ user_ldap และ user_webdavauth ไม่สามารถใช้งานร่วมกันได้. คุณอาจประสพปัญหาที่ไม่คาดคิดจากเหตุการณ์ดังกล่าว กรุณาติดต่อผู้ดูแลระบบของคุณเพื่อระงับการใช้งานแอปฯ ตัวใดตัวหนึ่งข้างต้น", +"Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "คำเตือน: โมดูล PHP LDAP ยังไม่ได้ถูกติดตั้ง, ระบบด้านหลังจะไม่สามารถทำงานได้ กรุณาติดต่อผู้ดูแลระบบของคุณเพื่อทำการติดตั้งโมดูลดังกล่าว", +"Server configuration" => "การกำหนดค่าเซิร์ฟเวอร์", +"Add Server Configuration" => "เพิ่มการกำหนดค่าเซิร์ฟเวอร์", "Host" => "โฮสต์", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "คุณสามารถปล่อยช่องโปรโตคอลเว้นไว้ได้, ยกเว้นกรณีที่คุณต้องการใช้ SSL จากนั้นเริ่มต้นด้วย ldaps://", "Base DN" => "DN ฐาน", +"One Base DN per line" => "หนึ่ง Base DN ต่อบรรทัด", "You can specify Base DN for users and groups in the Advanced tab" => "คุณสามารถระบุ DN หลักสำหรับผู้ใช้งานและกลุ่มต่างๆในแท็บขั้นสูงได้", "User DN" => "DN ของผู้ใช้งาน", "The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN ของผู้ใช้งานที่เป็นลูกค้าอะไรก็ตามที่ผูกอยู่ด้วย เช่น uid=agent, dc=example, dc=com, สำหรับการเข้าถึงโดยบุคคลนิรนาม, ให้เว้นว่าง DN และ รหัสผ่านเอาไว้", @@ -16,22 +32,31 @@ "Group Filter" => "ตัวกรองข้อมูลกลุ่ม", "Defines the filter to apply, when retrieving groups." => "ระบุตัวกรองข้อมูลที่ต้องการนำไปใช้งาน, เมื่อดึงข้อมูลกลุ่ม", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "โดยไม่ต้องมีตัวยึดใดๆ, เช่น \"objectClass=posixGroup\",", +"Connection Settings" => "ตั้งค่าการเชื่อมต่อ", "Port" => "พอร์ต", -"Base User Tree" => "รายการผู้ใช้งานหลักแบบ Tree", -"Base Group Tree" => "รายการกลุ่มหลักแบบ Tree", -"Group-Member association" => "ความสัมพันธ์ของสมาชิกในกลุ่ม", +"Disable Main Server" => "ปิดใช้งานเซิร์ฟเวอร์หลัก", "Use TLS" => "ใช้ TLS", "Do not use it for SSL connections, it will fail." => "กรุณาอย่าใช้การเชื่อมต่อแบบ SSL การเชื่อมต่อจะเกิดการล้มเหลว", "Case insensitve LDAP server (Windows)" => "เซิร์ฟเวอร์ LDAP ประเภท Case insensitive (วินโดวส์)", "Turn off SSL certificate validation." => "ปิดใช้งานการตรวจสอบความถูกต้องของใบรับรองความปลอดภัย SSL", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "หากการเชื่อมต่อสามารถทำงานได้เฉพาะกับตัวเลือกนี้เท่านั้น, ให้นำเข้าข้อมูลใบรับรองความปลอดภัยแบบ SSL ของเซิร์ฟเวอร์ LDAP ดังกล่าวเข้าไปไว้ในเซิร์ฟเวอร์ ownCloud", "Not recommended, use for testing only." => "ไม่แนะนำให้ใช้งาน, ใช้สำหรับการทดสอบเท่านั้น", +"in seconds. A change empties the cache." => "ในอีกไม่กี่วินาที ระบบจะเปลี่ยนแปลงข้อมูลในแคชให้ว่างเปล่า", +"Directory Settings" => "ตั้งค่าไดเร็กทอรี่", "User Display Name Field" => "ช่องแสดงชื่อผู้ใช้งานที่ต้องการ", "The LDAP attribute to use to generate the user`s ownCloud name." => "คุณลักษณะ LDAP ที่ต้องการใช้สำหรับสร้างชื่อของผู้ใช้งาน ownCloud", +"Base User Tree" => "รายการผู้ใช้งานหลักแบบ Tree", +"One User Base DN per line" => "หนึ่ง User Base DN ต่อบรรทัด", +"User Search Attributes" => "คุณลักษณะการค้นหาชื่อผู้ใช้", +"Optional; one attribute per line" => "ตัวเลือกเพิ่มเติม; หนึ่งคุณลักษณะต่อบรรทัด", "Group Display Name Field" => "ช่องแสดงชื่อกลุ่มที่ต้องการ", "The LDAP attribute to use to generate the groups`s ownCloud name." => "คุณลักษณะ LDAP ที่ต้องการใช้สร้างชื่อกลุ่มของ ownCloud", +"Base Group Tree" => "รายการกลุ่มหลักแบบ Tree", +"One Group Base DN per line" => "หนึ่ง Group Base DN ต่อบรรทัด", +"Group Search Attributes" => "คุณลักษณะการค้นหาแบบกลุ่ม", +"Group-Member association" => "ความสัมพันธ์ของสมาชิกในกลุ่ม", +"Special Attributes" => "คุณลักษณะพิเศษ", "in bytes" => "ในหน่วยไบต์", -"in seconds. A change empties the cache." => "ในอีกไม่กี่วินาที ระบบจะเปลี่ยนแปลงข้อมูลในแคชให้ว่างเปล่า", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "เว้นว่างไว้สำหรับ ชื่อผู้ใช้ (ค่าเริ่มต้น) หรือไม่กรุณาระบุคุณลักษณะของ LDAP/AD", "Help" => "ช่วยเหลือ" ); diff --git a/apps/user_ldap/l10n/tr.php b/apps/user_ldap/l10n/tr.php index 6da65d9832..8ded27a295 100644 --- a/apps/user_ldap/l10n/tr.php +++ b/apps/user_ldap/l10n/tr.php @@ -1,4 +1,5 @@ "Silme başarısız oldu", "Host" => "Konak", "Base DN" => "Base DN", "User DN" => "User DN", @@ -10,15 +11,15 @@ "without any placeholder, e.g. \"objectClass=person\"." => "bir yer tutucusu olmadan, örneğin \"objectClass=person\"", "Group Filter" => "Grup Süzgeci", "Port" => "Port", -"Base User Tree" => "Temel Kullanıcı Ağacı", -"Base Group Tree" => "Temel Grup Ağacı", -"Group-Member association" => "Grup-Üye işbirliği", "Use TLS" => "TLS kullan", "Do not use it for SSL connections, it will fail." => "SSL bağlantıları ile kullanmayın, başarısız olacaktır.", "Turn off SSL certificate validation." => "SSL sertifika doğrulamasını kapat.", "Not recommended, use for testing only." => "Önerilmez, sadece test için kullanın.", -"in bytes" => "byte cinsinden", "in seconds. A change empties the cache." => "saniye cinsinden. Bir değişiklik önbelleği temizleyecektir.", +"Base User Tree" => "Temel Kullanıcı Ağacı", +"Base Group Tree" => "Temel Grup Ağacı", +"Group-Member association" => "Grup-Üye işbirliği", +"in bytes" => "byte cinsinden", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Kullanıcı adı bölümünü boş bırakın (varsayılan). ", "Help" => "Yardım" ); diff --git a/apps/user_ldap/l10n/uk.php b/apps/user_ldap/l10n/uk.php index d617d93926..4dd1256ee3 100644 --- a/apps/user_ldap/l10n/uk.php +++ b/apps/user_ldap/l10n/uk.php @@ -1,4 +1,5 @@ "Видалення не було виконано", "Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Увага: Застосунки user_ldap та user_webdavauth не сумісні. Ви можете зіткнутися з несподіваною поведінкою. Будь ласка, зверніться до системного адміністратора, щоб відключити одну з них.", "Host" => "Хост", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Можна не вказувати протокол, якщо вам не потрібен SSL. Тоді почніть з ldaps://", @@ -18,21 +19,21 @@ "Defines the filter to apply, when retrieving groups." => "Визначає фільтр, який застосовується при отриманні груп.", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "без будь-якого заповнювача, наприклад: \"objectClass=posixGroup\".", "Port" => "Порт", -"Base User Tree" => "Основне Дерево Користувачів", -"Base Group Tree" => "Основне Дерево Груп", -"Group-Member association" => "Асоціація Група-Член", "Use TLS" => "Використовуйте TLS", "Do not use it for SSL connections, it will fail." => "Не використовуйте його для SSL з'єднань, це не буде виконано.", "Case insensitve LDAP server (Windows)" => "Нечутливий до регістру LDAP сервер (Windows)", "Turn off SSL certificate validation." => "Вимкнути перевірку SSL сертифіката.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Якщо з'єднання працює лише з цією опцією, імпортуйте SSL сертифікат LDAP сервера у ваший ownCloud сервер.", "Not recommended, use for testing only." => "Не рекомендується, використовуйте лише для тестів.", +"in seconds. A change empties the cache." => "в секундах. Зміна очищує кеш.", "User Display Name Field" => "Поле, яке відображає Ім'я Користувача", "The LDAP attribute to use to generate the user`s ownCloud name." => "Атрибут LDAP, який використовується для генерації імен користувачів ownCloud.", +"Base User Tree" => "Основне Дерево Користувачів", "Group Display Name Field" => "Поле, яке відображає Ім'я Групи", "The LDAP attribute to use to generate the groups`s ownCloud name." => "Атрибут LDAP, який використовується для генерації імен груп ownCloud.", +"Base Group Tree" => "Основне Дерево Груп", +"Group-Member association" => "Асоціація Група-Член", "in bytes" => "в байтах", -"in seconds. A change empties the cache." => "в секундах. Зміна очищує кеш.", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Залиште порожнім для імені користувача (за замовчанням). Інакше, вкажіть атрибут LDAP/AD.", "Help" => "Допомога" ); diff --git a/apps/user_ldap/l10n/vi.php b/apps/user_ldap/l10n/vi.php index 3d32c8125b..76ff6fe33a 100644 --- a/apps/user_ldap/l10n/vi.php +++ b/apps/user_ldap/l10n/vi.php @@ -1,4 +1,5 @@ "Xóa thất bại", "Host" => "Máy chủ", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Bạn có thể bỏ qua các giao thức, ngoại trừ SSL. Sau đó bắt đầu với ldaps://", "Base DN" => "DN cơ bản", @@ -17,21 +18,21 @@ "Defines the filter to apply, when retrieving groups." => "Xác định các bộ lọc để áp dụng, khi nhóm sử dụng.", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "mà không giữ chỗ nào, ví dụ như \"objectClass = osixGroup\".", "Port" => "Cổng", -"Base User Tree" => "Cây người dùng cơ bản", -"Base Group Tree" => "Cây nhóm cơ bản", -"Group-Member association" => "Nhóm thành viên Cộng đồng", "Use TLS" => "Sử dụng TLS", "Do not use it for SSL connections, it will fail." => "Kết nối SSL bị lỗi. ", "Case insensitve LDAP server (Windows)" => "Trường hợp insensitve LDAP máy chủ (Windows)", "Turn off SSL certificate validation." => "Tắt xác thực chứng nhận SSL", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Nếu kết nối chỉ hoạt động với tùy chọn này, vui lòng import LDAP certificate SSL trong máy chủ ownCloud của bạn.", "Not recommended, use for testing only." => "Không khuyến khích, Chỉ sử dụng để thử nghiệm.", +"in seconds. A change empties the cache." => "trong vài giây. Một sự thay đổi bộ nhớ cache.", "User Display Name Field" => "Hiển thị tên người sử dụng", "The LDAP attribute to use to generate the user`s ownCloud name." => "Các thuộc tính LDAP sử dụng để tạo tên người dùng ownCloud.", +"Base User Tree" => "Cây người dùng cơ bản", "Group Display Name Field" => "Hiển thị tên nhóm", "The LDAP attribute to use to generate the groups`s ownCloud name." => "Các thuộc tính LDAP sử dụng để tạo các nhóm ownCloud.", +"Base Group Tree" => "Cây nhóm cơ bản", +"Group-Member association" => "Nhóm thành viên Cộng đồng", "in bytes" => "Theo Byte", -"in seconds. A change empties the cache." => "trong vài giây. Một sự thay đổi bộ nhớ cache.", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Để trống tên người dùng (mặc định). Nếu không chỉ định thuộc tính LDAP/AD", "Help" => "Giúp đỡ" ); diff --git a/apps/user_ldap/l10n/zh_CN.GB2312.php b/apps/user_ldap/l10n/zh_CN.GB2312.php index 8b906aea5c..91b059afd0 100644 --- a/apps/user_ldap/l10n/zh_CN.GB2312.php +++ b/apps/user_ldap/l10n/zh_CN.GB2312.php @@ -1,4 +1,5 @@ "删除失败", "Host" => "主机", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "您可以忽略协议,除非您需要 SSL。然后用 ldaps:// 开头", "Base DN" => "基本判别名", @@ -17,21 +18,21 @@ "Defines the filter to apply, when retrieving groups." => "定义撷取群组时要应用的过滤器", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "不能使用占位符,例如 \"objectClass=posixGroup\"。", "Port" => "端口", -"Base User Tree" => "基本用户树", -"Base Group Tree" => "基本群组树", -"Group-Member association" => "群组-成员组合", "Use TLS" => "使用 TLS", "Do not use it for SSL connections, it will fail." => "不要使用它进行 SSL 连接,会失败的。", "Case insensitve LDAP server (Windows)" => "大小写不敏感的 LDAP 服务器 (Windows)", "Turn off SSL certificate validation." => "关闭 SSL 证书校验。", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "如果只有使用此选项才能连接,请导入 LDAP 服务器的 SSL 证书到您的 ownCloud 服务器。", "Not recommended, use for testing only." => "不推荐,仅供测试", +"in seconds. A change empties the cache." => "以秒计。修改会清空缓存。", "User Display Name Field" => "用户显示名称字段", "The LDAP attribute to use to generate the user`s ownCloud name." => "用于生成用户的 ownCloud 名称的 LDAP 属性。", +"Base User Tree" => "基本用户树", "Group Display Name Field" => "群组显示名称字段", "The LDAP attribute to use to generate the groups`s ownCloud name." => "用于生成群组的 ownCloud 名称的 LDAP 属性。", +"Base Group Tree" => "基本群组树", +"Group-Member association" => "群组-成员组合", "in bytes" => "以字节计", -"in seconds. A change empties the cache." => "以秒计。修改会清空缓存。", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "用户名请留空 (默认)。否则,请指定一个 LDAP/AD 属性。", "Help" => "帮助" ); diff --git a/apps/user_ldap/l10n/zh_CN.php b/apps/user_ldap/l10n/zh_CN.php index ed5041eff0..d0c32e94e0 100644 --- a/apps/user_ldap/l10n/zh_CN.php +++ b/apps/user_ldap/l10n/zh_CN.php @@ -1,4 +1,5 @@ "删除失败", "Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "警告:应用 user_ldap 和 user_webdavauth 不兼容。您可能遭遇未预料的行为。请垂询您的系统管理员禁用其中一个。", "Host" => "主机", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "可以忽略协议,但如要使用SSL,则需以ldaps://开头", @@ -18,21 +19,21 @@ "Defines the filter to apply, when retrieving groups." => "定义拉取组信息时的过滤器", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "无需占位符,例如\"objectClass=posixGroup\"", "Port" => "端口", -"Base User Tree" => "基础用户树", -"Base Group Tree" => "基础组树", -"Group-Member association" => "组成员关联", "Use TLS" => "使用TLS", "Do not use it for SSL connections, it will fail." => "不要在SSL链接中使用此选项,会导致失败。", "Case insensitve LDAP server (Windows)" => "大小写敏感LDAP服务器(Windows)", "Turn off SSL certificate validation." => "关闭SSL证书验证", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "如果链接仅在此选项时可用,在您的ownCloud服务器中导入LDAP服务器的SSL证书。", "Not recommended, use for testing only." => "暂不推荐,仅供测试", +"in seconds. A change empties the cache." => "以秒计。修改将清空缓存。", "User Display Name Field" => "用户显示名称字段", "The LDAP attribute to use to generate the user`s ownCloud name." => "用来生成用户的ownCloud名称的 LDAP属性", +"Base User Tree" => "基础用户树", "Group Display Name Field" => "组显示名称字段", "The LDAP attribute to use to generate the groups`s ownCloud name." => "用来生成组的ownCloud名称的LDAP属性", +"Base Group Tree" => "基础组树", +"Group-Member association" => "组成员关联", "in bytes" => "字节数", -"in seconds. A change empties the cache." => "以秒计。修改将清空缓存。", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "将用户名称留空(默认)。否则指定一个LDAP/AD属性", "Help" => "帮助" ); diff --git a/apps/user_ldap/l10n/zh_TW.php b/apps/user_ldap/l10n/zh_TW.php index abc1b03d49..9a12bad074 100644 --- a/apps/user_ldap/l10n/zh_TW.php +++ b/apps/user_ldap/l10n/zh_TW.php @@ -1,5 +1,8 @@ "移除失敗", +"Host" => "主機", "Password" => "密碼", +"Port" => "連接阜", "Use TLS" => "使用TLS", "Turn off SSL certificate validation." => "關閉 SSL 憑證驗證", "Help" => "說明" diff --git a/apps/user_ldap/lib/access.php b/apps/user_ldap/lib/access.php index 422e43fc00..68cbe4a5e7 100644 --- a/apps/user_ldap/lib/access.php +++ b/apps/user_ldap/lib/access.php @@ -719,6 +719,50 @@ abstract class Access { return $combinedFilter; } + /** + * @brief creates a filter part for to perfrom search for users + * @param string $search the search term + * @return string the final filter part to use in LDAP searches + */ + public function getFilterPartForUserSearch($search) { + return $this->getFilterPartForSearch($search, $this->connection->ldapAttributesForUserSearch, $this->connection->ldapUserDisplayName); + } + + /** + * @brief creates a filter part for to perfrom search for groups + * @param string $search the search term + * @return string the final filter part to use in LDAP searches + */ + public function getFilterPartForGroupSearch($search) { + return $this->getFilterPartForSearch($search, $this->connection->ldapAttributesForGroupSearch, $this->connection->ldapGroupDisplayName); + } + + /** + * @brief creates a filter part for searches + * @param string $search the search term + * @param string $fallbackAttribute a fallback attribute in case the user + * did not define search attributes. Typically the display name attribute. + * @returns string the final filter part to use in LDAP searches + */ + private function getFilterPartForSearch($search, $searchAttributes, $fallbackAttribute) { + $filter = array(); + $search = empty($search) ? '*' : '*'.$search.'*'; + if(!is_array($searchAttributes) || count($searchAttributes) == 0) { + if(empty($fallbackAttribute)) { + return ''; + } + $filter[] = $fallbackAttribute . '=' . $search; + } else { + foreach($searchAttributes as $attribute) { + $filter[] = $attribute . '=' . $search; + } + } + if(count($filter) == 1) { + return '('.$filter[0].')'; + } + return $this->combineFilterWithOr($filter); + } + public function areCredentialsValid($name, $password) { $name = $this->DNasBaseParameter($name); $testConnection = clone $this->connection; @@ -912,7 +956,7 @@ abstract class Access { $reOffset = ($offset - $limit) < 0 ? 0 : $offset - $limit; //a bit recursive, $offset of 0 is the exit \OCP\Util::writeLog('user_ldap', 'Looking for cookie L/O '.$limit.'/'.$reOffset, \OCP\Util::INFO); - $this->search($filter, $base, $attr, $limit, $reOffset, true); + $this->search($filter, array($base), $attr, $limit, $reOffset, true); $cookie = $this->getPagedResultCookie($base, $filter, $limit, $offset); //still no cookie? obviously, the server does not like us. Let's skip paging efforts. //TODO: remember this, probably does not change in the next request... diff --git a/apps/user_ldap/lib/connection.php b/apps/user_ldap/lib/connection.php index 7046cbbfc7..acc33e047c 100644 --- a/apps/user_ldap/lib/connection.php +++ b/apps/user_ldap/lib/connection.php @@ -4,7 +4,7 @@ * ownCloud – LDAP Access * * @author Arthur Schiwon - * @copyright 2012 Arthur Schiwon blizzz@owncloud.com + * @copyright 2012, 2013 Arthur Schiwon blizzz@owncloud.com * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -25,6 +25,7 @@ namespace OCA\user_ldap\lib; class Connection { private $ldapConnectionRes = null; + private $configPrefix; private $configID; private $configured = false; @@ -35,6 +36,8 @@ class Connection { protected $config = array( 'ldapHost' => null, 'ldapPort' => null, + 'ldapBackupHost' => null, + 'ldapBackupPort' => null, 'ldapBase' => null, 'ldapBaseUsers' => null, 'ldapBaseGroups' => null, @@ -48,6 +51,7 @@ class Connection { 'ldapUserFilter' => null, 'ldapGroupFilter' => null, 'ldapGroupDisplayName' => null, + 'ldapGroupMemberAssocAttr' => null, 'ldapLoginFilter' => null, 'ldapQuotaAttribute' => null, 'ldapQuotaDefault' => null, @@ -55,15 +59,24 @@ class Connection { 'ldapCacheTTL' => null, 'ldapUuidAttribute' => null, 'ldapOverrideUuidAttribute' => null, + 'ldapOverrideMainServer' => false, + 'ldapConfigurationActive' => false, + 'ldapAttributesForUserSearch' => null, + 'ldapAttributesForGroupSearch' => null, 'homeFolderNamingRule' => null, 'hasPagedResultSupport' => false, ); - public function __construct($configID = 'user_ldap') { + /** + * @brief Constructor + * @param $configPrefix a string with the prefix for the configkey column (appconfig table) + * @param $configID a string with the value for the appid column (appconfig table) or null for on-the-fly connections + */ + public function __construct($configPrefix = '', $configID = 'user_ldap') { + $this->configPrefix = $configPrefix; $this->configID = $configID; $this->cache = \OC_Cache::getGlobalCache(); $this->config['hasPagedResultSupport'] = (function_exists('ldap_control_paged_result') && function_exists('ldap_control_paged_result_response')); - \OCP\Util::writeLog('user_ldap', 'PHP supports paged results? '.print_r($this->config['hasPagedResultSupport'], true), \OCP\Util::INFO); } public function __destruct() { @@ -84,12 +97,12 @@ class Connection { public function __set($name, $value) { $changed = false; - //omly few options are writable + //only few options are writable if($name == 'ldapUuidAttribute') { \OCP\Util::writeLog('user_ldap', 'Set config ldapUuidAttribute to '.$value, \OCP\Util::DEBUG); $this->config[$name] = $value; if(!empty($this->configID)) { - \OCP\Config::setAppValue($this->configID, 'ldap_uuid_attribute', $value); + \OCP\Config::setAppValue($this->configID, $this->configPrefix.'ldap_uuid_attribute', $value); } $changed = true; } @@ -126,7 +139,7 @@ class Connection { } private function getCacheKey($key) { - $prefix = 'LDAP-'.$this->configID.'-'; + $prefix = 'LDAP-'.$this->configID.'-'.$this->configPrefix.'-'; if(is_null($key)) { return $prefix; } @@ -164,7 +177,8 @@ class Connection { if(!$this->configured) { $this->readConfiguration(); } - if(!$this->config['ldapCacheTTL']) { + if(!$this->config['ldapCacheTTL'] + || !$this->config['ldapConfigurationActive']) { return null; } $key = $this->getCacheKey($key); @@ -176,42 +190,96 @@ class Connection { $this->cache->clear($this->getCacheKey(null)); } + private function getValue($varname) { + static $defaults; + if(is_null($defaults)){ + $defaults = $this->getDefaults(); + } + return \OCP\Config::getAppValue($this->configID, + $this->configPrefix.$varname, + $defaults[$varname]); + } + + private function setValue($varname, $value) { + \OCP\Config::setAppValue($this->configID, + $this->configPrefix.$varname, + $value); + } + /** * Caches the general LDAP configuration. */ private function readConfiguration($force = false) { - \OCP\Util::writeLog('user_ldap', 'Checking conf state: isConfigured? '.print_r($this->configured, true).' isForce? '.print_r($force, true).' configID? '.print_r($this->configID, true), \OCP\Util::DEBUG); if((!$this->configured || $force) && !is_null($this->configID)) { - \OCP\Util::writeLog('user_ldap', 'Reading the configuration', \OCP\Util::DEBUG); - $this->config['ldapHost'] = \OCP\Config::getAppValue($this->configID, 'ldap_host', ''); - $this->config['ldapPort'] = \OCP\Config::getAppValue($this->configID, 'ldap_port', 389); - $this->config['ldapAgentName'] = \OCP\Config::getAppValue($this->configID, 'ldap_dn', ''); - $this->config['ldapAgentPassword'] = base64_decode(\OCP\Config::getAppValue($this->configID, 'ldap_agent_password', '')); - $this->config['ldapBase'] = preg_split('/\r\n|\r|\n/', \OCP\Config::getAppValue($this->configID, 'ldap_base', '')); - $this->config['ldapBaseUsers'] = preg_split('/\r\n|\r|\n/', \OCP\Config::getAppValue($this->configID, 'ldap_base_users', $this->config['ldapBase'])); - $this->config['ldapBaseGroups'] = preg_split('/\r\n|\r|\n/', \OCP\Config::getAppValue($this->configID, 'ldap_base_groups', $this->config['ldapBase'])); - $this->config['ldapTLS'] = \OCP\Config::getAppValue($this->configID, 'ldap_tls', 0); - $this->config['ldapNoCase'] = \OCP\Config::getAppValue($this->configID, 'ldap_nocase', 0); - $this->config['turnOffCertCheck'] = \OCP\Config::getAppValue($this->configID, 'ldap_turn_off_cert_check', 0); - $this->config['ldapUserDisplayName'] = mb_strtolower(\OCP\Config::getAppValue($this->configID, 'ldap_display_name', 'uid'), 'UTF-8'); - $this->config['ldapUserFilter'] = \OCP\Config::getAppValue($this->configID, 'ldap_userlist_filter', 'objectClass=person'); - $this->config['ldapGroupFilter'] = \OCP\Config::getAppValue($this->configID, 'ldap_group_filter', '(objectClass=posixGroup)'); - $this->config['ldapLoginFilter'] = \OCP\Config::getAppValue($this->configID, 'ldap_login_filter', '(uid=%uid)'); - $this->config['ldapGroupDisplayName'] = mb_strtolower(\OCP\Config::getAppValue($this->configID, 'ldap_group_display_name', 'uid'), 'UTF-8'); - $this->config['ldapQuotaAttribute'] = \OCP\Config::getAppValue($this->configID, 'ldap_quota_attr', ''); - $this->config['ldapQuotaDefault'] = \OCP\Config::getAppValue($this->configID, 'ldap_quota_def', ''); - $this->config['ldapEmailAttribute'] = \OCP\Config::getAppValue($this->configID, 'ldap_email_attr', ''); - $this->config['ldapGroupMemberAssocAttr'] = \OCP\Config::getAppValue($this->configID, 'ldap_group_member_assoc_attribute', 'uniqueMember'); - $this->config['ldapIgnoreNamingRules'] = \OCP\Config::getSystemValue('ldapIgnoreNamingRules', false); - $this->config['ldapCacheTTL'] = \OCP\Config::getAppValue($this->configID, 'ldap_cache_ttl', 10*60); - $this->config['ldapUuidAttribute'] = \OCP\Config::getAppValue($this->configID, 'ldap_uuid_attribute', 'auto'); - $this->config['ldapOverrideUuidAttribute'] = \OCP\Config::getAppValue($this->configID, 'ldap_override_uuid_attribute', 0); - $this->config['homeFolderNamingRule'] = \OCP\Config::getAppValue($this->configID, 'home_folder_naming_rule', 'opt:username'); + $defaults = $this->getDefaults(); + $v = 'getValue'; + $this->config['ldapHost'] = $this->$v('ldap_host'); + $this->config['ldapBackupHost'] = $this->$v('ldap_backup_host'); + $this->config['ldapPort'] = $this->$v('ldap_port'); + $this->config['ldapBackupPort'] = $this->$v('ldap_backup_port'); + $this->config['ldapOverrideMainServer'] + = $this->$v('ldap_override_main_server'); + $this->config['ldapAgentName'] = $this->$v('ldap_dn'); + $this->config['ldapAgentPassword'] + = base64_decode($this->$v('ldap_agent_password')); + $rawLdapBase = $this->$v('ldap_base'); + $this->config['ldapBase'] + = preg_split('/\r\n|\r|\n/', $rawLdapBase); + $this->config['ldapBaseUsers'] + = preg_split('/\r\n|\r|\n/', ($this->$v('ldap_base_users'))); + $this->config['ldapBaseGroups'] + = preg_split('/\r\n|\r|\n/', $this->$v('ldap_base_groups')); + unset($rawLdapBase); + $this->config['ldapTLS'] = $this->$v('ldap_tls'); + $this->config['ldapNoCase'] = $this->$v('ldap_nocase'); + $this->config['turnOffCertCheck'] + = $this->$v('ldap_turn_off_cert_check'); + $this->config['ldapUserDisplayName'] + = mb_strtolower($this->$v('ldap_display_name'),'UTF-8'); + $this->config['ldapUserFilter'] + = $this->$v('ldap_userlist_filter'); + $this->config['ldapGroupFilter'] = $this->$v('ldap_group_filter'); + $this->config['ldapLoginFilter'] = $this->$v('ldap_login_filter'); + $this->config['ldapGroupDisplayName'] + = mb_strtolower($this->$v('ldap_group_display_name'), 'UTF-8'); + $this->config['ldapQuotaAttribute'] + = $this->$v('ldap_quota_attr'); + $this->config['ldapQuotaDefault'] + = $this->$v('ldap_quota_def'); + $this->config['ldapEmailAttribute'] + = $this->$v('ldap_email_attr'); + $this->config['ldapGroupMemberAssocAttr'] + = $this->$v('ldap_group_member_assoc_attribute'); + $this->config['ldapIgnoreNamingRules'] + = \OCP\Config::getSystemValue('ldapIgnoreNamingRules', false); + $this->config['ldapCacheTTL'] = $this->$v('ldap_cache_ttl'); + $this->config['ldapUuidAttribute'] + = $this->$v('ldap_uuid_attribute'); + $this->config['ldapOverrideUuidAttribute'] + = $this->$v('ldap_override_uuid_attribute'); + $this->config['homeFolderNamingRule'] + = $this->$v('home_folder_naming_rule'); + $this->config['ldapConfigurationActive'] + = $this->$v('ldap_configuration_active'); + $this->config['ldapAttributesForUserSearch'] + = preg_split('/\r\n|\r|\n/', $this->$v('ldap_attributes_for_user_search')); + $this->config['ldapAttributesForGroupSearch'] + = preg_split('/\r\n|\r|\n/', $this->$v('ldap_attributes_for_group_search')); $this->configured = $this->validateConfiguration(); } } + /** + * @return returns an array that maps internal variable names to database fields + */ + private function getConfigTranslationArray() { + static $array = array('ldap_host'=>'ldapHost', 'ldap_port'=>'ldapPort', 'ldap_backup_host'=>'ldapBackupHost', 'ldap_backup_port'=>'ldapBackupPort', 'ldap_override_main_server' => 'ldapOverrideMainServer', 'ldap_dn'=>'ldapAgentName', 'ldap_agent_password'=>'ldapAgentPassword', 'ldap_base'=>'ldapBase', 'ldap_base_users'=>'ldapBaseUsers', 'ldap_base_groups'=>'ldapBaseGroups', 'ldap_userlist_filter'=>'ldapUserFilter', 'ldap_login_filter'=>'ldapLoginFilter', 'ldap_group_filter'=>'ldapGroupFilter', 'ldap_display_name'=>'ldapUserDisplayName', 'ldap_group_display_name'=>'ldapGroupDisplayName', + + 'ldap_tls'=>'ldapTLS', 'ldap_nocase'=>'ldapNoCase', 'ldap_quota_def'=>'ldapQuotaDefault', 'ldap_quota_attr'=>'ldapQuotaAttribute', 'ldap_email_attr'=>'ldapEmailAttribute', 'ldap_group_member_assoc_attribute'=>'ldapGroupMemberAssocAttr', 'ldap_cache_ttl'=>'ldapCacheTTL', 'home_folder_naming_rule' => 'homeFolderNamingRule', 'ldap_turn_off_cert_check' => 'turnOffCertCheck', 'ldap_configuration_active' => 'ldapConfigurationActive', 'ldap_attributes_for_user_search' => 'ldapAttributesForUserSearch', 'ldap_attributes_for_group_search' => 'ldapAttributesForGroupSearch'); + return $array; + } + /** * @brief set LDAP configuration with values delivered by an array, not read from configuration * @param $config array that holds the config parameters in an associated array @@ -223,9 +291,7 @@ class Connection { return false; } - $params = array('ldap_host'=>'ldapHost', 'ldap_port'=>'ldapPort', 'ldap_dn'=>'ldapAgentName', 'ldap_agent_password'=>'ldapAgentPassword', 'ldap_base'=>'ldapBase', 'ldap_base_users'=>'ldapBaseUsers', 'ldap_base_groups'=>'ldapBaseGroups', 'ldap_userlist_filter'=>'ldapUserFilter', 'ldap_login_filter'=>'ldapLoginFilter', 'ldap_group_filter'=>'ldapGroupFilter', 'ldap_display_name'=>'ldapUserDisplayName', 'ldap_group_display_name'=>'ldapGroupDisplayName', - - 'ldap_tls'=>'ldapTLS', 'ldap_nocase'=>'ldapNoCase', 'ldap_quota_def'=>'ldapQuotaDefault', 'ldap_quota_attr'=>'ldapQuotaAttribute', 'ldap_email_attr'=>'ldapEmailAttribute', 'ldap_group_member_assoc_attribute'=>'ldapGroupMemberAssocAttr', 'ldap_cache_ttl'=>'ldapCacheTTL', 'home_folder_naming_rule' => 'homeFolderNamingRule'); + $params = $this->getConfigTranslationArray(); foreach($config as $parameter => $value) { if(isset($this->config[$parameter])) { @@ -246,6 +312,71 @@ class Connection { return $this->configured; } + /** + * @brief saves the current Configuration in the database + */ + public function saveConfiguration() { + $trans = array_flip($this->getConfigTranslationArray()); + foreach($this->config as $key => $value) { + \OCP\Util::writeLog('user_ldap', 'LDAP: storing key '.$key.' value '.$value, \OCP\Util::DEBUG); + switch ($key) { + case 'ldapAgentPassword': + $value = base64_encode($value); + break; + case 'homeFolderNamingRule': + $value = empty($value) ? 'opt:username' : 'attr:'.$value; + break; + case 'ldapBase': + case 'ldapBaseUsers': + case 'ldapBaseGroups': + case 'ldapAttributesForUserSearch': + case 'ldapAttributesForGroupSearch': + if(is_array($value)){ + $value = implode("\n", $value); + } + break; + case 'ldapIgnoreNamingRules': + case 'ldapOverrideUuidAttribute': + case 'ldapUuidAttribute': + case 'hasPagedResultSupport': + continue 2; + } + if(is_null($value)) { + $value = ''; + } + + $this->setValue($trans[$key], $value); + } + $this->clearCache(); + } + + /** + * @brief get the current LDAP configuration + * @return array + */ + public function getConfiguration() { + $this->readConfiguration(); + $trans = $this->getConfigTranslationArray(); + $config = array(); + foreach($trans as $dbKey => $classKey) { + if($classKey == 'homeFolderNamingRule') { + if(strpos($this->config[$classKey], 'opt') === 0) { + $config[$dbKey] = ''; + } else { + $config[$dbKey] = substr($this->config[$classKey], 5); + } + continue; + } else if((strpos($classKey, 'ldapBase') !== false) + || (strpos($classKey, 'ldapAttributes') !== false)) { + $config[$dbKey] = implode("\n", $this->config[$classKey]); + continue; + } + $config[$dbKey] = $this->config[$classKey]; + } + + return $config; + } + /** * @brief Validates the user specified configuration * @returns true if configuration seems OK, false otherwise @@ -264,9 +395,21 @@ class Connection { \OCP\Util::writeLog('user_ldap', 'No group filter is specified, LDAP group feature will not be used.', \OCP\Util::INFO); } if(!in_array($this->config['ldapUuidAttribute'], array('auto', 'entryuuid', 'nsuniqueid', 'objectguid')) && (!is_null($this->configID))) { - \OCP\Config::setAppValue($this->configID, 'ldap_uuid_attribute', 'auto'); + \OCP\Config::setAppValue($this->configID, $this->configPrefix.'ldap_uuid_attribute', 'auto'); \OCP\Util::writeLog('user_ldap', 'Illegal value for the UUID Attribute, reset to autodetect.', \OCP\Util::INFO); } + if(empty($this->config['ldapBackupPort'])) { + //force default + $this->config['ldapBackupPort'] = $this->config['ldapPort']; + } + foreach(array('ldapAttributesForUserSearch', 'ldapAttributesForGroupSearch') as $key) { + if(is_array($this->config[$key]) + && count($this->config[$key]) == 1 + && empty($this->config[$key][0])) { + $this->config[$key] = array(); + } + } + //second step: critical checks. If left empty or filled wrong, set as unconfigured and give a warning. @@ -310,10 +453,51 @@ class Connection { return $configurationOK; } + /** + * @returns an associative array with the default values. Keys are correspond + * to config-value entries in the database table + */ + public function getDefaults() { + return array( + 'ldap_host' => '', + 'ldap_port' => '389', + 'ldap_backup_host' => '', + 'ldap_backup_port' => '', + 'ldap_override_main_server' => '', + 'ldap_dn' => '', + 'ldap_agent_password' => '', + 'ldap_base' => '', + 'ldap_base_users' => '', + 'ldap_base_groups' => '', + 'ldap_userlist_filter' => 'objectClass=person', + 'ldap_login_filter' => 'uid=%uid', + 'ldap_group_filter' => 'objectClass=posixGroup', + 'ldap_display_name' => 'cn', + 'ldap_group_display_name' => 'cn', + 'ldap_tls' => 1, + 'ldap_nocase' => 0, + 'ldap_quota_def' => '', + 'ldap_quota_attr' => '', + 'ldap_email_attr' => '', + 'ldap_group_member_assoc_attribute' => 'uniqueMember', + 'ldap_cache_ttl' => 600, + 'ldap_uuid_attribute' => 'auto', + 'ldap_override_uuid_attribute' => 0, + 'home_folder_naming_rule' => 'opt:username', + 'ldap_turn_off_cert_check' => 0, + 'ldap_configuration_active' => 1, + 'ldap_attributes_for_user_search' => '', + 'ldap_attributes_for_group_search' => '', + ); + } + /** * Connects and Binds to LDAP */ private function establishConnection() { + if(!$this->config['ldapConfigurationActive']) { + return null; + } static $phpLDAPinstalled = true; if(!$phpLDAPinstalled) { return false; @@ -336,16 +520,40 @@ class Connection { \OCP\Util::writeLog('user_ldap', 'Could not turn off SSL certificate validation.', \OCP\Util::WARN); } } - $this->ldapConnectionRes = ldap_connect($this->config['ldapHost'], $this->config['ldapPort']); - if(ldap_set_option($this->ldapConnectionRes, LDAP_OPT_PROTOCOL_VERSION, 3)) { - if(ldap_set_option($this->ldapConnectionRes, LDAP_OPT_REFERRALS, 0)) { - if($this->config['ldapTLS']) { - ldap_start_tls($this->ldapConnectionRes); - } - } + if(!$this->config['ldapOverrideMainServer'] && !$this->getFromCache('overrideMainServer')) { + $this->doConnect($this->config['ldapHost'], $this->config['ldapPort']); + $bindStatus = $this->bind(); + $error = ldap_errno($this->ldapConnectionRes); + } else { + $bindStatus = false; + $error = null; } - return $this->bind(); + $error = null; + //if LDAP server is not reachable, try the Backup (Replica!) Server + if((!$bindStatus && ($error == -1)) + || $this->config['ldapOverrideMainServer'] + || $this->getFromCache('overrideMainServer')) { + $this->doConnect($this->config['ldapBackupHost'], $this->config['ldapBackupPort']); + $bindStatus = $this->bind(); + if($bindStatus && $error == -1) { + //when bind to backup server succeeded and failed to main server, + //skip contacting him until next cache refresh + $this->writeToCache('overrideMainServer', true); + } + } + return $bindStatus; + } + } + + private function doConnect($host, $port) { + $this->ldapConnectionRes = ldap_connect($host, $port); + if(ldap_set_option($this->ldapConnectionRes, LDAP_OPT_PROTOCOL_VERSION, 3)) { + if(ldap_set_option($this->ldapConnectionRes, LDAP_OPT_REFERRALS, 0)) { + if($this->config['ldapTLS']) { + ldap_start_tls($this->ldapConnectionRes); + } + } } } @@ -353,6 +561,9 @@ class Connection { * Binds to LDAP */ public function bind() { + if(!$this->config['ldapConfigurationActive']) { + return false; + } $ldapLogin = @ldap_bind($this->getConnectionResource(), $this->config['ldapAgentName'], $this->config['ldapAgentPassword']); if(!$ldapLogin) { \OCP\Util::writeLog('user_ldap', 'Bind failed: ' . ldap_errno($this->ldapConnectionRes) . ': ' . ldap_error($this->ldapConnectionRes), \OCP\Util::ERROR); diff --git a/apps/user_ldap/lib/helper.php b/apps/user_ldap/lib/helper.php new file mode 100644 index 0000000000..29ce998dae --- /dev/null +++ b/apps/user_ldap/lib/helper.php @@ -0,0 +1,105 @@ +. + * + */ + +namespace OCA\user_ldap\lib; + +class Helper { + + /** + * @brief returns prefixes for each saved LDAP/AD server configuration. + * @param bool optional, whether only active configuration shall be + * retrieved, defaults to false + * @return array with a list of the available prefixes + * + * Configuration prefixes are used to set up configurations for n LDAP or + * AD servers. Since configuration is stored in the database, table + * appconfig under appid user_ldap, the common identifiers in column + * 'configkey' have a prefix. The prefix for the very first server + * configuration is empty. + * Configkey Examples: + * Server 1: ldap_login_filter + * Server 2: s1_ldap_login_filter + * Server 3: s2_ldap_login_filter + * + * The prefix needs to be passed to the constructor of Connection class, + * except the default (first) server shall be connected to. + * + */ + static public function getServerConfigurationPrefixes($activeConfigurations = false) { + $referenceConfigkey = 'ldap_configuration_active'; + + $query = ' + SELECT DISTINCT `configkey` + FROM `*PREFIX*appconfig` + WHERE `configkey` LIKE ? + '; + if($activeConfigurations) { + $query .= ' AND `configvalue` = 1'; + } + $query = \OCP\DB::prepare($query); + + $serverConfigs = $query->execute(array('%'.$referenceConfigkey))->fetchAll(); + $prefixes = array(); + + foreach($serverConfigs as $serverConfig) { + $len = strlen($serverConfig['configkey']) - strlen($referenceConfigkey); + $prefixes[] = substr($serverConfig['configkey'], 0, $len); + } + + return $prefixes; + } + + /** + * @brief deletes a given saved LDAP/AD server configuration. + * @param string the configuration prefix of the config to delete + * @return bool true on success, false otherwise + */ + static public function deleteServerConfiguration($prefix) { + //just to be on the safe side + \OCP\User::checkAdminUser(); + + if(!in_array($prefix, self::getServerConfigurationPrefixes())) { + return false; + } + + $query = \OCP\DB::prepare(' + DELETE + FROM `*PREFIX*appconfig` + WHERE `configkey` LIKE ? + AND `appid` = "user_ldap" + AND `configkey` NOT IN ("enabled", "installed_version", "types", "bgjUpdateGroupsLastRun") + '); + $res = $query->execute(array($prefix.'%')); + + if(\OCP\DB::isError($res)) { + return false; + } + + if($res->numRows() == 0) { + return false; + } + + return true; + } +} + diff --git a/apps/user_ldap/lib/proxy.php b/apps/user_ldap/lib/proxy.php new file mode 100644 index 0000000000..c80e216347 --- /dev/null +++ b/apps/user_ldap/lib/proxy.php @@ -0,0 +1,104 @@ +. + * + */ + +namespace OCA\user_ldap\lib; + +abstract class Proxy { + static private $connectors = array(); + + public function __construct() { + $this->cache = \OC_Cache::getGlobalCache(); + } + + private function addConnector($configPrefix) { + self::$connectors[$configPrefix] = new \OCA\user_ldap\lib\Connection($configPrefix); + } + + protected function getConnector($configPrefix) { + if(!isset(self::$connectors[$configPrefix])) { + $this->addConnector($configPrefix); + } + return self::$connectors[$configPrefix]; + } + + protected function getConnectors() { + return self::$connectors; + } + + protected function getUserCacheKey($uid) { + return 'user-'.$uid.'-lastSeenOn'; + } + + protected function getGroupCacheKey($gid) { + return 'group-'.$gid.'-lastSeenOn'; + } + + abstract protected function callOnLastSeenOn($id, $method, $parameters); + abstract protected function walkBackends($id, $method, $parameters); + + /** + * @brief Takes care of the request to the User backend + * @param $uid string, the uid connected to the request + * @param $method string, the method of the user backend that shall be called + * @param $parameters an array of parameters to be passed + * @return mixed, the result of the specified method + */ + protected function handleRequest($id, $method, $parameters) { + if(!$result = $this->callOnLastSeenOn($id, $method, $parameters)) { + $result = $this->walkBackends($id, $method, $parameters); + } + return $result; + } + + private function getCacheKey($key) { + $prefix = 'LDAP-Proxy-'; + if(is_null($key)) { + return $prefix; + } + return $prefix.md5($key); + } + + public function getFromCache($key) { + if(!$this->isCached($key)) { + return null; + } + $key = $this->getCacheKey($key); + + return unserialize(base64_decode($this->cache->get($key))); + } + + public function isCached($key) { + $key = $this->getCacheKey($key); + return $this->cache->hasKey($key); + } + + public function writeToCache($key, $value) { + $key = $this->getCacheKey($key); + $value = base64_encode(serialize($value)); + $this->cache->set($key, $value, '2592000'); + } + + public function clearCache() { + $this->cache->clear($this->getCacheKey(null)); + } +} \ No newline at end of file diff --git a/apps/user_ldap/settings.php b/apps/user_ldap/settings.php index 58ec8e7f7a..d5d2f648b3 100644 --- a/apps/user_ldap/settings.php +++ b/apps/user_ldap/settings.php @@ -23,58 +23,46 @@ OC_Util::checkAdminUser(); -$params = array('ldap_host', 'ldap_port', 'ldap_dn', 'ldap_agent_password', 'ldap_base', 'ldap_base_users', 'ldap_base_groups', 'ldap_userlist_filter', 'ldap_login_filter', 'ldap_group_filter', 'ldap_display_name', 'ldap_group_display_name', 'ldap_tls', 'ldap_turn_off_cert_check', 'ldap_nocase', 'ldap_quota_def', 'ldap_quota_attr', 'ldap_email_attr', 'ldap_group_member_assoc_attribute', 'ldap_cache_ttl', 'home_folder_naming_rule'); +$params = array('ldap_host', 'ldap_port', 'ldap_backup_host', + 'ldap_backup_port', 'ldap_override_main_server', 'ldap_dn', + 'ldap_agent_password', 'ldap_base', 'ldap_base_users', + 'ldap_base_groups', 'ldap_userlist_filter', + 'ldap_login_filter', 'ldap_group_filter', 'ldap_display_name', + 'ldap_group_display_name', 'ldap_tls', + 'ldap_turn_off_cert_check', 'ldap_nocase', 'ldap_quota_def', + 'ldap_quota_attr', 'ldap_email_attr', + 'ldap_group_member_assoc_attribute', 'ldap_cache_ttl', + 'home_folder_naming_rule' + ); OCP\Util::addscript('user_ldap', 'settings'); OCP\Util::addstyle('user_ldap', 'settings'); -if ($_POST) { - $clearCache = false; - foreach($params as $param) { - if(isset($_POST[$param])) { - $clearCache = true; - if('ldap_agent_password' == $param) { - OCP\Config::setAppValue('user_ldap', $param, base64_encode($_POST[$param])); - } elseif('home_folder_naming_rule' == $param) { - $value = empty($_POST[$param]) ? 'opt:username' : 'attr:'.$_POST[$param]; - OCP\Config::setAppValue('user_ldap', $param, $value); - } else { - OCP\Config::setAppValue('user_ldap', $param, $_POST[$param]); - } - } - elseif('ldap_tls' == $param) { - // unchecked checkboxes are not included in the post paramters - OCP\Config::setAppValue('user_ldap', $param, 0); - } - elseif('ldap_nocase' == $param) { - OCP\Config::setAppValue('user_ldap', $param, 0); - } - elseif('ldap_turn_off_cert_check' == $param) { - OCP\Config::setAppValue('user_ldap', $param, 0); - } - } - if($clearCache) { - $ldap = new \OCA\user_ldap\lib\Connection('user_ldap'); - $ldap->clearCache(); - } -} - // fill template -$tmpl = new OCP\Template( 'user_ldap', 'settings'); -foreach($params as $param) { - $value = OCP\Config::getAppValue('user_ldap', $param, ''); - $tmpl->assign($param, $value); +$tmpl = new OCP\Template('user_ldap', 'settings'); + +$prefixes = \OCA\user_ldap\lib\Helper::getServerConfigurationPrefixes(); +$scoHtml = ''; +$i = 1; +$sel = ' selected'; +foreach($prefixes as $prefix) { + $scoHtml .= ''; + $sel = ''; +} +if(count($prefixes) == 0) { + $scoHtml .= ''; +} +$tmpl->assign('serverConfigurationOptions', $scoHtml, false); + +// assign default values +if(!isset($ldap)) { + $ldap = new \OCA\user_ldap\lib\Connection(); +} +$defaults = $ldap->getDefaults(); +foreach($defaults as $key => $default) { + $tmpl->assign($key.'_default', $default); } -// settings with default values -$tmpl->assign( 'ldap_port', OCP\Config::getAppValue('user_ldap', 'ldap_port', '389')); -$tmpl->assign( 'ldap_display_name', OCP\Config::getAppValue('user_ldap', 'ldap_display_name', 'uid')); -$tmpl->assign( 'ldap_group_display_name', OCP\Config::getAppValue('user_ldap', 'ldap_group_display_name', 'cn')); -$tmpl->assign( 'ldap_group_member_assoc_attribute', OCP\Config::getAppValue('user_ldap', 'ldap_group_member_assoc_attribute', 'uniqueMember')); -$tmpl->assign( 'ldap_agent_password', base64_decode(OCP\Config::getAppValue('user_ldap', 'ldap_agent_password'))); -$tmpl->assign( 'ldap_cache_ttl', OCP\Config::getAppValue('user_ldap', 'ldap_cache_ttl', '600')); -$hfnr = OCP\Config::getAppValue('user_ldap', 'home_folder_naming_rule', 'opt:username'); -$hfnr = ($hfnr == 'opt:username') ? '' : substr($hfnr, strlen('attr:')); -$tmpl->assign( 'home_folder_naming_rule', $hfnr, ''); +// $tmpl->assign(); return $tmpl->fetchPage(); diff --git a/apps/user_ldap/templates/settings.php b/apps/user_ldap/templates/settings.php index b24c6e2f02..eb3840a611 100644 --- a/apps/user_ldap/templates/settings.php +++ b/apps/user_ldap/templates/settings.php @@ -12,31 +12,54 @@ } ?>
-

-

-

-

-


t('use %%uid placeholder, e.g. "uid=%%uid"');?>

-


t('without any placeholder, e.g. "objectClass=person".');?>

-


t('without any placeholder, e.g. "objectClass=posixGroup".');?>

+

+ +

+

+

+

+

+


t('use %%uid placeholder, e.g. "uid=%%uid"');?>

+


t('without any placeholder, e.g. "objectClass=person".');?>

+


t('without any placeholder, e.g. "objectClass=posixGroup".');?>

-

-

-

-

-

title="t('Do not use it for SSL connections, it will fail.');?>" />

-

>

-

>
t('Not recommended, use for testing only.');?>

-

-

-

-

-

-

-

+
+

t('Connection Settings');?>

+
+

+

+

+

+

+

+

>

+


t('Not recommended, use for testing only.');?>

+

+
+

t('Directory Settings');?>

+
+

+

+

+

+

+

+

+
+

t('Special Attributes');?>

+
+

+

+

+

+
+
- t('Help');?> + t('Help');?>
diff --git a/apps/user_ldap/tests/group_ldap.php b/apps/user_ldap/tests/group_ldap.php index f99902d32f..ae635597b7 100644 --- a/apps/user_ldap/tests/group_ldap.php +++ b/apps/user_ldap/tests/group_ldap.php @@ -20,7 +20,7 @@ * */ -class Test_Group_Ldap extends UnitTestCase { +class Test_Group_Ldap extends PHPUnit_Framework_TestCase { function setUp() { OC_Group::clearBackends(); } diff --git a/apps/user_ldap/user_ldap.php b/apps/user_ldap/user_ldap.php index 6591d1d5fe..6aa8cd9b83 100644 --- a/apps/user_ldap/user_ldap.php +++ b/apps/user_ldap/user_ldap.php @@ -116,10 +116,9 @@ class USER_LDAP extends lib\Access implements \OCP\UserInterface { if($limit <= 0) { $limit = null; } - $search = empty($search) ? '*' : '*'.$search.'*'; $filter = $this->combineFilterWithAnd(array( $this->connection->ldapUserFilter, - $this->connection->ldapUserDisplayName.'='.$search + $this->getFilterPartForUserSearch($search) )); \OCP\Util::writeLog('user_ldap', 'getUsers: Options: search '.$search.' limit '.$limit.' offset '.$offset.' Filter: '.$filter, \OCP\Util::DEBUG); @@ -156,6 +155,7 @@ class USER_LDAP extends lib\Access implements \OCP\UserInterface { } $this->connection->writeToCache('userExists'.$uid, true); + $this->updateQuota($dn); return true; } @@ -208,6 +208,50 @@ class USER_LDAP extends lib\Access implements \OCP\UserInterface { return false; } + /** + * @brief get display name of the user + * @param $uid user ID of the user + * @return display name + */ + public function getDisplayName($uid) { + $cacheKey = 'getDisplayName'.$uid; + if(!is_null($displayName = $this->connection->getFromCache($cacheKey))) { + return $displayName; + } + + $displayName = $this->readAttribute( + $this->username2dn($uid), + $this->connection->ldapUserDisplayName); + + if($displayName && (count($displayName) > 0)) { + $this->connection->writeToCache($cacheKey, $displayName); + return $displayName[0]; + } + + return null; + } + + /** + * @brief Get a list of all display names + * @returns array with all displayNames (value) and the correspondig uids (key) + * + * Get a list of all display names and user ids. + */ + public function getDisplayNames($search = '', $limit = null, $offset = null) { + $cacheKey = 'getDisplayNames-'.$search.'-'.$limit.'-'.$offset; + if(!is_null($displayNames = $this->connection->getFromCache($cacheKey))) { + return $displayNames; + } + + $displayNames = array(); + $users = $this->getUsers($search, $limit, $offset); + foreach ($users as $user) { + $displayNames[$user] = $this->getDisplayName($user); + } + $this->connection->writeToCache($cacheKey, $displayNames); + return $displayNames; + } + /** * @brief Check if backend implements actions * @param $actions bitwise-or'ed actions diff --git a/apps/user_ldap/user_proxy.php b/apps/user_ldap/user_proxy.php new file mode 100644 index 0000000000..a94be3354f --- /dev/null +++ b/apps/user_ldap/user_proxy.php @@ -0,0 +1,186 @@ +. + * + */ + +namespace OCA\user_ldap; + +class User_Proxy extends lib\Proxy implements \OCP\UserInterface { + private $backends = array(); + private $refBackend = null; + + /** + * @brief Constructor + * @param $serverConfigPrefixes array containing the config Prefixes + */ + public function __construct($serverConfigPrefixes) { + parent::__construct(); + foreach($serverConfigPrefixes as $configPrefix) { + $this->backends[$configPrefix] = new \OCA\user_ldap\USER_LDAP(); + $connector = $this->getConnector($configPrefix); + $this->backends[$configPrefix]->setConnector($connector); + if(is_null($this->refBackend)) { + $this->refBackend = &$this->backends[$configPrefix]; + } + } + } + + /** + * @brief Tries the backends one after the other until a positive result is returned from the specified method + * @param $uid string, the uid connected to the request + * @param $method string, the method of the user backend that shall be called + * @param $parameters an array of parameters to be passed + * @return mixed, the result of the method or false + */ + protected function walkBackends($uid, $method, $parameters) { + $cacheKey = $this->getUserCacheKey($uid); + foreach($this->backends as $configPrefix => $backend) { + if($result = call_user_func_array(array($backend, $method), $parameters)) { + $this->writeToCache($cacheKey, $configPrefix); + return $result; + } + } + return false; + } + + /** + * @brief Asks the backend connected to the server that supposely takes care of the uid from the request. + * @param $uid string, the uid connected to the request + * @param $method string, the method of the user backend that shall be called + * @param $parameters an array of parameters to be passed + * @return mixed, the result of the method or false + */ + protected function callOnLastSeenOn($uid, $method, $parameters) { + $cacheKey = $this->getUserCacheKey($uid); + $prefix = $this->getFromCache($cacheKey); + //in case the uid has been found in the past, try this stored connection first + if(!is_null($prefix)) { + if(isset($this->backends[$prefix])) { + $result = call_user_func_array(array($this->backends[$prefix], $method), $parameters); + if(!$result) { + //not found here, reset cache to null + $this->writeToCache($cacheKey, null); + } + return $result; + } + } + return false; + } + + /** + * @brief Check if backend implements actions + * @param $actions bitwise-or'ed actions + * @returns boolean + * + * Returns the supported actions as int to be + * compared with OC_USER_BACKEND_CREATE_USER etc. + */ + public function implementsActions($actions) { + //it's the same across all our user backends obviously + return $this->refBackend->implementsActions($actions); + } + + /** + * @brief Get a list of all users + * @returns array with all uids + * + * Get a list of all users. + */ + public function getUsers($search = '', $limit = 10, $offset = 0) { + //we do it just as the /OC_User implementation: do not play around with limit and offset but ask all backends + $users = array(); + foreach($this->backends as $backend) { + $backendUsers = $backend->getUsers($search, $limit, $offset); + if (is_array($backendUsers)) { + $users = array_merge($users, $backendUsers); + } + } + return $users; + } + + /** + * @brief check if a user exists + * @param string $uid the username + * @return boolean + */ + public function userExists($uid) { + return $this->handleRequest($uid, 'userExists', array($uid)); + } + + /** + * @brief Check if the password is correct + * @param $uid The username + * @param $password The password + * @returns true/false + * + * Check if the password is correct without logging in the user + */ + public function checkPassword($uid, $password) { + return $this->handleRequest($uid, 'checkPassword', array($uid, $password)); + } + + /** + * @brief get the user's home directory + * @param string $uid the username + * @return boolean + */ + public function getHome($uid) { + return $this->handleRequest($uid, 'getHome', array($uid)); + } + + /** + * @brief get display name of the user + * @param $uid user ID of the user + * @return display name + */ + public function getDisplayName($uid) { + return $this->handleRequest($uid, 'getDisplayName', array($uid)); + } + + /** + * @brief Get a list of all display names + * @returns array with all displayNames (value) and the corresponding uids (key) + * + * Get a list of all display names and user ids. + */ + public function getDisplayNames($search = '', $limit = null, $offset = null) { + //we do it just as the /OC_User implementation: do not play around with limit and offset but ask all backends + $users = array(); + foreach($this->backends as $backend) { + $backendUsers = $backend->getDisplayNames($search, $limit, $offset); + if (is_array($backendUsers)) { + $users = array_merge($users, $backendUsers); + } + } + return $users; + } + + /** + * @brief delete a user + * @param $uid The username of the user to delete + * @returns true/false + * + * Deletes a user + */ + public function deleteUser($uid) { + return false; + } +} \ No newline at end of file diff --git a/apps/user_webdavauth/appinfo/info.xml b/apps/user_webdavauth/appinfo/info.xml index e51f2e9ec4..f62f03577e 100755 --- a/apps/user_webdavauth/appinfo/info.xml +++ b/apps/user_webdavauth/appinfo/info.xml @@ -7,7 +7,7 @@ This app is not compatible to the LDAP user and group backend. AGPL Frank Karlitschek - 4.9 + 4.91 true diff --git a/apps/user_webdavauth/l10n/da.php b/apps/user_webdavauth/l10n/da.php index 245a510134..b268d3e15d 100644 --- a/apps/user_webdavauth/l10n/da.php +++ b/apps/user_webdavauth/l10n/da.php @@ -1,3 +1,5 @@ "URL: http://" +"WebDAV Authentication" => "WebDAV-godkendelse", +"URL: http://" => "URL: http://", +"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud vil sende brugerens oplysninger til denne URL. Plugin'et registrerer responsen og fortolker HTTP-statuskoder 401 og 403 som ugyldige oplysninger, men alle andre besvarelser som gyldige oplysninger." ); diff --git a/apps/user_webdavauth/l10n/eo.php b/apps/user_webdavauth/l10n/eo.php index 245a510134..d945f181e6 100644 --- a/apps/user_webdavauth/l10n/eo.php +++ b/apps/user_webdavauth/l10n/eo.php @@ -1,3 +1,4 @@ "WebDAV-aŭtentigo", "URL: http://" => "URL: http://" ); diff --git a/apps/user_webdavauth/l10n/es.php b/apps/user_webdavauth/l10n/es.php index 245a510134..103c3738e2 100644 --- a/apps/user_webdavauth/l10n/es.php +++ b/apps/user_webdavauth/l10n/es.php @@ -1,3 +1,5 @@ "URL: http://" +"WebDAV Authentication" => "Autenticación de WevDAV", +"URL: http://" => "URL: http://", +"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "onwCloud enviará las credenciales de usuario a esta URL. Este complemento verifica la respuesta e interpretará los códigos de respuesta HTTP 401 y 403 como credenciales inválidas y todas las otras respuestas como credenciales válidas." ); diff --git a/apps/user_webdavauth/l10n/es_AR.php b/apps/user_webdavauth/l10n/es_AR.php index 245a510134..103c3738e2 100644 --- a/apps/user_webdavauth/l10n/es_AR.php +++ b/apps/user_webdavauth/l10n/es_AR.php @@ -1,3 +1,5 @@ "URL: http://" +"WebDAV Authentication" => "Autenticación de WevDAV", +"URL: http://" => "URL: http://", +"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "onwCloud enviará las credenciales de usuario a esta URL. Este complemento verifica la respuesta e interpretará los códigos de respuesta HTTP 401 y 403 como credenciales inválidas y todas las otras respuestas como credenciales válidas." ); diff --git a/apps/user_webdavauth/l10n/eu.php b/apps/user_webdavauth/l10n/eu.php index 245a510134..d792c1588b 100644 --- a/apps/user_webdavauth/l10n/eu.php +++ b/apps/user_webdavauth/l10n/eu.php @@ -1,3 +1,5 @@ "URL: http://" +"WebDAV Authentication" => "WebDAV Autentikazioa", +"URL: http://" => "URL: http://", +"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloudek erabiltzailearen kredentzialak URL honetara bidaliko ditu. Plugin honek erantzuna aztertzen du eta HTTP 401 eta 403 egoera kodeak baliogabezko kredentzialtzat hartuko ditu, beste erantzunak kredentzial egokitzat hartuko dituelarik." ); diff --git a/apps/user_webdavauth/l10n/fr.php b/apps/user_webdavauth/l10n/fr.php index 339931c7ce..9d528a3a9d 100644 --- a/apps/user_webdavauth/l10n/fr.php +++ b/apps/user_webdavauth/l10n/fr.php @@ -1,3 +1,5 @@ "URL : http://" +"WebDAV Authentication" => "Authentification WebDAV", +"URL: http://" => "URL : http://", +"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud enverra les informations de connexion à cette adresse. Ce module complémentaire analyse le code réponse HTTP et considère tout code différent des codes 401 et 403 comme associé à une authentification correcte." ); diff --git a/apps/user_webdavauth/l10n/gl.php b/apps/user_webdavauth/l10n/gl.php index 245a510134..a6b8355c07 100644 --- a/apps/user_webdavauth/l10n/gl.php +++ b/apps/user_webdavauth/l10n/gl.php @@ -1,3 +1,5 @@ "URL: http://" +"WebDAV Authentication" => "Autenticación WebDAV", +"URL: http://" => "URL: http://", +"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud enviará as credenciais do usuario a esta URL. Este conector comproba a resposta e interpretará os códigos de estado 401 e 403 como credenciais non válidas, e todas as outras respostas como credenciais válidas." ); diff --git a/apps/user_webdavauth/l10n/hu_HU.php b/apps/user_webdavauth/l10n/hu_HU.php index 245a510134..6435280114 100644 --- a/apps/user_webdavauth/l10n/hu_HU.php +++ b/apps/user_webdavauth/l10n/hu_HU.php @@ -1,3 +1,5 @@ "URL: http://" +"WebDAV Authentication" => "WebDAV hitelesítés", +"URL: http://" => "URL: http://", +"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "Az ownCloud elküldi a felhasználói fiók adatai a következő URL-re. Ez a bővítőmodul leellenőrzi a választ és ha a HTTP hibakód nem 401 vagy 403 azaz érvénytelen hitelesítő, akkor minden más válasz érvényes lesz." ); diff --git a/apps/user_webdavauth/l10n/it.php b/apps/user_webdavauth/l10n/it.php index 8dd605d84a..a7cd6e8e4b 100644 --- a/apps/user_webdavauth/l10n/it.php +++ b/apps/user_webdavauth/l10n/it.php @@ -1,4 +1,5 @@ "Autenticazione WebDAV", -"URL: http://" => "URL: http://" +"URL: http://" => "URL: http://", +"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud invierà le credenziali dell'utente a questo URL. Questa estensione controlla la risposta e interpreta i codici di stato 401 e 403 come credenziali non valide, e tutte le altre risposte come credenziali valide." ); diff --git a/apps/user_webdavauth/l10n/ja_JP.php b/apps/user_webdavauth/l10n/ja_JP.php index 245a510134..1cd14a03c7 100644 --- a/apps/user_webdavauth/l10n/ja_JP.php +++ b/apps/user_webdavauth/l10n/ja_JP.php @@ -1,3 +1,5 @@ "URL: http://" +"WebDAV Authentication" => "WebDAV 認証", +"URL: http://" => "URL: http://", +"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloudはこのURLにユーザ資格情報を送信します。このプラグインは応答をチェックし、HTTP状態コードが 401 と 403 の場合は無効な資格情報とし、他の応答はすべて有効な資格情報として処理します。" ); diff --git a/apps/user_webdavauth/l10n/ko.php b/apps/user_webdavauth/l10n/ko.php index 245a510134..578ff35e72 100644 --- a/apps/user_webdavauth/l10n/ko.php +++ b/apps/user_webdavauth/l10n/ko.php @@ -1,3 +1,5 @@ "URL: http://" +"WebDAV Authentication" => "WebDAV 인증", +"URL: http://" => "URL: http://", +"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud에서 이 URL로 사용자 인증 정보를 보냅니다. 이 플러그인은 응답을 확인하여 HTTP 상태 코드 401이나 403이 돌아온 경우에 잘못된 인증 정보로 간주합니다. 다른 모든 상태 코드는 올바른 인증 정보로 간주합니다." ); diff --git a/apps/user_webdavauth/l10n/pt_BR.php b/apps/user_webdavauth/l10n/pt_BR.php index 991c746a22..6ddd00ccc3 100644 --- a/apps/user_webdavauth/l10n/pt_BR.php +++ b/apps/user_webdavauth/l10n/pt_BR.php @@ -1,3 +1,5 @@ "URL do WebDAV: http://" +"WebDAV Authentication" => "Autenticação WebDAV", +"URL: http://" => "URL: http://", +"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "O ownCloud enviará as credenciais do usuário para esta URL. Este plugin verifica a resposta e interpreta o os códigos de status do HTTP 401 e 403 como credenciais inválidas, e todas as outras respostas como credenciais válidas." ); diff --git a/apps/user_webdavauth/l10n/pt_PT.php b/apps/user_webdavauth/l10n/pt_PT.php index 245a510134..d7e87b5c8d 100644 --- a/apps/user_webdavauth/l10n/pt_PT.php +++ b/apps/user_webdavauth/l10n/pt_PT.php @@ -1,3 +1,5 @@ "URL: http://" +"WebDAV Authentication" => "Autenticação WebDAV", +"URL: http://" => "URL: http://", +"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "O ownCloud vai enviar as credenciais do utilizador através deste URL. Este plugin verifica a resposta e vai interpretar os códigos de estado HTTP 401 e 403 como credenciais inválidas, e todas as outras como válidas." ); diff --git a/apps/user_webdavauth/l10n/ro.php b/apps/user_webdavauth/l10n/ro.php index 245a510134..9df490e81e 100644 --- a/apps/user_webdavauth/l10n/ro.php +++ b/apps/user_webdavauth/l10n/ro.php @@ -1,3 +1,5 @@ "URL: http://" +"WebDAV Authentication" => "Autentificare WebDAV", +"URL: http://" => "URL: http://", +"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud va trimite datele de autentificare la acest URL. Acest modul verifică răspunsul și va interpreta codurile de status HTTP 401 sau 403 ca fiind date de autentificare invalide, și orice alt răspuns ca fiind date valide." ); diff --git a/apps/user_webdavauth/l10n/ru_RU.php b/apps/user_webdavauth/l10n/ru_RU.php index 245a510134..46f74cb972 100644 --- a/apps/user_webdavauth/l10n/ru_RU.php +++ b/apps/user_webdavauth/l10n/ru_RU.php @@ -1,3 +1,4 @@ "WebDAV аутентификация", "URL: http://" => "URL: http://" ); diff --git a/apps/user_webdavauth/l10n/sk_SK.php b/apps/user_webdavauth/l10n/sk_SK.php index 9bd32954b0..c4e6dfddc7 100644 --- a/apps/user_webdavauth/l10n/sk_SK.php +++ b/apps/user_webdavauth/l10n/sk_SK.php @@ -1,3 +1,5 @@ "WebDAV URL: http://" +"WebDAV Authentication" => "WebDAV overenie", +"URL: http://" => "URL: http://", +"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud odošle používateľské údaje na zadanú URL. Plugin skontroluje odpoveď a považuje návratovú hodnotu HTTP 401 a 403 za neplatné údaje a všetky ostatné hodnoty ako platné prihlasovacie údaje." ); diff --git a/apps/user_webdavauth/l10n/sr.php b/apps/user_webdavauth/l10n/sr.php new file mode 100644 index 0000000000..518fcbe9be --- /dev/null +++ b/apps/user_webdavauth/l10n/sr.php @@ -0,0 +1,5 @@ + "WebDAV провера идентитета", +"URL: http://" => "Адреса: http://", +"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud ће послати акредитиве корисника на ову адресу. Овај прикључак проверава одговор и тумачи HTTP статусне кодове 401 и 403 као неисправне акредитиве, а све остале одговоре као исправне." +); diff --git a/apps/user_webdavauth/l10n/sv.php b/apps/user_webdavauth/l10n/sv.php index 245a510134..c79b35c27c 100644 --- a/apps/user_webdavauth/l10n/sv.php +++ b/apps/user_webdavauth/l10n/sv.php @@ -1,3 +1,5 @@ "URL: http://" +"WebDAV Authentication" => "WebDAV Autentisering", +"URL: http://" => "URL: http://", +"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud kommer skicka användaruppgifterna till denna URL. Denna plugin kontrollerar svaret och tolkar HTTP-statuskoderna 401 och 403 som felaktiga uppgifter, och alla andra svar som giltiga uppgifter." ); diff --git a/apps/user_webdavauth/l10n/th_TH.php b/apps/user_webdavauth/l10n/th_TH.php index 9bd32954b0..2bd1f685e6 100644 --- a/apps/user_webdavauth/l10n/th_TH.php +++ b/apps/user_webdavauth/l10n/th_TH.php @@ -1,3 +1,5 @@ "WebDAV URL: http://" +"WebDAV Authentication" => "WebDAV Authentication", +"URL: http://" => "URL: http://", +"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud จะส่งข้อมูลการเข้าใช้งานของผู้ใช้งานไปยังที่อยู่ URL ดังกล่าวนี้ ปลั๊กอินดังกล่าวจะทำการตรวจสอบข้อมูลที่โต้ตอบกลับมาและจะทำการแปลรหัส HTTP statuscodes 401 และ 403 ให้เป็นข้อมูลการเข้าใช้งานที่ไม่สามารถใช้งานได้ ส่วนข้อมูลอื่นๆที่เหลือทั้งหมดจะเป็นข้อมูลการเข้าใช้งานที่สามารถใช้งานได้" ); diff --git a/apps/user_webdavauth/l10n/zh_CN.php b/apps/user_webdavauth/l10n/zh_CN.php index 5b06409b42..72d2a0c11d 100644 --- a/apps/user_webdavauth/l10n/zh_CN.php +++ b/apps/user_webdavauth/l10n/zh_CN.php @@ -1,3 +1,5 @@ "URL:http://" +"WebDAV Authentication" => "WebDAV 认证", +"URL: http://" => "URL:http://", +"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud 将会发送用户的身份到此 URL。这个插件检查返回值并且将 HTTP 状态编码 401 和 403 解释为非法身份,其他所有返回值为合法身份。" ); diff --git a/apps2 b/apps2 new file mode 160000 index 0000000000..1ac3c35a40 --- /dev/null +++ b/apps2 @@ -0,0 +1 @@ +Subproject commit 1ac3c35a40c6613aba8274b056ae40aa0ce559c7 diff --git a/build/phpcs.xml b/build/phpcs.xml index 1e10be1a11..d2909955ed 100644 --- a/build/phpcs.xml +++ b/build/phpcs.xml @@ -10,7 +10,7 @@ */files_pdfviewer/js/pdfjs/* */files_odfviewer/src/* */files_svgedit/svg-edit/* - *jquery-ui-1.8.16.custom.css + *jquery-ui-*.css php diff --git a/config/config.sample.php b/config/config.sample.php index dafb536fa6..163beaae2f 100644 --- a/config/config.sample.php +++ b/config/config.sample.php @@ -1,5 +1,7 @@ false, +/* Blacklist a specific file and disallow the upload of files with this name - WARNING: USE THIS ONLY IF YOU KNOW WHAT YOU ARE DOING. */ +"blacklisted_files" => array('.htaccess'), + /* The automatic hostname detection of ownCloud can fail in certain reverse proxy situations. This option allows to manually override the automatic detection. You can also add a port. For example "www.example.com:88" */ "overwritehost" => "", /* The automatic protocol detection of ownCloud can fail in certain reverse proxy situations. This option allows to manually override the protocol detection. For example "https" */ "overwriteprotocol" => "", +/* The automatic webroot detection of ownCloud can fail in certain reverse proxy situations. This option allows to manually override the automatic detection. For example "/domain.tld/ownCloud" */ +"overwritewebroot" => "", + +/* The automatic detection of ownCloud can fail in certain reverse proxy situations. This option allows to define a manually override condition as regular expression for the remote ip address. For example "^10\.0\.0\.[1-3]$" */ +"overwritecondaddr" => "", + /* A proxy to use to connect to the internet. For example "myproxy.org:88" */ "proxy" => "", @@ -66,6 +77,9 @@ $CONFIG = array( /* URL of the appstore to use, server should understand OCS */ "appstoreurl" => "http://api.apps.owncloud.com/v1", +/* Enable SMTP class debugging */ +"mail_smtpdebug" => false, + /* Mode to use for sending mail, can be sendmail, smtp, qmail or php, see PHPMailer docs */ "mail_smtpmode" => "sendmail", @@ -75,17 +89,31 @@ $CONFIG = array( /* Port to use for sending mail, depends on mail_smtpmode if this is used */ "mail_smtpport" => 25, +/* SMTP server timeout in seconds for sending mail, depends on mail_smtpmode if this is used */ +"mail_smtptimeout" => 10, + +/* SMTP connection prefix or sending mail, depends on mail_smtpmode if this is used. + Can be '', ssl or tls */ +"mail_smtpsecure" => "", + /* authentication needed to send mail, depends on mail_smtpmode if this is used * (false = disable authentication) */ "mail_smtpauth" => false, +/* authentication type needed to send mail, depends on mail_smtpmode if this is used + * Can be LOGIN (default), PLAIN or NTLM */ +"mail_smtpauthtype" => "LOGIN", + /* Username to use for sendmail mail, depends on mail_smtpauth if this is used */ "mail_smtpname" => "", /* Password to use for sendmail mail, depends on mail_smtpauth if this is used */ "mail_smtppassword" => "", +/* How long should ownCloud keep deleted files in the trash bin, default value: 180 days */ +'trashbin_retention_obligation' => 180, + /* Check 3rdparty apps for malicious code fragments */ "appcodechecker" => "", @@ -104,6 +132,9 @@ $CONFIG = array( /* Lifetime of the remember login cookie, default is 15 days */ "remember_login_cookie_lifetime" => 60*60*24*15, +/* Custom CSP policy, changing this will overwrite the standard policy */ +"custom_csp_policy" => "default-src \'self\'; script-src \'self\' \'unsafe-eval\'; style-src \'self\' \'unsafe-inline\'; frame-src *; img-src *", + /* The directory where the user data is stored, default to data in the owncloud * directory. The sqlite database is also stored here, when sqlite is used. */ diff --git a/core/ajax/share.php b/core/ajax/share.php index 72ffc52e99..6704a00c5a 100644 --- a/core/ajax/share.php +++ b/core/ajax/share.php @@ -72,6 +72,7 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo case 'email': // read post variables $user = OCP\USER::getUser(); + $displayName = OCP\User::getDisplayName(); $type = $_POST['itemType']; $link = $_POST['link']; $file = $_POST['file']; @@ -81,13 +82,13 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo $l = OC_L10N::get('core'); // setup the email - $subject = (string)$l->t('User %s shared a file with you', $user); + $subject = (string)$l->t('User %s shared a file with you', $displayName); if ($type === 'dir') - $subject = (string)$l->t('User %s shared a folder with you', $user); + $subject = (string)$l->t('User %s shared a folder with you', $displayName); - $text = (string)$l->t('User %s shared the file "%s" with you. It is available for download here: %s', array($user, $file, $link)); + $text = (string)$l->t('User %s shared the file "%s" with you. It is available for download here: %s', array($displayName, $file, $link)); if ($type === 'dir') - $text = (string)$l->t('User %s shared the folder "%s" with you. It is available for download here: %s', array($user, $file, $link)); + $text = (string)$l->t('User %s shared the folder "%s" with you. It is available for download here: %s', array($displayName, $file, $link)); $default_from = OCP\Util::getDefaultEmailAddress('sharing-noreply'); @@ -98,7 +99,7 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo OCP\Util::sendMail($to_address, $to_address, $subject, $text, $from_address, $user); OCP\JSON::success(); } catch (Exception $exception) { - OCP\JSON::error(array('data' => array('message' => $exception->getMessage()))); + OCP\JSON::error(array('data' => array('message' => OC_Util::sanitizeHTML($exception->getMessage())))); } break; } @@ -158,14 +159,14 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo while ($count < 4 && count($users) == $limit) { $limit = 4 - $count; if ($sharePolicy == 'groups_only') { - $users = OC_Group::usersInGroups($groups, $_GET['search'], $limit, $offset); + $users = OC_Group::DisplayNamesInGroups($groups, $_GET['search'], $limit, $offset); } else { - $users = OC_User::getUsers($_GET['search'], $limit, $offset); + $users = OC_User::getDisplayNames($_GET['search'], $limit, $offset); } $offset += $limit; - foreach ($users as $user) { - if ((!isset($_GET['itemShares']) || !is_array($_GET['itemShares'][OCP\Share::SHARE_TYPE_USER]) || !in_array($user, $_GET['itemShares'][OCP\Share::SHARE_TYPE_USER])) && $user != OC_User::getUser()) { - $shareWith[] = array('label' => $user, 'value' => array('shareType' => OCP\Share::SHARE_TYPE_USER, 'shareWith' => $user)); + foreach ($users as $uid => $displayName) { + if ((!isset($_GET['itemShares']) || !is_array($_GET['itemShares'][OCP\Share::SHARE_TYPE_USER]) || !in_array($uid, $_GET['itemShares'][OCP\Share::SHARE_TYPE_USER])) && $uid != OC_User::getUser()) { + $shareWith[] = array('label' => $displayName, 'value' => array('shareType' => OCP\Share::SHARE_TYPE_USER, 'shareWith' => $uid)); $count++; } } diff --git a/core/css/images/animated-overlay.gif b/core/css/images/animated-overlay.gif new file mode 100644 index 0000000000..d441f75ebf Binary files /dev/null and b/core/css/images/animated-overlay.gif differ diff --git a/core/css/images/ui-bg_diagonals-thick_18_b81900_40x40.png b/core/css/images/ui-bg_diagonals-thick_18_b81900_40x40.png new file mode 100644 index 0000000000..954e22dbd9 Binary files /dev/null and b/core/css/images/ui-bg_diagonals-thick_18_b81900_40x40.png differ diff --git a/core/css/images/ui-bg_diagonals-thick_20_666666_40x40.png b/core/css/images/ui-bg_diagonals-thick_20_666666_40x40.png new file mode 100644 index 0000000000..64ece5707d Binary files /dev/null and b/core/css/images/ui-bg_diagonals-thick_20_666666_40x40.png differ diff --git a/core/css/images/ui-bg_flat_100_ffffff_40x100.png b/core/css/images/ui-bg_flat_100_ffffff_40x100.png new file mode 100644 index 0000000000..ac8b229af9 Binary files /dev/null and b/core/css/images/ui-bg_flat_100_ffffff_40x100.png differ diff --git a/core/css/images/ui-bg_flat_10_000000_40x100.png b/core/css/images/ui-bg_flat_10_000000_40x100.png new file mode 100644 index 0000000000..abdc01082b Binary files /dev/null and b/core/css/images/ui-bg_flat_10_000000_40x100.png differ diff --git a/core/css/images/ui-bg_flat_35_1d2d44_40x100.png b/core/css/images/ui-bg_flat_35_1d2d44_40x100.png new file mode 100644 index 0000000000..904ef14c37 Binary files /dev/null and b/core/css/images/ui-bg_flat_35_1d2d44_40x100.png differ diff --git a/core/css/images/ui-bg_glass_100_f8f8f8_1x400.png b/core/css/images/ui-bg_glass_100_f8f8f8_1x400.png new file mode 100644 index 0000000000..cd79e9f196 Binary files /dev/null and b/core/css/images/ui-bg_glass_100_f8f8f8_1x400.png differ diff --git a/core/css/images/ui-bg_highlight-hard_100_f8f8f8_1x100.png b/core/css/images/ui-bg_highlight-hard_100_f8f8f8_1x100.png new file mode 100644 index 0000000000..268e650935 Binary files /dev/null and b/core/css/images/ui-bg_highlight-hard_100_f8f8f8_1x100.png differ diff --git a/core/css/images/ui-bg_highlight-soft_100_eeeeee_1x100.png b/core/css/images/ui-bg_highlight-soft_100_eeeeee_1x100.png new file mode 100644 index 0000000000..f1273672d2 Binary files /dev/null and b/core/css/images/ui-bg_highlight-soft_100_eeeeee_1x100.png differ diff --git a/core/css/images/ui-icons_1d2d44_256x240.png b/core/css/images/ui-icons_1d2d44_256x240.png new file mode 100644 index 0000000000..2a857e4da5 Binary files /dev/null and b/core/css/images/ui-icons_1d2d44_256x240.png differ diff --git a/core/css/images/ui-icons_222222_256x240.png b/core/css/images/ui-icons_222222_256x240.png new file mode 100644 index 0000000000..b273ff111d Binary files /dev/null and b/core/css/images/ui-icons_222222_256x240.png differ diff --git a/core/css/images/ui-icons_ffd27a_256x240.png b/core/css/images/ui-icons_ffd27a_256x240.png new file mode 100644 index 0000000000..e117effa3d Binary files /dev/null and b/core/css/images/ui-icons_ffd27a_256x240.png differ diff --git a/core/css/images/ui-icons_ffffff_256x240.png b/core/css/images/ui-icons_ffffff_256x240.png new file mode 100644 index 0000000000..42f8f992c7 Binary files /dev/null and b/core/css/images/ui-icons_ffffff_256x240.png differ diff --git a/core/css/jquery-ui-1.10.0.custom.css b/core/css/jquery-ui-1.10.0.custom.css new file mode 100644 index 0000000000..a1e9895c77 --- /dev/null +++ b/core/css/jquery-ui-1.10.0.custom.css @@ -0,0 +1,1174 @@ +/*! jQuery UI - v1.10.0 - 2013-01-22 +* http://jqueryui.com +* Includes: jquery.ui.core.css, jquery.ui.resizable.css, jquery.ui.selectable.css, jquery.ui.accordion.css, jquery.ui.autocomplete.css, jquery.ui.button.css, jquery.ui.datepicker.css, jquery.ui.dialog.css, jquery.ui.menu.css, jquery.ui.progressbar.css, jquery.ui.slider.css, jquery.ui.spinner.css, jquery.ui.tabs.css, jquery.ui.tooltip.css +* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=%22Lucida%20Grande%22%2C%20Arial%2C%20Verdana%2C%20sans-serif&fwDefault=bold&fsDefault=1em&cornerRadius=4px&bgColorHeader=1d2d44&bgTextureHeader=01_flat.png&bgImgOpacityHeader=35&borderColorHeader=1d2d44&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=eeeeee&bgTextureContent=03_highlight_soft.png&bgImgOpacityContent=100&borderColorContent=dddddd&fcContent=333333&iconColorContent=222222&bgColorDefault=f8f8f8&bgTextureDefault=02_glass.png&bgImgOpacityDefault=100&borderColorDefault=ddd&fcDefault=555&iconColorDefault=1d2d44&bgColorHover=ffffff&bgTextureHover=01_flat.png&bgImgOpacityHover=100&borderColorHover=ddd&fcHover=333&iconColorHover=1d2d44&bgColorActive=f8f8f8&bgTextureActive=02_glass.png&bgImgOpacityActive=100&borderColorActive=1d2d44&fcActive=1d2d44&iconColorActive=1d2d44&bgColorHighlight=f8f8f8&bgTextureHighlight=04_highlight_hard.png&bgImgOpacityHighlight=100&borderColorHighlight=ddd&fcHighlight=555&iconColorHighlight=ffffff&bgColorError=b81900&bgTextureError=08_diagonals_thick.png&bgImgOpacityError=18&borderColorError=cd0a0a&fcError=ffffff&iconColorError=ffd27a&bgColorOverlay=666666&bgTextureOverlay=08_diagonals_thick.png&bgImgOpacityOverlay=20&opacityOverlay=50&bgColorShadow=000000&bgTextureShadow=01_flat.png&bgImgOpacityShadow=10&opacityShadow=20&thicknessShadow=5px&offsetTopShadow=-5px&offsetLeftShadow=-5px&cornerRadiusShadow=5px +* Copyright (c) 2013 jQuery Foundation and other contributors Licensed MIT */ + +/* Layout helpers +----------------------------------*/ +.ui-helper-hidden { + display: none; +} +.ui-helper-hidden-accessible { + border: 0; + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; +} +.ui-helper-reset { + margin: 0; + padding: 0; + border: 0; + outline: 0; + line-height: 1.3; + text-decoration: none; + font-size: 100%; + list-style: none; +} +.ui-helper-clearfix:before, +.ui-helper-clearfix:after { + content: ""; + display: table; +} +.ui-helper-clearfix:after { + clear: both; +} +.ui-helper-clearfix { + min-height: 0; /* support: IE7 */ +} +.ui-helper-zfix { + width: 100%; + height: 100%; + top: 0; + left: 0; + position: absolute; + opacity: 0; + filter:Alpha(Opacity=0); +} + +.ui-front { + z-index: 100; +} + + +/* Interaction Cues +----------------------------------*/ +.ui-state-disabled { + cursor: default !important; +} + + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { + display: block; + text-indent: -99999px; + overflow: hidden; + background-repeat: no-repeat; +} + + +/* Misc visuals +----------------------------------*/ + +/* Overlays */ +.ui-widget-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; +} +.ui-resizable { + position: relative; +} +.ui-resizable-handle { + position: absolute; + font-size: 0.1px; + display: block; +} +.ui-resizable-disabled .ui-resizable-handle, +.ui-resizable-autohide .ui-resizable-handle { + display: none; +} +.ui-resizable-n { + cursor: n-resize; + height: 7px; + width: 100%; + top: -5px; + left: 0; +} +.ui-resizable-s { + cursor: s-resize; + height: 7px; + width: 100%; + bottom: -5px; + left: 0; +} +.ui-resizable-e { + cursor: e-resize; + width: 7px; + right: -5px; + top: 0; + height: 100%; +} +.ui-resizable-w { + cursor: w-resize; + width: 7px; + left: -5px; + top: 0; + height: 100%; +} +.ui-resizable-se { + cursor: se-resize; + width: 12px; + height: 12px; + right: 1px; + bottom: 1px; +} +.ui-resizable-sw { + cursor: sw-resize; + width: 9px; + height: 9px; + left: -5px; + bottom: -5px; +} +.ui-resizable-nw { + cursor: nw-resize; + width: 9px; + height: 9px; + left: -5px; + top: -5px; +} +.ui-resizable-ne { + cursor: ne-resize; + width: 9px; + height: 9px; + right: -5px; + top: -5px; +} +.ui-selectable-helper { + position: absolute; + z-index: 100; + border: 1px dotted black; +} +.ui-accordion .ui-accordion-header { + display: block; + cursor: pointer; + position: relative; + margin-top: 2px; + padding: .5em .5em .5em .7em; + min-height: 0; /* support: IE7 */ +} +.ui-accordion .ui-accordion-icons { + padding-left: 2.2em; +} +.ui-accordion .ui-accordion-noicons { + padding-left: .7em; +} +.ui-accordion .ui-accordion-icons .ui-accordion-icons { + padding-left: 2.2em; +} +.ui-accordion .ui-accordion-header .ui-accordion-header-icon { + position: absolute; + left: .5em; + top: 50%; + margin-top: -8px; +} +.ui-accordion .ui-accordion-content { + padding: 1em 2.2em; + border-top: 0; + overflow: auto; +} +.ui-autocomplete { + position: absolute; + top: 0; + left: 0; + cursor: default; +} +.ui-button { + display: inline-block; + position: relative; + padding: 0; + line-height: normal; + margin-right: .1em; + cursor: pointer; + vertical-align: middle; + text-align: center; + overflow: visible; /* removes extra width in IE */ +} +.ui-button, +.ui-button:link, +.ui-button:visited, +.ui-button:hover, +.ui-button:active { + text-decoration: none; +} +/* to make room for the icon, a width needs to be set here */ +.ui-button-icon-only { + width: 2.2em; +} +/* button elements seem to need a little more width */ +button.ui-button-icon-only { + width: 2.4em; +} +.ui-button-icons-only { + width: 3.4em; +} +button.ui-button-icons-only { + width: 3.7em; +} + +/* button text element */ +.ui-button .ui-button-text { + display: block; + line-height: normal; +} +.ui-button-text-only .ui-button-text { + padding: .4em 1em; +} +.ui-button-icon-only .ui-button-text, +.ui-button-icons-only .ui-button-text { + padding: .4em; + text-indent: -9999999px; +} +.ui-button-text-icon-primary .ui-button-text, +.ui-button-text-icons .ui-button-text { + padding: .4em 1em .4em 2.1em; +} +.ui-button-text-icon-secondary .ui-button-text, +.ui-button-text-icons .ui-button-text { + padding: .4em 2.1em .4em 1em; +} +.ui-button-text-icons .ui-button-text { + padding-left: 2.1em; + padding-right: 2.1em; +} +/* no icon support for input elements, provide padding by default */ +input.ui-button { + padding: .4em 1em; +} + +/* button icon element(s) */ +.ui-button-icon-only .ui-icon, +.ui-button-text-icon-primary .ui-icon, +.ui-button-text-icon-secondary .ui-icon, +.ui-button-text-icons .ui-icon, +.ui-button-icons-only .ui-icon { + position: absolute; + top: 50%; + margin-top: -8px; +} +.ui-button-icon-only .ui-icon { + left: 50%; + margin-left: -8px; +} +.ui-button-text-icon-primary .ui-button-icon-primary, +.ui-button-text-icons .ui-button-icon-primary, +.ui-button-icons-only .ui-button-icon-primary { + left: .5em; +} +.ui-button-text-icon-secondary .ui-button-icon-secondary, +.ui-button-text-icons .ui-button-icon-secondary, +.ui-button-icons-only .ui-button-icon-secondary { + right: .5em; +} + +/* button sets */ +.ui-buttonset { + margin-right: 7px; +} +.ui-buttonset .ui-button { + margin-left: 0; + margin-right: -.3em; +} + +/* workarounds */ +/* reset extra padding in Firefox, see h5bp.com/l */ +input.ui-button::-moz-focus-inner, +button.ui-button::-moz-focus-inner { + border: 0; + padding: 0; +} +.ui-datepicker { + width: 17em; + padding: .2em .2em 0; + display: none; +} +.ui-datepicker .ui-datepicker-header { + position: relative; + padding: .2em 0; +} +.ui-datepicker .ui-datepicker-prev, +.ui-datepicker .ui-datepicker-next { + position: absolute; + top: 2px; + width: 1.8em; + height: 1.8em; +} +.ui-datepicker .ui-datepicker-prev-hover, +.ui-datepicker .ui-datepicker-next-hover { + top: 1px; +} +.ui-datepicker .ui-datepicker-prev { + left: 2px; +} +.ui-datepicker .ui-datepicker-next { + right: 2px; +} +.ui-datepicker .ui-datepicker-prev-hover { + left: 1px; +} +.ui-datepicker .ui-datepicker-next-hover { + right: 1px; +} +.ui-datepicker .ui-datepicker-prev span, +.ui-datepicker .ui-datepicker-next span { + display: block; + position: absolute; + left: 50%; + margin-left: -8px; + top: 50%; + margin-top: -8px; +} +.ui-datepicker .ui-datepicker-title { + margin: 0 2.3em; + line-height: 1.8em; + text-align: center; +} +.ui-datepicker .ui-datepicker-title select { + font-size: 1em; + margin: 1px 0; +} +.ui-datepicker select.ui-datepicker-month-year { + width: 100%; +} +.ui-datepicker select.ui-datepicker-month, +.ui-datepicker select.ui-datepicker-year { + width: 49%; +} +.ui-datepicker table { + width: 100%; + font-size: .9em; + border-collapse: collapse; + margin: 0 0 .4em; +} +.ui-datepicker th { + padding: .7em .3em; + text-align: center; + font-weight: bold; + border: 0; +} +.ui-datepicker td { + border: 0; + padding: 1px; +} +.ui-datepicker td span, +.ui-datepicker td a { + display: block; + padding: .2em; + text-align: right; + text-decoration: none; +} +.ui-datepicker .ui-datepicker-buttonpane { + background-image: none; + margin: .7em 0 0 0; + padding: 0 .2em; + border-left: 0; + border-right: 0; + border-bottom: 0; +} +.ui-datepicker .ui-datepicker-buttonpane button { + float: right; + margin: .5em .2em .4em; + cursor: pointer; + padding: .2em .6em .3em .6em; + width: auto; + overflow: visible; +} +.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { + float: left; +} + +/* with multiple calendars */ +.ui-datepicker.ui-datepicker-multi { + width: auto; +} +.ui-datepicker-multi .ui-datepicker-group { + float: left; +} +.ui-datepicker-multi .ui-datepicker-group table { + width: 95%; + margin: 0 auto .4em; +} +.ui-datepicker-multi-2 .ui-datepicker-group { + width: 50%; +} +.ui-datepicker-multi-3 .ui-datepicker-group { + width: 33.3%; +} +.ui-datepicker-multi-4 .ui-datepicker-group { + width: 25%; +} +.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header, +.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { + border-left-width: 0; +} +.ui-datepicker-multi .ui-datepicker-buttonpane { + clear: left; +} +.ui-datepicker-row-break { + clear: both; + width: 100%; + font-size: 0; +} + +/* RTL support */ +.ui-datepicker-rtl { + direction: rtl; +} +.ui-datepicker-rtl .ui-datepicker-prev { + right: 2px; + left: auto; +} +.ui-datepicker-rtl .ui-datepicker-next { + left: 2px; + right: auto; +} +.ui-datepicker-rtl .ui-datepicker-prev:hover { + right: 1px; + left: auto; +} +.ui-datepicker-rtl .ui-datepicker-next:hover { + left: 1px; + right: auto; +} +.ui-datepicker-rtl .ui-datepicker-buttonpane { + clear: right; +} +.ui-datepicker-rtl .ui-datepicker-buttonpane button { + float: left; +} +.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current, +.ui-datepicker-rtl .ui-datepicker-group { + float: right; +} +.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header, +.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { + border-right-width: 0; + border-left-width: 1px; +} +.ui-dialog { + position: absolute; + top: 0; + left: 0; + padding: .2em; + outline: 0; +} +.ui-dialog .ui-dialog-titlebar { + padding: .4em 1em; + position: relative; +} +.ui-dialog .ui-dialog-title { + float: left; + margin: .1em 0; + white-space: nowrap; + width: 90%; + overflow: hidden; + text-overflow: ellipsis; +} +.ui-dialog .ui-dialog-titlebar-close { + position: absolute; + right: .3em; + top: 50%; + width: 21px; + margin: -10px 0 0 0; + padding: 1px; + height: 20px; +} +.ui-dialog .ui-dialog-content { + position: relative; + border: 0; + padding: .5em 1em; + background: none; + overflow: auto; +} +.ui-dialog .ui-dialog-buttonpane { + text-align: left; + border-width: 1px 0 0 0; + background-image: none; + margin-top: .5em; + padding: .3em 1em .5em .4em; +} +.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { + float: right; +} +.ui-dialog .ui-dialog-buttonpane button { + margin: .5em .4em .5em 0; + cursor: pointer; +} +.ui-dialog .ui-resizable-se { + width: 12px; + height: 12px; + right: -5px; + bottom: -5px; + background-position: 16px 16px; +} +.ui-draggable .ui-dialog-titlebar { + cursor: move; +} +.ui-menu { + list-style: none; + padding: 2px; + margin: 0; + display: block; + outline: none; +} +.ui-menu .ui-menu { + margin-top: -3px; + position: absolute; +} +.ui-menu .ui-menu-item { + margin: 0; + padding: 0; + width: 100%; +} +.ui-menu .ui-menu-divider { + margin: 5px -2px 5px -2px; + height: 0; + font-size: 0; + line-height: 0; + border-width: 1px 0 0 0; +} +.ui-menu .ui-menu-item a { + text-decoration: none; + display: block; + padding: 2px .4em; + line-height: 1.5; + min-height: 0; /* support: IE7 */ + font-weight: normal; +} +.ui-menu .ui-menu-item a.ui-state-focus, +.ui-menu .ui-menu-item a.ui-state-active { + font-weight: normal; + margin: -1px; +} + +.ui-menu .ui-state-disabled { + font-weight: normal; + margin: .4em 0 .2em; + line-height: 1.5; +} +.ui-menu .ui-state-disabled a { + cursor: default; +} + +/* icon support */ +.ui-menu-icons { + position: relative; +} +.ui-menu-icons .ui-menu-item a { + position: relative; + padding-left: 2em; +} + +/* left-aligned */ +.ui-menu .ui-icon { + position: absolute; + top: .2em; + left: .2em; +} + +/* right-aligned */ +.ui-menu .ui-menu-icon { + position: static; + float: right; +} +.ui-progressbar { + height: 2em; + text-align: left; + overflow: hidden; +} +.ui-progressbar .ui-progressbar-value { + margin: -1px; + height: 100%; +} +.ui-progressbar .ui-progressbar-overlay { + background: url("images/animated-overlay.gif"); + height: 100%; + filter: alpha(opacity=25); + opacity: 0.25; +} +.ui-progressbar-indeterminate .ui-progressbar-value { + background-image: none; +} +.ui-slider { + position: relative; + text-align: left; +} +.ui-slider .ui-slider-handle { + position: absolute; + z-index: 2; + width: 1.2em; + height: 1.2em; + cursor: default; +} +.ui-slider .ui-slider-range { + position: absolute; + z-index: 1; + font-size: .7em; + display: block; + border: 0; + background-position: 0 0; +} + +/* For IE8 - See #6727 */ +.ui-slider.ui-state-disabled .ui-slider-handle, +.ui-slider.ui-state-disabled .ui-slider-range { + filter: inherit; +} + +.ui-slider-horizontal { + height: .8em; +} +.ui-slider-horizontal .ui-slider-handle { + top: -.3em; + margin-left: -.6em; +} +.ui-slider-horizontal .ui-slider-range { + top: 0; + height: 100%; +} +.ui-slider-horizontal .ui-slider-range-min { + left: 0; +} +.ui-slider-horizontal .ui-slider-range-max { + right: 0; +} + +.ui-slider-vertical { + width: .8em; + height: 100px; +} +.ui-slider-vertical .ui-slider-handle { + left: -.3em; + margin-left: 0; + margin-bottom: -.6em; +} +.ui-slider-vertical .ui-slider-range { + left: 0; + width: 100%; +} +.ui-slider-vertical .ui-slider-range-min { + bottom: 0; +} +.ui-slider-vertical .ui-slider-range-max { + top: 0; +} +.ui-spinner { + position: relative; + display: inline-block; + overflow: hidden; + padding: 0; + vertical-align: middle; +} +.ui-spinner-input { + border: none; + background: none; + color: inherit; + padding: 0; + margin: .2em 0; + vertical-align: middle; + margin-left: .4em; + margin-right: 22px; +} +.ui-spinner-button { + width: 16px; + height: 50%; + font-size: .5em; + padding: 0; + margin: 0; + text-align: center; + position: absolute; + cursor: default; + display: block; + overflow: hidden; + right: 0; +} +/* more specificity required here to overide default borders */ +.ui-spinner a.ui-spinner-button { + border-top: none; + border-bottom: none; + border-right: none; +} +/* vertical centre icon */ +.ui-spinner .ui-icon { + position: absolute; + margin-top: -8px; + top: 50%; + left: 0; +} +.ui-spinner-up { + top: 0; +} +.ui-spinner-down { + bottom: 0; +} + +/* TR overrides */ +.ui-spinner .ui-icon-triangle-1-s { + /* need to fix icons sprite */ + background-position: -65px -16px; +} +.ui-tabs { + position: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ + padding: .2em; +} +.ui-tabs .ui-tabs-nav { + margin: 0; + padding: .2em .2em 0; +} +.ui-tabs .ui-tabs-nav li { + list-style: none; + float: left; + position: relative; + top: 0; + margin: 1px .2em 0 0; + border-bottom: 0; + padding: 0; + white-space: nowrap; +} +.ui-tabs .ui-tabs-nav li a { + float: left; + padding: .5em 1em; + text-decoration: none; +} +.ui-tabs .ui-tabs-nav li.ui-tabs-active { + margin-bottom: -1px; + padding-bottom: 1px; +} +.ui-tabs .ui-tabs-nav li.ui-tabs-active a, +.ui-tabs .ui-tabs-nav li.ui-state-disabled a, +.ui-tabs .ui-tabs-nav li.ui-tabs-loading a { + cursor: text; +} +.ui-tabs .ui-tabs-nav li a, /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */ +.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active a { + cursor: pointer; +} +.ui-tabs .ui-tabs-panel { + display: block; + border-width: 0; + padding: 1em 1.4em; + background: none; +} +.ui-tooltip { + padding: 8px; + position: absolute; + z-index: 9999; + max-width: 300px; + -webkit-box-shadow: 0 0 5px #aaa; + box-shadow: 0 0 5px #aaa; +} +body .ui-tooltip { + border-width: 2px; +} + +/* Component containers +----------------------------------*/ +.ui-widget { + font-family: "Lucida Grande", Arial, Verdana, sans-serif; + font-size: 1em; +} +.ui-widget .ui-widget { + font-size: 1em; +} +.ui-widget input, +.ui-widget select, +.ui-widget textarea, +.ui-widget button { + font-family: "Lucida Grande", Arial, Verdana, sans-serif; + font-size: 1em; +} +.ui-widget-content { + border: 1px solid #dddddd; + background: #eeeeee url(images/ui-bg_highlight-soft_100_eeeeee_1x100.png) 50% top repeat-x; + color: #333333; +} +.ui-widget-content a { + color: #333333; +} +.ui-widget-header { + border: 1px solid #1d2d44; + background: #1d2d44 url(images/ui-bg_flat_35_1d2d44_40x100.png) 50% 50% repeat-x; + color: #ffffff; + font-weight: bold; +} +.ui-widget-header a { + color: #ffffff; +} + +/* Interaction states +----------------------------------*/ +.ui-state-default, +.ui-widget-content .ui-state-default, +.ui-widget-header .ui-state-default { + border: 1px solid #ddd; + background: #f8f8f8 url(images/ui-bg_glass_100_f8f8f8_1x400.png) 50% 50% repeat-x; + font-weight: bold; + color: #555; +} +.ui-state-default a, +.ui-state-default a:link, +.ui-state-default a:visited { + color: #555; + text-decoration: none; +} +.ui-state-hover, +.ui-widget-content .ui-state-hover, +.ui-widget-header .ui-state-hover, +.ui-state-focus, +.ui-widget-content .ui-state-focus, +.ui-widget-header .ui-state-focus { + border: 1px solid #ddd; + background: #ffffff url(images/ui-bg_flat_100_ffffff_40x100.png) 50% 50% repeat-x; + font-weight: bold; + color: #333; +} +.ui-state-hover a, +.ui-state-hover a:hover, +.ui-state-hover a:link, +.ui-state-hover a:visited { + color: #333; + text-decoration: none; +} +.ui-state-active, +.ui-widget-content .ui-state-active, +.ui-widget-header .ui-state-active { + border: 1px solid #1d2d44; + background: #f8f8f8 url(images/ui-bg_glass_100_f8f8f8_1x400.png) 50% 50% repeat-x; + font-weight: bold; + color: #1d2d44; +} +.ui-state-active a, +.ui-state-active a:link, +.ui-state-active a:visited { + color: #1d2d44; + text-decoration: none; +} + +/* Interaction Cues +----------------------------------*/ +.ui-state-highlight, +.ui-widget-content .ui-state-highlight, +.ui-widget-header .ui-state-highlight { + border: 1px solid #ddd; + background: #f8f8f8 url(images/ui-bg_highlight-hard_100_f8f8f8_1x100.png) 50% top repeat-x; + color: #555; +} +.ui-state-highlight a, +.ui-widget-content .ui-state-highlight a, +.ui-widget-header .ui-state-highlight a { + color: #555; +} +.ui-state-error, +.ui-widget-content .ui-state-error, +.ui-widget-header .ui-state-error { + border: 1px solid #cd0a0a; + background: #b81900 url(images/ui-bg_diagonals-thick_18_b81900_40x40.png) 50% 50% repeat; + color: #ffffff; +} +.ui-state-error a, +.ui-widget-content .ui-state-error a, +.ui-widget-header .ui-state-error a { + color: #ffffff; +} +.ui-state-error-text, +.ui-widget-content .ui-state-error-text, +.ui-widget-header .ui-state-error-text { + color: #ffffff; +} +.ui-priority-primary, +.ui-widget-content .ui-priority-primary, +.ui-widget-header .ui-priority-primary { + font-weight: bold; +} +.ui-priority-secondary, +.ui-widget-content .ui-priority-secondary, +.ui-widget-header .ui-priority-secondary { + opacity: .7; + filter:Alpha(Opacity=70); + font-weight: normal; +} +.ui-state-disabled, +.ui-widget-content .ui-state-disabled, +.ui-widget-header .ui-state-disabled { + opacity: .35; + filter:Alpha(Opacity=35); + background-image: none; +} +.ui-state-disabled .ui-icon { + filter:Alpha(Opacity=35); /* For IE8 - See #6059 */ +} + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { + width: 16px; + height: 16px; + background-position: 16px 16px; +} +.ui-icon, +.ui-widget-content .ui-icon { + background-image: url(images/ui-icons_222222_256x240.png); +} +.ui-widget-header .ui-icon { + background-image: url(images/ui-icons_222222_256x240.png); +} +.ui-state-default .ui-icon { + background-image: url(images/ui-icons_1d2d44_256x240.png); +} +.ui-state-hover .ui-icon, +.ui-state-focus .ui-icon { + background-image: url(images/ui-icons_1d2d44_256x240.png); +} +.ui-state-active .ui-icon { + background-image: url(images/ui-icons_1d2d44_256x240.png); +} +.ui-state-highlight .ui-icon { + background-image: url(images/ui-icons_ffffff_256x240.png); +} +.ui-state-error .ui-icon, +.ui-state-error-text .ui-icon { + background-image: url(images/ui-icons_ffd27a_256x240.png); +} + +/* positioning */ +.ui-icon-carat-1-n { background-position: 0 0; } +.ui-icon-carat-1-ne { background-position: -16px 0; } +.ui-icon-carat-1-e { background-position: -32px 0; } +.ui-icon-carat-1-se { background-position: -48px 0; } +.ui-icon-carat-1-s { background-position: -64px 0; } +.ui-icon-carat-1-sw { background-position: -80px 0; } +.ui-icon-carat-1-w { background-position: -96px 0; } +.ui-icon-carat-1-nw { background-position: -112px 0; } +.ui-icon-carat-2-n-s { background-position: -128px 0; } +.ui-icon-carat-2-e-w { background-position: -144px 0; } +.ui-icon-triangle-1-n { background-position: 0 -16px; } +.ui-icon-triangle-1-ne { background-position: -16px -16px; } +.ui-icon-triangle-1-e { background-position: -32px -16px; } +.ui-icon-triangle-1-se { background-position: -48px -16px; } +.ui-icon-triangle-1-s { background-position: -64px -16px; } +.ui-icon-triangle-1-sw { background-position: -80px -16px; } +.ui-icon-triangle-1-w { background-position: -96px -16px; } +.ui-icon-triangle-1-nw { background-position: -112px -16px; } +.ui-icon-triangle-2-n-s { background-position: -128px -16px; } +.ui-icon-triangle-2-e-w { background-position: -144px -16px; } +.ui-icon-arrow-1-n { background-position: 0 -32px; } +.ui-icon-arrow-1-ne { background-position: -16px -32px; } +.ui-icon-arrow-1-e { background-position: -32px -32px; } +.ui-icon-arrow-1-se { background-position: -48px -32px; } +.ui-icon-arrow-1-s { background-position: -64px -32px; } +.ui-icon-arrow-1-sw { background-position: -80px -32px; } +.ui-icon-arrow-1-w { background-position: -96px -32px; } +.ui-icon-arrow-1-nw { background-position: -112px -32px; } +.ui-icon-arrow-2-n-s { background-position: -128px -32px; } +.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } +.ui-icon-arrow-2-e-w { background-position: -160px -32px; } +.ui-icon-arrow-2-se-nw { background-position: -176px -32px; } +.ui-icon-arrowstop-1-n { background-position: -192px -32px; } +.ui-icon-arrowstop-1-e { background-position: -208px -32px; } +.ui-icon-arrowstop-1-s { background-position: -224px -32px; } +.ui-icon-arrowstop-1-w { background-position: -240px -32px; } +.ui-icon-arrowthick-1-n { background-position: 0 -48px; } +.ui-icon-arrowthick-1-ne { background-position: -16px -48px; } +.ui-icon-arrowthick-1-e { background-position: -32px -48px; } +.ui-icon-arrowthick-1-se { background-position: -48px -48px; } +.ui-icon-arrowthick-1-s { background-position: -64px -48px; } +.ui-icon-arrowthick-1-sw { background-position: -80px -48px; } +.ui-icon-arrowthick-1-w { background-position: -96px -48px; } +.ui-icon-arrowthick-1-nw { background-position: -112px -48px; } +.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } +.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } +.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } +.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } +.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } +.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } +.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } +.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } +.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } +.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } +.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } +.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } +.ui-icon-arrowreturn-1-w { background-position: -64px -64px; } +.ui-icon-arrowreturn-1-n { background-position: -80px -64px; } +.ui-icon-arrowreturn-1-e { background-position: -96px -64px; } +.ui-icon-arrowreturn-1-s { background-position: -112px -64px; } +.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } +.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } +.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } +.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } +.ui-icon-arrow-4 { background-position: 0 -80px; } +.ui-icon-arrow-4-diag { background-position: -16px -80px; } +.ui-icon-extlink { background-position: -32px -80px; } +.ui-icon-newwin { background-position: -48px -80px; } +.ui-icon-refresh { background-position: -64px -80px; } +.ui-icon-shuffle { background-position: -80px -80px; } +.ui-icon-transfer-e-w { background-position: -96px -80px; } +.ui-icon-transferthick-e-w { background-position: -112px -80px; } +.ui-icon-folder-collapsed { background-position: 0 -96px; } +.ui-icon-folder-open { background-position: -16px -96px; } +.ui-icon-document { background-position: -32px -96px; } +.ui-icon-document-b { background-position: -48px -96px; } +.ui-icon-note { background-position: -64px -96px; } +.ui-icon-mail-closed { background-position: -80px -96px; } +.ui-icon-mail-open { background-position: -96px -96px; } +.ui-icon-suitcase { background-position: -112px -96px; } +.ui-icon-comment { background-position: -128px -96px; } +.ui-icon-person { background-position: -144px -96px; } +.ui-icon-print { background-position: -160px -96px; } +.ui-icon-trash { background-position: -176px -96px; } +.ui-icon-locked { background-position: -192px -96px; } +.ui-icon-unlocked { background-position: -208px -96px; } +.ui-icon-bookmark { background-position: -224px -96px; } +.ui-icon-tag { background-position: -240px -96px; } +.ui-icon-home { background-position: 0 -112px; } +.ui-icon-flag { background-position: -16px -112px; } +.ui-icon-calendar { background-position: -32px -112px; } +.ui-icon-cart { background-position: -48px -112px; } +.ui-icon-pencil { background-position: -64px -112px; } +.ui-icon-clock { background-position: -80px -112px; } +.ui-icon-disk { background-position: -96px -112px; } +.ui-icon-calculator { background-position: -112px -112px; } +.ui-icon-zoomin { background-position: -128px -112px; } +.ui-icon-zoomout { background-position: -144px -112px; } +.ui-icon-search { background-position: -160px -112px; } +.ui-icon-wrench { background-position: -176px -112px; } +.ui-icon-gear { background-position: -192px -112px; } +.ui-icon-heart { background-position: -208px -112px; } +.ui-icon-star { background-position: -224px -112px; } +.ui-icon-link { background-position: -240px -112px; } +.ui-icon-cancel { background-position: 0 -128px; } +.ui-icon-plus { background-position: -16px -128px; } +.ui-icon-plusthick { background-position: -32px -128px; } +.ui-icon-minus { background-position: -48px -128px; } +.ui-icon-minusthick { background-position: -64px -128px; } +.ui-icon-close { background-position: -80px -128px; } +.ui-icon-closethick { background-position: -96px -128px; } +.ui-icon-key { background-position: -112px -128px; } +.ui-icon-lightbulb { background-position: -128px -128px; } +.ui-icon-scissors { background-position: -144px -128px; } +.ui-icon-clipboard { background-position: -160px -128px; } +.ui-icon-copy { background-position: -176px -128px; } +.ui-icon-contact { background-position: -192px -128px; } +.ui-icon-image { background-position: -208px -128px; } +.ui-icon-video { background-position: -224px -128px; } +.ui-icon-script { background-position: -240px -128px; } +.ui-icon-alert { background-position: 0 -144px; } +.ui-icon-info { background-position: -16px -144px; } +.ui-icon-notice { background-position: -32px -144px; } +.ui-icon-help { background-position: -48px -144px; } +.ui-icon-check { background-position: -64px -144px; } +.ui-icon-bullet { background-position: -80px -144px; } +.ui-icon-radio-on { background-position: -96px -144px; } +.ui-icon-radio-off { background-position: -112px -144px; } +.ui-icon-pin-w { background-position: -128px -144px; } +.ui-icon-pin-s { background-position: -144px -144px; } +.ui-icon-play { background-position: 0 -160px; } +.ui-icon-pause { background-position: -16px -160px; } +.ui-icon-seek-next { background-position: -32px -160px; } +.ui-icon-seek-prev { background-position: -48px -160px; } +.ui-icon-seek-end { background-position: -64px -160px; } +.ui-icon-seek-start { background-position: -80px -160px; } +/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ +.ui-icon-seek-first { background-position: -80px -160px; } +.ui-icon-stop { background-position: -96px -160px; } +.ui-icon-eject { background-position: -112px -160px; } +.ui-icon-volume-off { background-position: -128px -160px; } +.ui-icon-volume-on { background-position: -144px -160px; } +.ui-icon-power { background-position: 0 -176px; } +.ui-icon-signal-diag { background-position: -16px -176px; } +.ui-icon-signal { background-position: -32px -176px; } +.ui-icon-battery-0 { background-position: -48px -176px; } +.ui-icon-battery-1 { background-position: -64px -176px; } +.ui-icon-battery-2 { background-position: -80px -176px; } +.ui-icon-battery-3 { background-position: -96px -176px; } +.ui-icon-circle-plus { background-position: 0 -192px; } +.ui-icon-circle-minus { background-position: -16px -192px; } +.ui-icon-circle-close { background-position: -32px -192px; } +.ui-icon-circle-triangle-e { background-position: -48px -192px; } +.ui-icon-circle-triangle-s { background-position: -64px -192px; } +.ui-icon-circle-triangle-w { background-position: -80px -192px; } +.ui-icon-circle-triangle-n { background-position: -96px -192px; } +.ui-icon-circle-arrow-e { background-position: -112px -192px; } +.ui-icon-circle-arrow-s { background-position: -128px -192px; } +.ui-icon-circle-arrow-w { background-position: -144px -192px; } +.ui-icon-circle-arrow-n { background-position: -160px -192px; } +.ui-icon-circle-zoomin { background-position: -176px -192px; } +.ui-icon-circle-zoomout { background-position: -192px -192px; } +.ui-icon-circle-check { background-position: -208px -192px; } +.ui-icon-circlesmall-plus { background-position: 0 -208px; } +.ui-icon-circlesmall-minus { background-position: -16px -208px; } +.ui-icon-circlesmall-close { background-position: -32px -208px; } +.ui-icon-squaresmall-plus { background-position: -48px -208px; } +.ui-icon-squaresmall-minus { background-position: -64px -208px; } +.ui-icon-squaresmall-close { background-position: -80px -208px; } +.ui-icon-grip-dotted-vertical { background-position: 0 -224px; } +.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } +.ui-icon-grip-solid-vertical { background-position: -32px -224px; } +.ui-icon-grip-solid-horizontal { background-position: -48px -224px; } +.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } +.ui-icon-grip-diagonal-se { background-position: -80px -224px; } + + +/* Misc visuals +----------------------------------*/ + +/* Corner radius */ +.ui-corner-all, +.ui-corner-top, +.ui-corner-left, +.ui-corner-tl { + border-top-left-radius: 4px; +} +.ui-corner-all, +.ui-corner-top, +.ui-corner-right, +.ui-corner-tr { + border-top-right-radius: 4px; +} +.ui-corner-all, +.ui-corner-bottom, +.ui-corner-left, +.ui-corner-bl { + border-bottom-left-radius: 4px; +} +.ui-corner-all, +.ui-corner-bottom, +.ui-corner-right, +.ui-corner-br { + border-bottom-right-radius: 4px; +} + +/* Overlays */ +.ui-widget-overlay { + background: #666666 url(images/ui-bg_diagonals-thick_20_666666_40x40.png) 50% 50% repeat; + opacity: .5; + filter: Alpha(Opacity=50); +} +.ui-widget-shadow { + margin: -5px 0 0 -5px; + padding: 5px; + background: #000000 url(images/ui-bg_flat_10_000000_40x100.png) 50% 50% repeat-x; + opacity: .2; + filter: Alpha(Opacity=20); + border-radius: 5px; +} diff --git a/core/css/jquery-ui-1.8.16.custom.css b/core/css/jquery-ui-1.8.16.custom.css deleted file mode 100644 index add1c6af08..0000000000 --- a/core/css/jquery-ui-1.8.16.custom.css +++ /dev/null @@ -1,362 +0,0 @@ -/* - * jQuery UI CSS Framework 1.8.16 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Theming/API - */ - -/* Layout helpers -----------------------------------*/ -.ui-helper-hidden { display: none; } -.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); } -.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; } -.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; } -.ui-helper-clearfix { display: inline-block; } -/* required comment for clearfix to work in Opera \*/ -* html .ui-helper-clearfix { height:1%; } -.ui-helper-clearfix { display:block; } -/* end clearfix */ -.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); } - - -/* Interaction Cues -----------------------------------*/ -.ui-state-disabled { cursor: default !important; } - - -/* Icons -----------------------------------*/ - -/* states and images */ -.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; } - - -/* Misc visuals -----------------------------------*/ - -/* Overlays */ -.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } - - -/* - * jQuery UI CSS Framework 1.8.16 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Theming/API - * - * To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault="Lucida%20Grande",%20Arial,%20Verdana,%20sans-serif&fwDefault=bold&fsDefault=1em&cornerRadius=4px&bgColorHeader=1d2d44&bgTextureHeader=01_flat.png&bgImgOpacityHeader=35&borderColorHeader=1d2d44&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=eeeeee&bgTextureContent=03_highlight_soft.png&bgImgOpacityContent=100&borderColorContent=dddddd&fcContent=333333&iconColorContent=222222&bgColorDefault=f8f8f8&bgTextureDefault=02_glass.png&bgImgOpacityDefault=100&borderColorDefault=ddd&fcDefault=555&iconColorDefault=1d2d44&bgColorHover=ffffff&bgTextureHover=01_flat.png&bgImgOpacityHover=100&borderColorHover=ddd&fcHover=333&iconColorHover=1d2d44&bgColorActive=f8f8f8&bgTextureActive=02_glass.png&bgImgOpacityActive=100&borderColorActive=1d2d44&fcActive=1d2d44&iconColorActive=1d2d44&bgColorHighlight=f8f8f8&bgTextureHighlight=04_highlight_hard.png&bgImgOpacityHighlight=100&borderColorHighlight=ddd&fcHighlight=555&iconColorHighlight=ffffff&bgColorError=b81900&bgTextureError=08_diagonals_thick.png&bgImgOpacityError=18&borderColorError=cd0a0a&fcError=ffffff&iconColorError=ffd27a&bgColorOverlay=666666&bgTextureOverlay=08_diagonals_thick.png&bgImgOpacityOverlay=20&opacityOverlay=50&bgColorShadow=000000&bgTextureShadow=01_flat.png&bgImgOpacityShadow=10&opacityShadow=20&thicknessShadow=5px&offsetTopShadow=-5px&offsetLeftShadow=-5px&cornerRadiusShadow=5px - */ - - -/* Component containers -----------------------------------*/ -.ui-widget { font-family: \\\"Lucida Grande\\\", Arial, Verdana, sans-serif; font-size: 1em; } -.ui-widget .ui-widget { font-size: 1em; } -.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: \\\"Lucida Grande\\\", Arial, Verdana, sans-serif; font-size: 1em; } -.ui-widget-content { border: 1px solid #dddddd; background: #eeeeee; color: #333333; } -.ui-widget-content a { color: #333333; } -.ui-widget-header { border: 1px solid #1d2d44; background: #1d2d44; color: #ffffff; font-weight: bold; } -.ui-widget-header a { color: #ffffff; } - -/* Interaction states -----------------------------------*/ -.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #ddd; background: #f8f8f8; font-weight: bold; color: #555; } -.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #555; text-decoration: none; } -.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #ddd; background: #ffffff; font-weight: bold; color: #333; } -.ui-state-hover a, .ui-state-hover a:hover { color: #333; text-decoration: none; } -.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #1d2d44; background: #f8f8f8; font-weight: bold; color: #1d2d44; } -.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #1d2d44; text-decoration: none; } -.ui-widget :active { outline: none; } - -/* Interaction Cues -----------------------------------*/ -.ui-state-disabled { cursor: default !important; } - -/* Icons -----------------------------------*/ - -/* states and images */ -.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; } - -/* states and images */ -.ui-icon { width: 16px; height: 16px; } -.ui-icon-closethick { background-image: url(../img/actions/delete.png); } - - -/* Misc visuals -----------------------------------*/ - -/* Corner radius */ -.ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; -khtml-border-top-left-radius: 4px; border-top-left-radius: 4px; } -.ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; -khtml-border-top-right-radius: 4px; border-top-right-radius: 4px; } -.ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; -khtml-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; } -.ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; -khtml-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; } - -/* Overlays */ -.ui-widget-overlay { background: #666666; opacity: .50;filter:Alpha(Opacity=50); } -.ui-widget-shadow { margin: -5px 0 0 -5px; padding: 5px; background: #000000; opacity: .20;filter:Alpha(Opacity=20); -moz-border-radius: 5px; -khtml-border-radius: 5px; -webkit-border-radius: 5px; border-radius: 5px; }/* - * jQuery UI Resizable 1.8.16 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Resizable#theming - */ -.ui-resizable { position: relative;} -.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block; } -.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; } -.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; } -.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; } -.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; } -.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; } -.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; } -.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; } -.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; } -.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/* - * jQuery UI Selectable 1.8.16 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Selectable#theming - */ -.ui-selectable-helper { position: absolute; z-index: 100; border:1px dotted black; } -/* - * jQuery UI Autocomplete 1.8.16 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Autocomplete#theming - */ -.ui-autocomplete { position: absolute; cursor: default; } - -/* workarounds */ -* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */ - -/* - * jQuery UI Menu 1.8.16 - * - * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Menu#theming - */ -.ui-menu { - list-style:none; - padding: 2px; - margin: 0; - display:block; - float: left; -} -.ui-menu .ui-menu { - margin-top: -3px; -} -.ui-menu .ui-menu-item { - margin:0; - padding: 0; - zoom: 1; - float: left; - clear: left; - width: 100%; -} -.ui-menu .ui-menu-item a { - text-decoration:none; - display:block; - padding:.2em .4em; - line-height:1.5; - zoom:1; -} -.ui-menu .ui-menu-item a.ui-state-hover, -.ui-menu .ui-menu-item a.ui-state-active { - font-weight: normal; - margin: -1px; -} -/* - * jQuery UI Button 1.8.16 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Button#theming - */ -.ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: visible; } /* the overflow property removes extra width in IE */ -.ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */ -button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */ -.ui-button-icons-only { width: 3.4em; } -button.ui-button-icons-only { width: 3.7em; } - -/*button text element */ -.ui-button .ui-button-text { display: block; line-height: 1.4; } -.ui-button-text-only .ui-button-text { padding: .4em 1em; } -.ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; } -.ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; } -.ui-button-text-icon-secondary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 2.1em .4em 1em; } -.ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; } -/* no icon support for input elements, provide padding by default */ -input.ui-button { padding: .4em 1em; } - -/*button icon element(s) */ -.ui-button-icon-only .ui-icon, .ui-button-text-icon-primary .ui-icon, .ui-button-text-icon-secondary .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; } -.ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; } -.ui-button-text-icon-primary .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; } -.ui-button-text-icon-secondary .ui-button-icon-secondary, .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; } -.ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; } - -/*button sets*/ -.ui-buttonset { margin-right: 7px; } -.ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; } - -/* workarounds */ -button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */ -/* - * jQuery UI Dialog 1.8.16 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Dialog#theming - */ -.ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; } -.ui-dialog .ui-dialog-titlebar { padding: .4em 1em; position: relative; } -.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .1em 0; } -.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; background-color: #EEE;} -.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; } -.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; } -.ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; } -.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; } -.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; } -.ui-dialog .ui-dialog-buttonpane button { margin: .5em .4em .5em 0; cursor: pointer; } -.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; } -.ui-draggable .ui-dialog-titlebar { cursor: move; } -/* - * jQuery UI Slider 1.8.16 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Slider#theming - */ -.ui-slider { position: relative; text-align: left; } -.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; } -.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; } - -.ui-slider-horizontal { height: .8em; } -.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; } -.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; } -.ui-slider-horizontal .ui-slider-range-min { left: 0; } -.ui-slider-horizontal .ui-slider-range-max { right: 0; } - -.ui-slider-vertical { width: .8em; height: 100px; } -.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; } -.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; } -.ui-slider-vertical .ui-slider-range-min { bottom: 0; } -.ui-slider-vertical .ui-slider-range-max { top: 0; }/* - * jQuery UI Tabs 1.8.16 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Tabs#theming - */ -.ui-tabs { position: relative; padding: .2em; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ -.ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; } -.ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; } -.ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; } -.ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; } -.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; } -.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */ -.ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; } -.ui-tabs .ui-tabs-hide { display: none !important; } -/* - * jQuery UI Datepicker 1.8.16 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Datepicker#theming - */ -.ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; } -.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; } -.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; } -.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; } -.ui-datepicker .ui-datepicker-prev { left:2px; } -.ui-datepicker .ui-datepicker-next { right:2px; } -.ui-datepicker .ui-datepicker-prev-hover { left:1px; } -.ui-datepicker .ui-datepicker-next-hover { right:1px; } -.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; } -.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; } -.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; } -.ui-datepicker select.ui-datepicker-month-year {width: 100%;} -.ui-datepicker select.ui-datepicker-month, -.ui-datepicker select.ui-datepicker-year { width: 49%;} -.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; } -.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; } -.ui-datepicker td { border: 0; padding: 1px; } -.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; } -.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; } -.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; } -.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; } - -/* with multiple calendars */ -.ui-datepicker.ui-datepicker-multi { width:auto; } -.ui-datepicker-multi .ui-datepicker-group { float:left; } -.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; } -.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; } -.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; } -.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; } -.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; } -.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; } -.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; } -.ui-datepicker-row-break { clear:both; width:100%; font-size:0em; } - -/* RTL support */ -.ui-datepicker-rtl { direction: rtl; } -.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; } -.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; } -.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; } -.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; } -.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; } -.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; } -.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; } -.ui-datepicker-rtl .ui-datepicker-group { float:right; } -.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; } -.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; } - -/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */ -.ui-datepicker-cover { - display: none; /*sorry for IE5*/ - display/**/: block; /*sorry for IE5*/ - position: absolute; /*must have*/ - z-index: -1; /*must have*/ - filter: mask(); /*must have*/ - top: -4px; /*must have*/ - left: -4px; /*must have*/ - width: 200px; /*must have*/ - height: 200px; /*must have*/ -}/* - * jQuery UI Progressbar 1.8.16 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Progressbar#theming - */ -.ui-progressbar { height:2em; text-align: left; } -.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; } \ No newline at end of file diff --git a/core/css/styles.css b/core/css/styles.css index 496320561f..19cfad7626 100644 --- a/core/css/styles.css +++ b/core/css/styles.css @@ -16,8 +16,8 @@ body { background:#fefefe; font:normal .8em/1.6em "Lucida Grande", Arial, Verdan /* HEADERS */ -#body-user #header, #body-settings #header { position:fixed; top:0; left:0; right:0; z-index:100; height:2.5em; line-height:2.5em; padding:.5em; background:#1d2d44; -moz-box-shadow:0 0 10px rgba(0, 0, 0, .5), inset 0 -2px 10px #222; -webkit-box-shadow:0 0 10px rgba(0, 0, 0, .5), inset 0 -2px 10px #222; box-shadow:0 0 10px rgba(0, 0, 0, .5), inset 0 -2px 10px #222; } -#body-login #header { margin:-2em auto 0; text-align:center; height:10em; padding:1em 0 .5em; +#body-user #header, #body-settings #header { position:fixed; top:0; left:0; right:0; z-index:100; height:45px; line-height:2.5em; background:#1d2d44; -moz-box-shadow:0 0 10px rgba(0, 0, 0, .5), inset 0 -2px 10px #222; -webkit-box-shadow:0 0 10px rgba(0, 0, 0, .5), inset 0 -2px 10px #222; box-shadow:0 0 10px rgba(0, 0, 0, .5), inset 0 -2px 10px #222; } +#body-login #header { margin: -2em auto 0; text-align:center; height:10em; padding:1em 0 .5em; -moz-box-shadow:0 0 1em rgba(0, 0, 0, .5); -webkit-box-shadow:0 0 1em rgba(0, 0, 0, .5); box-shadow:0 0 1em rgba(0, 0, 0, .5); background:#1d2d44; /* Old browsers */ background:-moz-linear-gradient(top, #35537a 0%, #1d2d42 100%); /* FF3.6+ */ @@ -28,7 +28,7 @@ background:-ms-linear-gradient(top, #35537a 0%,#1d2d42 100%); /* IE10+ */ background:linear-gradient(top, #35537a 0%,#1d2d42 100%); /* W3C */ filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#35537a', endColorstr='#1d2d42',GradientType=0 ); /* IE6-9 */ } -#owncloud { float:left; vertical-align:middle; } +#owncloud { position:absolute; top:0; left:0; padding:6px; padding-bottom:0; } .header-right { float:right; vertical-align:middle; padding:0 0.5em; } .header-right > * { vertical-align:middle; } @@ -53,17 +53,43 @@ input[type="checkbox"]:hover+label, input[type="checkbox"]:focus+label { color:# #quota { cursor:default; } +/* SCROLLING */ +::-webkit-scrollbar { width:8px; } +::-webkit-scrollbar-track-piece { background-color:transparent; } +::-webkit-scrollbar-thumb { background:#ddd; } + + +#show { float: right; position: absolute; right: 1em; top: 0.8em; display:none; } +#login form input[name="show"] + label { background: url("../img/actions/toggle.png") no-repeat; opacity:0.3; +float: right; width: 24px; position: absolute !important; height: 14px; right: 1em; top: 1.25em;} +#login form input[name="show"]:checked + label { background:url("../img/actions/toggle.png") no-repeat; opacity:0.8; } + + + +/* SHOW PASSWORD TOGGLE */ +#show { + position:absolute; right:1em; top:.8em; float:right; + display:none; +} +#login form input[name="show"] + label { + position:absolute !important; height:14px; width:24px; right:1em; top:1.25em; float:right; + background-image:url("../img/actions/toggle.png"); background-repeat:no-repeat; opacity:.3; +} +#login form input[name="show"]:checked + label { opacity:.8; } + + /* BUTTONS */ input[type="submit"], input[type="button"], button, .button, #quota, div.jp-progress, select, .pager li a { width:auto; padding:.4em; - background-color:rgba(230,230,230,.5); font-weight:bold; color:#555; text-shadow:#fff 0 1px 0; border:1px solid #bbb; border:1px solid rgba(180,180,180,.5); cursor:pointer; - -moz-box-shadow:0 1px 1px #fff, 0 1px 1px #fff inset; -webkit-box-shadow:0 1px 1px #fff, 0 1px 1px #fff inset; box-shadow:0 1px 1px #fff, 0 1px 1px #fff inset; + background-color:rgba(240,240,240,.9); font-weight:bold; color:#555; text-shadow:rgba(255,255,255,.9) 0 1px 0; border:1px solid rgba(190,190,190,.9); cursor:pointer; + -moz-box-shadow:0 1px 1px rgba(255,255,255,.9), 0 1px 1px rgba(255,255,255,.9) inset; -webkit-box-shadow:0 1px 1px rgba(255,255,255,.9), 0 1px 1px rgba(255,255,255,.9) inset; box-shadow:0 1px 1px rgba(255,255,255,.9), 0 1px 1px rgba(255,255,255,.9) inset; -moz-border-radius:.5em; -webkit-border-radius:.5em; border-radius:.5em; } input[type="submit"]:hover, input[type="submit"]:focus, input[type="button"]:hover, select:hover, select:focus, select:active, input[type="button"]:focus, .button:hover { - background:rgba(255,255,255,.5); color:#333; + background:rgba(250,250,250,.9); color:#333; } input[type="submit"] img, input[type="button"] img, button img, .button img { cursor:pointer; } +#header .button { border:none; -moz-box-shadow:none; -webkit-box-shadow:none; box-shadow:none; } /* Primary action button, use sparingly */ .primary, input[type="submit"].primary, input[type="button"].primary, button.primary, .button.primary { @@ -88,20 +114,30 @@ input[type="submit"] img, input[type="button"] img, button img, .button img { cu #body-login input[type="text"], #body-login input[type="password"] { width:13em; } #body-login input.login { width:auto; float:right; } #remember_login { margin:.8em .2em 0 1em; } -.searchbox input[type="search"] { font-size:1.2em; padding:.2em .5em .2em 1.5em; background:#fff url('../img/actions/search.svg') no-repeat .5em center; border:0; -moz-border-radius:1em; -webkit-border-radius:1em; border-radius:1em; -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=70)"; filter:alpha(opacity=70);opacity:.7; -webkit-transition:opacity 300ms; -moz-transition:opacity 300ms; -o-transition:opacity 300ms; transition:opacity 300ms; } +.searchbox input[type="search"] { font-size:1.2em; padding:.2em .5em .2em 1.5em; background:#fff url('../img/actions/search.svg') no-repeat .5em center; border:0; -moz-border-radius:1em; -webkit-border-radius:1em; border-radius:1em; -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=70)"; filter:alpha(opacity=70);opacity:.7; -webkit-transition:opacity 300ms; -moz-transition:opacity 300ms; -o-transition:opacity 300ms; transition:opacity 300ms; margin-top:10px; float:right; } input[type="submit"].enabled { background:#66f866; border:1px solid #5e5; -moz-box-shadow:0 1px 1px #f8f8f8, 0 1px 1px #cfc inset; -webkit-box-shadow:0 1px 1px #f8f8f8, 0 1px 1px #cfc inset; box-shadow:0 1px 1px #f8f8f8, 0 1px 1px #cfc inset; } #select_all{ margin-top:.4em !important;} + /* CONTENT ------------------------------------------------------------------ */ #controls { padding:0 0.5em; width:100%; top:3.5em; height:2.8em; margin:0; background:#f7f7f7; border-bottom:1px solid #eee; position:fixed; z-index:50; -moz-box-shadow:0 -3px 7px #000; -webkit-box-shadow:0 -3px 7px #000; box-shadow:0 -3px 7px #000; } #controls .button { display:inline-block; } -#content { top:3.5em; left:12.5em; position:absolute; } -#leftcontent, .leftcontent { position:fixed; overflow:auto; top:6.4em; width:20em; background:#f8f8f8; border-right:1px solid #ddd; } + +#content { position:relative; height:100%; width:100%; } +#content-wrapper { + position:absolute; height:100%; width:100%; padding-top:3.5em; padding-left:64px; + -moz-box-sizing:border-box; box-sizing:border-box; +} +#leftcontent, .leftcontent { + position:fixed; overflow:auto; top:0; width:20em; height:100%; + background:#f8f8f8; border-right:1px solid #ddd; + -moz-box-sizing:border-box; box-sizing:border-box; padding-top:6.4em; +} #leftcontent li, .leftcontent li { background:#f8f8f8; padding:.5em .8em; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; -webkit-transition:background-color 200ms; -moz-transition:background-color 200ms; -o-transition:background-color 200ms; transition:background-color 200ms; } #leftcontent li:hover, #leftcontent li:active, #leftcontent li.active, .leftcontent li:hover, .leftcontent li:active, .leftcontent li.active { background:#eee; } #leftcontent li.active, .leftcontent li.active { font-weight:bold; } #leftcontent li:hover, .leftcontent li:hover { color:#333; background:#ddd; } #leftcontent a { height:100%; display:block; margin:0; padding:0 1em 0 0; float:left; } -#rightcontent, .rightcontent { position:fixed; top:6.4em; left:32.5em; overflow:auto } +#rightcontent, .rightcontent { position:fixed; top:6.4em; left:24.5em; overflow:auto } /* LOG IN & INSTALLATION ------------------------------------------------------------ */ @@ -125,12 +161,13 @@ input[type="submit"].enabled { background:#66f866; border:1px solid #5e5; -moz-b /* Icons for username and password fields to better recognize them */ #adminlogin, #adminpass, #user, #password { width:11.7em!important; padding-left:1.8em; } -#adminlogin+label, #adminpass+label, #user+label, #password+label { left:2.2em; } #adminlogin+label+img, #adminpass+label+img, #user+label+img, #password+label+img { position:absolute; left:1.25em; top:1.65em; opacity:.3; } #adminpass+label+img, #password+label+img { top:1.1em; } +input[name="password-clone"] { padding-left:1.8em; width:11.7em !important; } +#pass_image { position: absolute; top: 1.2em; left: 1.4em; opacity: 0.3; } /* Nicely grouping input field sets */ @@ -154,7 +191,7 @@ input[type="submit"].enabled { background:#66f866; border:1px solid #5e5; -moz-b /* NEEDED FOR INFIELD LABELS */ p.infield { position:relative; } label.infield { cursor:text !important; top:1.05em; left:.85em; } -#login form label.infield { position:absolute; font-size:19px; color:#aaa; white-space:nowrap; } +#login form label.infield { position:absolute; font-size:19px; color:#aaa; white-space:nowrap; padding-left:1.2em; } #login form input[type="checkbox"]+label { position:relative; margin:0; font-size:1em; text-shadow:#fff 0 1px 0; } #login form .errors { background:#fed7d7; border:1px solid #f00; list-style-indent:inside; margin:0 0 2em; padding:1em; } @@ -176,25 +213,49 @@ fieldset.warning legend { color:#b94a48 !important; } /* NAVIGATION ------------------------------------------------------------- */ -#navigation { position:fixed; top:3.5em; float:left; width:12.5em; padding:0; z-index:75; height:100%; background:#eee; border-right:1px #ccc solid; -moz-box-shadow:-3px 0 7px #000; -webkit-box-shadow:-3px 0 7px #000; box-shadow:-3px 0 7px #000; overflow:hidden;} -#navigation a { display:block; padding:.6em .5em .4em 2.5em; background:#eee 1em center no-repeat; border-bottom:1px solid #ddd; border-top:1px solid #fff; text-decoration:none; font-size:1.2em; color:#666; text-shadow:#f8f8f8 0 1px 0; } -#navigation a.active, #navigation a:hover, #navigation a:focus { background-color:#dbdbdb; border-top:1px solid #d4d4d4; border-bottom:1px solid #ccc; color:#333; } -#navigation a.active { background-color:#ddd; } -#navigation #settings { position:absolute; bottom:3.5em; width:100%; } -#expand { position:relative; z-index:100; margin-bottom:-.5em; padding:.5em 10.1em .7em 1.2em; cursor:pointer; } -#expand+span { position:absolute; z-index:99; margin:-1.7em 0 0 2.5em; font-size:1.2em; color:#666; text-shadow:#f8f8f8 0 1px 0; -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; filter:alpha(opacity=0); opacity:0; -webkit-transition:opacity 300ms; -moz-transition:opacity 300ms; -o-transition:opacity 300ms; transition:opacity 300ms; } -#expand:hover+span, #expand+span:hover { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; filter:alpha(opacity=100); opacity:1; cursor:pointer; } +#navigation { + position:fixed; top:3.5em; float:left; width:64px; padding:0; z-index:75; height:100%; + background:#30343a url('../img/noise.png') repeat; border-right:1px #333 solid; + -moz-box-shadow:0 0 7px #000; -webkit-box-shadow:0 0 7px #000; box-shadow:0 0 7px #000; + overflow-x:scroll; +} +#navigation a { + display:block; padding:8px 0 4px; + text-decoration:none; font-size:10px; text-align:center; + color:#fff; text-shadow:#000 0 -1px 0; opacity:.4; + white-space:nowrap; overflow:hidden; text-overflow:ellipsis; // ellipsize long app names +} + #navigation a:hover, #navigation a:focus { opacity:.8; } + #navigation a.active { opacity:1; } + #navigation .icon { display:block; width:32px; height:32px; margin:0 16px 0; } + #navigation .enabled-app:hover, #navigation .enabled-app:focus { opacity:1; } + #navigation .enabled-app img { opacity:0.3; cursor:pointer;} + #navigation .enabled-app a {padding:4px 0 4px; } + #navigation .enabled-app:hover a, #navigation .enabled-app:focus a {opacity:0.8; } + #navigation .enabled-app:hover img, #navigation .enabled-app:focus img {opacity:0.8; } + #navigation li:first-child a { padding-top:16px; } +#settings { float:right; margin-top:7px; color:#bbb; text-shadow:0 -1px 0 #000; } +#expand { padding:15px; cursor:pointer; font-weight:bold; } +#expand:hover, #expand:focus, #expand:active { color:#fff; } +#expand img { opacity:.7; margin-bottom:-2px; } +#expand:hover img, #expand:focus img, #expand:active img { opacity:1; } +#expanddiv { position:absolute; right:0; top:45px; background-color:#444; border-bottom-left-radius:7px; box-shadow: 0 0 20px rgb(29,45,68); z-index:76; } + #expanddiv a { display:block; color:#fff; text-shadow:0 -1px 0 #000; padding:0 8px; opacity:.7; } + #expanddiv a img { margin-bottom:-3px; } + #expanddiv a:hover, #expanddiv a:focus, #expanddiv a:active { opacity:1; } + /* VARIOUS REUSABLE SELECTORS */ .hidden { display:none; } .bold { font-weight:bold; } .center { text-align:center; } -#notification { z-index:101; background-color:#fc4; border:0; padding:0 .7em .3em; display:none; position:fixed; left:50%; top:0; -moz-border-radius-bottomleft:1em; -webkit-border-bottom-left-radius:1em; border-bottom-left-radius:1em; -moz-border-radius-bottomright:1em; -webkit-border-bottom-right-radius:1em; border-bottom-right-radius:1em; } +#notification-container { position: fixed; top: 0px; width: 100%; text-align: center; z-index: 101; line-height: 1.2;} +#notification { z-index:101; background-color:#fc4; border:0; padding:0 .7em .3em; display:none; position: relative; top:0; -moz-border-radius-bottomleft:1em; -webkit-border-bottom-left-radius:1em; border-bottom-left-radius:1em; -moz-border-radius-bottomright:1em; -webkit-border-bottom-right-radius:1em; border-bottom-right-radius:1em; } #notification span { cursor:pointer; font-weight:bold; margin-left:1em; } -tr .action, .selectedActions a { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; filter:alpha(opacity=0); opacity:0; } -tr:hover .action, .selectedActions a { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; filter:alpha(opacity=50); opacity:.5; } +tr .action:not(.permanent), .selectedActions a { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; filter:alpha(opacity=0); opacity:0; } +tr:hover .action, tr .action.permanent, .selectedActions a { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; filter:alpha(opacity=50); opacity:.5; } tr .action { width:16px; height:16px; } .header-action { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=80)"; filter:alpha(opacity=80); opacity:.8; } tr:hover .action:hover, .selectedActions a:hover, .header-action:hover { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; filter:alpha(opacity=100); opacity:1; } @@ -257,6 +318,7 @@ a.bookmarklet { background-color:#ddd; border:1px solid #ccc; padding:5px;paddin .arrow.down { -webkit-transform:rotate(180deg); -moz-transform:rotate(180deg); -o-transform:rotate(180deg); -ms-transform:rotate(180deg); transform:rotate(180deg); } /* ---- BREADCRUMB ---- */ -div.crumb { float:left; display:block; background:no-repeat right 0; padding:.75em 1.5em 0 1em; height:2.9em; } -div.crumb:first-child { padding-left:1em; } -div.crumb.last { font-weight:bold; } +div.crumb { float:left; display:block; background:url('../img/breadcrumb.svg') no-repeat right 0; padding:.75em 1.5em 0 1em; height:2.9em; } +div.crumb:first-child { padding:10px 20px 10px 5px; } +div.crumb.last { font-weight:bold; background:none; padding-right:10px; } +div.crumb a{ padding: 0.9em 0 0.7em 0; } diff --git a/core/img/actions/caret.png b/core/img/actions/caret.png new file mode 100644 index 0000000000..e0ae969a94 Binary files /dev/null and b/core/img/actions/caret.png differ diff --git a/core/img/actions/caret.svg b/core/img/actions/caret.svg new file mode 100644 index 0000000000..7bb0c59cde --- /dev/null +++ b/core/img/actions/caret.svg @@ -0,0 +1,112 @@ + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/core/img/actions/logout.png b/core/img/actions/logout.png index 37f62543ac..e2f4b7af12 100644 Binary files a/core/img/actions/logout.png and b/core/img/actions/logout.png differ diff --git a/core/img/actions/logout.svg b/core/img/actions/logout.svg index 0281fad43e..e5edc24895 100644 --- a/core/img/actions/logout.svg +++ b/core/img/actions/logout.svg @@ -33,7 +33,7 @@ id="namedview3047" showgrid="false" inkscape:zoom="25.279067" - inkscape:cx="5.0341304" + inkscape:cx="-1.6512429" inkscape:cy="6.4537904" inkscape:window-x="0" inkscape:window-y="27" @@ -47,7 +47,7 @@ image/svg+xml - + @@ -163,15 +163,16 @@ x2="8.4964771" y2="15.216674" /> + - diff --git a/core/img/actions/toggle.png b/core/img/actions/toggle.png new file mode 100644 index 0000000000..6ef3f2227b Binary files /dev/null and b/core/img/actions/toggle.png differ diff --git a/core/img/actions/toggle.svg b/core/img/actions/toggle.svg new file mode 100644 index 0000000000..82a5171477 --- /dev/null +++ b/core/img/actions/toggle.svg @@ -0,0 +1,61 @@ + + + +image/svg+xml + + + + + \ No newline at end of file diff --git a/core/img/actions/undelete.png b/core/img/actions/undelete.png new file mode 100644 index 0000000000..d712527ef6 Binary files /dev/null and b/core/img/actions/undelete.png differ diff --git a/core/img/noise.png b/core/img/noise.png new file mode 100644 index 0000000000..8fdda17b5e Binary files /dev/null and b/core/img/noise.png differ diff --git a/core/img/places/files.png b/core/img/places/files.png new file mode 100644 index 0000000000..9c7ff2642f Binary files /dev/null and b/core/img/places/files.png differ diff --git a/core/img/places/files.svg b/core/img/places/files.svg new file mode 100644 index 0000000000..8ebf861f6d --- /dev/null +++ b/core/img/places/files.svg @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/core/img/places/home.png b/core/img/places/home.png index c3dbd3e353..2945b84e86 100644 Binary files a/core/img/places/home.png and b/core/img/places/home.png differ diff --git a/core/img/places/home.svg b/core/img/places/home.svg index 4b45ef12bc..a836a5999f 100644 --- a/core/img/places/home.svg +++ b/core/img/places/home.svg @@ -14,9 +14,9 @@ width="16" height="16" id="svg11300" - inkscape:version="0.48.1 r9760" - sodipodi:docname="help.svg" - inkscape:export-filename="/home/jancborchardt/jancborchardt/ownCloud/icons/help.png" + inkscape:version="0.48.3.1 r9886" + sodipodi:docname="home.svg" + inkscape:export-filename="home.png" inkscape:export-xdpi="90" inkscape:export-ydpi="90"> image/svg+xml - + @@ -41,16 +41,16 @@ inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-width="1280" - inkscape:window-height="776" + inkscape:window-height="773" id="namedview24" showgrid="true" showguides="true" inkscape:guide-bbox="true" - inkscape:zoom="22.627418" - inkscape:cx="14.025105" - inkscape:cy="9.2202448" + inkscape:zoom="16.000001" + inkscape:cx="2.7409248" + inkscape:cy="8.4568105" inkscape:window-x="0" - inkscape:window-y="24" + inkscape:window-y="-1" inkscape:window-maximized="1" inkscape:current-layer="g4146"> + + + + + + + + - - - - + diff --git a/core/img/places/music.png b/core/img/places/music.png index 85ee2474cd..5b71e19ee3 100644 Binary files a/core/img/places/music.png and b/core/img/places/music.png differ diff --git a/core/img/places/music.svg b/core/img/places/music.svg index 1f39766097..e8f91f4616 100644 --- a/core/img/places/music.svg +++ b/core/img/places/music.svg @@ -7,1664 +7,67 @@ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - version="1.0" - width="16" - height="16" - id="svg11300" - inkscape:version="0.48.1 r9760" - sodipodi:docname="search.svg" - inkscape:export-filename="/home/jancborchardt/jancborchardt/ownCloud/icons/search.png" + width="32" + height="32" + id="svg4375" + version="1.1" + inkscape:version="0.48.3.1 r9886" + sodipodi:docname="music.svg" + inkscape:export-filename="music.png" inkscape:export-xdpi="90" inkscape:export-ydpi="90"> + + + id="metadata4380"> image/svg+xml - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(581.71429,-2.0764682)"> + style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /> diff --git a/core/img/places/picture.png b/core/img/places/picture.png index 9abcd09722..a278240a6d 100644 Binary files a/core/img/places/picture.png and b/core/img/places/picture.png differ diff --git a/core/img/places/picture.svg b/core/img/places/picture.svg index 26c3d6312c..aba68e6206 100644 --- a/core/img/places/picture.svg +++ b/core/img/places/picture.svg @@ -7,1691 +7,69 @@ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - version="1.0" - width="16" - height="16" - id="svg11300" - inkscape:version="0.48.1 r9760" - sodipodi:docname="audio.svg" - inkscape:export-filename="/home/jancborchardt/jancborchardt/ownCloud/icons/audio.png" + width="32" + height="32" + id="svg4375" + version="1.1" + inkscape:version="0.48.3.1 r9886" + sodipodi:docname="picture.svg" + inkscape:export-filename="picture.png" inkscape:export-xdpi="90" inkscape:export-ydpi="90"> + + + id="metadata4380"> image/svg+xml - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(581.71429,-2.0764682)"> + + diff --git a/core/js/config.php b/core/js/config.php new file mode 100644 index 0000000000..9069175ed6 --- /dev/null +++ b/core/js/config.php @@ -0,0 +1,41 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +// Set the content type to Javascript +header("Content-type: text/javascript"); + +// Disallow caching +header("Cache-Control: no-cache, must-revalidate"); +header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); + +// Enable l10n support +$l = OC_L10N::get('core'); + +// Get the config +$apps_paths = array(); +foreach(OC_App::getEnabledApps() as $app) { + $apps_paths[$app] = OC_App::getAppWebPath($app); +} + +$array = array( + "oc_debug" => (defined('DEBUG') && DEBUG) ? 'true' : 'false', + "oc_webroot" => "\"".OC::$WEBROOT."\"", + "oc_appswebroots" => str_replace('\\/', '/', json_encode($apps_paths)), // Ugly unescape slashes waiting for better solution + "oc_current_user" => "\"".OC_User::getUser(). "\"", + "oc_requesttoken" => "\"".OC_Util::callRegister(). "\"", + "datepickerFormatDate" => json_encode($l->l('jsdate', 'jsdate')), + "dayNames" => json_encode(array((string)$l->t('Sunday'), (string)$l->t('Monday'), (string)$l->t('Tuesday'), (string)$l->t('Wednesday'), (string)$l->t('Thursday'), (string)$l->t('Friday'), (string)$l->t('Saturday'))), + "monthNames" => json_encode(array((string)$l->t('January'), (string)$l->t('February'), (string)$l->t('March'), (string)$l->t('April'), (string)$l->t('May'), (string)$l->t('June'), (string)$l->t('July'), (string)$l->t('August'), (string)$l->t('September'), (string)$l->t('October'), (string)$l->t('November'), (string)$l->t('December'))), + "firstDay" => json_encode($l->l('firstday', 'firstday')) , + ); + +// Echo it +foreach ($array as $setting => $value) { + echo("var ". $setting ."=".$value.";\n"); +} +?> \ No newline at end of file diff --git a/core/js/eventsource.js b/core/js/eventsource.js index 0c2a995f33..f783ade7ae 100644 --- a/core/js/eventsource.js +++ b/core/js/eventsource.js @@ -40,7 +40,7 @@ OC.EventSource=function(src,data){ dataStr+=name+'='+encodeURIComponent(data[name])+'&'; } } - dataStr+='requesttoken='+OC.EventSource.requesttoken; + dataStr+='requesttoken='+oc_requesttoken; if(!this.useFallBack && typeof EventSource !='undefined'){ var joinChar = '&'; if(src.indexOf('?') == -1) { diff --git a/core/js/jquery-ui-1.10.0.custom.js b/core/js/jquery-ui-1.10.0.custom.js new file mode 100644 index 0000000000..ca57f47ede --- /dev/null +++ b/core/js/jquery-ui-1.10.0.custom.js @@ -0,0 +1,14850 @@ +/*! jQuery UI - v1.10.0 - 2013-01-22 +* http://jqueryui.com +* Includes: jquery.ui.core.js, jquery.ui.widget.js, jquery.ui.mouse.js, jquery.ui.position.js, jquery.ui.draggable.js, jquery.ui.droppable.js, jquery.ui.resizable.js, jquery.ui.selectable.js, jquery.ui.sortable.js, jquery.ui.accordion.js, jquery.ui.autocomplete.js, jquery.ui.button.js, jquery.ui.datepicker.js, jquery.ui.dialog.js, jquery.ui.menu.js, jquery.ui.progressbar.js, jquery.ui.slider.js, jquery.ui.spinner.js, jquery.ui.tabs.js, jquery.ui.tooltip.js, jquery.ui.effect.js, jquery.ui.effect-blind.js, jquery.ui.effect-bounce.js, jquery.ui.effect-clip.js, jquery.ui.effect-drop.js, jquery.ui.effect-explode.js, jquery.ui.effect-fade.js, jquery.ui.effect-fold.js, jquery.ui.effect-highlight.js, jquery.ui.effect-pulsate.js, jquery.ui.effect-scale.js, jquery.ui.effect-shake.js, jquery.ui.effect-slide.js, jquery.ui.effect-transfer.js +* Copyright (c) 2013 jQuery Foundation and other contributors Licensed MIT */ + +(function( $, undefined ) { + +var uuid = 0, + runiqueId = /^ui-id-\d+$/; + +// prevent duplicate loading +// this is only a problem because we proxy existing functions +// and we don't want to double proxy them +$.ui = $.ui || {}; +if ( $.ui.version ) { + return; +} + +$.extend( $.ui, { + version: "1.10.0", + + keyCode: { + BACKSPACE: 8, + COMMA: 188, + DELETE: 46, + DOWN: 40, + END: 35, + ENTER: 13, + ESCAPE: 27, + HOME: 36, + LEFT: 37, + NUMPAD_ADD: 107, + NUMPAD_DECIMAL: 110, + NUMPAD_DIVIDE: 111, + NUMPAD_ENTER: 108, + NUMPAD_MULTIPLY: 106, + NUMPAD_SUBTRACT: 109, + PAGE_DOWN: 34, + PAGE_UP: 33, + PERIOD: 190, + RIGHT: 39, + SPACE: 32, + TAB: 9, + UP: 38 + } +}); + +// plugins +$.fn.extend({ + _focus: $.fn.focus, + focus: function( delay, fn ) { + return typeof delay === "number" ? + this.each(function() { + var elem = this; + setTimeout(function() { + $( elem ).focus(); + if ( fn ) { + fn.call( elem ); + } + }, delay ); + }) : + this._focus.apply( this, arguments ); + }, + + scrollParent: function() { + var scrollParent; + if (($.ui.ie && (/(static|relative)/).test(this.css("position"))) || (/absolute/).test(this.css("position"))) { + scrollParent = this.parents().filter(function() { + return (/(relative|absolute|fixed)/).test($.css(this,"position")) && (/(auto|scroll)/).test($.css(this,"overflow")+$.css(this,"overflow-y")+$.css(this,"overflow-x")); + }).eq(0); + } else { + scrollParent = this.parents().filter(function() { + return (/(auto|scroll)/).test($.css(this,"overflow")+$.css(this,"overflow-y")+$.css(this,"overflow-x")); + }).eq(0); + } + + return (/fixed/).test(this.css("position")) || !scrollParent.length ? $(document) : scrollParent; + }, + + zIndex: function( zIndex ) { + if ( zIndex !== undefined ) { + return this.css( "zIndex", zIndex ); + } + + if ( this.length ) { + var elem = $( this[ 0 ] ), position, value; + while ( elem.length && elem[ 0 ] !== document ) { + // Ignore z-index if position is set to a value where z-index is ignored by the browser + // This makes behavior of this function consistent across browsers + // WebKit always returns auto if the element is positioned + position = elem.css( "position" ); + if ( position === "absolute" || position === "relative" || position === "fixed" ) { + // IE returns 0 when zIndex is not specified + // other browsers return a string + // we ignore the case of nested elements with an explicit value of 0 + //
+ value = parseInt( elem.css( "zIndex" ), 10 ); + if ( !isNaN( value ) && value !== 0 ) { + return value; + } + } + elem = elem.parent(); + } + } + + return 0; + }, + + uniqueId: function() { + return this.each(function() { + if ( !this.id ) { + this.id = "ui-id-" + (++uuid); + } + }); + }, + + removeUniqueId: function() { + return this.each(function() { + if ( runiqueId.test( this.id ) ) { + $( this ).removeAttr( "id" ); + } + }); + } +}); + +// selectors +function focusable( element, isTabIndexNotNaN ) { + var map, mapName, img, + nodeName = element.nodeName.toLowerCase(); + if ( "area" === nodeName ) { + map = element.parentNode; + mapName = map.name; + if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) { + return false; + } + img = $( "img[usemap=#" + mapName + "]" )[0]; + return !!img && visible( img ); + } + return ( /input|select|textarea|button|object/.test( nodeName ) ? + !element.disabled : + "a" === nodeName ? + element.href || isTabIndexNotNaN : + isTabIndexNotNaN) && + // the element and all of its ancestors must be visible + visible( element ); +} + +function visible( element ) { + return $.expr.filters.visible( element ) && + !$( element ).parents().addBack().filter(function() { + return $.css( this, "visibility" ) === "hidden"; + }).length; +} + +$.extend( $.expr[ ":" ], { + data: $.expr.createPseudo ? + $.expr.createPseudo(function( dataName ) { + return function( elem ) { + return !!$.data( elem, dataName ); + }; + }) : + // support: jQuery <1.8 + function( elem, i, match ) { + return !!$.data( elem, match[ 3 ] ); + }, + + focusable: function( element ) { + return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) ); + }, + + tabbable: function( element ) { + var tabIndex = $.attr( element, "tabindex" ), + isTabIndexNaN = isNaN( tabIndex ); + return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN ); + } +}); + +// support: jQuery <1.8 +if ( !$( "" ).outerWidth( 1 ).jquery ) { + $.each( [ "Width", "Height" ], function( i, name ) { + var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ], + type = name.toLowerCase(), + orig = { + innerWidth: $.fn.innerWidth, + innerHeight: $.fn.innerHeight, + outerWidth: $.fn.outerWidth, + outerHeight: $.fn.outerHeight + }; + + function reduce( elem, size, border, margin ) { + $.each( side, function() { + size -= parseFloat( $.css( elem, "padding" + this ) ) || 0; + if ( border ) { + size -= parseFloat( $.css( elem, "border" + this + "Width" ) ) || 0; + } + if ( margin ) { + size -= parseFloat( $.css( elem, "margin" + this ) ) || 0; + } + }); + return size; + } + + $.fn[ "inner" + name ] = function( size ) { + if ( size === undefined ) { + return orig[ "inner" + name ].call( this ); + } + + return this.each(function() { + $( this ).css( type, reduce( this, size ) + "px" ); + }); + }; + + $.fn[ "outer" + name] = function( size, margin ) { + if ( typeof size !== "number" ) { + return orig[ "outer" + name ].call( this, size ); + } + + return this.each(function() { + $( this).css( type, reduce( this, size, true, margin ) + "px" ); + }); + }; + }); +} + +// support: jQuery <1.8 +if ( !$.fn.addBack ) { + $.fn.addBack = function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter( selector ) + ); + }; +} + +// support: jQuery 1.6.1, 1.6.2 (http://bugs.jquery.com/ticket/9413) +if ( $( "" ).data( "a-b", "a" ).removeData( "a-b" ).data( "a-b" ) ) { + $.fn.removeData = (function( removeData ) { + return function( key ) { + if ( arguments.length ) { + return removeData.call( this, $.camelCase( key ) ); + } else { + return removeData.call( this ); + } + }; + })( $.fn.removeData ); +} + + + + + +// deprecated +$.ui.ie = !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() ); + +$.support.selectstart = "onselectstart" in document.createElement( "div" ); +$.fn.extend({ + disableSelection: function() { + return this.bind( ( $.support.selectstart ? "selectstart" : "mousedown" ) + + ".ui-disableSelection", function( event ) { + event.preventDefault(); + }); + }, + + enableSelection: function() { + return this.unbind( ".ui-disableSelection" ); + } +}); + +$.extend( $.ui, { + // $.ui.plugin is deprecated. Use the proxy pattern instead. + plugin: { + add: function( module, option, set ) { + var i, + proto = $.ui[ module ].prototype; + for ( i in set ) { + proto.plugins[ i ] = proto.plugins[ i ] || []; + proto.plugins[ i ].push( [ option, set[ i ] ] ); + } + }, + call: function( instance, name, args ) { + var i, + set = instance.plugins[ name ]; + if ( !set || !instance.element[ 0 ].parentNode || instance.element[ 0 ].parentNode.nodeType === 11 ) { + return; + } + + for ( i = 0; i < set.length; i++ ) { + if ( instance.options[ set[ i ][ 0 ] ] ) { + set[ i ][ 1 ].apply( instance.element, args ); + } + } + } + }, + + // only used by resizable + hasScroll: function( el, a ) { + + //If overflow is hidden, the element might have extra content, but the user wants to hide it + if ( $( el ).css( "overflow" ) === "hidden") { + return false; + } + + var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop", + has = false; + + if ( el[ scroll ] > 0 ) { + return true; + } + + // TODO: determine which cases actually cause this to happen + // if the element doesn't have the scroll set, see if it's possible to + // set the scroll + el[ scroll ] = 1; + has = ( el[ scroll ] > 0 ); + el[ scroll ] = 0; + return has; + } +}); + +})( jQuery ); +(function( $, undefined ) { + +var uuid = 0, + slice = Array.prototype.slice, + _cleanData = $.cleanData; +$.cleanData = function( elems ) { + for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) { + try { + $( elem ).triggerHandler( "remove" ); + // http://bugs.jquery.com/ticket/8235 + } catch( e ) {} + } + _cleanData( elems ); +}; + +$.widget = function( name, base, prototype ) { + var fullName, existingConstructor, constructor, basePrototype, + // proxiedPrototype allows the provided prototype to remain unmodified + // so that it can be used as a mixin for multiple widgets (#8876) + proxiedPrototype = {}, + namespace = name.split( "." )[ 0 ]; + + name = name.split( "." )[ 1 ]; + fullName = namespace + "-" + name; + + if ( !prototype ) { + prototype = base; + base = $.Widget; + } + + // create selector for plugin + $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) { + return !!$.data( elem, fullName ); + }; + + $[ namespace ] = $[ namespace ] || {}; + existingConstructor = $[ namespace ][ name ]; + constructor = $[ namespace ][ name ] = function( options, element ) { + // allow instantiation without "new" keyword + if ( !this._createWidget ) { + return new constructor( options, element ); + } + + // allow instantiation without initializing for simple inheritance + // must use "new" keyword (the code above always passes args) + if ( arguments.length ) { + this._createWidget( options, element ); + } + }; + // extend with the existing constructor to carry over any static properties + $.extend( constructor, existingConstructor, { + version: prototype.version, + // copy the object used to create the prototype in case we need to + // redefine the widget later + _proto: $.extend( {}, prototype ), + // track widgets that inherit from this widget in case this widget is + // redefined after a widget inherits from it + _childConstructors: [] + }); + + basePrototype = new base(); + // we need to make the options hash a property directly on the new instance + // otherwise we'll modify the options hash on the prototype that we're + // inheriting from + basePrototype.options = $.widget.extend( {}, basePrototype.options ); + $.each( prototype, function( prop, value ) { + if ( !$.isFunction( value ) ) { + proxiedPrototype[ prop ] = value; + return; + } + proxiedPrototype[ prop ] = (function() { + var _super = function() { + return base.prototype[ prop ].apply( this, arguments ); + }, + _superApply = function( args ) { + return base.prototype[ prop ].apply( this, args ); + }; + return function() { + var __super = this._super, + __superApply = this._superApply, + returnValue; + + this._super = _super; + this._superApply = _superApply; + + returnValue = value.apply( this, arguments ); + + this._super = __super; + this._superApply = __superApply; + + return returnValue; + }; + })(); + }); + constructor.prototype = $.widget.extend( basePrototype, { + // TODO: remove support for widgetEventPrefix + // always use the name + a colon as the prefix, e.g., draggable:start + // don't prefix for widgets that aren't DOM-based + widgetEventPrefix: existingConstructor ? basePrototype.widgetEventPrefix : name + }, proxiedPrototype, { + constructor: constructor, + namespace: namespace, + widgetName: name, + widgetFullName: fullName + }); + + // If this widget is being redefined then we need to find all widgets that + // are inheriting from it and redefine all of them so that they inherit from + // the new version of this widget. We're essentially trying to replace one + // level in the prototype chain. + if ( existingConstructor ) { + $.each( existingConstructor._childConstructors, function( i, child ) { + var childPrototype = child.prototype; + + // redefine the child widget using the same prototype that was + // originally used, but inherit from the new version of the base + $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto ); + }); + // remove the list of existing child constructors from the old constructor + // so the old child constructors can be garbage collected + delete existingConstructor._childConstructors; + } else { + base._childConstructors.push( constructor ); + } + + $.widget.bridge( name, constructor ); +}; + +$.widget.extend = function( target ) { + var input = slice.call( arguments, 1 ), + inputIndex = 0, + inputLength = input.length, + key, + value; + for ( ; inputIndex < inputLength; inputIndex++ ) { + for ( key in input[ inputIndex ] ) { + value = input[ inputIndex ][ key ]; + if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) { + // Clone objects + if ( $.isPlainObject( value ) ) { + target[ key ] = $.isPlainObject( target[ key ] ) ? + $.widget.extend( {}, target[ key ], value ) : + // Don't extend strings, arrays, etc. with objects + $.widget.extend( {}, value ); + // Copy everything else by reference + } else { + target[ key ] = value; + } + } + } + } + return target; +}; + +$.widget.bridge = function( name, object ) { + var fullName = object.prototype.widgetFullName || name; + $.fn[ name ] = function( options ) { + var isMethodCall = typeof options === "string", + args = slice.call( arguments, 1 ), + returnValue = this; + + // allow multiple hashes to be passed on init + options = !isMethodCall && args.length ? + $.widget.extend.apply( null, [ options ].concat(args) ) : + options; + + if ( isMethodCall ) { + this.each(function() { + var methodValue, + instance = $.data( this, fullName ); + if ( !instance ) { + return $.error( "cannot call methods on " + name + " prior to initialization; " + + "attempted to call method '" + options + "'" ); + } + if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) { + return $.error( "no such method '" + options + "' for " + name + " widget instance" ); + } + methodValue = instance[ options ].apply( instance, args ); + if ( methodValue !== instance && methodValue !== undefined ) { + returnValue = methodValue && methodValue.jquery ? + returnValue.pushStack( methodValue.get() ) : + methodValue; + return false; + } + }); + } else { + this.each(function() { + var instance = $.data( this, fullName ); + if ( instance ) { + instance.option( options || {} )._init(); + } else { + $.data( this, fullName, new object( options, this ) ); + } + }); + } + + return returnValue; + }; +}; + +$.Widget = function( /* options, element */ ) {}; +$.Widget._childConstructors = []; + +$.Widget.prototype = { + widgetName: "widget", + widgetEventPrefix: "", + defaultElement: "
", + options: { + disabled: false, + + // callbacks + create: null + }, + _createWidget: function( options, element ) { + element = $( element || this.defaultElement || this )[ 0 ]; + this.element = $( element ); + this.uuid = uuid++; + this.eventNamespace = "." + this.widgetName + this.uuid; + this.options = $.widget.extend( {}, + this.options, + this._getCreateOptions(), + options ); + + this.bindings = $(); + this.hoverable = $(); + this.focusable = $(); + + if ( element !== this ) { + $.data( element, this.widgetFullName, this ); + this._on( true, this.element, { + remove: function( event ) { + if ( event.target === element ) { + this.destroy(); + } + } + }); + this.document = $( element.style ? + // element within the document + element.ownerDocument : + // element is window or document + element.document || element ); + this.window = $( this.document[0].defaultView || this.document[0].parentWindow ); + } + + this._create(); + this._trigger( "create", null, this._getCreateEventData() ); + this._init(); + }, + _getCreateOptions: $.noop, + _getCreateEventData: $.noop, + _create: $.noop, + _init: $.noop, + + destroy: function() { + this._destroy(); + // we can probably remove the unbind calls in 2.0 + // all event bindings should go through this._on() + this.element + .unbind( this.eventNamespace ) + // 1.9 BC for #7810 + // TODO remove dual storage + .removeData( this.widgetName ) + .removeData( this.widgetFullName ) + // support: jquery <1.6.3 + // http://bugs.jquery.com/ticket/9413 + .removeData( $.camelCase( this.widgetFullName ) ); + this.widget() + .unbind( this.eventNamespace ) + .removeAttr( "aria-disabled" ) + .removeClass( + this.widgetFullName + "-disabled " + + "ui-state-disabled" ); + + // clean up events and states + this.bindings.unbind( this.eventNamespace ); + this.hoverable.removeClass( "ui-state-hover" ); + this.focusable.removeClass( "ui-state-focus" ); + }, + _destroy: $.noop, + + widget: function() { + return this.element; + }, + + option: function( key, value ) { + var options = key, + parts, + curOption, + i; + + if ( arguments.length === 0 ) { + // don't return a reference to the internal hash + return $.widget.extend( {}, this.options ); + } + + if ( typeof key === "string" ) { + // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } } + options = {}; + parts = key.split( "." ); + key = parts.shift(); + if ( parts.length ) { + curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] ); + for ( i = 0; i < parts.length - 1; i++ ) { + curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {}; + curOption = curOption[ parts[ i ] ]; + } + key = parts.pop(); + if ( value === undefined ) { + return curOption[ key ] === undefined ? null : curOption[ key ]; + } + curOption[ key ] = value; + } else { + if ( value === undefined ) { + return this.options[ key ] === undefined ? null : this.options[ key ]; + } + options[ key ] = value; + } + } + + this._setOptions( options ); + + return this; + }, + _setOptions: function( options ) { + var key; + + for ( key in options ) { + this._setOption( key, options[ key ] ); + } + + return this; + }, + _setOption: function( key, value ) { + this.options[ key ] = value; + + if ( key === "disabled" ) { + this.widget() + .toggleClass( this.widgetFullName + "-disabled ui-state-disabled", !!value ) + .attr( "aria-disabled", value ); + this.hoverable.removeClass( "ui-state-hover" ); + this.focusable.removeClass( "ui-state-focus" ); + } + + return this; + }, + + enable: function() { + return this._setOption( "disabled", false ); + }, + disable: function() { + return this._setOption( "disabled", true ); + }, + + _on: function( suppressDisabledCheck, element, handlers ) { + var delegateElement, + instance = this; + + // no suppressDisabledCheck flag, shuffle arguments + if ( typeof suppressDisabledCheck !== "boolean" ) { + handlers = element; + element = suppressDisabledCheck; + suppressDisabledCheck = false; + } + + // no element argument, shuffle and use this.element + if ( !handlers ) { + handlers = element; + element = this.element; + delegateElement = this.widget(); + } else { + // accept selectors, DOM elements + element = delegateElement = $( element ); + this.bindings = this.bindings.add( element ); + } + + $.each( handlers, function( event, handler ) { + function handlerProxy() { + // allow widgets to customize the disabled handling + // - disabled as an array instead of boolean + // - disabled class as method for disabling individual parts + if ( !suppressDisabledCheck && + ( instance.options.disabled === true || + $( this ).hasClass( "ui-state-disabled" ) ) ) { + return; + } + return ( typeof handler === "string" ? instance[ handler ] : handler ) + .apply( instance, arguments ); + } + + // copy the guid so direct unbinding works + if ( typeof handler !== "string" ) { + handlerProxy.guid = handler.guid = + handler.guid || handlerProxy.guid || $.guid++; + } + + var match = event.match( /^(\w+)\s*(.*)$/ ), + eventName = match[1] + instance.eventNamespace, + selector = match[2]; + if ( selector ) { + delegateElement.delegate( selector, eventName, handlerProxy ); + } else { + element.bind( eventName, handlerProxy ); + } + }); + }, + + _off: function( element, eventName ) { + eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + this.eventNamespace; + element.unbind( eventName ).undelegate( eventName ); + }, + + _delay: function( handler, delay ) { + function handlerProxy() { + return ( typeof handler === "string" ? instance[ handler ] : handler ) + .apply( instance, arguments ); + } + var instance = this; + return setTimeout( handlerProxy, delay || 0 ); + }, + + _hoverable: function( element ) { + this.hoverable = this.hoverable.add( element ); + this._on( element, { + mouseenter: function( event ) { + $( event.currentTarget ).addClass( "ui-state-hover" ); + }, + mouseleave: function( event ) { + $( event.currentTarget ).removeClass( "ui-state-hover" ); + } + }); + }, + + _focusable: function( element ) { + this.focusable = this.focusable.add( element ); + this._on( element, { + focusin: function( event ) { + $( event.currentTarget ).addClass( "ui-state-focus" ); + }, + focusout: function( event ) { + $( event.currentTarget ).removeClass( "ui-state-focus" ); + } + }); + }, + + _trigger: function( type, event, data ) { + var prop, orig, + callback = this.options[ type ]; + + data = data || {}; + event = $.Event( event ); + event.type = ( type === this.widgetEventPrefix ? + type : + this.widgetEventPrefix + type ).toLowerCase(); + // the original event may come from any element + // so we need to reset the target on the new event + event.target = this.element[ 0 ]; + + // copy original event properties over to the new event + orig = event.originalEvent; + if ( orig ) { + for ( prop in orig ) { + if ( !( prop in event ) ) { + event[ prop ] = orig[ prop ]; + } + } + } + + this.element.trigger( event, data ); + return !( $.isFunction( callback ) && + callback.apply( this.element[0], [ event ].concat( data ) ) === false || + event.isDefaultPrevented() ); + } +}; + +$.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) { + $.Widget.prototype[ "_" + method ] = function( element, options, callback ) { + if ( typeof options === "string" ) { + options = { effect: options }; + } + var hasOptions, + effectName = !options ? + method : + options === true || typeof options === "number" ? + defaultEffect : + options.effect || defaultEffect; + options = options || {}; + if ( typeof options === "number" ) { + options = { duration: options }; + } + hasOptions = !$.isEmptyObject( options ); + options.complete = callback; + if ( options.delay ) { + element.delay( options.delay ); + } + if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) { + element[ method ]( options ); + } else if ( effectName !== method && element[ effectName ] ) { + element[ effectName ]( options.duration, options.easing, callback ); + } else { + element.queue(function( next ) { + $( this )[ method ](); + if ( callback ) { + callback.call( element[ 0 ] ); + } + next(); + }); + } + }; +}); + +})( jQuery ); +(function( $, undefined ) { + +var mouseHandled = false; +$( document ).mouseup( function() { + mouseHandled = false; +}); + +$.widget("ui.mouse", { + version: "1.10.0", + options: { + cancel: "input,textarea,button,select,option", + distance: 1, + delay: 0 + }, + _mouseInit: function() { + var that = this; + + this.element + .bind("mousedown."+this.widgetName, function(event) { + return that._mouseDown(event); + }) + .bind("click."+this.widgetName, function(event) { + if (true === $.data(event.target, that.widgetName + ".preventClickEvent")) { + $.removeData(event.target, that.widgetName + ".preventClickEvent"); + event.stopImmediatePropagation(); + return false; + } + }); + + this.started = false; + }, + + // TODO: make sure destroying one instance of mouse doesn't mess with + // other instances of mouse + _mouseDestroy: function() { + this.element.unbind("."+this.widgetName); + if ( this._mouseMoveDelegate ) { + $(document) + .unbind("mousemove."+this.widgetName, this._mouseMoveDelegate) + .unbind("mouseup."+this.widgetName, this._mouseUpDelegate); + } + }, + + _mouseDown: function(event) { + // don't let more than one widget handle mouseStart + if( mouseHandled ) { return; } + + // we may have missed mouseup (out of window) + (this._mouseStarted && this._mouseUp(event)); + + this._mouseDownEvent = event; + + var that = this, + btnIsLeft = (event.which === 1), + // event.target.nodeName works around a bug in IE 8 with + // disabled inputs (#7620) + elIsCancel = (typeof this.options.cancel === "string" && event.target.nodeName ? $(event.target).closest(this.options.cancel).length : false); + if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) { + return true; + } + + this.mouseDelayMet = !this.options.delay; + if (!this.mouseDelayMet) { + this._mouseDelayTimer = setTimeout(function() { + that.mouseDelayMet = true; + }, this.options.delay); + } + + if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) { + this._mouseStarted = (this._mouseStart(event) !== false); + if (!this._mouseStarted) { + event.preventDefault(); + return true; + } + } + + // Click event may never have fired (Gecko & Opera) + if (true === $.data(event.target, this.widgetName + ".preventClickEvent")) { + $.removeData(event.target, this.widgetName + ".preventClickEvent"); + } + + // these delegates are required to keep context + this._mouseMoveDelegate = function(event) { + return that._mouseMove(event); + }; + this._mouseUpDelegate = function(event) { + return that._mouseUp(event); + }; + $(document) + .bind("mousemove."+this.widgetName, this._mouseMoveDelegate) + .bind("mouseup."+this.widgetName, this._mouseUpDelegate); + + event.preventDefault(); + + mouseHandled = true; + return true; + }, + + _mouseMove: function(event) { + // IE mouseup check - mouseup happened when mouse was out of window + if ($.ui.ie && ( !document.documentMode || document.documentMode < 9 ) && !event.button) { + return this._mouseUp(event); + } + + if (this._mouseStarted) { + this._mouseDrag(event); + return event.preventDefault(); + } + + if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) { + this._mouseStarted = + (this._mouseStart(this._mouseDownEvent, event) !== false); + (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event)); + } + + return !this._mouseStarted; + }, + + _mouseUp: function(event) { + $(document) + .unbind("mousemove."+this.widgetName, this._mouseMoveDelegate) + .unbind("mouseup."+this.widgetName, this._mouseUpDelegate); + + if (this._mouseStarted) { + this._mouseStarted = false; + + if (event.target === this._mouseDownEvent.target) { + $.data(event.target, this.widgetName + ".preventClickEvent", true); + } + + this._mouseStop(event); + } + + return false; + }, + + _mouseDistanceMet: function(event) { + return (Math.max( + Math.abs(this._mouseDownEvent.pageX - event.pageX), + Math.abs(this._mouseDownEvent.pageY - event.pageY) + ) >= this.options.distance + ); + }, + + _mouseDelayMet: function(/* event */) { + return this.mouseDelayMet; + }, + + // These are placeholder methods, to be overriden by extending plugin + _mouseStart: function(/* event */) {}, + _mouseDrag: function(/* event */) {}, + _mouseStop: function(/* event */) {}, + _mouseCapture: function(/* event */) { return true; } +}); + +})(jQuery); +(function( $, undefined ) { + +$.ui = $.ui || {}; + +var cachedScrollbarWidth, + max = Math.max, + abs = Math.abs, + round = Math.round, + rhorizontal = /left|center|right/, + rvertical = /top|center|bottom/, + roffset = /[\+\-]\d+%?/, + rposition = /^\w+/, + rpercent = /%$/, + _position = $.fn.position; + +function getOffsets( offsets, width, height ) { + return [ + parseInt( offsets[ 0 ], 10 ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ), + parseInt( offsets[ 1 ], 10 ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 ) + ]; +} + +function parseCss( element, property ) { + return parseInt( $.css( element, property ), 10 ) || 0; +} + +function getDimensions( elem ) { + var raw = elem[0]; + if ( raw.nodeType === 9 ) { + return { + width: elem.width(), + height: elem.height(), + offset: { top: 0, left: 0 } + }; + } + if ( $.isWindow( raw ) ) { + return { + width: elem.width(), + height: elem.height(), + offset: { top: elem.scrollTop(), left: elem.scrollLeft() } + }; + } + if ( raw.preventDefault ) { + return { + width: 0, + height: 0, + offset: { top: raw.pageY, left: raw.pageX } + }; + } + return { + width: elem.outerWidth(), + height: elem.outerHeight(), + offset: elem.offset() + }; +} + +$.position = { + scrollbarWidth: function() { + if ( cachedScrollbarWidth !== undefined ) { + return cachedScrollbarWidth; + } + var w1, w2, + div = $( "
" ), + innerDiv = div.children()[0]; + + $( "body" ).append( div ); + w1 = innerDiv.offsetWidth; + div.css( "overflow", "scroll" ); + + w2 = innerDiv.offsetWidth; + + if ( w1 === w2 ) { + w2 = div[0].clientWidth; + } + + div.remove(); + + return (cachedScrollbarWidth = w1 - w2); + }, + getScrollInfo: function( within ) { + var overflowX = within.isWindow ? "" : within.element.css( "overflow-x" ), + overflowY = within.isWindow ? "" : within.element.css( "overflow-y" ), + hasOverflowX = overflowX === "scroll" || + ( overflowX === "auto" && within.width < within.element[0].scrollWidth ), + hasOverflowY = overflowY === "scroll" || + ( overflowY === "auto" && within.height < within.element[0].scrollHeight ); + return { + width: hasOverflowX ? $.position.scrollbarWidth() : 0, + height: hasOverflowY ? $.position.scrollbarWidth() : 0 + }; + }, + getWithinInfo: function( element ) { + var withinElement = $( element || window ), + isWindow = $.isWindow( withinElement[0] ); + return { + element: withinElement, + isWindow: isWindow, + offset: withinElement.offset() || { left: 0, top: 0 }, + scrollLeft: withinElement.scrollLeft(), + scrollTop: withinElement.scrollTop(), + width: isWindow ? withinElement.width() : withinElement.outerWidth(), + height: isWindow ? withinElement.height() : withinElement.outerHeight() + }; + } +}; + +$.fn.position = function( options ) { + if ( !options || !options.of ) { + return _position.apply( this, arguments ); + } + + // make a copy, we don't want to modify arguments + options = $.extend( {}, options ); + + var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions, + target = $( options.of ), + within = $.position.getWithinInfo( options.within ), + scrollInfo = $.position.getScrollInfo( within ), + collision = ( options.collision || "flip" ).split( " " ), + offsets = {}; + + dimensions = getDimensions( target ); + if ( target[0].preventDefault ) { + // force left top to allow flipping + options.at = "left top"; + } + targetWidth = dimensions.width; + targetHeight = dimensions.height; + targetOffset = dimensions.offset; + // clone to reuse original targetOffset later + basePosition = $.extend( {}, targetOffset ); + + // force my and at to have valid horizontal and vertical positions + // if a value is missing or invalid, it will be converted to center + $.each( [ "my", "at" ], function() { + var pos = ( options[ this ] || "" ).split( " " ), + horizontalOffset, + verticalOffset; + + if ( pos.length === 1) { + pos = rhorizontal.test( pos[ 0 ] ) ? + pos.concat( [ "center" ] ) : + rvertical.test( pos[ 0 ] ) ? + [ "center" ].concat( pos ) : + [ "center", "center" ]; + } + pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center"; + pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center"; + + // calculate offsets + horizontalOffset = roffset.exec( pos[ 0 ] ); + verticalOffset = roffset.exec( pos[ 1 ] ); + offsets[ this ] = [ + horizontalOffset ? horizontalOffset[ 0 ] : 0, + verticalOffset ? verticalOffset[ 0 ] : 0 + ]; + + // reduce to just the positions without the offsets + options[ this ] = [ + rposition.exec( pos[ 0 ] )[ 0 ], + rposition.exec( pos[ 1 ] )[ 0 ] + ]; + }); + + // normalize collision option + if ( collision.length === 1 ) { + collision[ 1 ] = collision[ 0 ]; + } + + if ( options.at[ 0 ] === "right" ) { + basePosition.left += targetWidth; + } else if ( options.at[ 0 ] === "center" ) { + basePosition.left += targetWidth / 2; + } + + if ( options.at[ 1 ] === "bottom" ) { + basePosition.top += targetHeight; + } else if ( options.at[ 1 ] === "center" ) { + basePosition.top += targetHeight / 2; + } + + atOffset = getOffsets( offsets.at, targetWidth, targetHeight ); + basePosition.left += atOffset[ 0 ]; + basePosition.top += atOffset[ 1 ]; + + return this.each(function() { + var collisionPosition, using, + elem = $( this ), + elemWidth = elem.outerWidth(), + elemHeight = elem.outerHeight(), + marginLeft = parseCss( this, "marginLeft" ), + marginTop = parseCss( this, "marginTop" ), + collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) + scrollInfo.width, + collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) + scrollInfo.height, + position = $.extend( {}, basePosition ), + myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() ); + + if ( options.my[ 0 ] === "right" ) { + position.left -= elemWidth; + } else if ( options.my[ 0 ] === "center" ) { + position.left -= elemWidth / 2; + } + + if ( options.my[ 1 ] === "bottom" ) { + position.top -= elemHeight; + } else if ( options.my[ 1 ] === "center" ) { + position.top -= elemHeight / 2; + } + + position.left += myOffset[ 0 ]; + position.top += myOffset[ 1 ]; + + // if the browser doesn't support fractions, then round for consistent results + if ( !$.support.offsetFractions ) { + position.left = round( position.left ); + position.top = round( position.top ); + } + + collisionPosition = { + marginLeft: marginLeft, + marginTop: marginTop + }; + + $.each( [ "left", "top" ], function( i, dir ) { + if ( $.ui.position[ collision[ i ] ] ) { + $.ui.position[ collision[ i ] ][ dir ]( position, { + targetWidth: targetWidth, + targetHeight: targetHeight, + elemWidth: elemWidth, + elemHeight: elemHeight, + collisionPosition: collisionPosition, + collisionWidth: collisionWidth, + collisionHeight: collisionHeight, + offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ], + my: options.my, + at: options.at, + within: within, + elem : elem + }); + } + }); + + if ( options.using ) { + // adds feedback as second argument to using callback, if present + using = function( props ) { + var left = targetOffset.left - position.left, + right = left + targetWidth - elemWidth, + top = targetOffset.top - position.top, + bottom = top + targetHeight - elemHeight, + feedback = { + target: { + element: target, + left: targetOffset.left, + top: targetOffset.top, + width: targetWidth, + height: targetHeight + }, + element: { + element: elem, + left: position.left, + top: position.top, + width: elemWidth, + height: elemHeight + }, + horizontal: right < 0 ? "left" : left > 0 ? "right" : "center", + vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle" + }; + if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) { + feedback.horizontal = "center"; + } + if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) { + feedback.vertical = "middle"; + } + if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) { + feedback.important = "horizontal"; + } else { + feedback.important = "vertical"; + } + options.using.call( this, props, feedback ); + }; + } + + elem.offset( $.extend( position, { using: using } ) ); + }); +}; + +$.ui.position = { + fit: { + left: function( position, data ) { + var within = data.within, + withinOffset = within.isWindow ? within.scrollLeft : within.offset.left, + outerWidth = within.width, + collisionPosLeft = position.left - data.collisionPosition.marginLeft, + overLeft = withinOffset - collisionPosLeft, + overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset, + newOverRight; + + // element is wider than within + if ( data.collisionWidth > outerWidth ) { + // element is initially over the left side of within + if ( overLeft > 0 && overRight <= 0 ) { + newOverRight = position.left + overLeft + data.collisionWidth - outerWidth - withinOffset; + position.left += overLeft - newOverRight; + // element is initially over right side of within + } else if ( overRight > 0 && overLeft <= 0 ) { + position.left = withinOffset; + // element is initially over both left and right sides of within + } else { + if ( overLeft > overRight ) { + position.left = withinOffset + outerWidth - data.collisionWidth; + } else { + position.left = withinOffset; + } + } + // too far left -> align with left edge + } else if ( overLeft > 0 ) { + position.left += overLeft; + // too far right -> align with right edge + } else if ( overRight > 0 ) { + position.left -= overRight; + // adjust based on position and margin + } else { + position.left = max( position.left - collisionPosLeft, position.left ); + } + }, + top: function( position, data ) { + var within = data.within, + withinOffset = within.isWindow ? within.scrollTop : within.offset.top, + outerHeight = data.within.height, + collisionPosTop = position.top - data.collisionPosition.marginTop, + overTop = withinOffset - collisionPosTop, + overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset, + newOverBottom; + + // element is taller than within + if ( data.collisionHeight > outerHeight ) { + // element is initially over the top of within + if ( overTop > 0 && overBottom <= 0 ) { + newOverBottom = position.top + overTop + data.collisionHeight - outerHeight - withinOffset; + position.top += overTop - newOverBottom; + // element is initially over bottom of within + } else if ( overBottom > 0 && overTop <= 0 ) { + position.top = withinOffset; + // element is initially over both top and bottom of within + } else { + if ( overTop > overBottom ) { + position.top = withinOffset + outerHeight - data.collisionHeight; + } else { + position.top = withinOffset; + } + } + // too far up -> align with top + } else if ( overTop > 0 ) { + position.top += overTop; + // too far down -> align with bottom edge + } else if ( overBottom > 0 ) { + position.top -= overBottom; + // adjust based on position and margin + } else { + position.top = max( position.top - collisionPosTop, position.top ); + } + } + }, + flip: { + left: function( position, data ) { + var within = data.within, + withinOffset = within.offset.left + within.scrollLeft, + outerWidth = within.width, + offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left, + collisionPosLeft = position.left - data.collisionPosition.marginLeft, + overLeft = collisionPosLeft - offsetLeft, + overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft, + myOffset = data.my[ 0 ] === "left" ? + -data.elemWidth : + data.my[ 0 ] === "right" ? + data.elemWidth : + 0, + atOffset = data.at[ 0 ] === "left" ? + data.targetWidth : + data.at[ 0 ] === "right" ? + -data.targetWidth : + 0, + offset = -2 * data.offset[ 0 ], + newOverRight, + newOverLeft; + + if ( overLeft < 0 ) { + newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth - outerWidth - withinOffset; + if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) { + position.left += myOffset + atOffset + offset; + } + } + else if ( overRight > 0 ) { + newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset + atOffset + offset - offsetLeft; + if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) { + position.left += myOffset + atOffset + offset; + } + } + }, + top: function( position, data ) { + var within = data.within, + withinOffset = within.offset.top + within.scrollTop, + outerHeight = within.height, + offsetTop = within.isWindow ? within.scrollTop : within.offset.top, + collisionPosTop = position.top - data.collisionPosition.marginTop, + overTop = collisionPosTop - offsetTop, + overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop, + top = data.my[ 1 ] === "top", + myOffset = top ? + -data.elemHeight : + data.my[ 1 ] === "bottom" ? + data.elemHeight : + 0, + atOffset = data.at[ 1 ] === "top" ? + data.targetHeight : + data.at[ 1 ] === "bottom" ? + -data.targetHeight : + 0, + offset = -2 * data.offset[ 1 ], + newOverTop, + newOverBottom; + if ( overTop < 0 ) { + newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight - outerHeight - withinOffset; + if ( ( position.top + myOffset + atOffset + offset) > overTop && ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) ) { + position.top += myOffset + atOffset + offset; + } + } + else if ( overBottom > 0 ) { + newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset + offset - offsetTop; + if ( ( position.top + myOffset + atOffset + offset) > overBottom && ( newOverTop > 0 || abs( newOverTop ) < overBottom ) ) { + position.top += myOffset + atOffset + offset; + } + } + } + }, + flipfit: { + left: function() { + $.ui.position.flip.left.apply( this, arguments ); + $.ui.position.fit.left.apply( this, arguments ); + }, + top: function() { + $.ui.position.flip.top.apply( this, arguments ); + $.ui.position.fit.top.apply( this, arguments ); + } + } +}; + +// fraction support test +(function () { + var testElement, testElementParent, testElementStyle, offsetLeft, i, + body = document.getElementsByTagName( "body" )[ 0 ], + div = document.createElement( "div" ); + + //Create a "fake body" for testing based on method used in jQuery.support + testElement = document.createElement( body ? "div" : "body" ); + testElementStyle = { + visibility: "hidden", + width: 0, + height: 0, + border: 0, + margin: 0, + background: "none" + }; + if ( body ) { + $.extend( testElementStyle, { + position: "absolute", + left: "-1000px", + top: "-1000px" + }); + } + for ( i in testElementStyle ) { + testElement.style[ i ] = testElementStyle[ i ]; + } + testElement.appendChild( div ); + testElementParent = body || document.documentElement; + testElementParent.insertBefore( testElement, testElementParent.firstChild ); + + div.style.cssText = "position: absolute; left: 10.7432222px;"; + + offsetLeft = $( div ).offset().left; + $.support.offsetFractions = offsetLeft > 10 && offsetLeft < 11; + + testElement.innerHTML = ""; + testElementParent.removeChild( testElement ); +})(); + +}( jQuery ) ); +(function( $, undefined ) { + +$.widget("ui.draggable", $.ui.mouse, { + version: "1.10.0", + widgetEventPrefix: "drag", + options: { + addClasses: true, + appendTo: "parent", + axis: false, + connectToSortable: false, + containment: false, + cursor: "auto", + cursorAt: false, + grid: false, + handle: false, + helper: "original", + iframeFix: false, + opacity: false, + refreshPositions: false, + revert: false, + revertDuration: 500, + scope: "default", + scroll: true, + scrollSensitivity: 20, + scrollSpeed: 20, + snap: false, + snapMode: "both", + snapTolerance: 20, + stack: false, + zIndex: false, + + // callbacks + drag: null, + start: null, + stop: null + }, + _create: function() { + + if (this.options.helper === "original" && !(/^(?:r|a|f)/).test(this.element.css("position"))) { + this.element[0].style.position = "relative"; + } + if (this.options.addClasses){ + this.element.addClass("ui-draggable"); + } + if (this.options.disabled){ + this.element.addClass("ui-draggable-disabled"); + } + + this._mouseInit(); + + }, + + _destroy: function() { + this.element.removeClass( "ui-draggable ui-draggable-dragging ui-draggable-disabled" ); + this._mouseDestroy(); + }, + + _mouseCapture: function(event) { + + var o = this.options; + + // among others, prevent a drag on a resizable-handle + if (this.helper || o.disabled || $(event.target).closest(".ui-resizable-handle").length > 0) { + return false; + } + + //Quit if we're not on a valid handle + this.handle = this._getHandle(event); + if (!this.handle) { + return false; + } + + $(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() { + $("
") + .css({ + width: this.offsetWidth+"px", height: this.offsetHeight+"px", + position: "absolute", opacity: "0.001", zIndex: 1000 + }) + .css($(this).offset()) + .appendTo("body"); + }); + + return true; + + }, + + _mouseStart: function(event) { + + var o = this.options; + + //Create and append the visible helper + this.helper = this._createHelper(event); + + this.helper.addClass("ui-draggable-dragging"); + + //Cache the helper size + this._cacheHelperProportions(); + + //If ddmanager is used for droppables, set the global draggable + if($.ui.ddmanager) { + $.ui.ddmanager.current = this; + } + + /* + * - Position generation - + * This block generates everything position related - it's the core of draggables. + */ + + //Cache the margins of the original element + this._cacheMargins(); + + //Store the helper's css position + this.cssPosition = this.helper.css("position"); + this.scrollParent = this.helper.scrollParent(); + + //The element's absolute position on the page minus margins + this.offset = this.positionAbs = this.element.offset(); + this.offset = { + top: this.offset.top - this.margins.top, + left: this.offset.left - this.margins.left + }; + + $.extend(this.offset, { + click: { //Where the click happened, relative to the element + left: event.pageX - this.offset.left, + top: event.pageY - this.offset.top + }, + parent: this._getParentOffset(), + relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper + }); + + //Generate the original position + this.originalPosition = this.position = this._generatePosition(event); + this.originalPageX = event.pageX; + this.originalPageY = event.pageY; + + //Adjust the mouse offset relative to the helper if "cursorAt" is supplied + (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt)); + + //Set a containment if given in the options + if(o.containment) { + this._setContainment(); + } + + //Trigger event + callbacks + if(this._trigger("start", event) === false) { + this._clear(); + return false; + } + + //Recache the helper size + this._cacheHelperProportions(); + + //Prepare the droppable offsets + if ($.ui.ddmanager && !o.dropBehaviour) { + $.ui.ddmanager.prepareOffsets(this, event); + } + + + this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position + + //If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003) + if ( $.ui.ddmanager ) { + $.ui.ddmanager.dragStart(this, event); + } + + return true; + }, + + _mouseDrag: function(event, noPropagation) { + + //Compute the helpers position + this.position = this._generatePosition(event); + this.positionAbs = this._convertPositionTo("absolute"); + + //Call plugins and callbacks and use the resulting position if something is returned + if (!noPropagation) { + var ui = this._uiHash(); + if(this._trigger("drag", event, ui) === false) { + this._mouseUp({}); + return false; + } + this.position = ui.position; + } + + if(!this.options.axis || this.options.axis !== "y") { + this.helper[0].style.left = this.position.left+"px"; + } + if(!this.options.axis || this.options.axis !== "x") { + this.helper[0].style.top = this.position.top+"px"; + } + if($.ui.ddmanager) { + $.ui.ddmanager.drag(this, event); + } + + return false; + }, + + _mouseStop: function(event) { + + //If we are using droppables, inform the manager about the drop + var element, + that = this, + elementInDom = false, + dropped = false; + if ($.ui.ddmanager && !this.options.dropBehaviour) { + dropped = $.ui.ddmanager.drop(this, event); + } + + //if a drop comes from outside (a sortable) + if(this.dropped) { + dropped = this.dropped; + this.dropped = false; + } + + //if the original element is no longer in the DOM don't bother to continue (see #8269) + element = this.element[0]; + while ( element && (element = element.parentNode) ) { + if (element === document ) { + elementInDom = true; + } + } + if ( !elementInDom && this.options.helper === "original" ) { + return false; + } + + if((this.options.revert === "invalid" && !dropped) || (this.options.revert === "valid" && dropped) || this.options.revert === true || ($.isFunction(this.options.revert) && this.options.revert.call(this.element, dropped))) { + $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() { + if(that._trigger("stop", event) !== false) { + that._clear(); + } + }); + } else { + if(this._trigger("stop", event) !== false) { + this._clear(); + } + } + + return false; + }, + + _mouseUp: function(event) { + //Remove frame helpers + $("div.ui-draggable-iframeFix").each(function() { + this.parentNode.removeChild(this); + }); + + //If the ddmanager is used for droppables, inform the manager that dragging has stopped (see #5003) + if( $.ui.ddmanager ) { + $.ui.ddmanager.dragStop(this, event); + } + + return $.ui.mouse.prototype._mouseUp.call(this, event); + }, + + cancel: function() { + + if(this.helper.is(".ui-draggable-dragging")) { + this._mouseUp({}); + } else { + this._clear(); + } + + return this; + + }, + + _getHandle: function(event) { + + var handle = !this.options.handle || !$(this.options.handle, this.element).length ? true : false; + $(this.options.handle, this.element) + .find("*") + .addBack() + .each(function() { + if(this === event.target) { + handle = true; + } + }); + + return handle; + + }, + + _createHelper: function(event) { + + var o = this.options, + helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper === "clone" ? this.element.clone().removeAttr("id") : this.element); + + if(!helper.parents("body").length) { + helper.appendTo((o.appendTo === "parent" ? this.element[0].parentNode : o.appendTo)); + } + + if(helper[0] !== this.element[0] && !(/(fixed|absolute)/).test(helper.css("position"))) { + helper.css("position", "absolute"); + } + + return helper; + + }, + + _adjustOffsetFromHelper: function(obj) { + if (typeof obj === "string") { + obj = obj.split(" "); + } + if ($.isArray(obj)) { + obj = {left: +obj[0], top: +obj[1] || 0}; + } + if ("left" in obj) { + this.offset.click.left = obj.left + this.margins.left; + } + if ("right" in obj) { + this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left; + } + if ("top" in obj) { + this.offset.click.top = obj.top + this.margins.top; + } + if ("bottom" in obj) { + this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top; + } + }, + + _getParentOffset: function() { + + //Get the offsetParent and cache its position + this.offsetParent = this.helper.offsetParent(); + var po = this.offsetParent.offset(); + + // This is a special case where we need to modify a offset calculated on start, since the following happened: + // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent + // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that + // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag + if(this.cssPosition === "absolute" && this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) { + po.left += this.scrollParent.scrollLeft(); + po.top += this.scrollParent.scrollTop(); + } + + //This needs to be actually done for all browsers, since pageX/pageY includes this information + //Ugly IE fix + if((this.offsetParent[0] === document.body) || + (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() === "html" && $.ui.ie)) { + po = { top: 0, left: 0 }; + } + + return { + top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0), + left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0) + }; + + }, + + _getRelativeOffset: function() { + + if(this.cssPosition === "relative") { + var p = this.element.position(); + return { + top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(), + left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft() + }; + } else { + return { top: 0, left: 0 }; + } + + }, + + _cacheMargins: function() { + this.margins = { + left: (parseInt(this.element.css("marginLeft"),10) || 0), + top: (parseInt(this.element.css("marginTop"),10) || 0), + right: (parseInt(this.element.css("marginRight"),10) || 0), + bottom: (parseInt(this.element.css("marginBottom"),10) || 0) + }; + }, + + _cacheHelperProportions: function() { + this.helperProportions = { + width: this.helper.outerWidth(), + height: this.helper.outerHeight() + }; + }, + + _setContainment: function() { + + var over, c, ce, + o = this.options; + + if(o.containment === "parent") { + o.containment = this.helper[0].parentNode; + } + if(o.containment === "document" || o.containment === "window") { + this.containment = [ + o.containment === "document" ? 0 : $(window).scrollLeft() - this.offset.relative.left - this.offset.parent.left, + o.containment === "document" ? 0 : $(window).scrollTop() - this.offset.relative.top - this.offset.parent.top, + (o.containment === "document" ? 0 : $(window).scrollLeft()) + $(o.containment === "document" ? document : window).width() - this.helperProportions.width - this.margins.left, + (o.containment === "document" ? 0 : $(window).scrollTop()) + ($(o.containment === "document" ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top + ]; + } + + if(!(/^(document|window|parent)$/).test(o.containment) && o.containment.constructor !== Array) { + c = $(o.containment); + ce = c[0]; + + if(!ce) { + return; + } + + over = ($(ce).css("overflow") !== "hidden"); + + this.containment = [ + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0), + (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0), + (over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left - this.margins.right, + (over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top - this.margins.bottom + ]; + this.relative_container = c; + + } else if(o.containment.constructor === Array) { + this.containment = o.containment; + } + + }, + + _convertPositionTo: function(d, pos) { + + if(!pos) { + pos = this.position; + } + + var mod = d === "absolute" ? 1 : -1, + scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName); + + return { + top: ( + pos.top + // The absolute mouse position + this.offset.relative.top * mod + // Only for relative positioned nodes: Relative offset from element to offset parent + this.offset.parent.top * mod - // The offsetParent's offset without borders (offset + border) + ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod) + ), + left: ( + pos.left + // The absolute mouse position + this.offset.relative.left * mod + // Only for relative positioned nodes: Relative offset from element to offset parent + this.offset.parent.left * mod - // The offsetParent's offset without borders (offset + border) + ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod) + ) + }; + + }, + + _generatePosition: function(event) { + + var containment, co, top, left, + o = this.options, + scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, + scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName), + pageX = event.pageX, + pageY = event.pageY; + + /* + * - Position constraining - + * Constrain the position to a mix of grid, containment. + */ + + if(this.originalPosition) { //If we are not dragging yet, we won't check for options + if(this.containment) { + if (this.relative_container){ + co = this.relative_container.offset(); + containment = [ this.containment[0] + co.left, + this.containment[1] + co.top, + this.containment[2] + co.left, + this.containment[3] + co.top ]; + } + else { + containment = this.containment; + } + + if(event.pageX - this.offset.click.left < containment[0]) { + pageX = containment[0] + this.offset.click.left; + } + if(event.pageY - this.offset.click.top < containment[1]) { + pageY = containment[1] + this.offset.click.top; + } + if(event.pageX - this.offset.click.left > containment[2]) { + pageX = containment[2] + this.offset.click.left; + } + if(event.pageY - this.offset.click.top > containment[3]) { + pageY = containment[3] + this.offset.click.top; + } + } + + if(o.grid) { + //Check for grid elements set to 0 to prevent divide by 0 error causing invalid argument errors in IE (see ticket #6950) + top = o.grid[1] ? this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1] : this.originalPageY; + pageY = containment ? ((top - this.offset.click.top >= containment[1] || top - this.offset.click.top > containment[3]) ? top : ((top - this.offset.click.top >= containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top; + + left = o.grid[0] ? this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0] : this.originalPageX; + pageX = containment ? ((left - this.offset.click.left >= containment[0] || left - this.offset.click.left > containment[2]) ? left : ((left - this.offset.click.left >= containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left; + } + + } + + return { + top: ( + pageY - // The absolute mouse position + this.offset.click.top - // Click offset (relative to the element) + this.offset.relative.top - // Only for relative positioned nodes: Relative offset from element to offset parent + this.offset.parent.top + // The offsetParent's offset without borders (offset + border) + ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) )) + ), + left: ( + pageX - // The absolute mouse position + this.offset.click.left - // Click offset (relative to the element) + this.offset.relative.left - // Only for relative positioned nodes: Relative offset from element to offset parent + this.offset.parent.left + // The offsetParent's offset without borders (offset + border) + ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() )) + ) + }; + + }, + + _clear: function() { + this.helper.removeClass("ui-draggable-dragging"); + if(this.helper[0] !== this.element[0] && !this.cancelHelperRemoval) { + this.helper.remove(); + } + this.helper = null; + this.cancelHelperRemoval = false; + }, + + // From now on bulk stuff - mainly helpers + + _trigger: function(type, event, ui) { + ui = ui || this._uiHash(); + $.ui.plugin.call(this, type, [event, ui]); + //The absolute position has to be recalculated after plugins + if(type === "drag") { + this.positionAbs = this._convertPositionTo("absolute"); + } + return $.Widget.prototype._trigger.call(this, type, event, ui); + }, + + plugins: {}, + + _uiHash: function() { + return { + helper: this.helper, + position: this.position, + originalPosition: this.originalPosition, + offset: this.positionAbs + }; + } + +}); + +$.ui.plugin.add("draggable", "connectToSortable", { + start: function(event, ui) { + + var inst = $(this).data("ui-draggable"), o = inst.options, + uiSortable = $.extend({}, ui, { item: inst.element }); + inst.sortables = []; + $(o.connectToSortable).each(function() { + var sortable = $.data(this, "ui-sortable"); + if (sortable && !sortable.options.disabled) { + inst.sortables.push({ + instance: sortable, + shouldRevert: sortable.options.revert + }); + sortable.refreshPositions(); // Call the sortable's refreshPositions at drag start to refresh the containerCache since the sortable container cache is used in drag and needs to be up to date (this will ensure it's initialised as well as being kept in step with any changes that might have happened on the page). + sortable._trigger("activate", event, uiSortable); + } + }); + + }, + stop: function(event, ui) { + + //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper + var inst = $(this).data("ui-draggable"), + uiSortable = $.extend({}, ui, { item: inst.element }); + + $.each(inst.sortables, function() { + if(this.instance.isOver) { + + this.instance.isOver = 0; + + inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance + this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work) + + //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: "valid/invalid" + if(this.shouldRevert) { + this.instance.options.revert = true; + } + + //Trigger the stop of the sortable + this.instance._mouseStop(event); + + this.instance.options.helper = this.instance.options._helper; + + //If the helper has been the original item, restore properties in the sortable + if(inst.options.helper === "original") { + this.instance.currentItem.css({ top: "auto", left: "auto" }); + } + + } else { + this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance + this.instance._trigger("deactivate", event, uiSortable); + } + + }); + + }, + drag: function(event, ui) { + + var inst = $(this).data("ui-draggable"), that = this; + + $.each(inst.sortables, function() { + + var innermostIntersecting = false, + thisSortable = this; + + //Copy over some variables to allow calling the sortable's native _intersectsWith + this.instance.positionAbs = inst.positionAbs; + this.instance.helperProportions = inst.helperProportions; + this.instance.offset.click = inst.offset.click; + + if(this.instance._intersectsWith(this.instance.containerCache)) { + innermostIntersecting = true; + $.each(inst.sortables, function () { + this.instance.positionAbs = inst.positionAbs; + this.instance.helperProportions = inst.helperProportions; + this.instance.offset.click = inst.offset.click; + if (this !== thisSortable && + this.instance._intersectsWith(this.instance.containerCache) && + $.ui.contains(thisSortable.instance.element[0], this.instance.element[0]) + ) { + innermostIntersecting = false; + } + return innermostIntersecting; + }); + } + + + if(innermostIntersecting) { + //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once + if(!this.instance.isOver) { + + this.instance.isOver = 1; + //Now we fake the start of dragging for the sortable instance, + //by cloning the list group item, appending it to the sortable and using it as inst.currentItem + //We can then fire the start event of the sortable with our passed browser event, and our own helper (so it doesn't create a new one) + this.instance.currentItem = $(that).clone().removeAttr("id").appendTo(this.instance.element).data("ui-sortable-item", true); + this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it + this.instance.options.helper = function() { return ui.helper[0]; }; + + event.target = this.instance.currentItem[0]; + this.instance._mouseCapture(event, true); + this.instance._mouseStart(event, true, true); + + //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes + this.instance.offset.click.top = inst.offset.click.top; + this.instance.offset.click.left = inst.offset.click.left; + this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left; + this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top; + + inst._trigger("toSortable", event); + inst.dropped = this.instance.element; //draggable revert needs that + //hack so receive/update callbacks work (mostly) + inst.currentItem = inst.element; + this.instance.fromOutside = inst; + + } + + //Provided we did all the previous steps, we can fire the drag event of the sortable on every draggable drag, when it intersects with the sortable + if(this.instance.currentItem) { + this.instance._mouseDrag(event); + } + + } else { + + //If it doesn't intersect with the sortable, and it intersected before, + //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval + if(this.instance.isOver) { + + this.instance.isOver = 0; + this.instance.cancelHelperRemoval = true; + + //Prevent reverting on this forced stop + this.instance.options.revert = false; + + // The out event needs to be triggered independently + this.instance._trigger("out", event, this.instance._uiHash(this.instance)); + + this.instance._mouseStop(event, true); + this.instance.options.helper = this.instance.options._helper; + + //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size + this.instance.currentItem.remove(); + if(this.instance.placeholder) { + this.instance.placeholder.remove(); + } + + inst._trigger("fromSortable", event); + inst.dropped = false; //draggable revert needs that + } + + } + + }); + + } +}); + +$.ui.plugin.add("draggable", "cursor", { + start: function() { + var t = $("body"), o = $(this).data("ui-draggable").options; + if (t.css("cursor")) { + o._cursor = t.css("cursor"); + } + t.css("cursor", o.cursor); + }, + stop: function() { + var o = $(this).data("ui-draggable").options; + if (o._cursor) { + $("body").css("cursor", o._cursor); + } + } +}); + +$.ui.plugin.add("draggable", "opacity", { + start: function(event, ui) { + var t = $(ui.helper), o = $(this).data("ui-draggable").options; + if(t.css("opacity")) { + o._opacity = t.css("opacity"); + } + t.css("opacity", o.opacity); + }, + stop: function(event, ui) { + var o = $(this).data("ui-draggable").options; + if(o._opacity) { + $(ui.helper).css("opacity", o._opacity); + } + } +}); + +$.ui.plugin.add("draggable", "scroll", { + start: function() { + var i = $(this).data("ui-draggable"); + if(i.scrollParent[0] !== document && i.scrollParent[0].tagName !== "HTML") { + i.overflowOffset = i.scrollParent.offset(); + } + }, + drag: function( event ) { + + var i = $(this).data("ui-draggable"), o = i.options, scrolled = false; + + if(i.scrollParent[0] !== document && i.scrollParent[0].tagName !== "HTML") { + + if(!o.axis || o.axis !== "x") { + if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) { + i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed; + } else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity) { + i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed; + } + } + + if(!o.axis || o.axis !== "y") { + if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) { + i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed; + } else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity) { + i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed; + } + } + + } else { + + if(!o.axis || o.axis !== "x") { + if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) { + scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed); + } else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) { + scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed); + } + } + + if(!o.axis || o.axis !== "y") { + if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) { + scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed); + } else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) { + scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed); + } + } + + } + + if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) { + $.ui.ddmanager.prepareOffsets(i, event); + } + + } +}); + +$.ui.plugin.add("draggable", "snap", { + start: function() { + + var i = $(this).data("ui-draggable"), + o = i.options; + + i.snapElements = []; + + $(o.snap.constructor !== String ? ( o.snap.items || ":data(ui-draggable)" ) : o.snap).each(function() { + var $t = $(this), + $o = $t.offset(); + if(this !== i.element[0]) { + i.snapElements.push({ + item: this, + width: $t.outerWidth(), height: $t.outerHeight(), + top: $o.top, left: $o.left + }); + } + }); + + }, + drag: function(event, ui) { + + var ts, bs, ls, rs, l, r, t, b, i, first, + inst = $(this).data("ui-draggable"), + o = inst.options, + d = o.snapTolerance, + x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width, + y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height; + + for (i = inst.snapElements.length - 1; i >= 0; i--){ + + l = inst.snapElements[i].left; + r = l + inst.snapElements[i].width; + t = inst.snapElements[i].top; + b = t + inst.snapElements[i].height; + + //Yes, I know, this is insane ;) + if(!((l-d < x1 && x1 < r+d && t-d < y1 && y1 < b+d) || (l-d < x1 && x1 < r+d && t-d < y2 && y2 < b+d) || (l-d < x2 && x2 < r+d && t-d < y1 && y1 < b+d) || (l-d < x2 && x2 < r+d && t-d < y2 && y2 < b+d))) { + if(inst.snapElements[i].snapping) { + (inst.options.snap.release && inst.options.snap.release.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item }))); + } + inst.snapElements[i].snapping = false; + continue; + } + + if(o.snapMode !== "inner") { + ts = Math.abs(t - y2) <= d; + bs = Math.abs(b - y1) <= d; + ls = Math.abs(l - x2) <= d; + rs = Math.abs(r - x1) <= d; + if(ts) { + ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top; + } + if(bs) { + ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top; + } + if(ls) { + ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left; + } + if(rs) { + ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left; + } + } + + first = (ts || bs || ls || rs); + + if(o.snapMode !== "outer") { + ts = Math.abs(t - y1) <= d; + bs = Math.abs(b - y2) <= d; + ls = Math.abs(l - x1) <= d; + rs = Math.abs(r - x2) <= d; + if(ts) { + ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top; + } + if(bs) { + ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top; + } + if(ls) { + ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left; + } + if(rs) { + ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left; + } + } + + if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first)) { + (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item }))); + } + inst.snapElements[i].snapping = (ts || bs || ls || rs || first); + + } + + } +}); + +$.ui.plugin.add("draggable", "stack", { + start: function() { + + var min, + o = $(this).data("ui-draggable").options, + group = $.makeArray($(o.stack)).sort(function(a,b) { + return (parseInt($(a).css("zIndex"),10) || 0) - (parseInt($(b).css("zIndex"),10) || 0); + }); + + if (!group.length) { return; } + + min = parseInt(group[0].style.zIndex, 10) || 0; + $(group).each(function(i) { + this.style.zIndex = min + i; + }); + + this[0].style.zIndex = min + group.length; + + } +}); + +$.ui.plugin.add("draggable", "zIndex", { + start: function(event, ui) { + var t = $(ui.helper), o = $(this).data("ui-draggable").options; + if(t.css("zIndex")) { + o._zIndex = t.css("zIndex"); + } + t.css("zIndex", o.zIndex); + }, + stop: function(event, ui) { + var o = $(this).data("ui-draggable").options; + if(o._zIndex) { + $(ui.helper).css("zIndex", o._zIndex); + } + } +}); + +})(jQuery); +(function( $, undefined ) { + +function isOverAxis( x, reference, size ) { + return ( x > reference ) && ( x < ( reference + size ) ); +} + +$.widget("ui.droppable", { + version: "1.10.0", + widgetEventPrefix: "drop", + options: { + accept: "*", + activeClass: false, + addClasses: true, + greedy: false, + hoverClass: false, + scope: "default", + tolerance: "intersect", + + // callbacks + activate: null, + deactivate: null, + drop: null, + out: null, + over: null + }, + _create: function() { + + var o = this.options, + accept = o.accept; + + this.isover = false; + this.isout = true; + + this.accept = $.isFunction(accept) ? accept : function(d) { + return d.is(accept); + }; + + //Store the droppable's proportions + this.proportions = { width: this.element[0].offsetWidth, height: this.element[0].offsetHeight }; + + // Add the reference and positions to the manager + $.ui.ddmanager.droppables[o.scope] = $.ui.ddmanager.droppables[o.scope] || []; + $.ui.ddmanager.droppables[o.scope].push(this); + + (o.addClasses && this.element.addClass("ui-droppable")); + + }, + + _destroy: function() { + var i = 0, + drop = $.ui.ddmanager.droppables[this.options.scope]; + + for ( ; i < drop.length; i++ ) { + if ( drop[i] === this ) { + drop.splice(i, 1); + } + } + + this.element.removeClass("ui-droppable ui-droppable-disabled"); + }, + + _setOption: function(key, value) { + + if(key === "accept") { + this.accept = $.isFunction(value) ? value : function(d) { + return d.is(value); + }; + } + $.Widget.prototype._setOption.apply(this, arguments); + }, + + _activate: function(event) { + var draggable = $.ui.ddmanager.current; + if(this.options.activeClass) { + this.element.addClass(this.options.activeClass); + } + if(draggable){ + this._trigger("activate", event, this.ui(draggable)); + } + }, + + _deactivate: function(event) { + var draggable = $.ui.ddmanager.current; + if(this.options.activeClass) { + this.element.removeClass(this.options.activeClass); + } + if(draggable){ + this._trigger("deactivate", event, this.ui(draggable)); + } + }, + + _over: function(event) { + + var draggable = $.ui.ddmanager.current; + + // Bail if draggable and droppable are same element + if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) { + return; + } + + if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) { + if(this.options.hoverClass) { + this.element.addClass(this.options.hoverClass); + } + this._trigger("over", event, this.ui(draggable)); + } + + }, + + _out: function(event) { + + var draggable = $.ui.ddmanager.current; + + // Bail if draggable and droppable are same element + if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) { + return; + } + + if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) { + if(this.options.hoverClass) { + this.element.removeClass(this.options.hoverClass); + } + this._trigger("out", event, this.ui(draggable)); + } + + }, + + _drop: function(event,custom) { + + var draggable = custom || $.ui.ddmanager.current, + childrenIntersection = false; + + // Bail if draggable and droppable are same element + if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) { + return false; + } + + this.element.find(":data(ui-droppable)").not(".ui-draggable-dragging").each(function() { + var inst = $.data(this, "ui-droppable"); + if( + inst.options.greedy && + !inst.options.disabled && + inst.options.scope === draggable.options.scope && + inst.accept.call(inst.element[0], (draggable.currentItem || draggable.element)) && + $.ui.intersect(draggable, $.extend(inst, { offset: inst.element.offset() }), inst.options.tolerance) + ) { childrenIntersection = true; return false; } + }); + if(childrenIntersection) { + return false; + } + + if(this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) { + if(this.options.activeClass) { + this.element.removeClass(this.options.activeClass); + } + if(this.options.hoverClass) { + this.element.removeClass(this.options.hoverClass); + } + this._trigger("drop", event, this.ui(draggable)); + return this.element; + } + + return false; + + }, + + ui: function(c) { + return { + draggable: (c.currentItem || c.element), + helper: c.helper, + position: c.position, + offset: c.positionAbs + }; + } + +}); + +$.ui.intersect = function(draggable, droppable, toleranceMode) { + + if (!droppable.offset) { + return false; + } + + var draggableLeft, draggableTop, + x1 = (draggable.positionAbs || draggable.position.absolute).left, x2 = x1 + draggable.helperProportions.width, + y1 = (draggable.positionAbs || draggable.position.absolute).top, y2 = y1 + draggable.helperProportions.height, + l = droppable.offset.left, r = l + droppable.proportions.width, + t = droppable.offset.top, b = t + droppable.proportions.height; + + switch (toleranceMode) { + case "fit": + return (l <= x1 && x2 <= r && t <= y1 && y2 <= b); + case "intersect": + return (l < x1 + (draggable.helperProportions.width / 2) && // Right Half + x2 - (draggable.helperProportions.width / 2) < r && // Left Half + t < y1 + (draggable.helperProportions.height / 2) && // Bottom Half + y2 - (draggable.helperProportions.height / 2) < b ); // Top Half + case "pointer": + draggableLeft = ((draggable.positionAbs || draggable.position.absolute).left + (draggable.clickOffset || draggable.offset.click).left); + draggableTop = ((draggable.positionAbs || draggable.position.absolute).top + (draggable.clickOffset || draggable.offset.click).top); + return isOverAxis( draggableTop, t, droppable.proportions.height ) && isOverAxis( draggableLeft, l, droppable.proportions.width ); + case "touch": + return ( + (y1 >= t && y1 <= b) || // Top edge touching + (y2 >= t && y2 <= b) || // Bottom edge touching + (y1 < t && y2 > b) // Surrounded vertically + ) && ( + (x1 >= l && x1 <= r) || // Left edge touching + (x2 >= l && x2 <= r) || // Right edge touching + (x1 < l && x2 > r) // Surrounded horizontally + ); + default: + return false; + } + +}; + +/* + This manager tracks offsets of draggables and droppables +*/ +$.ui.ddmanager = { + current: null, + droppables: { "default": [] }, + prepareOffsets: function(t, event) { + + var i, j, + m = $.ui.ddmanager.droppables[t.options.scope] || [], + type = event ? event.type : null, // workaround for #2317 + list = (t.currentItem || t.element).find(":data(ui-droppable)").addBack(); + + droppablesLoop: for (i = 0; i < m.length; i++) { + + //No disabled and non-accepted + if(m[i].options.disabled || (t && !m[i].accept.call(m[i].element[0],(t.currentItem || t.element)))) { + continue; + } + + // Filter out elements in the current dragged item + for (j=0; j < list.length; j++) { + if(list[j] === m[i].element[0]) { + m[i].proportions.height = 0; + continue droppablesLoop; + } + } + + m[i].visible = m[i].element.css("display") !== "none"; + if(!m[i].visible) { + continue; + } + + //Activate the droppable if used directly from draggables + if(type === "mousedown") { + m[i]._activate.call(m[i], event); + } + + m[i].offset = m[i].element.offset(); + m[i].proportions = { width: m[i].element[0].offsetWidth, height: m[i].element[0].offsetHeight }; + + } + + }, + drop: function(draggable, event) { + + var dropped = false; + $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() { + + if(!this.options) { + return; + } + if (!this.options.disabled && this.visible && $.ui.intersect(draggable, this, this.options.tolerance)) { + dropped = this._drop.call(this, event) || dropped; + } + + if (!this.options.disabled && this.visible && this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) { + this.isout = true; + this.isover = false; + this._deactivate.call(this, event); + } + + }); + return dropped; + + }, + dragStart: function( draggable, event ) { + //Listen for scrolling so that if the dragging causes scrolling the position of the droppables can be recalculated (see #5003) + draggable.element.parentsUntil( "body" ).bind( "scroll.droppable", function() { + if( !draggable.options.refreshPositions ) { + $.ui.ddmanager.prepareOffsets( draggable, event ); + } + }); + }, + drag: function(draggable, event) { + + //If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse. + if(draggable.options.refreshPositions) { + $.ui.ddmanager.prepareOffsets(draggable, event); + } + + //Run through all droppables and check their positions based on specific tolerance options + $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() { + + if(this.options.disabled || this.greedyChild || !this.visible) { + return; + } + + var parentInstance, scope, parent, + intersects = $.ui.intersect(draggable, this, this.options.tolerance), + c = !intersects && this.isover ? "isout" : (intersects && !this.isover ? "isover" : null); + if(!c) { + return; + } + + if (this.options.greedy) { + // find droppable parents with same scope + scope = this.options.scope; + parent = this.element.parents(":data(ui-droppable)").filter(function () { + return $.data(this, "ui-droppable").options.scope === scope; + }); + + if (parent.length) { + parentInstance = $.data(parent[0], "ui-droppable"); + parentInstance.greedyChild = (c === "isover"); + } + } + + // we just moved into a greedy child + if (parentInstance && c === "isover") { + parentInstance.isover = false; + parentInstance.isout = true; + parentInstance._out.call(parentInstance, event); + } + + this[c] = true; + this[c === "isout" ? "isover" : "isout"] = false; + this[c === "isover" ? "_over" : "_out"].call(this, event); + + // we just moved out of a greedy child + if (parentInstance && c === "isout") { + parentInstance.isout = false; + parentInstance.isover = true; + parentInstance._over.call(parentInstance, event); + } + }); + + }, + dragStop: function( draggable, event ) { + draggable.element.parentsUntil( "body" ).unbind( "scroll.droppable" ); + //Call prepareOffsets one final time since IE does not fire return scroll events when overflow was caused by drag (see #5003) + if( !draggable.options.refreshPositions ) { + $.ui.ddmanager.prepareOffsets( draggable, event ); + } + } +}; + +})(jQuery); +(function( $, undefined ) { + +function num(v) { + return parseInt(v, 10) || 0; +} + +function isNumber(value) { + return !isNaN(parseInt(value, 10)); +} + +$.widget("ui.resizable", $.ui.mouse, { + version: "1.10.0", + widgetEventPrefix: "resize", + options: { + alsoResize: false, + animate: false, + animateDuration: "slow", + animateEasing: "swing", + aspectRatio: false, + autoHide: false, + containment: false, + ghost: false, + grid: false, + handles: "e,s,se", + helper: false, + maxHeight: null, + maxWidth: null, + minHeight: 10, + minWidth: 10, + // See #7960 + zIndex: 90, + + // callbacks + resize: null, + start: null, + stop: null + }, + _create: function() { + + var n, i, handle, axis, hname, + that = this, + o = this.options; + this.element.addClass("ui-resizable"); + + $.extend(this, { + _aspectRatio: !!(o.aspectRatio), + aspectRatio: o.aspectRatio, + originalElement: this.element, + _proportionallyResizeElements: [], + _helper: o.helper || o.ghost || o.animate ? o.helper || "ui-resizable-helper" : null + }); + + //Wrap the element if it cannot hold child nodes + if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)) { + + //Create a wrapper element and set the wrapper to the new current internal element + this.element.wrap( + $("
").css({ + position: this.element.css("position"), + width: this.element.outerWidth(), + height: this.element.outerHeight(), + top: this.element.css("top"), + left: this.element.css("left") + }) + ); + + //Overwrite the original this.element + this.element = this.element.parent().data( + "ui-resizable", this.element.data("ui-resizable") + ); + + this.elementIsWrapper = true; + + //Move margins to the wrapper + this.element.css({ marginLeft: this.originalElement.css("marginLeft"), marginTop: this.originalElement.css("marginTop"), marginRight: this.originalElement.css("marginRight"), marginBottom: this.originalElement.css("marginBottom") }); + this.originalElement.css({ marginLeft: 0, marginTop: 0, marginRight: 0, marginBottom: 0}); + + //Prevent Safari textarea resize + this.originalResizeStyle = this.originalElement.css("resize"); + this.originalElement.css("resize", "none"); + + //Push the actual element to our proportionallyResize internal array + this._proportionallyResizeElements.push(this.originalElement.css({ position: "static", zoom: 1, display: "block" })); + + // avoid IE jump (hard set the margin) + this.originalElement.css({ margin: this.originalElement.css("margin") }); + + // fix handlers offset + this._proportionallyResize(); + + } + + this.handles = o.handles || (!$(".ui-resizable-handle", this.element).length ? "e,s,se" : { n: ".ui-resizable-n", e: ".ui-resizable-e", s: ".ui-resizable-s", w: ".ui-resizable-w", se: ".ui-resizable-se", sw: ".ui-resizable-sw", ne: ".ui-resizable-ne", nw: ".ui-resizable-nw" }); + if(this.handles.constructor === String) { + + if ( this.handles === "all") { + this.handles = "n,e,s,w,se,sw,ne,nw"; + } + + n = this.handles.split(","); + this.handles = {}; + + for(i = 0; i < n.length; i++) { + + handle = $.trim(n[i]); + hname = "ui-resizable-"+handle; + axis = $("
"); + + // Apply zIndex to all handles - see #7960 + axis.css({ zIndex: o.zIndex }); + + //TODO : What's going on here? + if ("se" === handle) { + axis.addClass("ui-icon ui-icon-gripsmall-diagonal-se"); + } + + //Insert into internal handles object and append to element + this.handles[handle] = ".ui-resizable-"+handle; + this.element.append(axis); + } + + } + + this._renderAxis = function(target) { + + var i, axis, padPos, padWrapper; + + target = target || this.element; + + for(i in this.handles) { + + if(this.handles[i].constructor === String) { + this.handles[i] = $(this.handles[i], this.element).show(); + } + + //Apply pad to wrapper element, needed to fix axis position (textarea, inputs, scrolls) + if (this.elementIsWrapper && this.originalElement[0].nodeName.match(/textarea|input|select|button/i)) { + + axis = $(this.handles[i], this.element); + + //Checking the correct pad and border + padWrapper = /sw|ne|nw|se|n|s/.test(i) ? axis.outerHeight() : axis.outerWidth(); + + //The padding type i have to apply... + padPos = [ "padding", + /ne|nw|n/.test(i) ? "Top" : + /se|sw|s/.test(i) ? "Bottom" : + /^e$/.test(i) ? "Right" : "Left" ].join(""); + + target.css(padPos, padWrapper); + + this._proportionallyResize(); + + } + + //TODO: What's that good for? There's not anything to be executed left + if(!$(this.handles[i]).length) { + continue; + } + } + }; + + //TODO: make renderAxis a prototype function + this._renderAxis(this.element); + + this._handles = $(".ui-resizable-handle", this.element) + .disableSelection(); + + //Matching axis name + this._handles.mouseover(function() { + if (!that.resizing) { + if (this.className) { + axis = this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i); + } + //Axis, default = se + that.axis = axis && axis[1] ? axis[1] : "se"; + } + }); + + //If we want to auto hide the elements + if (o.autoHide) { + this._handles.hide(); + $(this.element) + .addClass("ui-resizable-autohide") + .mouseenter(function() { + if (o.disabled) { + return; + } + $(this).removeClass("ui-resizable-autohide"); + that._handles.show(); + }) + .mouseleave(function(){ + if (o.disabled) { + return; + } + if (!that.resizing) { + $(this).addClass("ui-resizable-autohide"); + that._handles.hide(); + } + }); + } + + //Initialize the mouse interaction + this._mouseInit(); + + }, + + _destroy: function() { + + this._mouseDestroy(); + + var wrapper, + _destroy = function(exp) { + $(exp).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing") + .removeData("resizable").removeData("ui-resizable").unbind(".resizable").find(".ui-resizable-handle").remove(); + }; + + //TODO: Unwrap at same DOM position + if (this.elementIsWrapper) { + _destroy(this.element); + wrapper = this.element; + this.originalElement.css({ + position: wrapper.css("position"), + width: wrapper.outerWidth(), + height: wrapper.outerHeight(), + top: wrapper.css("top"), + left: wrapper.css("left") + }).insertAfter( wrapper ); + wrapper.remove(); + } + + this.originalElement.css("resize", this.originalResizeStyle); + _destroy(this.originalElement); + + return this; + }, + + _mouseCapture: function(event) { + var i, handle, + capture = false; + + for (i in this.handles) { + handle = $(this.handles[i])[0]; + if (handle === event.target || $.contains(handle, event.target)) { + capture = true; + } + } + + return !this.options.disabled && capture; + }, + + _mouseStart: function(event) { + + var curleft, curtop, cursor, + o = this.options, + iniPos = this.element.position(), + el = this.element; + + this.resizing = true; + + // bugfix for http://dev.jquery.com/ticket/1749 + if ( (/absolute/).test( el.css("position") ) ) { + el.css({ position: "absolute", top: el.css("top"), left: el.css("left") }); + } else if (el.is(".ui-draggable")) { + el.css({ position: "absolute", top: iniPos.top, left: iniPos.left }); + } + + this._renderProxy(); + + curleft = num(this.helper.css("left")); + curtop = num(this.helper.css("top")); + + if (o.containment) { + curleft += $(o.containment).scrollLeft() || 0; + curtop += $(o.containment).scrollTop() || 0; + } + + //Store needed variables + this.offset = this.helper.offset(); + this.position = { left: curleft, top: curtop }; + this.size = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() }; + this.originalSize = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() }; + this.originalPosition = { left: curleft, top: curtop }; + this.sizeDiff = { width: el.outerWidth() - el.width(), height: el.outerHeight() - el.height() }; + this.originalMousePosition = { left: event.pageX, top: event.pageY }; + + //Aspect Ratio + this.aspectRatio = (typeof o.aspectRatio === "number") ? o.aspectRatio : ((this.originalSize.width / this.originalSize.height) || 1); + + cursor = $(".ui-resizable-" + this.axis).css("cursor"); + $("body").css("cursor", cursor === "auto" ? this.axis + "-resize" : cursor); + + el.addClass("ui-resizable-resizing"); + this._propagate("start", event); + return true; + }, + + _mouseDrag: function(event) { + + //Increase performance, avoid regex + var data, + el = this.helper, props = {}, + smp = this.originalMousePosition, + a = this.axis, + prevTop = this.position.top, + prevLeft = this.position.left, + prevWidth = this.size.width, + prevHeight = this.size.height, + dx = (event.pageX-smp.left)||0, + dy = (event.pageY-smp.top)||0, + trigger = this._change[a]; + + if (!trigger) { + return false; + } + + // Calculate the attrs that will be change + data = trigger.apply(this, [event, dx, dy]); + + // Put this in the mouseDrag handler since the user can start pressing shift while resizing + this._updateVirtualBoundaries(event.shiftKey); + if (this._aspectRatio || event.shiftKey) { + data = this._updateRatio(data, event); + } + + data = this._respectSize(data, event); + + this._updateCache(data); + + // plugins callbacks need to be called first + this._propagate("resize", event); + + if (this.position.top !== prevTop) { + props.top = this.position.top + "px"; + } + if (this.position.left !== prevLeft) { + props.left = this.position.left + "px"; + } + if (this.size.width !== prevWidth) { + props.width = this.size.width + "px"; + } + if (this.size.height !== prevHeight) { + props.height = this.size.height + "px"; + } + el.css(props); + + if (!this._helper && this._proportionallyResizeElements.length) { + this._proportionallyResize(); + } + + // Call the user callback if the element was resized + if ( ! $.isEmptyObject(props) ) { + this._trigger("resize", event, this.ui()); + } + + return false; + }, + + _mouseStop: function(event) { + + this.resizing = false; + var pr, ista, soffseth, soffsetw, s, left, top, + o = this.options, that = this; + + if(this._helper) { + + pr = this._proportionallyResizeElements; + ista = pr.length && (/textarea/i).test(pr[0].nodeName); + soffseth = ista && $.ui.hasScroll(pr[0], "left") /* TODO - jump height */ ? 0 : that.sizeDiff.height; + soffsetw = ista ? 0 : that.sizeDiff.width; + + s = { width: (that.helper.width() - soffsetw), height: (that.helper.height() - soffseth) }; + left = (parseInt(that.element.css("left"), 10) + (that.position.left - that.originalPosition.left)) || null; + top = (parseInt(that.element.css("top"), 10) + (that.position.top - that.originalPosition.top)) || null; + + if (!o.animate) { + this.element.css($.extend(s, { top: top, left: left })); + } + + that.helper.height(that.size.height); + that.helper.width(that.size.width); + + if (this._helper && !o.animate) { + this._proportionallyResize(); + } + } + + $("body").css("cursor", "auto"); + + this.element.removeClass("ui-resizable-resizing"); + + this._propagate("stop", event); + + if (this._helper) { + this.helper.remove(); + } + + return false; + + }, + + _updateVirtualBoundaries: function(forceAspectRatio) { + var pMinWidth, pMaxWidth, pMinHeight, pMaxHeight, b, + o = this.options; + + b = { + minWidth: isNumber(o.minWidth) ? o.minWidth : 0, + maxWidth: isNumber(o.maxWidth) ? o.maxWidth : Infinity, + minHeight: isNumber(o.minHeight) ? o.minHeight : 0, + maxHeight: isNumber(o.maxHeight) ? o.maxHeight : Infinity + }; + + if(this._aspectRatio || forceAspectRatio) { + // We want to create an enclosing box whose aspect ration is the requested one + // First, compute the "projected" size for each dimension based on the aspect ratio and other dimension + pMinWidth = b.minHeight * this.aspectRatio; + pMinHeight = b.minWidth / this.aspectRatio; + pMaxWidth = b.maxHeight * this.aspectRatio; + pMaxHeight = b.maxWidth / this.aspectRatio; + + if(pMinWidth > b.minWidth) { + b.minWidth = pMinWidth; + } + if(pMinHeight > b.minHeight) { + b.minHeight = pMinHeight; + } + if(pMaxWidth < b.maxWidth) { + b.maxWidth = pMaxWidth; + } + if(pMaxHeight < b.maxHeight) { + b.maxHeight = pMaxHeight; + } + } + this._vBoundaries = b; + }, + + _updateCache: function(data) { + this.offset = this.helper.offset(); + if (isNumber(data.left)) { + this.position.left = data.left; + } + if (isNumber(data.top)) { + this.position.top = data.top; + } + if (isNumber(data.height)) { + this.size.height = data.height; + } + if (isNumber(data.width)) { + this.size.width = data.width; + } + }, + + _updateRatio: function( data ) { + + var cpos = this.position, + csize = this.size, + a = this.axis; + + if (isNumber(data.height)) { + data.width = (data.height * this.aspectRatio); + } else if (isNumber(data.width)) { + data.height = (data.width / this.aspectRatio); + } + + if (a === "sw") { + data.left = cpos.left + (csize.width - data.width); + data.top = null; + } + if (a === "nw") { + data.top = cpos.top + (csize.height - data.height); + data.left = cpos.left + (csize.width - data.width); + } + + return data; + }, + + _respectSize: function( data ) { + + var o = this._vBoundaries, + a = this.axis, + ismaxw = isNumber(data.width) && o.maxWidth && (o.maxWidth < data.width), ismaxh = isNumber(data.height) && o.maxHeight && (o.maxHeight < data.height), + isminw = isNumber(data.width) && o.minWidth && (o.minWidth > data.width), isminh = isNumber(data.height) && o.minHeight && (o.minHeight > data.height), + dw = this.originalPosition.left + this.originalSize.width, + dh = this.position.top + this.size.height, + cw = /sw|nw|w/.test(a), ch = /nw|ne|n/.test(a); + if (isminw) { + data.width = o.minWidth; + } + if (isminh) { + data.height = o.minHeight; + } + if (ismaxw) { + data.width = o.maxWidth; + } + if (ismaxh) { + data.height = o.maxHeight; + } + + if (isminw && cw) { + data.left = dw - o.minWidth; + } + if (ismaxw && cw) { + data.left = dw - o.maxWidth; + } + if (isminh && ch) { + data.top = dh - o.minHeight; + } + if (ismaxh && ch) { + data.top = dh - o.maxHeight; + } + + // fixing jump error on top/left - bug #2330 + if (!data.width && !data.height && !data.left && data.top) { + data.top = null; + } else if (!data.width && !data.height && !data.top && data.left) { + data.left = null; + } + + return data; + }, + + _proportionallyResize: function() { + + if (!this._proportionallyResizeElements.length) { + return; + } + + var i, j, borders, paddings, prel, + element = this.helper || this.element; + + for ( i=0; i < this._proportionallyResizeElements.length; i++) { + + prel = this._proportionallyResizeElements[i]; + + if (!this.borderDif) { + this.borderDif = []; + borders = [prel.css("borderTopWidth"), prel.css("borderRightWidth"), prel.css("borderBottomWidth"), prel.css("borderLeftWidth")]; + paddings = [prel.css("paddingTop"), prel.css("paddingRight"), prel.css("paddingBottom"), prel.css("paddingLeft")]; + + for ( j = 0; j < borders.length; j++ ) { + this.borderDif[ j ] = ( parseInt( borders[ j ], 10 ) || 0 ) + ( parseInt( paddings[ j ], 10 ) || 0 ); + } + } + + prel.css({ + height: (element.height() - this.borderDif[0] - this.borderDif[2]) || 0, + width: (element.width() - this.borderDif[1] - this.borderDif[3]) || 0 + }); + + } + + }, + + _renderProxy: function() { + + var el = this.element, o = this.options; + this.elementOffset = el.offset(); + + if(this._helper) { + + this.helper = this.helper || $("
"); + + this.helper.addClass(this._helper).css({ + width: this.element.outerWidth() - 1, + height: this.element.outerHeight() - 1, + position: "absolute", + left: this.elementOffset.left +"px", + top: this.elementOffset.top +"px", + zIndex: ++o.zIndex //TODO: Don't modify option + }); + + this.helper + .appendTo("body") + .disableSelection(); + + } else { + this.helper = this.element; + } + + }, + + _change: { + e: function(event, dx) { + return { width: this.originalSize.width + dx }; + }, + w: function(event, dx) { + var cs = this.originalSize, sp = this.originalPosition; + return { left: sp.left + dx, width: cs.width - dx }; + }, + n: function(event, dx, dy) { + var cs = this.originalSize, sp = this.originalPosition; + return { top: sp.top + dy, height: cs.height - dy }; + }, + s: function(event, dx, dy) { + return { height: this.originalSize.height + dy }; + }, + se: function(event, dx, dy) { + return $.extend(this._change.s.apply(this, arguments), this._change.e.apply(this, [event, dx, dy])); + }, + sw: function(event, dx, dy) { + return $.extend(this._change.s.apply(this, arguments), this._change.w.apply(this, [event, dx, dy])); + }, + ne: function(event, dx, dy) { + return $.extend(this._change.n.apply(this, arguments), this._change.e.apply(this, [event, dx, dy])); + }, + nw: function(event, dx, dy) { + return $.extend(this._change.n.apply(this, arguments), this._change.w.apply(this, [event, dx, dy])); + } + }, + + _propagate: function(n, event) { + $.ui.plugin.call(this, n, [event, this.ui()]); + (n !== "resize" && this._trigger(n, event, this.ui())); + }, + + plugins: {}, + + ui: function() { + return { + originalElement: this.originalElement, + element: this.element, + helper: this.helper, + position: this.position, + size: this.size, + originalSize: this.originalSize, + originalPosition: this.originalPosition + }; + } + +}); + +/* + * Resizable Extensions + */ + +$.ui.plugin.add("resizable", "animate", { + + stop: function( event ) { + var that = $(this).data("ui-resizable"), + o = that.options, + pr = that._proportionallyResizeElements, + ista = pr.length && (/textarea/i).test(pr[0].nodeName), + soffseth = ista && $.ui.hasScroll(pr[0], "left") /* TODO - jump height */ ? 0 : that.sizeDiff.height, + soffsetw = ista ? 0 : that.sizeDiff.width, + style = { width: (that.size.width - soffsetw), height: (that.size.height - soffseth) }, + left = (parseInt(that.element.css("left"), 10) + (that.position.left - that.originalPosition.left)) || null, + top = (parseInt(that.element.css("top"), 10) + (that.position.top - that.originalPosition.top)) || null; + + that.element.animate( + $.extend(style, top && left ? { top: top, left: left } : {}), { + duration: o.animateDuration, + easing: o.animateEasing, + step: function() { + + var data = { + width: parseInt(that.element.css("width"), 10), + height: parseInt(that.element.css("height"), 10), + top: parseInt(that.element.css("top"), 10), + left: parseInt(that.element.css("left"), 10) + }; + + if (pr && pr.length) { + $(pr[0]).css({ width: data.width, height: data.height }); + } + + // propagating resize, and updating values for each animation step + that._updateCache(data); + that._propagate("resize", event); + + } + } + ); + } + +}); + +$.ui.plugin.add("resizable", "containment", { + + start: function() { + var element, p, co, ch, cw, width, height, + that = $(this).data("ui-resizable"), + o = that.options, + el = that.element, + oc = o.containment, + ce = (oc instanceof $) ? oc.get(0) : (/parent/.test(oc)) ? el.parent().get(0) : oc; + + if (!ce) { + return; + } + + that.containerElement = $(ce); + + if (/document/.test(oc) || oc === document) { + that.containerOffset = { left: 0, top: 0 }; + that.containerPosition = { left: 0, top: 0 }; + + that.parentData = { + element: $(document), left: 0, top: 0, + width: $(document).width(), height: $(document).height() || document.body.parentNode.scrollHeight + }; + } + + // i'm a node, so compute top, left, right, bottom + else { + element = $(ce); + p = []; + $([ "Top", "Right", "Left", "Bottom" ]).each(function(i, name) { p[i] = num(element.css("padding" + name)); }); + + that.containerOffset = element.offset(); + that.containerPosition = element.position(); + that.containerSize = { height: (element.innerHeight() - p[3]), width: (element.innerWidth() - p[1]) }; + + co = that.containerOffset; + ch = that.containerSize.height; + cw = that.containerSize.width; + width = ($.ui.hasScroll(ce, "left") ? ce.scrollWidth : cw ); + height = ($.ui.hasScroll(ce) ? ce.scrollHeight : ch); + + that.parentData = { + element: ce, left: co.left, top: co.top, width: width, height: height + }; + } + }, + + resize: function( event ) { + var woset, hoset, isParent, isOffsetRelative, + that = $(this).data("ui-resizable"), + o = that.options, + co = that.containerOffset, cp = that.position, + pRatio = that._aspectRatio || event.shiftKey, + cop = { top:0, left:0 }, ce = that.containerElement; + + if (ce[0] !== document && (/static/).test(ce.css("position"))) { + cop = co; + } + + if (cp.left < (that._helper ? co.left : 0)) { + that.size.width = that.size.width + (that._helper ? (that.position.left - co.left) : (that.position.left - cop.left)); + if (pRatio) { + that.size.height = that.size.width / that.aspectRatio; + } + that.position.left = o.helper ? co.left : 0; + } + + if (cp.top < (that._helper ? co.top : 0)) { + that.size.height = that.size.height + (that._helper ? (that.position.top - co.top) : that.position.top); + if (pRatio) { + that.size.width = that.size.height * that.aspectRatio; + } + that.position.top = that._helper ? co.top : 0; + } + + that.offset.left = that.parentData.left+that.position.left; + that.offset.top = that.parentData.top+that.position.top; + + woset = Math.abs( (that._helper ? that.offset.left - cop.left : (that.offset.left - cop.left)) + that.sizeDiff.width ); + hoset = Math.abs( (that._helper ? that.offset.top - cop.top : (that.offset.top - co.top)) + that.sizeDiff.height ); + + isParent = that.containerElement.get(0) === that.element.parent().get(0); + isOffsetRelative = /relative|absolute/.test(that.containerElement.css("position")); + + if(isParent && isOffsetRelative) { + woset -= that.parentData.left; + } + + if (woset + that.size.width >= that.parentData.width) { + that.size.width = that.parentData.width - woset; + if (pRatio) { + that.size.height = that.size.width / that.aspectRatio; + } + } + + if (hoset + that.size.height >= that.parentData.height) { + that.size.height = that.parentData.height - hoset; + if (pRatio) { + that.size.width = that.size.height * that.aspectRatio; + } + } + }, + + stop: function(){ + var that = $(this).data("ui-resizable"), + o = that.options, + co = that.containerOffset, + cop = that.containerPosition, + ce = that.containerElement, + helper = $(that.helper), + ho = helper.offset(), + w = helper.outerWidth() - that.sizeDiff.width, + h = helper.outerHeight() - that.sizeDiff.height; + + if (that._helper && !o.animate && (/relative/).test(ce.css("position"))) { + $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h }); + } + + if (that._helper && !o.animate && (/static/).test(ce.css("position"))) { + $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h }); + } + + } +}); + +$.ui.plugin.add("resizable", "alsoResize", { + + start: function () { + var that = $(this).data("ui-resizable"), + o = that.options, + _store = function (exp) { + $(exp).each(function() { + var el = $(this); + el.data("ui-resizable-alsoresize", { + width: parseInt(el.width(), 10), height: parseInt(el.height(), 10), + left: parseInt(el.css("left"), 10), top: parseInt(el.css("top"), 10) + }); + }); + }; + + if (typeof(o.alsoResize) === "object" && !o.alsoResize.parentNode) { + if (o.alsoResize.length) { o.alsoResize = o.alsoResize[0]; _store(o.alsoResize); } + else { $.each(o.alsoResize, function (exp) { _store(exp); }); } + }else{ + _store(o.alsoResize); + } + }, + + resize: function (event, ui) { + var that = $(this).data("ui-resizable"), + o = that.options, + os = that.originalSize, + op = that.originalPosition, + delta = { + height: (that.size.height - os.height) || 0, width: (that.size.width - os.width) || 0, + top: (that.position.top - op.top) || 0, left: (that.position.left - op.left) || 0 + }, + + _alsoResize = function (exp, c) { + $(exp).each(function() { + var el = $(this), start = $(this).data("ui-resizable-alsoresize"), style = {}, + css = c && c.length ? c : el.parents(ui.originalElement[0]).length ? ["width", "height"] : ["width", "height", "top", "left"]; + + $.each(css, function (i, prop) { + var sum = (start[prop]||0) + (delta[prop]||0); + if (sum && sum >= 0) { + style[prop] = sum || null; + } + }); + + el.css(style); + }); + }; + + if (typeof(o.alsoResize) === "object" && !o.alsoResize.nodeType) { + $.each(o.alsoResize, function (exp, c) { _alsoResize(exp, c); }); + }else{ + _alsoResize(o.alsoResize); + } + }, + + stop: function () { + $(this).removeData("resizable-alsoresize"); + } +}); + +$.ui.plugin.add("resizable", "ghost", { + + start: function() { + + var that = $(this).data("ui-resizable"), o = that.options, cs = that.size; + + that.ghost = that.originalElement.clone(); + that.ghost + .css({ opacity: 0.25, display: "block", position: "relative", height: cs.height, width: cs.width, margin: 0, left: 0, top: 0 }) + .addClass("ui-resizable-ghost") + .addClass(typeof o.ghost === "string" ? o.ghost : ""); + + that.ghost.appendTo(that.helper); + + }, + + resize: function(){ + var that = $(this).data("ui-resizable"); + if (that.ghost) { + that.ghost.css({ position: "relative", height: that.size.height, width: that.size.width }); + } + }, + + stop: function() { + var that = $(this).data("ui-resizable"); + if (that.ghost && that.helper) { + that.helper.get(0).removeChild(that.ghost.get(0)); + } + } + +}); + +$.ui.plugin.add("resizable", "grid", { + + resize: function() { + var that = $(this).data("ui-resizable"), + o = that.options, + cs = that.size, + os = that.originalSize, + op = that.originalPosition, + a = that.axis, + grid = typeof o.grid === "number" ? [o.grid, o.grid] : o.grid, + gridX = (grid[0]||1), + gridY = (grid[1]||1), + ox = Math.round((cs.width - os.width) / gridX) * gridX, + oy = Math.round((cs.height - os.height) / gridY) * gridY, + newWidth = os.width + ox, + newHeight = os.height + oy, + isMaxWidth = o.maxWidth && (o.maxWidth < newWidth), + isMaxHeight = o.maxHeight && (o.maxHeight < newHeight), + isMinWidth = o.minWidth && (o.minWidth > newWidth), + isMinHeight = o.minHeight && (o.minHeight > newHeight); + + o.grid = grid; + + if (isMinWidth) { + newWidth = newWidth + gridX; + } + if (isMinHeight) { + newHeight = newHeight + gridY; + } + if (isMaxWidth) { + newWidth = newWidth - gridX; + } + if (isMaxHeight) { + newHeight = newHeight - gridY; + } + + if (/^(se|s|e)$/.test(a)) { + that.size.width = newWidth; + that.size.height = newHeight; + } else if (/^(ne)$/.test(a)) { + that.size.width = newWidth; + that.size.height = newHeight; + that.position.top = op.top - oy; + } else if (/^(sw)$/.test(a)) { + that.size.width = newWidth; + that.size.height = newHeight; + that.position.left = op.left - ox; + } else { + that.size.width = newWidth; + that.size.height = newHeight; + that.position.top = op.top - oy; + that.position.left = op.left - ox; + } + } + +}); + +})(jQuery); +(function( $, undefined ) { + +$.widget("ui.selectable", $.ui.mouse, { + version: "1.10.0", + options: { + appendTo: "body", + autoRefresh: true, + distance: 0, + filter: "*", + tolerance: "touch", + + // callbacks + selected: null, + selecting: null, + start: null, + stop: null, + unselected: null, + unselecting: null + }, + _create: function() { + var selectees, + that = this; + + this.element.addClass("ui-selectable"); + + this.dragged = false; + + // cache selectee children based on filter + this.refresh = function() { + selectees = $(that.options.filter, that.element[0]); + selectees.addClass("ui-selectee"); + selectees.each(function() { + var $this = $(this), + pos = $this.offset(); + $.data(this, "selectable-item", { + element: this, + $element: $this, + left: pos.left, + top: pos.top, + right: pos.left + $this.outerWidth(), + bottom: pos.top + $this.outerHeight(), + startselected: false, + selected: $this.hasClass("ui-selected"), + selecting: $this.hasClass("ui-selecting"), + unselecting: $this.hasClass("ui-unselecting") + }); + }); + }; + this.refresh(); + + this.selectees = selectees.addClass("ui-selectee"); + + this._mouseInit(); + + this.helper = $("
"); + }, + + _destroy: function() { + this.selectees + .removeClass("ui-selectee") + .removeData("selectable-item"); + this.element + .removeClass("ui-selectable ui-selectable-disabled"); + this._mouseDestroy(); + }, + + _mouseStart: function(event) { + var that = this, + options = this.options; + + this.opos = [event.pageX, event.pageY]; + + if (this.options.disabled) { + return; + } + + this.selectees = $(options.filter, this.element[0]); + + this._trigger("start", event); + + $(options.appendTo).append(this.helper); + // position helper (lasso) + this.helper.css({ + "left": event.pageX, + "top": event.pageY, + "width": 0, + "height": 0 + }); + + if (options.autoRefresh) { + this.refresh(); + } + + this.selectees.filter(".ui-selected").each(function() { + var selectee = $.data(this, "selectable-item"); + selectee.startselected = true; + if (!event.metaKey && !event.ctrlKey) { + selectee.$element.removeClass("ui-selected"); + selectee.selected = false; + selectee.$element.addClass("ui-unselecting"); + selectee.unselecting = true; + // selectable UNSELECTING callback + that._trigger("unselecting", event, { + unselecting: selectee.element + }); + } + }); + + $(event.target).parents().addBack().each(function() { + var doSelect, + selectee = $.data(this, "selectable-item"); + if (selectee) { + doSelect = (!event.metaKey && !event.ctrlKey) || !selectee.$element.hasClass("ui-selected"); + selectee.$element + .removeClass(doSelect ? "ui-unselecting" : "ui-selected") + .addClass(doSelect ? "ui-selecting" : "ui-unselecting"); + selectee.unselecting = !doSelect; + selectee.selecting = doSelect; + selectee.selected = doSelect; + // selectable (UN)SELECTING callback + if (doSelect) { + that._trigger("selecting", event, { + selecting: selectee.element + }); + } else { + that._trigger("unselecting", event, { + unselecting: selectee.element + }); + } + return false; + } + }); + + }, + + _mouseDrag: function(event) { + + this.dragged = true; + + if (this.options.disabled) { + return; + } + + var tmp, + that = this, + options = this.options, + x1 = this.opos[0], + y1 = this.opos[1], + x2 = event.pageX, + y2 = event.pageY; + + if (x1 > x2) { tmp = x2; x2 = x1; x1 = tmp; } + if (y1 > y2) { tmp = y2; y2 = y1; y1 = tmp; } + this.helper.css({left: x1, top: y1, width: x2-x1, height: y2-y1}); + + this.selectees.each(function() { + var selectee = $.data(this, "selectable-item"), + hit = false; + + //prevent helper from being selected if appendTo: selectable + if (!selectee || selectee.element === that.element[0]) { + return; + } + + if (options.tolerance === "touch") { + hit = ( !(selectee.left > x2 || selectee.right < x1 || selectee.top > y2 || selectee.bottom < y1) ); + } else if (options.tolerance === "fit") { + hit = (selectee.left > x1 && selectee.right < x2 && selectee.top > y1 && selectee.bottom < y2); + } + + if (hit) { + // SELECT + if (selectee.selected) { + selectee.$element.removeClass("ui-selected"); + selectee.selected = false; + } + if (selectee.unselecting) { + selectee.$element.removeClass("ui-unselecting"); + selectee.unselecting = false; + } + if (!selectee.selecting) { + selectee.$element.addClass("ui-selecting"); + selectee.selecting = true; + // selectable SELECTING callback + that._trigger("selecting", event, { + selecting: selectee.element + }); + } + } else { + // UNSELECT + if (selectee.selecting) { + if ((event.metaKey || event.ctrlKey) && selectee.startselected) { + selectee.$element.removeClass("ui-selecting"); + selectee.selecting = false; + selectee.$element.addClass("ui-selected"); + selectee.selected = true; + } else { + selectee.$element.removeClass("ui-selecting"); + selectee.selecting = false; + if (selectee.startselected) { + selectee.$element.addClass("ui-unselecting"); + selectee.unselecting = true; + } + // selectable UNSELECTING callback + that._trigger("unselecting", event, { + unselecting: selectee.element + }); + } + } + if (selectee.selected) { + if (!event.metaKey && !event.ctrlKey && !selectee.startselected) { + selectee.$element.removeClass("ui-selected"); + selectee.selected = false; + + selectee.$element.addClass("ui-unselecting"); + selectee.unselecting = true; + // selectable UNSELECTING callback + that._trigger("unselecting", event, { + unselecting: selectee.element + }); + } + } + } + }); + + return false; + }, + + _mouseStop: function(event) { + var that = this; + + this.dragged = false; + + $(".ui-unselecting", this.element[0]).each(function() { + var selectee = $.data(this, "selectable-item"); + selectee.$element.removeClass("ui-unselecting"); + selectee.unselecting = false; + selectee.startselected = false; + that._trigger("unselected", event, { + unselected: selectee.element + }); + }); + $(".ui-selecting", this.element[0]).each(function() { + var selectee = $.data(this, "selectable-item"); + selectee.$element.removeClass("ui-selecting").addClass("ui-selected"); + selectee.selecting = false; + selectee.selected = true; + selectee.startselected = true; + that._trigger("selected", event, { + selected: selectee.element + }); + }); + this._trigger("stop", event); + + this.helper.remove(); + + return false; + } + +}); + +})(jQuery); +(function( $, undefined ) { + +/*jshint loopfunc: true */ + +function isOverAxis( x, reference, size ) { + return ( x > reference ) && ( x < ( reference + size ) ); +} + +$.widget("ui.sortable", $.ui.mouse, { + version: "1.10.0", + widgetEventPrefix: "sort", + ready: false, + options: { + appendTo: "parent", + axis: false, + connectWith: false, + containment: false, + cursor: "auto", + cursorAt: false, + dropOnEmpty: true, + forcePlaceholderSize: false, + forceHelperSize: false, + grid: false, + handle: false, + helper: "original", + items: "> *", + opacity: false, + placeholder: false, + revert: false, + scroll: true, + scrollSensitivity: 20, + scrollSpeed: 20, + scope: "default", + tolerance: "intersect", + zIndex: 1000, + + // callbacks + activate: null, + beforeStop: null, + change: null, + deactivate: null, + out: null, + over: null, + receive: null, + remove: null, + sort: null, + start: null, + stop: null, + update: null + }, + _create: function() { + + var o = this.options; + this.containerCache = {}; + this.element.addClass("ui-sortable"); + + //Get the items + this.refresh(); + + //Let's determine if the items are being displayed horizontally + this.floating = this.items.length ? o.axis === "x" || (/left|right/).test(this.items[0].item.css("float")) || (/inline|table-cell/).test(this.items[0].item.css("display")) : false; + + //Let's determine the parent's offset + this.offset = this.element.offset(); + + //Initialize mouse events for interaction + this._mouseInit(); + + //We're ready to go + this.ready = true; + + }, + + _destroy: function() { + this.element + .removeClass("ui-sortable ui-sortable-disabled"); + this._mouseDestroy(); + + for ( var i = this.items.length - 1; i >= 0; i-- ) { + this.items[i].item.removeData(this.widgetName + "-item"); + } + + return this; + }, + + _setOption: function(key, value){ + if ( key === "disabled" ) { + this.options[ key ] = value; + + this.widget().toggleClass( "ui-sortable-disabled", !!value ); + } else { + // Don't call widget base _setOption for disable as it adds ui-state-disabled class + $.Widget.prototype._setOption.apply(this, arguments); + } + }, + + _mouseCapture: function(event, overrideHandle) { + var currentItem = null, + validHandle = false, + that = this; + + if (this.reverting) { + return false; + } + + if(this.options.disabled || this.options.type === "static") { + return false; + } + + //We have to refresh the items data once first + this._refreshItems(event); + + //Find out if the clicked node (or one of its parents) is a actual item in this.items + $(event.target).parents().each(function() { + if($.data(this, that.widgetName + "-item") === that) { + currentItem = $(this); + return false; + } + }); + if($.data(event.target, that.widgetName + "-item") === that) { + currentItem = $(event.target); + } + + if(!currentItem) { + return false; + } + if(this.options.handle && !overrideHandle) { + $(this.options.handle, currentItem).find("*").addBack().each(function() { + if(this === event.target) { + validHandle = true; + } + }); + if(!validHandle) { + return false; + } + } + + this.currentItem = currentItem; + this._removeCurrentsFromItems(); + return true; + + }, + + _mouseStart: function(event, overrideHandle, noActivation) { + + var i, + o = this.options; + + this.currentContainer = this; + + //We only need to call refreshPositions, because the refreshItems call has been moved to mouseCapture + this.refreshPositions(); + + //Create and append the visible helper + this.helper = this._createHelper(event); + + //Cache the helper size + this._cacheHelperProportions(); + + /* + * - Position generation - + * This block generates everything position related - it's the core of draggables. + */ + + //Cache the margins of the original element + this._cacheMargins(); + + //Get the next scrolling parent + this.scrollParent = this.helper.scrollParent(); + + //The element's absolute position on the page minus margins + this.offset = this.currentItem.offset(); + this.offset = { + top: this.offset.top - this.margins.top, + left: this.offset.left - this.margins.left + }; + + $.extend(this.offset, { + click: { //Where the click happened, relative to the element + left: event.pageX - this.offset.left, + top: event.pageY - this.offset.top + }, + parent: this._getParentOffset(), + relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper + }); + + // Only after we got the offset, we can change the helper's position to absolute + // TODO: Still need to figure out a way to make relative sorting possible + this.helper.css("position", "absolute"); + this.cssPosition = this.helper.css("position"); + + //Generate the original position + this.originalPosition = this._generatePosition(event); + this.originalPageX = event.pageX; + this.originalPageY = event.pageY; + + //Adjust the mouse offset relative to the helper if "cursorAt" is supplied + (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt)); + + //Cache the former DOM position + this.domPosition = { prev: this.currentItem.prev()[0], parent: this.currentItem.parent()[0] }; + + //If the helper is not the original, hide the original so it's not playing any role during the drag, won't cause anything bad this way + if(this.helper[0] !== this.currentItem[0]) { + this.currentItem.hide(); + } + + //Create the placeholder + this._createPlaceholder(); + + //Set a containment if given in the options + if(o.containment) { + this._setContainment(); + } + + if(o.cursor) { // cursor option + if ($("body").css("cursor")) { + this._storedCursor = $("body").css("cursor"); + } + $("body").css("cursor", o.cursor); + } + + if(o.opacity) { // opacity option + if (this.helper.css("opacity")) { + this._storedOpacity = this.helper.css("opacity"); + } + this.helper.css("opacity", o.opacity); + } + + if(o.zIndex) { // zIndex option + if (this.helper.css("zIndex")) { + this._storedZIndex = this.helper.css("zIndex"); + } + this.helper.css("zIndex", o.zIndex); + } + + //Prepare scrolling + if(this.scrollParent[0] !== document && this.scrollParent[0].tagName !== "HTML") { + this.overflowOffset = this.scrollParent.offset(); + } + + //Call callbacks + this._trigger("start", event, this._uiHash()); + + //Recache the helper size + if(!this._preserveHelperProportions) { + this._cacheHelperProportions(); + } + + + //Post "activate" events to possible containers + if( !noActivation ) { + for ( i = this.containers.length - 1; i >= 0; i-- ) { + this.containers[ i ]._trigger( "activate", event, this._uiHash( this ) ); + } + } + + //Prepare possible droppables + if($.ui.ddmanager) { + $.ui.ddmanager.current = this; + } + + if ($.ui.ddmanager && !o.dropBehaviour) { + $.ui.ddmanager.prepareOffsets(this, event); + } + + this.dragging = true; + + this.helper.addClass("ui-sortable-helper"); + this._mouseDrag(event); //Execute the drag once - this causes the helper not to be visible before getting its correct position + return true; + + }, + + _mouseDrag: function(event) { + var i, item, itemElement, intersection, + o = this.options, + scrolled = false; + + //Compute the helpers position + this.position = this._generatePosition(event); + this.positionAbs = this._convertPositionTo("absolute"); + + if (!this.lastPositionAbs) { + this.lastPositionAbs = this.positionAbs; + } + + //Do scrolling + if(this.options.scroll) { + if(this.scrollParent[0] !== document && this.scrollParent[0].tagName !== "HTML") { + + if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) { + this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed; + } else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity) { + this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed; + } + + if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) { + this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed; + } else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity) { + this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed; + } + + } else { + + if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) { + scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed); + } else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) { + scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed); + } + + if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) { + scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed); + } else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) { + scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed); + } + + } + + if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) { + $.ui.ddmanager.prepareOffsets(this, event); + } + } + + //Regenerate the absolute position used for position checks + this.positionAbs = this._convertPositionTo("absolute"); + + //Set the helper position + if(!this.options.axis || this.options.axis !== "y") { + this.helper[0].style.left = this.position.left+"px"; + } + if(!this.options.axis || this.options.axis !== "x") { + this.helper[0].style.top = this.position.top+"px"; + } + + //Rearrange + for (i = this.items.length - 1; i >= 0; i--) { + + //Cache variables and intersection, continue if no intersection + item = this.items[i]; + itemElement = item.item[0]; + intersection = this._intersectsWithPointer(item); + if (!intersection) { + continue; + } + + // Only put the placeholder inside the current Container, skip all + // items form other containers. This works because when moving + // an item from one container to another the + // currentContainer is switched before the placeholder is moved. + // + // Without this moving items in "sub-sortables" can cause the placeholder to jitter + // beetween the outer and inner container. + if (item.instance !== this.currentContainer) { + continue; + } + + // cannot intersect with itself + // no useless actions that have been done before + // no action if the item moved is the parent of the item checked + if (itemElement !== this.currentItem[0] && + this.placeholder[intersection === 1 ? "next" : "prev"]()[0] !== itemElement && + !$.contains(this.placeholder[0], itemElement) && + (this.options.type === "semi-dynamic" ? !$.contains(this.element[0], itemElement) : true) + ) { + + this.direction = intersection === 1 ? "down" : "up"; + + if (this.options.tolerance === "pointer" || this._intersectsWithSides(item)) { + this._rearrange(event, item); + } else { + break; + } + + this._trigger("change", event, this._uiHash()); + break; + } + } + + //Post events to containers + this._contactContainers(event); + + //Interconnect with droppables + if($.ui.ddmanager) { + $.ui.ddmanager.drag(this, event); + } + + //Call callbacks + this._trigger("sort", event, this._uiHash()); + + this.lastPositionAbs = this.positionAbs; + return false; + + }, + + _mouseStop: function(event, noPropagation) { + + if(!event) { + return; + } + + //If we are using droppables, inform the manager about the drop + if ($.ui.ddmanager && !this.options.dropBehaviour) { + $.ui.ddmanager.drop(this, event); + } + + if(this.options.revert) { + var that = this, + cur = this.placeholder.offset(); + + this.reverting = true; + + $(this.helper).animate({ + left: cur.left - this.offset.parent.left - this.margins.left + (this.offsetParent[0] === document.body ? 0 : this.offsetParent[0].scrollLeft), + top: cur.top - this.offset.parent.top - this.margins.top + (this.offsetParent[0] === document.body ? 0 : this.offsetParent[0].scrollTop) + }, parseInt(this.options.revert, 10) || 500, function() { + that._clear(event); + }); + } else { + this._clear(event, noPropagation); + } + + return false; + + }, + + cancel: function() { + + if(this.dragging) { + + this._mouseUp({ target: null }); + + if(this.options.helper === "original") { + this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"); + } else { + this.currentItem.show(); + } + + //Post deactivating events to containers + for (var i = this.containers.length - 1; i >= 0; i--){ + this.containers[i]._trigger("deactivate", null, this._uiHash(this)); + if(this.containers[i].containerCache.over) { + this.containers[i]._trigger("out", null, this._uiHash(this)); + this.containers[i].containerCache.over = 0; + } + } + + } + + if (this.placeholder) { + //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node! + if(this.placeholder[0].parentNode) { + this.placeholder[0].parentNode.removeChild(this.placeholder[0]); + } + if(this.options.helper !== "original" && this.helper && this.helper[0].parentNode) { + this.helper.remove(); + } + + $.extend(this, { + helper: null, + dragging: false, + reverting: false, + _noFinalSort: null + }); + + if(this.domPosition.prev) { + $(this.domPosition.prev).after(this.currentItem); + } else { + $(this.domPosition.parent).prepend(this.currentItem); + } + } + + return this; + + }, + + serialize: function(o) { + + var items = this._getItemsAsjQuery(o && o.connected), + str = []; + o = o || {}; + + $(items).each(function() { + var res = ($(o.item || this).attr(o.attribute || "id") || "").match(o.expression || (/(.+)[\-=_](.+)/)); + if (res) { + str.push((o.key || res[1]+"[]")+"="+(o.key && o.expression ? res[1] : res[2])); + } + }); + + if(!str.length && o.key) { + str.push(o.key + "="); + } + + return str.join("&"); + + }, + + toArray: function(o) { + + var items = this._getItemsAsjQuery(o && o.connected), + ret = []; + + o = o || {}; + + items.each(function() { ret.push($(o.item || this).attr(o.attribute || "id") || ""); }); + return ret; + + }, + + /* Be careful with the following core functions */ + _intersectsWith: function(item) { + + var x1 = this.positionAbs.left, + x2 = x1 + this.helperProportions.width, + y1 = this.positionAbs.top, + y2 = y1 + this.helperProportions.height, + l = item.left, + r = l + item.width, + t = item.top, + b = t + item.height, + dyClick = this.offset.click.top, + dxClick = this.offset.click.left, + isOverElement = (y1 + dyClick) > t && (y1 + dyClick) < b && (x1 + dxClick) > l && (x1 + dxClick) < r; + + if ( this.options.tolerance === "pointer" || + this.options.forcePointerForContainers || + (this.options.tolerance !== "pointer" && this.helperProportions[this.floating ? "width" : "height"] > item[this.floating ? "width" : "height"]) + ) { + return isOverElement; + } else { + + return (l < x1 + (this.helperProportions.width / 2) && // Right Half + x2 - (this.helperProportions.width / 2) < r && // Left Half + t < y1 + (this.helperProportions.height / 2) && // Bottom Half + y2 - (this.helperProportions.height / 2) < b ); // Top Half + + } + }, + + _intersectsWithPointer: function(item) { + + var isOverElementHeight = (this.options.axis === "x") || isOverAxis(this.positionAbs.top + this.offset.click.top, item.top, item.height), + isOverElementWidth = (this.options.axis === "y") || isOverAxis(this.positionAbs.left + this.offset.click.left, item.left, item.width), + isOverElement = isOverElementHeight && isOverElementWidth, + verticalDirection = this._getDragVerticalDirection(), + horizontalDirection = this._getDragHorizontalDirection(); + + if (!isOverElement) { + return false; + } + + return this.floating ? + ( ((horizontalDirection && horizontalDirection === "right") || verticalDirection === "down") ? 2 : 1 ) + : ( verticalDirection && (verticalDirection === "down" ? 2 : 1) ); + + }, + + _intersectsWithSides: function(item) { + + var isOverBottomHalf = isOverAxis(this.positionAbs.top + this.offset.click.top, item.top + (item.height/2), item.height), + isOverRightHalf = isOverAxis(this.positionAbs.left + this.offset.click.left, item.left + (item.width/2), item.width), + verticalDirection = this._getDragVerticalDirection(), + horizontalDirection = this._getDragHorizontalDirection(); + + if (this.floating && horizontalDirection) { + return ((horizontalDirection === "right" && isOverRightHalf) || (horizontalDirection === "left" && !isOverRightHalf)); + } else { + return verticalDirection && ((verticalDirection === "down" && isOverBottomHalf) || (verticalDirection === "up" && !isOverBottomHalf)); + } + + }, + + _getDragVerticalDirection: function() { + var delta = this.positionAbs.top - this.lastPositionAbs.top; + return delta !== 0 && (delta > 0 ? "down" : "up"); + }, + + _getDragHorizontalDirection: function() { + var delta = this.positionAbs.left - this.lastPositionAbs.left; + return delta !== 0 && (delta > 0 ? "right" : "left"); + }, + + refresh: function(event) { + this._refreshItems(event); + this.refreshPositions(); + return this; + }, + + _connectWith: function() { + var options = this.options; + return options.connectWith.constructor === String ? [options.connectWith] : options.connectWith; + }, + + _getItemsAsjQuery: function(connected) { + + var i, j, cur, inst, + items = [], + queries = [], + connectWith = this._connectWith(); + + if(connectWith && connected) { + for (i = connectWith.length - 1; i >= 0; i--){ + cur = $(connectWith[i]); + for ( j = cur.length - 1; j >= 0; j--){ + inst = $.data(cur[j], this.widgetFullName); + if(inst && inst !== this && !inst.options.disabled) { + queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element) : $(inst.options.items, inst.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"), inst]); + } + } + } + } + + queries.push([$.isFunction(this.options.items) ? this.options.items.call(this.element, null, { options: this.options, item: this.currentItem }) : $(this.options.items, this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"), this]); + + for (i = queries.length - 1; i >= 0; i--){ + queries[i][0].each(function() { + items.push(this); + }); + } + + return $(items); + + }, + + _removeCurrentsFromItems: function() { + + var list = this.currentItem.find(":data(" + this.widgetName + "-item)"); + + this.items = $.grep(this.items, function (item) { + for (var j=0; j < list.length; j++) { + if(list[j] === item.item[0]) { + return false; + } + } + return true; + }); + + }, + + _refreshItems: function(event) { + + this.items = []; + this.containers = [this]; + + var i, j, cur, inst, targetData, _queries, item, queriesLength, + items = this.items, + queries = [[$.isFunction(this.options.items) ? this.options.items.call(this.element[0], event, { item: this.currentItem }) : $(this.options.items, this.element), this]], + connectWith = this._connectWith(); + + if(connectWith && this.ready) { //Shouldn't be run the first time through due to massive slow-down + for (i = connectWith.length - 1; i >= 0; i--){ + cur = $(connectWith[i]); + for (j = cur.length - 1; j >= 0; j--){ + inst = $.data(cur[j], this.widgetFullName); + if(inst && inst !== this && !inst.options.disabled) { + queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element[0], event, { item: this.currentItem }) : $(inst.options.items, inst.element), inst]); + this.containers.push(inst); + } + } + } + } + + for (i = queries.length - 1; i >= 0; i--) { + targetData = queries[i][1]; + _queries = queries[i][0]; + + for (j=0, queriesLength = _queries.length; j < queriesLength; j++) { + item = $(_queries[j]); + + item.data(this.widgetName + "-item", targetData); // Data for target checking (mouse manager) + + items.push({ + item: item, + instance: targetData, + width: 0, height: 0, + left: 0, top: 0 + }); + } + } + + }, + + refreshPositions: function(fast) { + + //This has to be redone because due to the item being moved out/into the offsetParent, the offsetParent's position will change + if(this.offsetParent && this.helper) { + this.offset.parent = this._getParentOffset(); + } + + var i, item, t, p; + + for (i = this.items.length - 1; i >= 0; i--){ + item = this.items[i]; + + //We ignore calculating positions of all connected containers when we're not over them + if(item.instance !== this.currentContainer && this.currentContainer && item.item[0] !== this.currentItem[0]) { + continue; + } + + t = this.options.toleranceElement ? $(this.options.toleranceElement, item.item) : item.item; + + if (!fast) { + item.width = t.outerWidth(); + item.height = t.outerHeight(); + } + + p = t.offset(); + item.left = p.left; + item.top = p.top; + } + + if(this.options.custom && this.options.custom.refreshContainers) { + this.options.custom.refreshContainers.call(this); + } else { + for (i = this.containers.length - 1; i >= 0; i--){ + p = this.containers[i].element.offset(); + this.containers[i].containerCache.left = p.left; + this.containers[i].containerCache.top = p.top; + this.containers[i].containerCache.width = this.containers[i].element.outerWidth(); + this.containers[i].containerCache.height = this.containers[i].element.outerHeight(); + } + } + + return this; + }, + + _createPlaceholder: function(that) { + that = that || this; + var className, + o = that.options; + + if(!o.placeholder || o.placeholder.constructor === String) { + className = o.placeholder; + o.placeholder = { + element: function() { + + var el = $(document.createElement(that.currentItem[0].nodeName)) + .addClass(className || that.currentItem[0].className+" ui-sortable-placeholder") + .removeClass("ui-sortable-helper")[0]; + + if(!className) { + el.style.visibility = "hidden"; + } + + return el; + }, + update: function(container, p) { + + // 1. If a className is set as 'placeholder option, we don't force sizes - the class is responsible for that + // 2. The option 'forcePlaceholderSize can be enabled to force it even if a class name is specified + if(className && !o.forcePlaceholderSize) { + return; + } + + //If the element doesn't have a actual height by itself (without styles coming from a stylesheet), it receives the inline height from the dragged item + if(!p.height()) { p.height(that.currentItem.innerHeight() - parseInt(that.currentItem.css("paddingTop")||0, 10) - parseInt(that.currentItem.css("paddingBottom")||0, 10)); } + if(!p.width()) { p.width(that.currentItem.innerWidth() - parseInt(that.currentItem.css("paddingLeft")||0, 10) - parseInt(that.currentItem.css("paddingRight")||0, 10)); } + } + }; + } + + //Create the placeholder + that.placeholder = $(o.placeholder.element.call(that.element, that.currentItem)); + + //Append it after the actual current item + that.currentItem.after(that.placeholder); + + //Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317) + o.placeholder.update(that, that.placeholder); + + }, + + _contactContainers: function(event) { + var i, j, dist, itemWithLeastDistance, posProperty, sizeProperty, base, cur, nearBottom, + innermostContainer = null, + innermostIndex = null; + + // get innermost container that intersects with item + for (i = this.containers.length - 1; i >= 0; i--) { + + // never consider a container that's located within the item itself + if($.contains(this.currentItem[0], this.containers[i].element[0])) { + continue; + } + + if(this._intersectsWith(this.containers[i].containerCache)) { + + // if we've already found a container and it's more "inner" than this, then continue + if(innermostContainer && $.contains(this.containers[i].element[0], innermostContainer.element[0])) { + continue; + } + + innermostContainer = this.containers[i]; + innermostIndex = i; + + } else { + // container doesn't intersect. trigger "out" event if necessary + if(this.containers[i].containerCache.over) { + this.containers[i]._trigger("out", event, this._uiHash(this)); + this.containers[i].containerCache.over = 0; + } + } + + } + + // if no intersecting containers found, return + if(!innermostContainer) { + return; + } + + // move the item into the container if it's not there already + if(this.containers.length === 1) { + this.containers[innermostIndex]._trigger("over", event, this._uiHash(this)); + this.containers[innermostIndex].containerCache.over = 1; + } else { + + //When entering a new container, we will find the item with the least distance and append our item near it + dist = 10000; + itemWithLeastDistance = null; + posProperty = this.containers[innermostIndex].floating ? "left" : "top"; + sizeProperty = this.containers[innermostIndex].floating ? "width" : "height"; + base = this.positionAbs[posProperty] + this.offset.click[posProperty]; + for (j = this.items.length - 1; j >= 0; j--) { + if(!$.contains(this.containers[innermostIndex].element[0], this.items[j].item[0])) { + continue; + } + if(this.items[j].item[0] === this.currentItem[0]) { + continue; + } + cur = this.items[j].item.offset()[posProperty]; + nearBottom = false; + if(Math.abs(cur - base) > Math.abs(cur + this.items[j][sizeProperty] - base)){ + nearBottom = true; + cur += this.items[j][sizeProperty]; + } + + if(Math.abs(cur - base) < dist) { + dist = Math.abs(cur - base); itemWithLeastDistance = this.items[j]; + this.direction = nearBottom ? "up": "down"; + } + } + + //Check if dropOnEmpty is enabled + if(!itemWithLeastDistance && !this.options.dropOnEmpty) { + return; + } + + this.currentContainer = this.containers[innermostIndex]; + itemWithLeastDistance ? this._rearrange(event, itemWithLeastDistance, null, true) : this._rearrange(event, null, this.containers[innermostIndex].element, true); + this._trigger("change", event, this._uiHash()); + this.containers[innermostIndex]._trigger("change", event, this._uiHash(this)); + + //Update the placeholder + this.options.placeholder.update(this.currentContainer, this.placeholder); + + this.containers[innermostIndex]._trigger("over", event, this._uiHash(this)); + this.containers[innermostIndex].containerCache.over = 1; + } + + + }, + + _createHelper: function(event) { + + var o = this.options, + helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event, this.currentItem])) : (o.helper === "clone" ? this.currentItem.clone() : this.currentItem); + + //Add the helper to the DOM if that didn't happen already + if(!helper.parents("body").length) { + $(o.appendTo !== "parent" ? o.appendTo : this.currentItem[0].parentNode)[0].appendChild(helper[0]); + } + + if(helper[0] === this.currentItem[0]) { + this._storedCSS = { width: this.currentItem[0].style.width, height: this.currentItem[0].style.height, position: this.currentItem.css("position"), top: this.currentItem.css("top"), left: this.currentItem.css("left") }; + } + + if(!helper[0].style.width || o.forceHelperSize) { + helper.width(this.currentItem.width()); + } + if(!helper[0].style.height || o.forceHelperSize) { + helper.height(this.currentItem.height()); + } + + return helper; + + }, + + _adjustOffsetFromHelper: function(obj) { + if (typeof obj === "string") { + obj = obj.split(" "); + } + if ($.isArray(obj)) { + obj = {left: +obj[0], top: +obj[1] || 0}; + } + if ("left" in obj) { + this.offset.click.left = obj.left + this.margins.left; + } + if ("right" in obj) { + this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left; + } + if ("top" in obj) { + this.offset.click.top = obj.top + this.margins.top; + } + if ("bottom" in obj) { + this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top; + } + }, + + _getParentOffset: function() { + + + //Get the offsetParent and cache its position + this.offsetParent = this.helper.offsetParent(); + var po = this.offsetParent.offset(); + + // This is a special case where we need to modify a offset calculated on start, since the following happened: + // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent + // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that + // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag + if(this.cssPosition === "absolute" && this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) { + po.left += this.scrollParent.scrollLeft(); + po.top += this.scrollParent.scrollTop(); + } + + // This needs to be actually done for all browsers, since pageX/pageY includes this information + // with an ugly IE fix + if( this.offsetParent[0] === document.body || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() === "html" && $.ui.ie)) { + po = { top: 0, left: 0 }; + } + + return { + top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0), + left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0) + }; + + }, + + _getRelativeOffset: function() { + + if(this.cssPosition === "relative") { + var p = this.currentItem.position(); + return { + top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(), + left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft() + }; + } else { + return { top: 0, left: 0 }; + } + + }, + + _cacheMargins: function() { + this.margins = { + left: (parseInt(this.currentItem.css("marginLeft"),10) || 0), + top: (parseInt(this.currentItem.css("marginTop"),10) || 0) + }; + }, + + _cacheHelperProportions: function() { + this.helperProportions = { + width: this.helper.outerWidth(), + height: this.helper.outerHeight() + }; + }, + + _setContainment: function() { + + var ce, co, over, + o = this.options; + if(o.containment === "parent") { + o.containment = this.helper[0].parentNode; + } + if(o.containment === "document" || o.containment === "window") { + this.containment = [ + 0 - this.offset.relative.left - this.offset.parent.left, + 0 - this.offset.relative.top - this.offset.parent.top, + $(o.containment === "document" ? document : window).width() - this.helperProportions.width - this.margins.left, + ($(o.containment === "document" ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top + ]; + } + + if(!(/^(document|window|parent)$/).test(o.containment)) { + ce = $(o.containment)[0]; + co = $(o.containment).offset(); + over = ($(ce).css("overflow") !== "hidden"); + + this.containment = [ + co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0) - this.margins.left, + co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0) - this.margins.top, + co.left+(over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left, + co.top+(over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top + ]; + } + + }, + + _convertPositionTo: function(d, pos) { + + if(!pos) { + pos = this.position; + } + var mod = d === "absolute" ? 1 : -1, + scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, + scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName); + + return { + top: ( + pos.top + // The absolute mouse position + this.offset.relative.top * mod + // Only for relative positioned nodes: Relative offset from element to offset parent + this.offset.parent.top * mod - // The offsetParent's offset without borders (offset + border) + ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod) + ), + left: ( + pos.left + // The absolute mouse position + this.offset.relative.left * mod + // Only for relative positioned nodes: Relative offset from element to offset parent + this.offset.parent.left * mod - // The offsetParent's offset without borders (offset + border) + ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod) + ) + }; + + }, + + _generatePosition: function(event) { + + var top, left, + o = this.options, + pageX = event.pageX, + pageY = event.pageY, + scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName); + + // This is another very weird special case that only happens for relative elements: + // 1. If the css position is relative + // 2. and the scroll parent is the document or similar to the offset parent + // we have to refresh the relative offset during the scroll so there are no jumps + if(this.cssPosition === "relative" && !(this.scrollParent[0] !== document && this.scrollParent[0] !== this.offsetParent[0])) { + this.offset.relative = this._getRelativeOffset(); + } + + /* + * - Position constraining - + * Constrain the position to a mix of grid, containment. + */ + + if(this.originalPosition) { //If we are not dragging yet, we won't check for options + + if(this.containment) { + if(event.pageX - this.offset.click.left < this.containment[0]) { + pageX = this.containment[0] + this.offset.click.left; + } + if(event.pageY - this.offset.click.top < this.containment[1]) { + pageY = this.containment[1] + this.offset.click.top; + } + if(event.pageX - this.offset.click.left > this.containment[2]) { + pageX = this.containment[2] + this.offset.click.left; + } + if(event.pageY - this.offset.click.top > this.containment[3]) { + pageY = this.containment[3] + this.offset.click.top; + } + } + + if(o.grid) { + top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1]; + pageY = this.containment ? ( (top - this.offset.click.top >= this.containment[1] && top - this.offset.click.top <= this.containment[3]) ? top : ((top - this.offset.click.top >= this.containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top; + + left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0]; + pageX = this.containment ? ( (left - this.offset.click.left >= this.containment[0] && left - this.offset.click.left <= this.containment[2]) ? left : ((left - this.offset.click.left >= this.containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left; + } + + } + + return { + top: ( + pageY - // The absolute mouse position + this.offset.click.top - // Click offset (relative to the element) + this.offset.relative.top - // Only for relative positioned nodes: Relative offset from element to offset parent + this.offset.parent.top + // The offsetParent's offset without borders (offset + border) + ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) )) + ), + left: ( + pageX - // The absolute mouse position + this.offset.click.left - // Click offset (relative to the element) + this.offset.relative.left - // Only for relative positioned nodes: Relative offset from element to offset parent + this.offset.parent.left + // The offsetParent's offset without borders (offset + border) + ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() )) + ) + }; + + }, + + _rearrange: function(event, i, a, hardRefresh) { + + a ? a[0].appendChild(this.placeholder[0]) : i.item[0].parentNode.insertBefore(this.placeholder[0], (this.direction === "down" ? i.item[0] : i.item[0].nextSibling)); + + //Various things done here to improve the performance: + // 1. we create a setTimeout, that calls refreshPositions + // 2. on the instance, we have a counter variable, that get's higher after every append + // 3. on the local scope, we copy the counter variable, and check in the timeout, if it's still the same + // 4. this lets only the last addition to the timeout stack through + this.counter = this.counter ? ++this.counter : 1; + var counter = this.counter; + + this._delay(function() { + if(counter === this.counter) { + this.refreshPositions(!hardRefresh); //Precompute after each DOM insertion, NOT on mousemove + } + }); + + }, + + _clear: function(event, noPropagation) { + + this.reverting = false; + // We delay all events that have to be triggered to after the point where the placeholder has been removed and + // everything else normalized again + var i, + delayedTriggers = []; + + // We first have to update the dom position of the actual currentItem + // Note: don't do it if the current item is already removed (by a user), or it gets reappended (see #4088) + if(!this._noFinalSort && this.currentItem.parent().length) { + this.placeholder.before(this.currentItem); + } + this._noFinalSort = null; + + if(this.helper[0] === this.currentItem[0]) { + for(i in this._storedCSS) { + if(this._storedCSS[i] === "auto" || this._storedCSS[i] === "static") { + this._storedCSS[i] = ""; + } + } + this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"); + } else { + this.currentItem.show(); + } + + if(this.fromOutside && !noPropagation) { + delayedTriggers.push(function(event) { this._trigger("receive", event, this._uiHash(this.fromOutside)); }); + } + if((this.fromOutside || this.domPosition.prev !== this.currentItem.prev().not(".ui-sortable-helper")[0] || this.domPosition.parent !== this.currentItem.parent()[0]) && !noPropagation) { + delayedTriggers.push(function(event) { this._trigger("update", event, this._uiHash()); }); //Trigger update callback if the DOM position has changed + } + + // Check if the items Container has Changed and trigger appropriate + // events. + if (this !== this.currentContainer) { + if(!noPropagation) { + delayedTriggers.push(function(event) { this._trigger("remove", event, this._uiHash()); }); + delayedTriggers.push((function(c) { return function(event) { c._trigger("receive", event, this._uiHash(this)); }; }).call(this, this.currentContainer)); + delayedTriggers.push((function(c) { return function(event) { c._trigger("update", event, this._uiHash(this)); }; }).call(this, this.currentContainer)); + } + } + + + //Post events to containers + for (i = this.containers.length - 1; i >= 0; i--){ + if(!noPropagation) { + delayedTriggers.push((function(c) { return function(event) { c._trigger("deactivate", event, this._uiHash(this)); }; }).call(this, this.containers[i])); + } + if(this.containers[i].containerCache.over) { + delayedTriggers.push((function(c) { return function(event) { c._trigger("out", event, this._uiHash(this)); }; }).call(this, this.containers[i])); + this.containers[i].containerCache.over = 0; + } + } + + //Do what was originally in plugins + if(this._storedCursor) { + $("body").css("cursor", this._storedCursor); + } + if(this._storedOpacity) { + this.helper.css("opacity", this._storedOpacity); + } + if(this._storedZIndex) { + this.helper.css("zIndex", this._storedZIndex === "auto" ? "" : this._storedZIndex); + } + + this.dragging = false; + if(this.cancelHelperRemoval) { + if(!noPropagation) { + this._trigger("beforeStop", event, this._uiHash()); + for (i=0; i < delayedTriggers.length; i++) { + delayedTriggers[i].call(this, event); + } //Trigger all delayed events + this._trigger("stop", event, this._uiHash()); + } + + this.fromOutside = false; + return false; + } + + if(!noPropagation) { + this._trigger("beforeStop", event, this._uiHash()); + } + + //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node! + this.placeholder[0].parentNode.removeChild(this.placeholder[0]); + + if(this.helper[0] !== this.currentItem[0]) { + this.helper.remove(); + } + this.helper = null; + + if(!noPropagation) { + for (i=0; i < delayedTriggers.length; i++) { + delayedTriggers[i].call(this, event); + } //Trigger all delayed events + this._trigger("stop", event, this._uiHash()); + } + + this.fromOutside = false; + return true; + + }, + + _trigger: function() { + if ($.Widget.prototype._trigger.apply(this, arguments) === false) { + this.cancel(); + } + }, + + _uiHash: function(_inst) { + var inst = _inst || this; + return { + helper: inst.helper, + placeholder: inst.placeholder || $([]), + position: inst.position, + originalPosition: inst.originalPosition, + offset: inst.positionAbs, + item: inst.currentItem, + sender: _inst ? _inst.element : null + }; + } + +}); + +})(jQuery); +(function( $, undefined ) { + +var uid = 0, + hideProps = {}, + showProps = {}; + +hideProps.height = hideProps.paddingTop = hideProps.paddingBottom = + hideProps.borderTopWidth = hideProps.borderBottomWidth = "hide"; +showProps.height = showProps.paddingTop = showProps.paddingBottom = + showProps.borderTopWidth = showProps.borderBottomWidth = "show"; + +$.widget( "ui.accordion", { + version: "1.10.0", + options: { + active: 0, + animate: {}, + collapsible: false, + event: "click", + header: "> li > :first-child,> :not(li):even", + heightStyle: "auto", + icons: { + activeHeader: "ui-icon-triangle-1-s", + header: "ui-icon-triangle-1-e" + }, + + // callbacks + activate: null, + beforeActivate: null + }, + + _create: function() { + var options = this.options; + this.prevShow = this.prevHide = $(); + this.element.addClass( "ui-accordion ui-widget ui-helper-reset" ) + // ARIA + .attr( "role", "tablist" ); + + // don't allow collapsible: false and active: false / null + if ( !options.collapsible && (options.active === false || options.active == null) ) { + options.active = 0; + } + + this._processPanels(); + // handle negative values + if ( options.active < 0 ) { + options.active += this.headers.length; + } + this._refresh(); + }, + + _getCreateEventData: function() { + return { + header: this.active, + content: !this.active.length ? $() : this.active.next() + }; + }, + + _createIcons: function() { + var icons = this.options.icons; + if ( icons ) { + $( "" ) + .addClass( "ui-accordion-header-icon ui-icon " + icons.header ) + .prependTo( this.headers ); + this.active.children( ".ui-accordion-header-icon" ) + .removeClass( icons.header ) + .addClass( icons.activeHeader ); + this.headers.addClass( "ui-accordion-icons" ); + } + }, + + _destroyIcons: function() { + this.headers + .removeClass( "ui-accordion-icons" ) + .children( ".ui-accordion-header-icon" ) + .remove(); + }, + + _destroy: function() { + var contents; + + // clean up main element + this.element + .removeClass( "ui-accordion ui-widget ui-helper-reset" ) + .removeAttr( "role" ); + + // clean up headers + this.headers + .removeClass( "ui-accordion-header ui-accordion-header-active ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top" ) + .removeAttr( "role" ) + .removeAttr( "aria-selected" ) + .removeAttr( "aria-controls" ) + .removeAttr( "tabIndex" ) + .each(function() { + if ( /^ui-accordion/.test( this.id ) ) { + this.removeAttribute( "id" ); + } + }); + this._destroyIcons(); + + // clean up content panels + contents = this.headers.next() + .css( "display", "" ) + .removeAttr( "role" ) + .removeAttr( "aria-expanded" ) + .removeAttr( "aria-hidden" ) + .removeAttr( "aria-labelledby" ) + .removeClass( "ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-state-disabled" ) + .each(function() { + if ( /^ui-accordion/.test( this.id ) ) { + this.removeAttribute( "id" ); + } + }); + if ( this.options.heightStyle !== "content" ) { + contents.css( "height", "" ); + } + }, + + _setOption: function( key, value ) { + if ( key === "active" ) { + // _activate() will handle invalid values and update this.options + this._activate( value ); + return; + } + + if ( key === "event" ) { + if ( this.options.event ) { + this._off( this.headers, this.options.event ); + } + this._setupEvents( value ); + } + + this._super( key, value ); + + // setting collapsible: false while collapsed; open first panel + if ( key === "collapsible" && !value && this.options.active === false ) { + this._activate( 0 ); + } + + if ( key === "icons" ) { + this._destroyIcons(); + if ( value ) { + this._createIcons(); + } + } + + // #5332 - opacity doesn't cascade to positioned elements in IE + // so we need to add the disabled class to the headers and panels + if ( key === "disabled" ) { + this.headers.add( this.headers.next() ) + .toggleClass( "ui-state-disabled", !!value ); + } + }, + + _keydown: function( event ) { + /*jshint maxcomplexity:15*/ + if ( event.altKey || event.ctrlKey ) { + return; + } + + var keyCode = $.ui.keyCode, + length = this.headers.length, + currentIndex = this.headers.index( event.target ), + toFocus = false; + + switch ( event.keyCode ) { + case keyCode.RIGHT: + case keyCode.DOWN: + toFocus = this.headers[ ( currentIndex + 1 ) % length ]; + break; + case keyCode.LEFT: + case keyCode.UP: + toFocus = this.headers[ ( currentIndex - 1 + length ) % length ]; + break; + case keyCode.SPACE: + case keyCode.ENTER: + this._eventHandler( event ); + break; + case keyCode.HOME: + toFocus = this.headers[ 0 ]; + break; + case keyCode.END: + toFocus = this.headers[ length - 1 ]; + break; + } + + if ( toFocus ) { + $( event.target ).attr( "tabIndex", -1 ); + $( toFocus ).attr( "tabIndex", 0 ); + toFocus.focus(); + event.preventDefault(); + } + }, + + _panelKeyDown : function( event ) { + if ( event.keyCode === $.ui.keyCode.UP && event.ctrlKey ) { + $( event.currentTarget ).prev().focus(); + } + }, + + refresh: function() { + var options = this.options; + this._processPanels(); + + // was collapsed or no panel + if ( ( options.active === false && options.collapsible === true ) || !this.headers.length ) { + options.active = false; + this.active = $(); + // active false only when collapsible is true + } if ( options.active === false ) { + this._activate( 0 ); + // was active, but active panel is gone + } else if ( this.active.length && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) { + // all remaining panel are disabled + if ( this.headers.length === this.headers.find(".ui-state-disabled").length ) { + options.active = false; + this.active = $(); + // activate previous panel + } else { + this._activate( Math.max( 0, options.active - 1 ) ); + } + // was active, active panel still exists + } else { + // make sure active index is correct + options.active = this.headers.index( this.active ); + } + + this._destroyIcons(); + + this._refresh(); + }, + + _processPanels: function() { + this.headers = this.element.find( this.options.header ) + .addClass( "ui-accordion-header ui-helper-reset ui-state-default ui-corner-all" ); + + this.headers.next() + .addClass( "ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom" ) + .filter(":not(.ui-accordion-content-active)") + .hide(); + }, + + _refresh: function() { + var maxHeight, + options = this.options, + heightStyle = options.heightStyle, + parent = this.element.parent(), + accordionId = this.accordionId = "ui-accordion-" + + (this.element.attr( "id" ) || ++uid); + + this.active = this._findActive( options.active ) + .addClass( "ui-accordion-header-active ui-state-active" ) + .toggleClass( "ui-corner-all ui-corner-top" ); + this.active.next() + .addClass( "ui-accordion-content-active" ) + .show(); + + this.headers + .attr( "role", "tab" ) + .each(function( i ) { + var header = $( this ), + headerId = header.attr( "id" ), + panel = header.next(), + panelId = panel.attr( "id" ); + if ( !headerId ) { + headerId = accordionId + "-header-" + i; + header.attr( "id", headerId ); + } + if ( !panelId ) { + panelId = accordionId + "-panel-" + i; + panel.attr( "id", panelId ); + } + header.attr( "aria-controls", panelId ); + panel.attr( "aria-labelledby", headerId ); + }) + .next() + .attr( "role", "tabpanel" ); + + this.headers + .not( this.active ) + .attr({ + "aria-selected": "false", + tabIndex: -1 + }) + .next() + .attr({ + "aria-expanded": "false", + "aria-hidden": "true" + }) + .hide(); + + // make sure at least one header is in the tab order + if ( !this.active.length ) { + this.headers.eq( 0 ).attr( "tabIndex", 0 ); + } else { + this.active.attr({ + "aria-selected": "true", + tabIndex: 0 + }) + .next() + .attr({ + "aria-expanded": "true", + "aria-hidden": "false" + }); + } + + this._createIcons(); + + this._setupEvents( options.event ); + + if ( heightStyle === "fill" ) { + maxHeight = parent.height(); + this.element.siblings( ":visible" ).each(function() { + var elem = $( this ), + position = elem.css( "position" ); + + if ( position === "absolute" || position === "fixed" ) { + return; + } + maxHeight -= elem.outerHeight( true ); + }); + + this.headers.each(function() { + maxHeight -= $( this ).outerHeight( true ); + }); + + this.headers.next() + .each(function() { + $( this ).height( Math.max( 0, maxHeight - + $( this ).innerHeight() + $( this ).height() ) ); + }) + .css( "overflow", "auto" ); + } else if ( heightStyle === "auto" ) { + maxHeight = 0; + this.headers.next() + .each(function() { + maxHeight = Math.max( maxHeight, $( this ).css( "height", "" ).height() ); + }) + .height( maxHeight ); + } + }, + + _activate: function( index ) { + var active = this._findActive( index )[ 0 ]; + + // trying to activate the already active panel + if ( active === this.active[ 0 ] ) { + return; + } + + // trying to collapse, simulate a click on the currently active header + active = active || this.active[ 0 ]; + + this._eventHandler({ + target: active, + currentTarget: active, + preventDefault: $.noop + }); + }, + + _findActive: function( selector ) { + return typeof selector === "number" ? this.headers.eq( selector ) : $(); + }, + + _setupEvents: function( event ) { + var events = { + keydown: "_keydown" + }; + if ( event ) { + $.each( event.split(" "), function( index, eventName ) { + events[ eventName ] = "_eventHandler"; + }); + } + + this._off( this.headers.add( this.headers.next() ) ); + this._on( this.headers, events ); + this._on( this.headers.next(), { keydown: "_panelKeyDown" }); + this._hoverable( this.headers ); + this._focusable( this.headers ); + }, + + _eventHandler: function( event ) { + var options = this.options, + active = this.active, + clicked = $( event.currentTarget ), + clickedIsActive = clicked[ 0 ] === active[ 0 ], + collapsing = clickedIsActive && options.collapsible, + toShow = collapsing ? $() : clicked.next(), + toHide = active.next(), + eventData = { + oldHeader: active, + oldPanel: toHide, + newHeader: collapsing ? $() : clicked, + newPanel: toShow + }; + + event.preventDefault(); + + if ( + // click on active header, but not collapsible + ( clickedIsActive && !options.collapsible ) || + // allow canceling activation + ( this._trigger( "beforeActivate", event, eventData ) === false ) ) { + return; + } + + options.active = collapsing ? false : this.headers.index( clicked ); + + // when the call to ._toggle() comes after the class changes + // it causes a very odd bug in IE 8 (see #6720) + this.active = clickedIsActive ? $() : clicked; + this._toggle( eventData ); + + // switch classes + // corner classes on the previously active header stay after the animation + active.removeClass( "ui-accordion-header-active ui-state-active" ); + if ( options.icons ) { + active.children( ".ui-accordion-header-icon" ) + .removeClass( options.icons.activeHeader ) + .addClass( options.icons.header ); + } + + if ( !clickedIsActive ) { + clicked + .removeClass( "ui-corner-all" ) + .addClass( "ui-accordion-header-active ui-state-active ui-corner-top" ); + if ( options.icons ) { + clicked.children( ".ui-accordion-header-icon" ) + .removeClass( options.icons.header ) + .addClass( options.icons.activeHeader ); + } + + clicked + .next() + .addClass( "ui-accordion-content-active" ); + } + }, + + _toggle: function( data ) { + var toShow = data.newPanel, + toHide = this.prevShow.length ? this.prevShow : data.oldPanel; + + // handle activating a panel during the animation for another activation + this.prevShow.add( this.prevHide ).stop( true, true ); + this.prevShow = toShow; + this.prevHide = toHide; + + if ( this.options.animate ) { + this._animate( toShow, toHide, data ); + } else { + toHide.hide(); + toShow.show(); + this._toggleComplete( data ); + } + + toHide.attr({ + "aria-expanded": "false", + "aria-hidden": "true" + }); + toHide.prev().attr( "aria-selected", "false" ); + // if we're switching panels, remove the old header from the tab order + // if we're opening from collapsed state, remove the previous header from the tab order + // if we're collapsing, then keep the collapsing header in the tab order + if ( toShow.length && toHide.length ) { + toHide.prev().attr( "tabIndex", -1 ); + } else if ( toShow.length ) { + this.headers.filter(function() { + return $( this ).attr( "tabIndex" ) === 0; + }) + .attr( "tabIndex", -1 ); + } + + toShow + .attr({ + "aria-expanded": "true", + "aria-hidden": "false" + }) + .prev() + .attr({ + "aria-selected": "true", + tabIndex: 0 + }); + }, + + _animate: function( toShow, toHide, data ) { + var total, easing, duration, + that = this, + adjust = 0, + down = toShow.length && + ( !toHide.length || ( toShow.index() < toHide.index() ) ), + animate = this.options.animate || {}, + options = down && animate.down || animate, + complete = function() { + that._toggleComplete( data ); + }; + + if ( typeof options === "number" ) { + duration = options; + } + if ( typeof options === "string" ) { + easing = options; + } + // fall back from options to animation in case of partial down settings + easing = easing || options.easing || animate.easing; + duration = duration || options.duration || animate.duration; + + if ( !toHide.length ) { + return toShow.animate( showProps, duration, easing, complete ); + } + if ( !toShow.length ) { + return toHide.animate( hideProps, duration, easing, complete ); + } + + total = toShow.show().outerHeight(); + toHide.animate( hideProps, { + duration: duration, + easing: easing, + step: function( now, fx ) { + fx.now = Math.round( now ); + } + }); + toShow + .hide() + .animate( showProps, { + duration: duration, + easing: easing, + complete: complete, + step: function( now, fx ) { + fx.now = Math.round( now ); + if ( fx.prop !== "height" ) { + adjust += fx.now; + } else if ( that.options.heightStyle !== "content" ) { + fx.now = Math.round( total - toHide.outerHeight() - adjust ); + adjust = 0; + } + } + }); + }, + + _toggleComplete: function( data ) { + var toHide = data.oldPanel; + + toHide + .removeClass( "ui-accordion-content-active" ) + .prev() + .removeClass( "ui-corner-top" ) + .addClass( "ui-corner-all" ); + + // Work around for rendering bug in IE (#5421) + if ( toHide.length ) { + toHide.parent()[0].className = toHide.parent()[0].className; + } + + this._trigger( "activate", null, data ); + } +}); + +})( jQuery ); +(function( $, undefined ) { + +// used to prevent race conditions with remote data sources +var requestIndex = 0; + +$.widget( "ui.autocomplete", { + version: "1.10.0", + defaultElement: "", + options: { + appendTo: null, + autoFocus: false, + delay: 300, + minLength: 1, + position: { + my: "left top", + at: "left bottom", + collision: "none" + }, + source: null, + + // callbacks + change: null, + close: null, + focus: null, + open: null, + response: null, + search: null, + select: null + }, + + pending: 0, + + _create: function() { + // Some browsers only repeat keydown events, not keypress events, + // so we use the suppressKeyPress flag to determine if we've already + // handled the keydown event. #7269 + // Unfortunately the code for & in keypress is the same as the up arrow, + // so we use the suppressKeyPressRepeat flag to avoid handling keypress + // events when we know the keydown event was used to modify the + // search term. #7799 + var suppressKeyPress, suppressKeyPressRepeat, suppressInput; + + this.isMultiLine = this._isMultiLine(); + this.valueMethod = this.element[ this.element.is( "input,textarea" ) ? "val" : "text" ]; + this.isNewMenu = true; + + this.element + .addClass( "ui-autocomplete-input" ) + .attr( "autocomplete", "off" ); + + this._on( this.element, { + keydown: function( event ) { + /*jshint maxcomplexity:15*/ + if ( this.element.prop( "readOnly" ) ) { + suppressKeyPress = true; + suppressInput = true; + suppressKeyPressRepeat = true; + return; + } + + suppressKeyPress = false; + suppressInput = false; + suppressKeyPressRepeat = false; + var keyCode = $.ui.keyCode; + switch( event.keyCode ) { + case keyCode.PAGE_UP: + suppressKeyPress = true; + this._move( "previousPage", event ); + break; + case keyCode.PAGE_DOWN: + suppressKeyPress = true; + this._move( "nextPage", event ); + break; + case keyCode.UP: + suppressKeyPress = true; + this._keyEvent( "previous", event ); + break; + case keyCode.DOWN: + suppressKeyPress = true; + this._keyEvent( "next", event ); + break; + case keyCode.ENTER: + case keyCode.NUMPAD_ENTER: + // when menu is open and has focus + if ( this.menu.active ) { + // #6055 - Opera still allows the keypress to occur + // which causes forms to submit + suppressKeyPress = true; + event.preventDefault(); + this.menu.select( event ); + } + break; + case keyCode.TAB: + if ( this.menu.active ) { + this.menu.select( event ); + } + break; + case keyCode.ESCAPE: + if ( this.menu.element.is( ":visible" ) ) { + this._value( this.term ); + this.close( event ); + // Different browsers have different default behavior for escape + // Single press can mean undo or clear + // Double press in IE means clear the whole form + event.preventDefault(); + } + break; + default: + suppressKeyPressRepeat = true; + // search timeout should be triggered before the input value is changed + this._searchTimeout( event ); + break; + } + }, + keypress: function( event ) { + if ( suppressKeyPress ) { + suppressKeyPress = false; + event.preventDefault(); + return; + } + if ( suppressKeyPressRepeat ) { + return; + } + + // replicate some key handlers to allow them to repeat in Firefox and Opera + var keyCode = $.ui.keyCode; + switch( event.keyCode ) { + case keyCode.PAGE_UP: + this._move( "previousPage", event ); + break; + case keyCode.PAGE_DOWN: + this._move( "nextPage", event ); + break; + case keyCode.UP: + this._keyEvent( "previous", event ); + break; + case keyCode.DOWN: + this._keyEvent( "next", event ); + break; + } + }, + input: function( event ) { + if ( suppressInput ) { + suppressInput = false; + event.preventDefault(); + return; + } + this._searchTimeout( event ); + }, + focus: function() { + this.selectedItem = null; + this.previous = this._value(); + }, + blur: function( event ) { + if ( this.cancelBlur ) { + delete this.cancelBlur; + return; + } + + clearTimeout( this.searching ); + this.close( event ); + this._change( event ); + } + }); + + this._initSource(); + this.menu = $( "
").addClass("ui-widget-overlay")).appendTo(document.body).css({width:this.width(),height:this.height()});c.fn.bgiframe&&b.bgiframe();this.instances.push(b);return b},destroy:function(a){var b=c.inArray(a,this.instances);b!=-1&&this.oldInstances.push(this.instances.splice(b,1)[0]);this.instances.length===0&&c([document,window]).unbind(".dialog-overlay");a.remove();var d=0;c.each(this.instances,function(){d=Math.max(d,this.css("z-index"))});this.maxZ=d},height:function(){var a,b;if(c.browser.msie&& -c.browser.version<7){a=Math.max(document.documentElement.scrollHeight,document.body.scrollHeight);b=Math.max(document.documentElement.offsetHeight,document.body.offsetHeight);return a").appendTo(this.element).addClass("ui-slider-range ui-widget-header"+(b.range==="min"||b.range==="max"?" ui-slider-range-"+b.range:""))}for(var j=c.length;j"); -this.handles=c.add(d(e.join("")).appendTo(a.element));this.handle=this.handles.eq(0);this.handles.add(this.range).filter("a").click(function(g){g.preventDefault()}).hover(function(){b.disabled||d(this).addClass("ui-state-hover")},function(){d(this).removeClass("ui-state-hover")}).focus(function(){if(b.disabled)d(this).blur();else{d(".ui-slider .ui-state-focus").removeClass("ui-state-focus");d(this).addClass("ui-state-focus")}}).blur(function(){d(this).removeClass("ui-state-focus")});this.handles.each(function(g){d(this).data("index.ui-slider-handle", -g)});this.handles.keydown(function(g){var k=true,l=d(this).data("index.ui-slider-handle"),i,h,m;if(!a.options.disabled){switch(g.keyCode){case d.ui.keyCode.HOME:case d.ui.keyCode.END:case d.ui.keyCode.PAGE_UP:case d.ui.keyCode.PAGE_DOWN:case d.ui.keyCode.UP:case d.ui.keyCode.RIGHT:case d.ui.keyCode.DOWN:case d.ui.keyCode.LEFT:k=false;if(!a._keySliding){a._keySliding=true;d(this).addClass("ui-state-active");i=a._start(g,l);if(i===false)return}break}m=a.options.step;i=a.options.values&&a.options.values.length? -(h=a.values(l)):(h=a.value());switch(g.keyCode){case d.ui.keyCode.HOME:h=a._valueMin();break;case d.ui.keyCode.END:h=a._valueMax();break;case d.ui.keyCode.PAGE_UP:h=a._trimAlignValue(i+(a._valueMax()-a._valueMin())/5);break;case d.ui.keyCode.PAGE_DOWN:h=a._trimAlignValue(i-(a._valueMax()-a._valueMin())/5);break;case d.ui.keyCode.UP:case d.ui.keyCode.RIGHT:if(i===a._valueMax())return;h=a._trimAlignValue(i+m);break;case d.ui.keyCode.DOWN:case d.ui.keyCode.LEFT:if(i===a._valueMin())return;h=a._trimAlignValue(i- -m);break}a._slide(g,l,h);return k}}).keyup(function(g){var k=d(this).data("index.ui-slider-handle");if(a._keySliding){a._keySliding=false;a._stop(g,k);a._change(g,k);d(this).removeClass("ui-state-active")}});this._refreshValue();this._animateOff=false},destroy:function(){this.handles.remove();this.range.remove();this.element.removeClass("ui-slider ui-slider-horizontal ui-slider-vertical ui-slider-disabled ui-widget ui-widget-content ui-corner-all").removeData("slider").unbind(".slider");this._mouseDestroy(); -return this},_mouseCapture:function(a){var b=this.options,c,f,e,j,g;if(b.disabled)return false;this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()};this.elementOffset=this.element.offset();c=this._normValueFromMouse({x:a.pageX,y:a.pageY});f=this._valueMax()-this._valueMin()+1;j=this;this.handles.each(function(k){var l=Math.abs(c-j.values(k));if(f>l){f=l;e=d(this);g=k}});if(b.range===true&&this.values(1)===b.min){g+=1;e=d(this.handles[g])}if(this._start(a,g)===false)return false; -this._mouseSliding=true;j._handleIndex=g;e.addClass("ui-state-active").focus();b=e.offset();this._clickOffset=!d(a.target).parents().andSelf().is(".ui-slider-handle")?{left:0,top:0}:{left:a.pageX-b.left-e.width()/2,top:a.pageY-b.top-e.height()/2-(parseInt(e.css("borderTopWidth"),10)||0)-(parseInt(e.css("borderBottomWidth"),10)||0)+(parseInt(e.css("marginTop"),10)||0)};this.handles.hasClass("ui-state-hover")||this._slide(a,g,c);return this._animateOff=true},_mouseStart:function(){return true},_mouseDrag:function(a){var b= -this._normValueFromMouse({x:a.pageX,y:a.pageY});this._slide(a,this._handleIndex,b);return false},_mouseStop:function(a){this.handles.removeClass("ui-state-active");this._mouseSliding=false;this._stop(a,this._handleIndex);this._change(a,this._handleIndex);this._clickOffset=this._handleIndex=null;return this._animateOff=false},_detectOrientation:function(){this.orientation=this.options.orientation==="vertical"?"vertical":"horizontal"},_normValueFromMouse:function(a){var b;if(this.orientation==="horizontal"){b= -this.elementSize.width;a=a.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)}else{b=this.elementSize.height;a=a.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)}b=a/b;if(b>1)b=1;if(b<0)b=0;if(this.orientation==="vertical")b=1-b;a=this._valueMax()-this._valueMin();return this._trimAlignValue(this._valueMin()+b*a)},_start:function(a,b){var c={handle:this.handles[b],value:this.value()};if(this.options.values&&this.options.values.length){c.value=this.values(b); -c.values=this.values()}return this._trigger("start",a,c)},_slide:function(a,b,c){var f;if(this.options.values&&this.options.values.length){f=this.values(b?0:1);if(this.options.values.length===2&&this.options.range===true&&(b===0&&c>f||b===1&&c1){this.options.values[a]=this._trimAlignValue(b);this._refreshValue();this._change(null,a)}else if(arguments.length)if(d.isArray(arguments[0])){c=this.options.values;f=arguments[0];for(e=0;e=this._valueMax())return this._valueMax();var b=this.options.step>0?this.options.step:1,c=(a-this._valueMin())%b;a=a-c;if(Math.abs(c)*2>=b)a+=c>0?b:-b;return parseFloat(a.toFixed(5))},_valueMin:function(){return this.options.min},_valueMax:function(){return this.options.max},_refreshValue:function(){var a= -this.options.range,b=this.options,c=this,f=!this._animateOff?b.animate:false,e,j={},g,k,l,i;if(this.options.values&&this.options.values.length)this.handles.each(function(h){e=(c.values(h)-c._valueMin())/(c._valueMax()-c._valueMin())*100;j[c.orientation==="horizontal"?"left":"bottom"]=e+"%";d(this).stop(1,1)[f?"animate":"css"](j,b.animate);if(c.options.range===true)if(c.orientation==="horizontal"){if(h===0)c.range.stop(1,1)[f?"animate":"css"]({left:e+"%"},b.animate);if(h===1)c.range[f?"animate":"css"]({width:e- -g+"%"},{queue:false,duration:b.animate})}else{if(h===0)c.range.stop(1,1)[f?"animate":"css"]({bottom:e+"%"},b.animate);if(h===1)c.range[f?"animate":"css"]({height:e-g+"%"},{queue:false,duration:b.animate})}g=e});else{k=this.value();l=this._valueMin();i=this._valueMax();e=i!==l?(k-l)/(i-l)*100:0;j[c.orientation==="horizontal"?"left":"bottom"]=e+"%";this.handle.stop(1,1)[f?"animate":"css"](j,b.animate);if(a==="min"&&this.orientation==="horizontal")this.range.stop(1,1)[f?"animate":"css"]({width:e+"%"}, -b.animate);if(a==="max"&&this.orientation==="horizontal")this.range[f?"animate":"css"]({width:100-e+"%"},{queue:false,duration:b.animate});if(a==="min"&&this.orientation==="vertical")this.range.stop(1,1)[f?"animate":"css"]({height:e+"%"},b.animate);if(a==="max"&&this.orientation==="vertical")this.range[f?"animate":"css"]({height:100-e+"%"},{queue:false,duration:b.animate})}}});d.extend(d.ui.slider,{version:"1.8.16"})})(jQuery); -;/* - * jQuery UI Tabs 1.8.16 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Tabs - * - * Depends: - * jquery.ui.core.js - * jquery.ui.widget.js - */ -(function(d,p){function u(){return++v}function w(){return++x}var v=0,x=0;d.widget("ui.tabs",{options:{add:null,ajaxOptions:null,cache:false,cookie:null,collapsible:false,disable:null,disabled:[],enable:null,event:"click",fx:null,idPrefix:"ui-tabs-",load:null,panelTemplate:"
",remove:null,select:null,show:null,spinner:"Loading…",tabTemplate:"
  • #{label}
  • "},_create:function(){this._tabify(true)},_setOption:function(b,e){if(b=="selected")this.options.collapsible&& -e==this.options.selected||this.select(e);else{this.options[b]=e;this._tabify()}},_tabId:function(b){return b.title&&b.title.replace(/\s/g,"_").replace(/[^\w\u00c0-\uFFFF-]/g,"")||this.options.idPrefix+u()},_sanitizeSelector:function(b){return b.replace(/:/g,"\\:")},_cookie:function(){var b=this.cookie||(this.cookie=this.options.cookie.name||"ui-tabs-"+w());return d.cookie.apply(null,[b].concat(d.makeArray(arguments)))},_ui:function(b,e){return{tab:b,panel:e,index:this.anchors.index(b)}},_cleanup:function(){this.lis.filter(".ui-state-processing").removeClass("ui-state-processing").find("span:data(label.tabs)").each(function(){var b= -d(this);b.html(b.data("label.tabs")).removeData("label.tabs")})},_tabify:function(b){function e(g,f){g.css("display","");!d.support.opacity&&f.opacity&&g[0].style.removeAttribute("filter")}var a=this,c=this.options,h=/^#.+/;this.list=this.element.find("ol,ul").eq(0);this.lis=d(" > li:has(a[href])",this.list);this.anchors=this.lis.map(function(){return d("a",this)[0]});this.panels=d([]);this.anchors.each(function(g,f){var i=d(f).attr("href"),l=i.split("#")[0],q;if(l&&(l===location.toString().split("#")[0]|| -(q=d("base")[0])&&l===q.href)){i=f.hash;f.href=i}if(h.test(i))a.panels=a.panels.add(a.element.find(a._sanitizeSelector(i)));else if(i&&i!=="#"){d.data(f,"href.tabs",i);d.data(f,"load.tabs",i.replace(/#.*$/,""));i=a._tabId(f);f.href="#"+i;f=a.element.find("#"+i);if(!f.length){f=d(c.panelTemplate).attr("id",i).addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").insertAfter(a.panels[g-1]||a.list);f.data("destroy.tabs",true)}a.panels=a.panels.add(f)}else c.disabled.push(g)});if(b){this.element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all"); -this.list.addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");this.lis.addClass("ui-state-default ui-corner-top");this.panels.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom");if(c.selected===p){location.hash&&this.anchors.each(function(g,f){if(f.hash==location.hash){c.selected=g;return false}});if(typeof c.selected!=="number"&&c.cookie)c.selected=parseInt(a._cookie(),10);if(typeof c.selected!=="number"&&this.lis.filter(".ui-tabs-selected").length)c.selected= -this.lis.index(this.lis.filter(".ui-tabs-selected"));c.selected=c.selected||(this.lis.length?0:-1)}else if(c.selected===null)c.selected=-1;c.selected=c.selected>=0&&this.anchors[c.selected]||c.selected<0?c.selected:0;c.disabled=d.unique(c.disabled.concat(d.map(this.lis.filter(".ui-state-disabled"),function(g){return a.lis.index(g)}))).sort();d.inArray(c.selected,c.disabled)!=-1&&c.disabled.splice(d.inArray(c.selected,c.disabled),1);this.panels.addClass("ui-tabs-hide");this.lis.removeClass("ui-tabs-selected ui-state-active"); -if(c.selected>=0&&this.anchors.length){a.element.find(a._sanitizeSelector(a.anchors[c.selected].hash)).removeClass("ui-tabs-hide");this.lis.eq(c.selected).addClass("ui-tabs-selected ui-state-active");a.element.queue("tabs",function(){a._trigger("show",null,a._ui(a.anchors[c.selected],a.element.find(a._sanitizeSelector(a.anchors[c.selected].hash))[0]))});this.load(c.selected)}d(window).bind("unload",function(){a.lis.add(a.anchors).unbind(".tabs");a.lis=a.anchors=a.panels=null})}else c.selected=this.lis.index(this.lis.filter(".ui-tabs-selected")); -this.element[c.collapsible?"addClass":"removeClass"]("ui-tabs-collapsible");c.cookie&&this._cookie(c.selected,c.cookie);b=0;for(var j;j=this.lis[b];b++)d(j)[d.inArray(b,c.disabled)!=-1&&!d(j).hasClass("ui-tabs-selected")?"addClass":"removeClass"]("ui-state-disabled");c.cache===false&&this.anchors.removeData("cache.tabs");this.lis.add(this.anchors).unbind(".tabs");if(c.event!=="mouseover"){var k=function(g,f){f.is(":not(.ui-state-disabled)")&&f.addClass("ui-state-"+g)},n=function(g,f){f.removeClass("ui-state-"+ -g)};this.lis.bind("mouseover.tabs",function(){k("hover",d(this))});this.lis.bind("mouseout.tabs",function(){n("hover",d(this))});this.anchors.bind("focus.tabs",function(){k("focus",d(this).closest("li"))});this.anchors.bind("blur.tabs",function(){n("focus",d(this).closest("li"))})}var m,o;if(c.fx)if(d.isArray(c.fx)){m=c.fx[0];o=c.fx[1]}else m=o=c.fx;var r=o?function(g,f){d(g).closest("li").addClass("ui-tabs-selected ui-state-active");f.hide().removeClass("ui-tabs-hide").animate(o,o.duration||"normal", -function(){e(f,o);a._trigger("show",null,a._ui(g,f[0]))})}:function(g,f){d(g).closest("li").addClass("ui-tabs-selected ui-state-active");f.removeClass("ui-tabs-hide");a._trigger("show",null,a._ui(g,f[0]))},s=m?function(g,f){f.animate(m,m.duration||"normal",function(){a.lis.removeClass("ui-tabs-selected ui-state-active");f.addClass("ui-tabs-hide");e(f,m);a.element.dequeue("tabs")})}:function(g,f){a.lis.removeClass("ui-tabs-selected ui-state-active");f.addClass("ui-tabs-hide");a.element.dequeue("tabs")}; -this.anchors.bind(c.event+".tabs",function(){var g=this,f=d(g).closest("li"),i=a.panels.filter(":not(.ui-tabs-hide)"),l=a.element.find(a._sanitizeSelector(g.hash));if(f.hasClass("ui-tabs-selected")&&!c.collapsible||f.hasClass("ui-state-disabled")||f.hasClass("ui-state-processing")||a.panels.filter(":animated").length||a._trigger("select",null,a._ui(this,l[0]))===false){this.blur();return false}c.selected=a.anchors.index(this);a.abort();if(c.collapsible)if(f.hasClass("ui-tabs-selected")){c.selected= --1;c.cookie&&a._cookie(c.selected,c.cookie);a.element.queue("tabs",function(){s(g,i)}).dequeue("tabs");this.blur();return false}else if(!i.length){c.cookie&&a._cookie(c.selected,c.cookie);a.element.queue("tabs",function(){r(g,l)});a.load(a.anchors.index(this));this.blur();return false}c.cookie&&a._cookie(c.selected,c.cookie);if(l.length){i.length&&a.element.queue("tabs",function(){s(g,i)});a.element.queue("tabs",function(){r(g,l)});a.load(a.anchors.index(this))}else throw"jQuery UI Tabs: Mismatching fragment identifier."; -d.browser.msie&&this.blur()});this.anchors.bind("click.tabs",function(){return false})},_getIndex:function(b){if(typeof b=="string")b=this.anchors.index(this.anchors.filter("[href$="+b+"]"));return b},destroy:function(){var b=this.options;this.abort();this.element.unbind(".tabs").removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible").removeData("tabs");this.list.removeClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");this.anchors.each(function(){var e= -d.data(this,"href.tabs");if(e)this.href=e;var a=d(this).unbind(".tabs");d.each(["href","load","cache"],function(c,h){a.removeData(h+".tabs")})});this.lis.unbind(".tabs").add(this.panels).each(function(){d.data(this,"destroy.tabs")?d(this).remove():d(this).removeClass("ui-state-default ui-corner-top ui-tabs-selected ui-state-active ui-state-hover ui-state-focus ui-state-disabled ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide")});b.cookie&&this._cookie(null,b.cookie);return this},add:function(b, -e,a){if(a===p)a=this.anchors.length;var c=this,h=this.options;e=d(h.tabTemplate.replace(/#\{href\}/g,b).replace(/#\{label\}/g,e));b=!b.indexOf("#")?b.replace("#",""):this._tabId(d("a",e)[0]);e.addClass("ui-state-default ui-corner-top").data("destroy.tabs",true);var j=c.element.find("#"+b);j.length||(j=d(h.panelTemplate).attr("id",b).data("destroy.tabs",true));j.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide");if(a>=this.lis.length){e.appendTo(this.list);j.appendTo(this.list[0].parentNode)}else{e.insertBefore(this.lis[a]); -j.insertBefore(this.panels[a])}h.disabled=d.map(h.disabled,function(k){return k>=a?++k:k});this._tabify();if(this.anchors.length==1){h.selected=0;e.addClass("ui-tabs-selected ui-state-active");j.removeClass("ui-tabs-hide");this.element.queue("tabs",function(){c._trigger("show",null,c._ui(c.anchors[0],c.panels[0]))});this.load(0)}this._trigger("add",null,this._ui(this.anchors[a],this.panels[a]));return this},remove:function(b){b=this._getIndex(b);var e=this.options,a=this.lis.eq(b).remove(),c=this.panels.eq(b).remove(); -if(a.hasClass("ui-tabs-selected")&&this.anchors.length>1)this.select(b+(b+1=b?--h:h});this._tabify();this._trigger("remove",null,this._ui(a.find("a")[0],c[0]));return this},enable:function(b){b=this._getIndex(b);var e=this.options;if(d.inArray(b,e.disabled)!=-1){this.lis.eq(b).removeClass("ui-state-disabled");e.disabled=d.grep(e.disabled,function(a){return a!=b});this._trigger("enable",null, -this._ui(this.anchors[b],this.panels[b]));return this}},disable:function(b){b=this._getIndex(b);var e=this.options;if(b!=e.selected){this.lis.eq(b).addClass("ui-state-disabled");e.disabled.push(b);e.disabled.sort();this._trigger("disable",null,this._ui(this.anchors[b],this.panels[b]))}return this},select:function(b){b=this._getIndex(b);if(b==-1)if(this.options.collapsible&&this.options.selected!=-1)b=this.options.selected;else return this;this.anchors.eq(b).trigger(this.options.event+".tabs");return this}, -load:function(b){b=this._getIndex(b);var e=this,a=this.options,c=this.anchors.eq(b)[0],h=d.data(c,"load.tabs");this.abort();if(!h||this.element.queue("tabs").length!==0&&d.data(c,"cache.tabs"))this.element.dequeue("tabs");else{this.lis.eq(b).addClass("ui-state-processing");if(a.spinner){var j=d("span",c);j.data("label.tabs",j.html()).html(a.spinner)}this.xhr=d.ajax(d.extend({},a.ajaxOptions,{url:h,success:function(k,n){e.element.find(e._sanitizeSelector(c.hash)).html(k);e._cleanup();a.cache&&d.data(c, -"cache.tabs",true);e._trigger("load",null,e._ui(e.anchors[b],e.panels[b]));try{a.ajaxOptions.success(k,n)}catch(m){}},error:function(k,n){e._cleanup();e._trigger("load",null,e._ui(e.anchors[b],e.panels[b]));try{a.ajaxOptions.error(k,n,b,c)}catch(m){}}}));e.element.dequeue("tabs");return this}},abort:function(){this.element.queue([]);this.panels.stop(false,true);this.element.queue("tabs",this.element.queue("tabs").splice(-2,2));if(this.xhr){this.xhr.abort();delete this.xhr}this._cleanup();return this}, -url:function(b,e){this.anchors.eq(b).removeData("cache.tabs").data("load.tabs",e);return this},length:function(){return this.anchors.length}});d.extend(d.ui.tabs,{version:"1.8.16"});d.extend(d.ui.tabs.prototype,{rotation:null,rotate:function(b,e){var a=this,c=this.options,h=a._rotate||(a._rotate=function(j){clearTimeout(a.rotation);a.rotation=setTimeout(function(){var k=c.selected;a.select(++k'))}function N(a){return a.bind("mouseout", -function(b){b=d(b.target).closest("button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a");b.length&&b.removeClass("ui-state-hover ui-datepicker-prev-hover ui-datepicker-next-hover")}).bind("mouseover",function(b){b=d(b.target).closest("button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a");if(!(d.datepicker._isDisabledDatepicker(J.inline?a.parent()[0]:J.input[0])||!b.length)){b.parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover"); -b.addClass("ui-state-hover");b.hasClass("ui-datepicker-prev")&&b.addClass("ui-datepicker-prev-hover");b.hasClass("ui-datepicker-next")&&b.addClass("ui-datepicker-next-hover")}})}function H(a,b){d.extend(a,b);for(var c in b)if(b[c]==null||b[c]==C)a[c]=b[c];return a}d.extend(d.ui,{datepicker:{version:"1.8.16"}});var B=(new Date).getTime(),J;d.extend(M.prototype,{markerClassName:"hasDatepicker",maxRows:4,log:function(){this.debug&&console.log.apply("",arguments)},_widgetDatepicker:function(){return this.dpDiv}, -setDefaults:function(a){H(this._defaults,a||{});return this},_attachDatepicker:function(a,b){var c=null;for(var e in this._defaults){var f=a.getAttribute("date:"+e);if(f){c=c||{};try{c[e]=eval(f)}catch(h){c[e]=f}}}e=a.nodeName.toLowerCase();f=e=="div"||e=="span";if(!a.id){this.uuid+=1;a.id="dp"+this.uuid}var i=this._newInst(d(a),f);i.settings=d.extend({},b||{},c||{});if(e=="input")this._connectDatepicker(a,i);else f&&this._inlineDatepicker(a,i)},_newInst:function(a,b){return{id:a[0].id.replace(/([^A-Za-z0-9_-])/g, -"\\\\$1"),input:a,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:b,dpDiv:!b?this.dpDiv:N(d('
    '))}},_connectDatepicker:function(a,b){var c=d(a);b.append=d([]);b.trigger=d([]);if(!c.hasClass(this.markerClassName)){this._attachments(c,b);c.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).keyup(this._doKeyUp).bind("setData.datepicker", -function(e,f,h){b.settings[f]=h}).bind("getData.datepicker",function(e,f){return this._get(b,f)});this._autoSize(b);d.data(a,"datepicker",b);b.settings.disabled&&this._disableDatepicker(a)}},_attachments:function(a,b){var c=this._get(b,"appendText"),e=this._get(b,"isRTL");b.append&&b.append.remove();if(c){b.append=d(''+c+"");a[e?"before":"after"](b.append)}a.unbind("focus",this._showDatepicker);b.trigger&&b.trigger.remove();c=this._get(b,"showOn");if(c== -"focus"||c=="both")a.focus(this._showDatepicker);if(c=="button"||c=="both"){c=this._get(b,"buttonText");var f=this._get(b,"buttonImage");b.trigger=d(this._get(b,"buttonImageOnly")?d("").addClass(this._triggerClass).attr({src:f,alt:c,title:c}):d('').addClass(this._triggerClass).html(f==""?c:d("").attr({src:f,alt:c,title:c})));a[e?"before":"after"](b.trigger);b.trigger.click(function(){d.datepicker._datepickerShowing&&d.datepicker._lastInput==a[0]?d.datepicker._hideDatepicker(): -d.datepicker._showDatepicker(a[0]);return false})}},_autoSize:function(a){if(this._get(a,"autoSize")&&!a.inline){var b=new Date(2009,11,20),c=this._get(a,"dateFormat");if(c.match(/[DM]/)){var e=function(f){for(var h=0,i=0,g=0;gh){h=f[g].length;i=g}return i};b.setMonth(e(this._get(a,c.match(/MM/)?"monthNames":"monthNamesShort")));b.setDate(e(this._get(a,c.match(/DD/)?"dayNames":"dayNamesShort"))+20-b.getDay())}a.input.attr("size",this._formatDate(a,b).length)}},_inlineDatepicker:function(a, -b){var c=d(a);if(!c.hasClass(this.markerClassName)){c.addClass(this.markerClassName).append(b.dpDiv).bind("setData.datepicker",function(e,f,h){b.settings[f]=h}).bind("getData.datepicker",function(e,f){return this._get(b,f)});d.data(a,"datepicker",b);this._setDate(b,this._getDefaultDate(b),true);this._updateDatepicker(b);this._updateAlternate(b);b.settings.disabled&&this._disableDatepicker(a);b.dpDiv.css("display","block")}},_dialogDatepicker:function(a,b,c,e,f){a=this._dialogInst;if(!a){this.uuid+= -1;this._dialogInput=d('');this._dialogInput.keydown(this._doKeyDown);d("body").append(this._dialogInput);a=this._dialogInst=this._newInst(this._dialogInput,false);a.settings={};d.data(this._dialogInput[0],"datepicker",a)}H(a.settings,e||{});b=b&&b.constructor==Date?this._formatDate(a,b):b;this._dialogInput.val(b);this._pos=f?f.length?f:[f.pageX,f.pageY]:null;if(!this._pos)this._pos=[document.documentElement.clientWidth/ -2-100+(document.documentElement.scrollLeft||document.body.scrollLeft),document.documentElement.clientHeight/2-150+(document.documentElement.scrollTop||document.body.scrollTop)];this._dialogInput.css("left",this._pos[0]+20+"px").css("top",this._pos[1]+"px");a.settings.onSelect=c;this._inDialog=true;this.dpDiv.addClass(this._dialogClass);this._showDatepicker(this._dialogInput[0]);d.blockUI&&d.blockUI(this.dpDiv);d.data(this._dialogInput[0],"datepicker",a);return this},_destroyDatepicker:function(a){var b= -d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();d.removeData(a,"datepicker");if(e=="input"){c.append.remove();c.trigger.remove();b.removeClass(this.markerClassName).unbind("focus",this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKeyPress).unbind("keyup",this._doKeyUp)}else if(e=="div"||e=="span")b.removeClass(this.markerClassName).empty()}},_enableDatepicker:function(a){var b=d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e= -a.nodeName.toLowerCase();if(e=="input"){a.disabled=false;c.trigger.filter("button").each(function(){this.disabled=false}).end().filter("img").css({opacity:"1.0",cursor:""})}else if(e=="div"||e=="span"){b=b.children("."+this._inlineClass);b.children().removeClass("ui-state-disabled");b.find("select.ui-datepicker-month, select.ui-datepicker-year").removeAttr("disabled")}this._disabledInputs=d.map(this._disabledInputs,function(f){return f==a?null:f})}},_disableDatepicker:function(a){var b=d(a),c=d.data(a, -"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();if(e=="input"){a.disabled=true;c.trigger.filter("button").each(function(){this.disabled=true}).end().filter("img").css({opacity:"0.5",cursor:"default"})}else if(e=="div"||e=="span"){b=b.children("."+this._inlineClass);b.children().addClass("ui-state-disabled");b.find("select.ui-datepicker-month, select.ui-datepicker-year").attr("disabled","disabled")}this._disabledInputs=d.map(this._disabledInputs,function(f){return f== -a?null:f});this._disabledInputs[this._disabledInputs.length]=a}},_isDisabledDatepicker:function(a){if(!a)return false;for(var b=0;b-1}},_doKeyUp:function(a){a=d.datepicker._getInst(a.target);if(a.input.val()!=a.lastVal)try{if(d.datepicker.parseDate(d.datepicker._get(a,"dateFormat"),a.input?a.input.val():null,d.datepicker._getFormatConfig(a))){d.datepicker._setDateFromField(a);d.datepicker._updateAlternate(a);d.datepicker._updateDatepicker(a)}}catch(b){d.datepicker.log(b)}return true},_showDatepicker:function(a){a=a.target||a;if(a.nodeName.toLowerCase()!="input")a=d("input", -a.parentNode)[0];if(!(d.datepicker._isDisabledDatepicker(a)||d.datepicker._lastInput==a)){var b=d.datepicker._getInst(a);if(d.datepicker._curInst&&d.datepicker._curInst!=b){d.datepicker._datepickerShowing&&d.datepicker._triggerOnClose(d.datepicker._curInst);d.datepicker._curInst.dpDiv.stop(true,true)}var c=d.datepicker._get(b,"beforeShow");c=c?c.apply(a,[a,b]):{};if(c!==false){H(b.settings,c);b.lastVal=null;d.datepicker._lastInput=a;d.datepicker._setDateFromField(b);if(d.datepicker._inDialog)a.value= -"";if(!d.datepicker._pos){d.datepicker._pos=d.datepicker._findPos(a);d.datepicker._pos[1]+=a.offsetHeight}var e=false;d(a).parents().each(function(){e|=d(this).css("position")=="fixed";return!e});if(e&&d.browser.opera){d.datepicker._pos[0]-=document.documentElement.scrollLeft;d.datepicker._pos[1]-=document.documentElement.scrollTop}c={left:d.datepicker._pos[0],top:d.datepicker._pos[1]};d.datepicker._pos=null;b.dpDiv.empty();b.dpDiv.css({position:"absolute",display:"block",top:"-1000px"});d.datepicker._updateDatepicker(b); -c=d.datepicker._checkOffset(b,c,e);b.dpDiv.css({position:d.datepicker._inDialog&&d.blockUI?"static":e?"fixed":"absolute",display:"none",left:c.left+"px",top:c.top+"px"});if(!b.inline){c=d.datepicker._get(b,"showAnim");var f=d.datepicker._get(b,"duration"),h=function(){var i=b.dpDiv.find("iframe.ui-datepicker-cover");if(i.length){var g=d.datepicker._getBorders(b.dpDiv);i.css({left:-g[0],top:-g[1],width:b.dpDiv.outerWidth(),height:b.dpDiv.outerHeight()})}};b.dpDiv.zIndex(d(a).zIndex()+1);d.datepicker._datepickerShowing= -true;d.effects&&d.effects[c]?b.dpDiv.show(c,d.datepicker._get(b,"showOptions"),f,h):b.dpDiv[c||"show"](c?f:null,h);if(!c||!f)h();b.input.is(":visible")&&!b.input.is(":disabled")&&b.input.focus();d.datepicker._curInst=b}}}},_updateDatepicker:function(a){this.maxRows=4;var b=d.datepicker._getBorders(a.dpDiv);J=a;a.dpDiv.empty().append(this._generateHTML(a));var c=a.dpDiv.find("iframe.ui-datepicker-cover");c.length&&c.css({left:-b[0],top:-b[1],width:a.dpDiv.outerWidth(),height:a.dpDiv.outerHeight()}); -a.dpDiv.find("."+this._dayOverClass+" a").mouseover();b=this._getNumberOfMonths(a);c=b[1];a.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width("");c>1&&a.dpDiv.addClass("ui-datepicker-multi-"+c).css("width",17*c+"em");a.dpDiv[(b[0]!=1||b[1]!=1?"add":"remove")+"Class"]("ui-datepicker-multi");a.dpDiv[(this._get(a,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl");a==d.datepicker._curInst&&d.datepicker._datepickerShowing&&a.input&&a.input.is(":visible")&& -!a.input.is(":disabled")&&a.input[0]!=document.activeElement&&a.input.focus();if(a.yearshtml){var e=a.yearshtml;setTimeout(function(){e===a.yearshtml&&a.yearshtml&&a.dpDiv.find("select.ui-datepicker-year:first").replaceWith(a.yearshtml);e=a.yearshtml=null},0)}},_getBorders:function(a){var b=function(c){return{thin:1,medium:2,thick:3}[c]||c};return[parseFloat(b(a.css("border-left-width"))),parseFloat(b(a.css("border-top-width")))]},_checkOffset:function(a,b,c){var e=a.dpDiv.outerWidth(),f=a.dpDiv.outerHeight(), -h=a.input?a.input.outerWidth():0,i=a.input?a.input.outerHeight():0,g=document.documentElement.clientWidth+d(document).scrollLeft(),j=document.documentElement.clientHeight+d(document).scrollTop();b.left-=this._get(a,"isRTL")?e-h:0;b.left-=c&&b.left==a.input.offset().left?d(document).scrollLeft():0;b.top-=c&&b.top==a.input.offset().top+i?d(document).scrollTop():0;b.left-=Math.min(b.left,b.left+e>g&&g>e?Math.abs(b.left+e-g):0);b.top-=Math.min(b.top,b.top+f>j&&j>f?Math.abs(f+i):0);return b},_findPos:function(a){for(var b= -this._get(this._getInst(a),"isRTL");a&&(a.type=="hidden"||a.nodeType!=1||d.expr.filters.hidden(a));)a=a[b?"previousSibling":"nextSibling"];a=d(a).offset();return[a.left,a.top]},_triggerOnClose:function(a){var b=this._get(a,"onClose");if(b)b.apply(a.input?a.input[0]:null,[a.input?a.input.val():"",a])},_hideDatepicker:function(a){var b=this._curInst;if(!(!b||a&&b!=d.data(a,"datepicker")))if(this._datepickerShowing){a=this._get(b,"showAnim");var c=this._get(b,"duration"),e=function(){d.datepicker._tidyDialog(b); -this._curInst=null};d.effects&&d.effects[a]?b.dpDiv.hide(a,d.datepicker._get(b,"showOptions"),c,e):b.dpDiv[a=="slideDown"?"slideUp":a=="fadeIn"?"fadeOut":"hide"](a?c:null,e);a||e();d.datepicker._triggerOnClose(b);this._datepickerShowing=false;this._lastInput=null;if(this._inDialog){this._dialogInput.css({position:"absolute",left:"0",top:"-100px"});if(d.blockUI){d.unblockUI();d("body").append(this.dpDiv)}}this._inDialog=false}},_tidyDialog:function(a){a.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")}, -_checkExternalClick:function(a){if(d.datepicker._curInst){a=d(a.target);a[0].id!=d.datepicker._mainDivId&&a.parents("#"+d.datepicker._mainDivId).length==0&&!a.hasClass(d.datepicker.markerClassName)&&!a.hasClass(d.datepicker._triggerClass)&&d.datepicker._datepickerShowing&&!(d.datepicker._inDialog&&d.blockUI)&&d.datepicker._hideDatepicker()}},_adjustDate:function(a,b,c){a=d(a);var e=this._getInst(a[0]);if(!this._isDisabledDatepicker(a[0])){this._adjustInstDate(e,b+(c=="M"?this._get(e,"showCurrentAtPos"): -0),c);this._updateDatepicker(e)}},_gotoToday:function(a){a=d(a);var b=this._getInst(a[0]);if(this._get(b,"gotoCurrent")&&b.currentDay){b.selectedDay=b.currentDay;b.drawMonth=b.selectedMonth=b.currentMonth;b.drawYear=b.selectedYear=b.currentYear}else{var c=new Date;b.selectedDay=c.getDate();b.drawMonth=b.selectedMonth=c.getMonth();b.drawYear=b.selectedYear=c.getFullYear()}this._notifyChange(b);this._adjustDate(a)},_selectMonthYear:function(a,b,c){a=d(a);var e=this._getInst(a[0]);e["selected"+(c=="M"? -"Month":"Year")]=e["draw"+(c=="M"?"Month":"Year")]=parseInt(b.options[b.selectedIndex].value,10);this._notifyChange(e);this._adjustDate(a)},_selectDay:function(a,b,c,e){var f=d(a);if(!(d(e).hasClass(this._unselectableClass)||this._isDisabledDatepicker(f[0]))){f=this._getInst(f[0]);f.selectedDay=f.currentDay=d("a",e).html();f.selectedMonth=f.currentMonth=b;f.selectedYear=f.currentYear=c;this._selectDate(a,this._formatDate(f,f.currentDay,f.currentMonth,f.currentYear))}},_clearDate:function(a){a=d(a); -this._getInst(a[0]);this._selectDate(a,"")},_selectDate:function(a,b){a=this._getInst(d(a)[0]);b=b!=null?b:this._formatDate(a);a.input&&a.input.val(b);this._updateAlternate(a);var c=this._get(a,"onSelect");if(c)c.apply(a.input?a.input[0]:null,[b,a]);else a.input&&a.input.trigger("change");if(a.inline)this._updateDatepicker(a);else{this._hideDatepicker();this._lastInput=a.input[0];typeof a.input[0]!="object"&&a.input.focus();this._lastInput=null}},_updateAlternate:function(a){var b=this._get(a,"altField"); -if(b){var c=this._get(a,"altFormat")||this._get(a,"dateFormat"),e=this._getDate(a),f=this.formatDate(c,e,this._getFormatConfig(a));d(b).each(function(){d(this).val(f)})}},noWeekends:function(a){a=a.getDay();return[a>0&&a<6,""]},iso8601Week:function(a){a=new Date(a.getTime());a.setDate(a.getDate()+4-(a.getDay()||7));var b=a.getTime();a.setMonth(0);a.setDate(1);return Math.floor(Math.round((b-a)/864E5)/7)+1},parseDate:function(a,b,c){if(a==null||b==null)throw"Invalid arguments";b=typeof b=="object"? -b.toString():b+"";if(b=="")return null;var e=(c?c.shortYearCutoff:null)||this._defaults.shortYearCutoff;e=typeof e!="string"?e:(new Date).getFullYear()%100+parseInt(e,10);for(var f=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,h=(c?c.dayNames:null)||this._defaults.dayNames,i=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort,g=(c?c.monthNames:null)||this._defaults.monthNames,j=c=-1,l=-1,u=-1,k=false,o=function(p){(p=A+1-1){j=1;l=u;do{e=this._getDaysInMonth(c,j-1);if(l<=e)break;j++;l-=e}while(1)}v=this._daylightSavingAdjust(new Date(c,j-1,l));if(v.getFullYear()!=c||v.getMonth()+1!=j||v.getDate()!=l)throw"Invalid date";return v},ATOM:"yy-mm-dd", -COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925))*24*60*60*1E7,formatDate:function(a,b,c){if(!b)return"";var e=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,f=(c?c.dayNames:null)||this._defaults.dayNames,h=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort;c=(c?c.monthNames: -null)||this._defaults.monthNames;var i=function(o){(o=k+1 -12?a.getHours()+2:0);return a},_setDate:function(a,b,c){var e=!b,f=a.selectedMonth,h=a.selectedYear;b=this._restrictMinMax(a,this._determineDate(a,b,new Date));a.selectedDay=a.currentDay=b.getDate();a.drawMonth=a.selectedMonth=a.currentMonth=b.getMonth();a.drawYear=a.selectedYear=a.currentYear=b.getFullYear();if((f!=a.selectedMonth||h!=a.selectedYear)&&!c)this._notifyChange(a);this._adjustInstDate(a);if(a.input)a.input.val(e?"":this._formatDate(a))},_getDate:function(a){return!a.currentYear||a.input&& -a.input.val()==""?null:this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay))},_generateHTML:function(a){var b=new Date;b=this._daylightSavingAdjust(new Date(b.getFullYear(),b.getMonth(),b.getDate()));var c=this._get(a,"isRTL"),e=this._get(a,"showButtonPanel"),f=this._get(a,"hideIfNoPrevNext"),h=this._get(a,"navigationAsDateFormat"),i=this._getNumberOfMonths(a),g=this._get(a,"showCurrentAtPos"),j=this._get(a,"stepMonths"),l=i[0]!=1||i[1]!=1,u=this._daylightSavingAdjust(!a.currentDay? -new Date(9999,9,9):new Date(a.currentYear,a.currentMonth,a.currentDay)),k=this._getMinMaxDate(a,"min"),o=this._getMinMaxDate(a,"max");g=a.drawMonth-g;var m=a.drawYear;if(g<0){g+=12;m--}if(o){var n=this._daylightSavingAdjust(new Date(o.getFullYear(),o.getMonth()-i[0]*i[1]+1,o.getDate()));for(n=k&&nn;){g--;if(g<0){g=11;m--}}}a.drawMonth=g;a.drawYear=m;n=this._get(a,"prevText");n=!h?n:this.formatDate(n,this._daylightSavingAdjust(new Date(m,g-j,1)),this._getFormatConfig(a)); -n=this._canAdjustMonth(a,-1,m,g)?''+n+"":f?"":''+n+"";var s=this._get(a,"nextText");s=!h?s:this.formatDate(s,this._daylightSavingAdjust(new Date(m, -g+j,1)),this._getFormatConfig(a));f=this._canAdjustMonth(a,+1,m,g)?''+s+"":f?"":''+s+"";j=this._get(a,"currentText");s=this._get(a,"gotoCurrent")&& -a.currentDay?u:b;j=!h?j:this.formatDate(j,s,this._getFormatConfig(a));h=!a.inline?'":"";e=e?'
    '+(c?h:"")+(this._isInRange(a,s)?'":"")+(c?"":h)+"
    ":"";h=parseInt(this._get(a,"firstDay"),10);h=isNaN(h)?0:h;j=this._get(a,"showWeek");s=this._get(a,"dayNames");this._get(a,"dayNamesShort");var q=this._get(a,"dayNamesMin"),A=this._get(a,"monthNames"),v=this._get(a,"monthNamesShort"),p=this._get(a,"beforeShowDay"),D=this._get(a,"showOtherMonths"),K=this._get(a,"selectOtherMonths");this._get(a,"calculateWeek");for(var E=this._getDefaultDate(a),w="",x=0;x1)switch(G){case 0:y+=" ui-datepicker-group-first";t=" ui-corner-"+(c?"right":"left");break;case i[1]-1:y+=" ui-datepicker-group-last";t=" ui-corner-"+(c?"left":"right");break;default:y+=" ui-datepicker-group-middle";t="";break}y+='">'}y+='
    '+(/all|left/.test(t)&& -x==0?c?f:n:"")+(/all|right/.test(t)&&x==0?c?n:f:"")+this._generateMonthYearHeader(a,g,m,k,o,x>0||G>0,A,v)+'
    ';var z=j?'":"";for(t=0;t<7;t++){var r=(t+h)%7;z+="=5?' class="ui-datepicker-week-end"':"")+'>'+q[r]+""}y+=z+"";z=this._getDaysInMonth(m,g);if(m==a.selectedYear&&g==a.selectedMonth)a.selectedDay=Math.min(a.selectedDay, -z);t=(this._getFirstDayOfMonth(m,g)-h+7)%7;z=Math.ceil((t+z)/7);this.maxRows=z=l?this.maxRows>z?this.maxRows:z:z;r=this._daylightSavingAdjust(new Date(m,g,1-t));for(var Q=0;Q";var R=!j?"":'";for(t=0;t<7;t++){var I=p?p.apply(a.input?a.input[0]:null,[r]):[true,""],F=r.getMonth()!=g,L=F&&!K||!I[0]||k&&ro;R+='";r.setDate(r.getDate()+1);r=this._daylightSavingAdjust(r)}y+=R+""}g++;if(g>11){g=0;m++}y+="
    '+this._get(a,"weekHeader")+"
    '+this._get(a,"calculateWeek")(r)+""+(F&&!D?" ":L?''+ -r.getDate()+"":''+r.getDate()+"")+"
    "+(l?""+(i[0]>0&&G==i[1]-1?'
    ':""):"");O+=y}w+=O}w+=e+(d.browser.msie&&parseInt(d.browser.version,10)<7&&!a.inline?'': -"");a._keyEvent=false;return w},_generateMonthYearHeader:function(a,b,c,e,f,h,i,g){var j=this._get(a,"changeMonth"),l=this._get(a,"changeYear"),u=this._get(a,"showMonthAfterYear"),k='
    ',o="";if(h||!j)o+=''+i[b]+"";else{i=e&&e.getFullYear()==c;var m=f&&f.getFullYear()==c;o+='"}u||(k+=o+(h||!(j&&l)?" ":""));if(!a.yearshtml){a.yearshtml="";if(h||!l)k+=''+c+"";else{g=this._get(a,"yearRange").split(":");var s=(new Date).getFullYear();i=function(q){q=q.match(/c[+-].*/)?c+parseInt(q.substring(1),10):q.match(/[+-].*/)?s+parseInt(q,10):parseInt(q,10);return isNaN(q)?s:q};b=i(g[0]);g=Math.max(b,i(g[1]||""));b=e?Math.max(b, -e.getFullYear()):b;g=f?Math.min(g,f.getFullYear()):g;for(a.yearshtml+='";k+=a.yearshtml;a.yearshtml=null}}k+=this._get(a,"yearSuffix");if(u)k+=(h||!(j&&l)?" ":"")+o;k+="
    ";return k},_adjustInstDate:function(a,b,c){var e=a.drawYear+(c=="Y"?b:0),f=a.drawMonth+ -(c=="M"?b:0);b=Math.min(a.selectedDay,this._getDaysInMonth(e,f))+(c=="D"?b:0);e=this._restrictMinMax(a,this._daylightSavingAdjust(new Date(e,f,b)));a.selectedDay=e.getDate();a.drawMonth=a.selectedMonth=e.getMonth();a.drawYear=a.selectedYear=e.getFullYear();if(c=="M"||c=="Y")this._notifyChange(a)},_restrictMinMax:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");b=c&&ba?a:b},_notifyChange:function(a){var b=this._get(a,"onChangeMonthYear");if(b)b.apply(a.input? -a.input[0]:null,[a.selectedYear,a.selectedMonth+1,a])},_getNumberOfMonths:function(a){a=this._get(a,"numberOfMonths");return a==null?[1,1]:typeof a=="number"?[1,a]:a},_getMinMaxDate:function(a,b){return this._determineDate(a,this._get(a,b+"Date"),null)},_getDaysInMonth:function(a,b){return 32-this._daylightSavingAdjust(new Date(a,b,32)).getDate()},_getFirstDayOfMonth:function(a,b){return(new Date(a,b,1)).getDay()},_canAdjustMonth:function(a,b,c,e){var f=this._getNumberOfMonths(a);c=this._daylightSavingAdjust(new Date(c, -e+(b<0?b:f[0]*f[1]),1));b<0&&c.setDate(this._getDaysInMonth(c.getFullYear(),c.getMonth()));return this._isInRange(a,c)},_isInRange:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");return(!c||b.getTime()>=c.getTime())&&(!a||b.getTime()<=a.getTime())},_getFormatConfig:function(a){var b=this._get(a,"shortYearCutoff");b=typeof b!="string"?b:(new Date).getFullYear()%100+parseInt(b,10);return{shortYearCutoff:b,dayNamesShort:this._get(a,"dayNamesShort"),dayNames:this._get(a, -"dayNames"),monthNamesShort:this._get(a,"monthNamesShort"),monthNames:this._get(a,"monthNames")}},_formatDate:function(a,b,c,e){if(!b){a.currentDay=a.selectedDay;a.currentMonth=a.selectedMonth;a.currentYear=a.selectedYear}b=b?typeof b=="object"?b:this._daylightSavingAdjust(new Date(e,c,b)):this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay));return this.formatDate(this._get(a,"dateFormat"),b,this._getFormatConfig(a))}});d.fn.datepicker=function(a){if(!this.length)return this; -if(!d.datepicker.initialized){d(document).mousedown(d.datepicker._checkExternalClick).find("body").append(d.datepicker.dpDiv);d.datepicker.initialized=true}var b=Array.prototype.slice.call(arguments,1);if(typeof a=="string"&&(a=="isDisabled"||a=="getDate"||a=="widget"))return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b));if(a=="option"&&arguments.length==2&&typeof arguments[1]=="string")return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b));return this.each(function(){typeof a== -"string"?d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this].concat(b)):d.datepicker._attachDatepicker(this,a)})};d.datepicker=new M;d.datepicker.initialized=false;d.datepicker.uuid=(new Date).getTime();d.datepicker.version="1.8.16";window["DP_jQuery_"+B]=d})(jQuery); -;/* - * jQuery UI Progressbar 1.8.16 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Progressbar - * - * Depends: - * jquery.ui.core.js - * jquery.ui.widget.js - */ -(function(b,d){b.widget("ui.progressbar",{options:{value:0,max:100},min:0,_create:function(){this.element.addClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").attr({role:"progressbar","aria-valuemin":this.min,"aria-valuemax":this.options.max,"aria-valuenow":this._value()});this.valueDiv=b("
    ").appendTo(this.element);this.oldValue=this._value();this._refreshValue()},destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"); -this.valueDiv.remove();b.Widget.prototype.destroy.apply(this,arguments)},value:function(a){if(a===d)return this._value();this._setOption("value",a);return this},_setOption:function(a,c){if(a==="value"){this.options.value=c;this._refreshValue();this._value()===this.options.max&&this._trigger("complete")}b.Widget.prototype._setOption.apply(this,arguments)},_value:function(){var a=this.options.value;if(typeof a!=="number")a=0;return Math.min(this.options.max,Math.max(this.min,a))},_percentage:function(){return 100* -this._value()/this.options.max},_refreshValue:function(){var a=this.value(),c=this._percentage();if(this.oldValue!==a){this.oldValue=a;this._trigger("change")}this.valueDiv.toggle(a>this.min).toggleClass("ui-corner-right",a===this.options.max).width(c.toFixed(0)+"%");this.element.attr("aria-valuenow",a)}});b.extend(b.ui.progressbar,{version:"1.8.16"})})(jQuery); -;/* - * jQuery UI Effects 1.8.16 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Effects/ - */ -jQuery.effects||function(f,j){function m(c){var a;if(c&&c.constructor==Array&&c.length==3)return c;if(a=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(c))return[parseInt(a[1],10),parseInt(a[2],10),parseInt(a[3],10)];if(a=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(c))return[parseFloat(a[1])*2.55,parseFloat(a[2])*2.55,parseFloat(a[3])*2.55];if(a=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(c))return[parseInt(a[1], -16),parseInt(a[2],16),parseInt(a[3],16)];if(a=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(c))return[parseInt(a[1]+a[1],16),parseInt(a[2]+a[2],16),parseInt(a[3]+a[3],16)];if(/rgba\(0, 0, 0, 0\)/.exec(c))return n.transparent;return n[f.trim(c).toLowerCase()]}function s(c,a){var b;do{b=f.curCSS(c,a);if(b!=""&&b!="transparent"||f.nodeName(c,"body"))break;a="backgroundColor"}while(c=c.parentNode);return m(b)}function o(){var c=document.defaultView?document.defaultView.getComputedStyle(this,null):this.currentStyle, -a={},b,d;if(c&&c.length&&c[0]&&c[c[0]])for(var e=c.length;e--;){b=c[e];if(typeof c[b]=="string"){d=b.replace(/\-(\w)/g,function(g,h){return h.toUpperCase()});a[d]=c[b]}}else for(b in c)if(typeof c[b]==="string")a[b]=c[b];return a}function p(c){var a,b;for(a in c){b=c[a];if(b==null||f.isFunction(b)||a in t||/scrollbar/.test(a)||!/color/i.test(a)&&isNaN(parseFloat(b)))delete c[a]}return c}function u(c,a){var b={_:0},d;for(d in a)if(c[d]!=a[d])b[d]=a[d];return b}function k(c,a,b,d){if(typeof c=="object"){d= -a;b=null;a=c;c=a.effect}if(f.isFunction(a)){d=a;b=null;a={}}if(typeof a=="number"||f.fx.speeds[a]){d=b;b=a;a={}}if(f.isFunction(b)){d=b;b=null}a=a||{};b=b||a.duration;b=f.fx.off?0:typeof b=="number"?b:b in f.fx.speeds?f.fx.speeds[b]:f.fx.speeds._default;d=d||a.complete;return[c,a,b,d]}function l(c){if(!c||typeof c==="number"||f.fx.speeds[c])return true;if(typeof c==="string"&&!f.effects[c])return true;return false}f.effects={};f.each(["backgroundColor","borderBottomColor","borderLeftColor","borderRightColor", -"borderTopColor","borderColor","color","outlineColor"],function(c,a){f.fx.step[a]=function(b){if(!b.colorInit){b.start=s(b.elem,a);b.end=m(b.end);b.colorInit=true}b.elem.style[a]="rgb("+Math.max(Math.min(parseInt(b.pos*(b.end[0]-b.start[0])+b.start[0],10),255),0)+","+Math.max(Math.min(parseInt(b.pos*(b.end[1]-b.start[1])+b.start[1],10),255),0)+","+Math.max(Math.min(parseInt(b.pos*(b.end[2]-b.start[2])+b.start[2],10),255),0)+")"}});var n={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0, -0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211, -211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0],transparent:[255,255,255]},q=["add","remove","toggle"],t={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};f.effects.animateClass=function(c,a,b, -d){if(f.isFunction(b)){d=b;b=null}return this.queue(function(){var e=f(this),g=e.attr("style")||" ",h=p(o.call(this)),r,v=e.attr("class");f.each(q,function(w,i){c[i]&&e[i+"Class"](c[i])});r=p(o.call(this));e.attr("class",v);e.animate(u(h,r),{queue:false,duration:a,easing:b,complete:function(){f.each(q,function(w,i){c[i]&&e[i+"Class"](c[i])});if(typeof e.attr("style")=="object"){e.attr("style").cssText="";e.attr("style").cssText=g}else e.attr("style",g);d&&d.apply(this,arguments);f.dequeue(this)}})})}; -f.fn.extend({_addClass:f.fn.addClass,addClass:function(c,a,b,d){return a?f.effects.animateClass.apply(this,[{add:c},a,b,d]):this._addClass(c)},_removeClass:f.fn.removeClass,removeClass:function(c,a,b,d){return a?f.effects.animateClass.apply(this,[{remove:c},a,b,d]):this._removeClass(c)},_toggleClass:f.fn.toggleClass,toggleClass:function(c,a,b,d,e){return typeof a=="boolean"||a===j?b?f.effects.animateClass.apply(this,[a?{add:c}:{remove:c},b,d,e]):this._toggleClass(c,a):f.effects.animateClass.apply(this, -[{toggle:c},a,b,d])},switchClass:function(c,a,b,d,e){return f.effects.animateClass.apply(this,[{add:a,remove:c},b,d,e])}});f.extend(f.effects,{version:"1.8.16",save:function(c,a){for(var b=0;b").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}), -d=document.activeElement;c.wrap(b);if(c[0]===d||f.contains(c[0],d))f(d).focus();b=c.parent();if(c.css("position")=="static"){b.css({position:"relative"});c.css({position:"relative"})}else{f.extend(a,{position:c.css("position"),zIndex:c.css("z-index")});f.each(["top","left","bottom","right"],function(e,g){a[g]=c.css(g);if(isNaN(parseInt(a[g],10)))a[g]="auto"});c.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})}return b.css(a).show()},removeWrapper:function(c){var a,b=document.activeElement; -if(c.parent().is(".ui-effects-wrapper")){a=c.parent().replaceWith(c);if(c[0]===b||f.contains(c[0],b))f(b).focus();return a}return c},setTransition:function(c,a,b,d){d=d||{};f.each(a,function(e,g){unit=c.cssUnit(g);if(unit[0]>0)d[g]=unit[0]*b+unit[1]});return d}});f.fn.extend({effect:function(c){var a=k.apply(this,arguments),b={options:a[1],duration:a[2],callback:a[3]};a=b.options.mode;var d=f.effects[c];if(f.fx.off||!d)return a?this[a](b.duration,b.callback):this.each(function(){b.callback&&b.callback.call(this)}); -return d.call(this,b)},_show:f.fn.show,show:function(c){if(l(c))return this._show.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="show";return this.effect.apply(this,a)}},_hide:f.fn.hide,hide:function(c){if(l(c))return this._hide.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="hide";return this.effect.apply(this,a)}},__toggle:f.fn.toggle,toggle:function(c){if(l(c)||typeof c==="boolean"||f.isFunction(c))return this.__toggle.apply(this,arguments);else{var a=k.apply(this, -arguments);a[1].mode="toggle";return this.effect.apply(this,a)}},cssUnit:function(c){var a=this.css(c),b=[];f.each(["em","px","%","pt"],function(d,e){if(a.indexOf(e)>0)b=[parseFloat(a),e]});return b}});f.easing.jswing=f.easing.swing;f.extend(f.easing,{def:"easeOutQuad",swing:function(c,a,b,d,e){return f.easing[f.easing.def](c,a,b,d,e)},easeInQuad:function(c,a,b,d,e){return d*(a/=e)*a+b},easeOutQuad:function(c,a,b,d,e){return-d*(a/=e)*(a-2)+b},easeInOutQuad:function(c,a,b,d,e){if((a/=e/2)<1)return d/ -2*a*a+b;return-d/2*(--a*(a-2)-1)+b},easeInCubic:function(c,a,b,d,e){return d*(a/=e)*a*a+b},easeOutCubic:function(c,a,b,d,e){return d*((a=a/e-1)*a*a+1)+b},easeInOutCubic:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a+b;return d/2*((a-=2)*a*a+2)+b},easeInQuart:function(c,a,b,d,e){return d*(a/=e)*a*a*a+b},easeOutQuart:function(c,a,b,d,e){return-d*((a=a/e-1)*a*a*a-1)+b},easeInOutQuart:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a*a+b;return-d/2*((a-=2)*a*a*a-2)+b},easeInQuint:function(c,a,b, -d,e){return d*(a/=e)*a*a*a*a+b},easeOutQuint:function(c,a,b,d,e){return d*((a=a/e-1)*a*a*a*a+1)+b},easeInOutQuint:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a*a*a+b;return d/2*((a-=2)*a*a*a*a+2)+b},easeInSine:function(c,a,b,d,e){return-d*Math.cos(a/e*(Math.PI/2))+d+b},easeOutSine:function(c,a,b,d,e){return d*Math.sin(a/e*(Math.PI/2))+b},easeInOutSine:function(c,a,b,d,e){return-d/2*(Math.cos(Math.PI*a/e)-1)+b},easeInExpo:function(c,a,b,d,e){return a==0?b:d*Math.pow(2,10*(a/e-1))+b},easeOutExpo:function(c, -a,b,d,e){return a==e?b+d:d*(-Math.pow(2,-10*a/e)+1)+b},easeInOutExpo:function(c,a,b,d,e){if(a==0)return b;if(a==e)return b+d;if((a/=e/2)<1)return d/2*Math.pow(2,10*(a-1))+b;return d/2*(-Math.pow(2,-10*--a)+2)+b},easeInCirc:function(c,a,b,d,e){return-d*(Math.sqrt(1-(a/=e)*a)-1)+b},easeOutCirc:function(c,a,b,d,e){return d*Math.sqrt(1-(a=a/e-1)*a)+b},easeInOutCirc:function(c,a,b,d,e){if((a/=e/2)<1)return-d/2*(Math.sqrt(1-a*a)-1)+b;return d/2*(Math.sqrt(1-(a-=2)*a)+1)+b},easeInElastic:function(c,a,b, -d,e){c=1.70158;var g=0,h=d;if(a==0)return b;if((a/=e)==1)return b+d;g||(g=e*0.3);if(h").css({position:"absolute",visibility:"visible",left:-f*(h/d),top:-e*(i/c)}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:h/d,height:i/c,left:g.left+f*(h/d)+(a.options.mode=="show"?(f-Math.floor(d/2))*(h/d):0),top:g.top+e*(i/c)+(a.options.mode=="show"?(e-Math.floor(c/2))*(i/c):0),opacity:a.options.mode=="show"?0:1}).animate({left:g.left+f*(h/d)+(a.options.mode=="show"?0:(f-Math.floor(d/2))*(h/d)),top:g.top+ -e*(i/c)+(a.options.mode=="show"?0:(e-Math.floor(c/2))*(i/c)),opacity:a.options.mode=="show"?1:0},a.duration||500);setTimeout(function(){a.options.mode=="show"?b.css({visibility:"visible"}):b.css({visibility:"visible"}).hide();a.callback&&a.callback.apply(b[0]);b.dequeue();j("div.ui-effects-explode").remove()},a.duration||500)})}})(jQuery); -;/* - * jQuery UI Effects Fade 1.8.16 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Effects/Fade - * - * Depends: - * jquery.effects.core.js - */ -(function(b){b.effects.fade=function(a){return this.queue(function(){var c=b(this),d=b.effects.setMode(c,a.options.mode||"hide");c.animate({opacity:d},{queue:false,duration:a.duration,easing:a.options.easing,complete:function(){a.callback&&a.callback.apply(this,arguments);c.dequeue()}})})}})(jQuery); -;/* - * jQuery UI Effects Fold 1.8.16 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Effects/Fold - * - * Depends: - * jquery.effects.core.js - */ -(function(c){c.effects.fold=function(a){return this.queue(function(){var b=c(this),j=["position","top","bottom","left","right"],d=c.effects.setMode(b,a.options.mode||"hide"),g=a.options.size||15,h=!!a.options.horizFirst,k=a.duration?a.duration/2:c.fx.speeds._default/2;c.effects.save(b,j);b.show();var e=c.effects.createWrapper(b).css({overflow:"hidden"}),f=d=="show"!=h,l=f?["width","height"]:["height","width"];f=f?[e.width(),e.height()]:[e.height(),e.width()];var i=/([0-9]+)%/.exec(g);if(i)g=parseInt(i[1], -10)/100*f[d=="hide"?0:1];if(d=="show")e.css(h?{height:0,width:g}:{height:g,width:0});h={};i={};h[l[0]]=d=="show"?f[0]:g;i[l[1]]=d=="show"?f[1]:0;e.animate(h,k,a.options.easing).animate(i,k,a.options.easing,function(){d=="hide"&&b.hide();c.effects.restore(b,j);c.effects.removeWrapper(b);a.callback&&a.callback.apply(b[0],arguments);b.dequeue()})})}})(jQuery); -;/* - * jQuery UI Effects Highlight 1.8.16 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Effects/Highlight - * - * Depends: - * jquery.effects.core.js - */ -(function(b){b.effects.highlight=function(c){return this.queue(function(){var a=b(this),e=["backgroundImage","backgroundColor","opacity"],d=b.effects.setMode(a,c.options.mode||"show"),f={backgroundColor:a.css("backgroundColor")};if(d=="hide")f.opacity=0;b.effects.save(a,e);a.show().css({backgroundImage:"none",backgroundColor:c.options.color||"#ffff99"}).animate(f,{queue:false,duration:c.duration,easing:c.options.easing,complete:function(){d=="hide"&&a.hide();b.effects.restore(a,e);d=="show"&&!b.support.opacity&& -this.style.removeAttribute("filter");c.callback&&c.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery); -;/* - * jQuery UI Effects Pulsate 1.8.16 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Effects/Pulsate - * - * Depends: - * jquery.effects.core.js - */ -(function(d){d.effects.pulsate=function(a){return this.queue(function(){var b=d(this),c=d.effects.setMode(b,a.options.mode||"show");times=(a.options.times||5)*2-1;duration=a.duration?a.duration/2:d.fx.speeds._default/2;isVisible=b.is(":visible");animateTo=0;if(!isVisible){b.css("opacity",0).show();animateTo=1}if(c=="hide"&&isVisible||c=="show"&&!isVisible)times--;for(c=0;c').appendTo(document.body).addClass(a.options.className).css({top:d.top,left:d.left,height:b.innerHeight(),width:b.innerWidth(),position:"absolute"}).animate(c,a.duration,a.options.easing,function(){f.remove();a.callback&&a.callback.apply(b[0],arguments); -b.dequeue()})})}})(jQuery); -; \ No newline at end of file diff --git a/core/js/js.js b/core/js/js.js index 95889ac8a2..6b0c289850 100644 --- a/core/js/js.js +++ b/core/js/js.js @@ -37,14 +37,14 @@ function t(app,text, vars){ t.cache[app] = []; } } - var _build = function(text, vars) { - return text.replace(/{([^{}]*)}/g, - function (a, b) { - var r = vars[b]; - return typeof r === 'string' || typeof r === 'number' ? r : a; - } - ); - } + var _build = function (text, vars) { + return text.replace(/{([^{}]*)}/g, + function (a, b) { + var r = vars[b]; + return typeof r === 'string' || typeof r === 'number' ? r : a; + } + ); + }; if( typeof( t.cache[app][text] ) !== 'undefined' ){ if(typeof vars === 'object') { return _build(t.cache[app][text], vars); @@ -88,7 +88,7 @@ var OC={ PERMISSION_DELETE:8, PERMISSION_SHARE:16, webroot:oc_webroot, - appswebroots:oc_appswebroots, + appswebroots:(typeof oc_appswebroots !== 'undefined') ? oc_appswebroots:false, currentUser:(typeof oc_current_user!=='undefined')?oc_current_user:false, coreApps:['', 'admin','log','search','settings','core','3rdparty'], /** @@ -100,6 +100,27 @@ var OC={ linkTo:function(app,file){ return OC.filePath(app,'',file); }, + /** + * Creates an url for remote use + * @param string $service id + * @return string the url + * + * Returns a url to the given service. + */ + linkToRemoteBase:function(service) { + return OC.webroot + '/remote.php/' + service; + }, + /** + * @brief Creates an absolute url for remote use + * @param string $service id + * @param bool $add_slash + * @return string the url + * + * Returns a absolute url to the given service. + */ + linkToRemote:function(service) { + return window.location.protocol + '//' + window.location.host + OC.linkToRemoteBase(service); + }, /** * get the absolute url for a file in an app * @param app the id of the app @@ -247,7 +268,7 @@ var OC={ var popup = $('#appsettings_popup'); if(popup.length == 0) { $('body').prepend(''); - popup = $('#appsettings_popup'); + popup = $('#appsettings_popup'); popup.addClass(settings.hasClass('topright') ? 'topright' : 'bottomleft'); } if(popup.is(':visible')) { @@ -289,6 +310,41 @@ OC.search.lastResults={}; OC.addStyle.loaded=[]; OC.addScript.loaded=[]; +OC.Notification={ + getDefaultNotificationFunction: null, + setDefault: function(callback) { + OC.Notification.getDefaultNotificationFunction = callback; + }, + hide: function(callback) { + $("#notification").text(''); + $('#notification').fadeOut('400', function(){ + if (OC.Notification.isHidden()) { + if (OC.Notification.getDefaultNotificationFunction) { + OC.Notification.getDefaultNotificationFunction.call(); + } + } + if (callback) { + callback.call(); + } + }); + }, + showHtml: function(html) { + var notification = $('#notification'); + notification.hide(); + notification.html(html); + notification.fadeIn().css("display","inline"); + }, + show: function(text) { + var notification = $('#notification'); + notification.hide(); + notification.text(text); + notification.fadeIn().css("display","inline"); + }, + isHidden: function() { + return ($("#notification").text() === ''); + } +}; + OC.Breadcrumb={ container:null, crumbs:[], @@ -298,7 +354,6 @@ OC.Breadcrumb={ } var crumb=$('
    '); crumb.addClass('crumb').addClass('last'); - crumb.attr('style','background-image:url("'+OC.imagePath('core','breadcrumb')+'")'); var crumbLink=$(''); crumbLink.attr('href',link); @@ -491,7 +546,6 @@ function object(o) { return new F(); } - /** * Fills height of window. (more precise than height: 100%;) */ @@ -504,6 +558,7 @@ function fillHeight(selector) { if(selector.outerHeight() > selector.height()){ selector.css('height', height-(selector.outerHeight()-selector.height()) + 'px'); } + console.warn("This function is deprecated! Use CSS instead"); } /** @@ -519,17 +574,11 @@ function fillWindow(selector) { if(selector.outerWidth() > selector.width()){ selector.css('width', width-(selector.outerWidth()-selector.width()) + 'px'); } + console.warn("This function is deprecated! Use CSS instead"); } $(document).ready(function(){ - $(window).resize(function () { - fillHeight($('#leftcontent')); - fillWindow($('#content')); - fillWindow($('#rightcontent')); - }); - $(window).trigger('resize'); - if(!SVGSupport()){ //replace all svg images with png images for browser that dont support svg replaceSVG(); }else{ @@ -573,6 +622,7 @@ $(document).ready(function(){ }); // 'show password' checkbox + $('#password').showPassword(); $('#pass2').showPassword(); //use infield labels @@ -613,14 +663,13 @@ $(document).ready(function(){ event.stopPropagation(); }); $(window).click(function(){//hide the settings menu when clicking outside it - if($('body').attr("id")==="body-user"){ - $('#settings #expanddiv').slideUp(); - } + $('#settings #expanddiv').slideUp(); }); // all the tipsy stuff needs to be here (in reverse order) to work $('.jp-controls .jp-previous').tipsy({gravity:'nw', fade:true, live:true}); $('.jp-controls .jp-next').tipsy({gravity:'n', fade:true, live:true}); + $('.displayName .action').tipsy({gravity:'se', fade:true, live:true}); $('.password .action').tipsy({gravity:'se', fade:true, live:true}); $('#upload').tipsy({gravity:'w', fade:true}); $('.selectedActions a').tipsy({gravity:'s', fade:true, live:true}); diff --git a/core/js/oc-requesttoken.js b/core/js/oc-requesttoken.js new file mode 100644 index 0000000000..f4cf286b8a --- /dev/null +++ b/core/js/oc-requesttoken.js @@ -0,0 +1,3 @@ +$(document).bind('ajaxSend', function(elm, xhr, s) { + xhr.setRequestHeader('requesttoken', oc_requesttoken); +}); \ No newline at end of file diff --git a/core/js/oc-vcategories.js b/core/js/oc-vcategories.js index 609703f2cc..3e75767c49 100644 --- a/core/js/oc-vcategories.js +++ b/core/js/oc-vcategories.js @@ -50,7 +50,7 @@ var OCCategories= { $('#category_dialog').remove(); }, open : function(event, ui) { - $('#category_addinput').live('input',function() { + $('#category_addinput').on('input',function() { if($(this).val().length > 0) { $('#category_addbutton').removeAttr('disabled'); } @@ -61,7 +61,7 @@ var OCCategories= { $('#category_addbutton').attr('disabled', 'disabled'); return false; }); - $('#category_addbutton').live('click',function(e) { + $('#category_addbutton').on('click',function(e) { e.preventDefault(); if($('#category_addinput').val().length > 0) { OCCategories.add($('#category_addinput').val()); diff --git a/core/js/setup.js b/core/js/setup.js index 39fcf4a271..9aded6591c 100644 --- a/core/js/setup.js +++ b/core/js/setup.js @@ -52,11 +52,12 @@ $(document).ready(function() { // Save form parameters var post = $(this).serializeArray(); + // FIXME: This lines are breaking the installation // Disable inputs - $(':submit', this).attr('disabled','disabled').val('Finishing …'); - $('input', this).addClass('ui-state-disabled').attr('disabled','disabled'); - $('#selectDbType').button('disable'); - $('label.ui-button', this).addClass('ui-state-disabled').attr('aria-disabled', 'true').button('disable'); + // $(':submit', this).attr('disabled','disabled').val('Finishing …'); + // $('input', this).addClass('ui-state-disabled').attr('disabled','disabled'); + // $('#selectDbType').button('disable'); + // $('label.ui-button', this).addClass('ui-state-disabled').attr('aria-disabled', 'true').button('disable'); // Create the form var form = $('
    '); diff --git a/core/js/share.js b/core/js/share.js index bb3ec010ff..6ad4130690 100644 --- a/core/js/share.js +++ b/core/js/share.js @@ -23,7 +23,10 @@ OC.Share={ } else { var file = $('tr').filterAttr('data-file', OC.basename(item)); if (file.length > 0) { - $(file).find('.fileactions .action').filterAttr('data-action', 'Share').find('img').attr('src', image); + var action = $(file).find('.fileactions .action').filterAttr('data-action', 'Share'); + action.find('img').attr('src', image); + action.addClass('permanent'); + action.html(action.html().replace(t('core', 'Share'), t('core', 'Shared'))); } var dir = $('#dir').val(); if (dir.length > 1) { @@ -32,9 +35,12 @@ OC.Share={ // Search for possible parent folders that are shared while (path != last) { if (path == item) { - var img = $('.fileactions .action').filterAttr('data-action', 'Share').find('img'); + var action = $('.fileactions .action').filterAttr('data-action', 'Share'); + var img = action.find('img'); if (img.attr('src') != OC.imagePath('core', 'actions/public')) { img.attr('src', image); + action.addClass('permanent'); + action.html(action.html().replace(t('core', 'Share'), t('core', 'Shared'))); } } last = path; @@ -48,7 +54,8 @@ OC.Share={ }, updateIcon:function(itemType, itemSource) { if (itemType == 'file' || itemType == 'folder') { - var filename = $('tr').filterAttr('data-id', String(itemSource)).data('file'); + var file = $('tr').filterAttr('data-id', String(itemSource)); + var filename = file.data('file'); if ($('#dir').val() == '/') { itemSource = $('#dir').val() + filename; } else { @@ -75,6 +82,16 @@ OC.Share={ }); if (itemType != 'file' && itemType != 'folder') { $('a.share[data-item="'+itemSource+'"]').css('background', 'url('+image+') no-repeat center'); + } else { + var action = $(file).find('.fileactions .action').filterAttr('data-action', 'Share'); + action.find('img').attr('src', image); + if (shares) { + action.addClass('permanent'); + action.html(action.html().replace(t('core', 'Share'), t('core', 'Shared'))); + } else { + action.removeClass('permanent'); + action.html(action.html().replace(t('core', 'Shared'), t('core', 'Share'))); + } } if (shares) { OC.Share.statuses[itemSource] = link; @@ -148,9 +165,9 @@ OC.Share={ var html = '