diff options
author | Jan-Christoph Borchardt <JanCBorchardt@fsfe.org> | 2011-08-17 13:48:36 +0200 |
---|---|---|
committer | Jan-Christoph Borchardt <JanCBorchardt@fsfe.org> | 2011-08-17 13:48:36 +0200 |
commit | 683e1250427d75d0d3431c486967059871850d6a (patch) | |
tree | c5b06d8bd2875b5576c675dec9a6bf6476682d85 | |
parent | 0bd92fecec6a3fa1db66a84291317bc6f00fce48 (diff) | |
parent | b7b3a4920341f00e2f544ca7f883a93dab019756 (diff) | |
download | nextcloud-server-683e1250427d75d0d3431c486967059871850d6a.tar.gz nextcloud-server-683e1250427d75d0d3431c486967059871850d6a.zip |
Merge branch 'sharing'
23 files changed, 1877 insertions, 5 deletions
diff --git a/3rdparty/css/chosen/chosen-sprite.png b/3rdparty/css/chosen/chosen-sprite.png Binary files differnew file mode 100644 index 00000000000..f20db4439ea --- /dev/null +++ b/3rdparty/css/chosen/chosen-sprite.png diff --git a/3rdparty/css/chosen/chosen.css b/3rdparty/css/chosen/chosen.css new file mode 100644 index 00000000000..247d07bf021 --- /dev/null +++ b/3rdparty/css/chosen/chosen.css @@ -0,0 +1,340 @@ +/* @group Base */ +select.chzn-select { + visibility: hidden; + height: 28px !important; + min-height: 28px !important; +} +.chzn-container { + font-size: 13px; + position: relative; + display: inline-block; + zoom: 1; + *display: inline; +} +.chzn-container .chzn-drop { + background: #fff; + border: 1px solid #aaa; + border-top: 0; + position: absolute; + top: 29px; + left: 0; + -webkit-box-shadow: 0 4px 5px rgba(0,0,0,.15); + -moz-box-shadow : 0 4px 5px rgba(0,0,0,.15); + -o-box-shadow : 0 4px 5px rgba(0,0,0,.15); + box-shadow : 0 4px 5px rgba(0,0,0,.15); + z-index: 999; +} +/* @end */ + +/* @group Single Chosen */ +.chzn-container-single .chzn-single { + background-color: #fff; + background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eeeeee), color-stop(0.5, white)); + background-image: -webkit-linear-gradient(center bottom, #eeeeee 0%, white 50%); + background-image: -moz-linear-gradient(center bottom, #eeeeee 0%, white 50%); + background-image: -o-linear-gradient(top, #eeeeee 0%,#ffffff 50%); + background-image: -ms-linear-gradient(top, #eeeeee 0%,#ffffff 50%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#ffffff',GradientType=0 ); + background-image: linear-gradient(top, #eeeeee 0%,#ffffff 50%); + -webkit-border-radius: 4px; + -moz-border-radius : 4px; + border-radius : 4px; + -moz-background-clip : padding; + -webkit-background-clip: padding-box; + background-clip : padding-box; + border: 1px solid #aaa; + display: block; + overflow: hidden; + white-space: nowrap; + position: relative; + height: 26px; + line-height: 26px; + padding: 0 0 0 8px; + color: #444; + text-decoration: none; +} +.chzn-container-single .chzn-single span { + margin-right: 26px; + display: block; + overflow: hidden; + white-space: nowrap; + -o-text-overflow: ellipsis; + -ms-text-overflow: ellipsis; + -moz-binding: url('/xml/ellipsis.xml#ellipsis'); + text-overflow: ellipsis; +} +.chzn-container-single .chzn-single div { + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius : 0 4px 4px 0; + border-radius : 0 4px 4px 0; + -moz-background-clip : padding; + -webkit-background-clip: padding-box; + background-clip : padding-box; + background: #ccc; + background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #ccc), color-stop(0.6, #eee)); + background-image: -webkit-linear-gradient(center bottom, #ccc 0%, #eee 60%); + background-image: -moz-linear-gradient(center bottom, #ccc 0%, #eee 60%); + background-image: -o-linear-gradient(bottom, #ccc 0%, #eee 60%); + background-image: -ms-linear-gradient(top, #cccccc 0%,#eeeeee 60%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#cccccc', endColorstr='#eeeeee',GradientType=0 ); + background-image: linear-gradient(top, #cccccc 0%,#eeeeee 60%); + border-left: 1px solid #aaa; + position: absolute; + right: 0; + top: 0; + display: block; + height: 100%; + width: 18px; +} +.chzn-container-single .chzn-single div b { + background: url('chosen-sprite.png') no-repeat 0 1px; + display: block; + width: 100%; + height: 100%; +} +.chzn-container-single .chzn-search { + padding: 3px 4px; + margin: 0; + white-space: nowrap; +} +.chzn-container-single .chzn-search input { + background: #fff url('chosen-sprite.png') no-repeat 100% -20px; + background: url('chosen-sprite.png') no-repeat 100% -20px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee)); + background: url('chosen-sprite.png') no-repeat 100% -20px, -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%); + background: url('chosen-sprite.png') no-repeat 100% -20px, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%); + background: url('chosen-sprite.png') no-repeat 100% -20px, -o-linear-gradient(bottom, white 85%, #eeeeee 99%); + background: url('chosen-sprite.png') no-repeat 100% -20px, -ms-linear-gradient(top, #ffffff 85%,#eeeeee 99%); + background: url('chosen-sprite.png') no-repeat 100% -20px, -ms-linear-gradient(top, #ffffff 85%,#eeeeee 99%); + background: url('chosen-sprite.png') no-repeat 100% -20px, linear-gradient(top, #ffffff 85%,#eeeeee 99%); + margin: 1px 0; + padding: 4px 20px 4px 5px; + outline: 0; + border: 1px solid #aaa; + font-family: sans-serif; + font-size: 1em; +} +.chzn-container-single .chzn-drop { + -webkit-border-radius: 0 0 4px 4px; + -moz-border-radius : 0 0 4px 4px; + border-radius : 0 0 4px 4px; + -moz-background-clip : padding; + -webkit-background-clip: padding-box; + background-clip : padding-box; +} +/* @end */ + +/* @group Multi Chosen */ +.chzn-container-multi .chzn-choices { + background-color: #fff; + background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee)); + background-image: -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%); + background-image: -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%); + background-image: -o-linear-gradient(bottom, white 85%, #eeeeee 99%); + background-image: -ms-linear-gradient(top, #ffffff 85%,#eeeeee 99%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#eeeeee',GradientType=0 ); + background-image: linear-gradient(top, #ffffff 85%,#eeeeee 99%); + border: 1px solid #aaa; + margin: 0; + padding: 0; + cursor: text; + overflow: hidden; + height: auto !important; + height: 1%; + position: relative; +} +.chzn-container-multi .chzn-choices li { + float: left; + list-style: none; +} +.chzn-container-multi .chzn-choices .search-field { + white-space: nowrap; + margin: 0; + padding: 0; +} +.chzn-container-multi .chzn-choices .search-field input { + color: #666; + background: transparent !important; + border: 0 !important; + padding: 5px; + margin: 1px 0; + outline: 0; + -webkit-box-shadow: none; + -moz-box-shadow : none; + -o-box-shadow : none; + box-shadow : none; +} +.chzn-container-multi .chzn-choices .search-field .default { + color: #999; +} +.chzn-container-multi .chzn-choices .search-choice { + -webkit-border-radius: 3px; + -moz-border-radius : 3px; + border-radius : 3px; + -moz-background-clip : padding; + -webkit-background-clip: padding-box; + background-clip : padding-box; + background-color: #e4e4e4; + background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #e4e4e4), color-stop(0.7, #eeeeee)); + background-image: -webkit-linear-gradient(center bottom, #e4e4e4 0%, #eeeeee 70%); + background-image: -moz-linear-gradient(center bottom, #e4e4e4 0%, #eeeeee 70%); + background-image: -o-linear-gradient(bottom, #e4e4e4 0%, #eeeeee 70%); + background-image: -ms-linear-gradient(top, #e4e4e4 0%,#eeeeee 70%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#e4e4e4', endColorstr='#eeeeee',GradientType=0 ); + background-image: linear-gradient(top, #e4e4e4 0%,#eeeeee 70%); + color: #333; + border: 1px solid #b4b4b4; + line-height: 13px; + padding: 3px 19px 3px 6px; + margin: 3px 0 3px 5px; + position: relative; +} +.chzn-container-multi .chzn-choices .search-choice span { + cursor: default; +} +.chzn-container-multi .chzn-choices .search-choice-focus { + background: #d4d4d4; +} +.chzn-container-multi .chzn-choices .search-choice .search-choice-close { + display: block; + position: absolute; + right: 5px; + top: 6px; + width: 8px; + height: 9px; + font-size: 1px; + background: url(chosen-sprite.png) right top no-repeat; +} +.chzn-container-multi .chzn-choices .search-choice .search-choice-close:hover { + background-position: right -9px; +} +.chzn-container-multi .chzn-choices .search-choice-focus .search-choice-close { + background-position: right -9px; +} +/* @end */ + +/* @group Results */ +.chzn-container .chzn-results { + margin: 0 4px 4px 0; + max-height: 190px; + padding: 0 0 0 4px; + position: relative; + overflow-x: hidden; + overflow-y: auto; +} +.chzn-container-multi .chzn-results { + margin: -1px 0 0; + padding: 0; +} +.chzn-container .chzn-results li { + line-height: 80%; + padding: 7px 7px 8px; + margin: 0; + list-style: none; +} +.chzn-container .chzn-results .active-result { + cursor: pointer; +} +.chzn-container .chzn-results .highlighted { + background: #3875d7; + color: #fff; +} +.chzn-container .chzn-results li em { + background: #feffde; + font-style: normal; +} +.chzn-container .chzn-results .highlighted em { + background: transparent; +} +.chzn-container .chzn-results .no-results { + background: #f4f4f4; +} +.chzn-container .chzn-results .group-result { + cursor: default; + color: #999; + font-weight: bold; +} +.chzn-container .chzn-results .group-option { + padding-left: 20px; +} +.chzn-container-multi .chzn-drop .result-selected { + display: none; +} +/* @end */ + +/* @group Active */ +.chzn-container-active .chzn-single { + -webkit-box-shadow: 0 0 5px rgba(0,0,0,.3); + -moz-box-shadow : 0 0 5px rgba(0,0,0,.3); + -o-box-shadow : 0 0 5px rgba(0,0,0,.3); + box-shadow : 0 0 5px rgba(0,0,0,.3); + border: 1px solid #5897fb; +} +.chzn-container-active .chzn-single-with-drop { + border: 1px solid #aaa; + -webkit-box-shadow: 0 1px 0 #fff inset; + -moz-box-shadow : 0 1px 0 #fff inset; + -o-box-shadow : 0 1px 0 #fff inset; + box-shadow : 0 1px 0 #fff inset; + background-color: #eee; + background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, white), color-stop(0.5, #eeeeee)); + background-image: -webkit-linear-gradient(center bottom, white 0%, #eeeeee 50%); + background-image: -moz-linear-gradient(center bottom, white 0%, #eeeeee 50%); + background-image: -o-linear-gradient(bottom, white 0%, #eeeeee 50%); + background-image: -ms-linear-gradient(top, #ffffff 0%,#eeeeee 50%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#eeeeee',GradientType=0 ); + background-image: linear-gradient(top, #ffffff 0%,#eeeeee 50%); + -webkit-border-bottom-left-radius : 0; + -webkit-border-bottom-right-radius: 0; + -moz-border-radius-bottomleft : 0; + -moz-border-radius-bottomright: 0; + border-bottom-left-radius : 0; + border-bottom-right-radius: 0; +} +.chzn-container-active .chzn-single-with-drop div { + background: transparent; + border-left: none; +} +.chzn-container-active .chzn-single-with-drop div b { + background-position: -18px 1px; +} +.chzn-container-active .chzn-choices { + -webkit-box-shadow: 0 0 5px rgba(0,0,0,.3); + -moz-box-shadow : 0 0 5px rgba(0,0,0,.3); + -o-box-shadow : 0 0 5px rgba(0,0,0,.3); + box-shadow : 0 0 5px rgba(0,0,0,.3); + border: 1px solid #5897fb; +} +.chzn-container-active .chzn-choices .search-field input { + color: #111 !important; +} +/* @end */ + +/* @group Right to Left */ +.chzn-rtl { direction:rtl;text-align: right; } +.chzn-rtl .chzn-single { padding-left: 0; padding-right: 8px; } +.chzn-rtl .chzn-single span { margin-left: 26px; margin-right: 0; } +.chzn-rtl .chzn-single div { + left: 0; right: auto; + border-left: none; border-right: 1px solid #aaaaaa; + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius : 4px 0 0 4px; + border-radius : 4px 0 0 4px; +} +.chzn-rtl .chzn-choices li { float: right; } +.chzn-rtl .chzn-choices .search-choice { padding: 3px 6px 3px 19px; margin: 3px 5px 3px 0; } +.chzn-rtl .chzn-choices .search-choice .search-choice-close { left: 5px; right: auto; background-position: right top;} +.chzn-rtl.chzn-container-single .chzn-results { margin-left: 4px; margin-right: 0; padding-left: 0; padding-right: 4px; } +.chzn-rtl .chzn-results .group-option { padding-left: 0; padding-right: 20px; } +.chzn-rtl.chzn-container-active .chzn-single-with-drop div { border-right: none; } +.chzn-rtl .chzn-search input { + background: url('chosen-sprite.png') no-repeat -38px -20px, #ffffff; + background: url('chosen-sprite.png') no-repeat -38px -20px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee)); + background: url('chosen-sprite.png') no-repeat -38px -20px, -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%); + background: url('chosen-sprite.png') no-repeat -38px -20px, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%); + background: url('chosen-sprite.png') no-repeat -38px -20px, -o-linear-gradient(bottom, white 85%, #eeeeee 99%); + background: url('chosen-sprite.png') no-repeat -38px -20px, -ms-linear-gradient(top, #ffffff 85%,#eeeeee 99%); + background: url('chosen-sprite.png') no-repeat -38px -20px, -ms-linear-gradient(top, #ffffff 85%,#eeeeee 99%); + background: url('chosen-sprite.png') no-repeat -38px -20px, linear-gradient(top, #ffffff 85%,#eeeeee 99%); + padding: 4px 5px 4px 20px; +} +/* @end */
\ No newline at end of file diff --git a/apps/files_publiclink/ajax/getlink.php b/apps/files_publiclink/ajax/getlink.php new file mode 100644 index 00000000000..551bcc8780c --- /dev/null +++ b/apps/files_publiclink/ajax/getlink.php @@ -0,0 +1,8 @@ +<?php +$RUNTIME_NOAPPS = true; + +require_once('../../../lib/base.php'); +require_once('../lib_public.php'); + +$path = $_GET['path']; +echo json_encode(OC_PublicLink::getLink($path));
\ No newline at end of file diff --git a/apps/files_publiclink/appinfo/app.php b/apps/files_publiclink/appinfo/app.php index e2d8d27c007..5866e2d1c5e 100644 --- a/apps/files_publiclink/appinfo/app.php +++ b/apps/files_publiclink/appinfo/app.php @@ -1,6 +1,6 @@ <?php -OC_App::addNavigationEntry(array( "id" => "files_publiclink_administration", "order" => 2, "href" => OC_Helper::linkTo( "files_publiclink", "admin.php" ), "name" => "Public Links")); +// OC_App::addNavigationEntry(array( "id" => "files_publiclink_administration", "order" => 2, "href" => OC_Helper::linkTo( "files_publiclink", "admin.php" ), "name" => "Public Links")); ?> diff --git a/apps/files_publiclink/lib_public.php b/apps/files_publiclink/lib_public.php index 22f06156e17..ece0a540d39 100644 --- a/apps/files_publiclink/lib_public.php +++ b/apps/files_publiclink/lib_public.php @@ -49,7 +49,15 @@ class OC_PublicLink{ public function getToken(){ return $this->token; } - + + public static function getLink($path) { + $query=OC_DB::prepare("SELECT token FROM *PREFIX*publiclink WHERE user=? AND path=? LIMIT 1"); + $result=$query->execute(array(OC_User::getUser(),$path))->fetchAll(); + if(count($result)>0){ + return $result[0]['token']; + } + } + /** * gets all public links * @return array diff --git a/apps/files_sharing/ajax/getitem.php b/apps/files_sharing/ajax/getitem.php new file mode 100644 index 00000000000..43105d334ee --- /dev/null +++ b/apps/files_sharing/ajax/getitem.php @@ -0,0 +1,24 @@ +<?php +$RUNTIME_NOAPPS = true; + +require_once('../../../lib/base.php'); +require_once('../lib_share.php'); + +$userDirectory = "/".OC_User::getUser()."/files"; +$source = $userDirectory.$_GET['source']; +$users = OC_Share::getMySharedItem($source); +$source = dirname($source); +while ($source != "" && $source != "/" && $source != "." && $source != $userDirectory) { + $values = array_values(OC_Share::getMySharedItem($source)); + if (count($values) > 0) { + $parentUsers = array(); + for ($i = 0; $i < count($values); $i++) { + $parentUsers[basename($source)."-".$i] = $values[$i]; + } + $users = array_merge($users, $parentUsers); + } + $source = dirname($source); +} +echo json_encode($users); + +?>
\ No newline at end of file diff --git a/apps/files_sharing/ajax/setpermissions.php b/apps/files_sharing/ajax/setpermissions.php new file mode 100644 index 00000000000..8e0bac0b06f --- /dev/null +++ b/apps/files_sharing/ajax/setpermissions.php @@ -0,0 +1,12 @@ +<?php +$RUNTIME_NOAPPS = true; + +require_once('../../../lib/base.php'); +require_once('../lib_share.php'); + +$source = "/".OC_User::getUser()."/files".$_GET['source']; +$uid_shared_with = $_GET['uid_shared_with']; +$permissions = $_GET['permissions']; +OC_Share::setPermissions($source, $uid_shared_with, $permissions); + +?>
\ No newline at end of file diff --git a/apps/files_sharing/ajax/share.php b/apps/files_sharing/ajax/share.php new file mode 100644 index 00000000000..d9bf4ff7abe --- /dev/null +++ b/apps/files_sharing/ajax/share.php @@ -0,0 +1,24 @@ +<?php +$RUNTIME_NOAPPS = true; + +require_once('../../../lib/base.php'); +require_once('../lib_share.php'); + +$sources = explode(";", $_POST['sources']); +$uid_shared_with = $_POST['uid_shared_with']; +$permissions = $_POST['permissions']; +foreach ($sources as $source) { + if ($source && OC_FILESYSTEM::file_exists($source) && OC_FILESYSTEM::is_readable($source)) { + $source = "/".OC_User::getUser()."/files".$source; + try { + $shared = new OC_Share($source, $uid_shared_with, $permissions); + if ($uid_shared_with == OC_Share::PUBLICLINK) { + echo $shared->getToken(); + } + } catch (Exception $exception) { + echo "false"; + } + } +} + +?>
\ No newline at end of file diff --git a/apps/files_sharing/ajax/unshare.php b/apps/files_sharing/ajax/unshare.php new file mode 100644 index 00000000000..b9230d257b7 --- /dev/null +++ b/apps/files_sharing/ajax/unshare.php @@ -0,0 +1,11 @@ +<?php +$RUNTIME_NOAPPS = true; + +require_once('../../../lib/base.php'); +require_once('../lib_share.php'); + +$source = "/".OC_User::getUser()."/files".$_GET['source']; +$uid_shared_with = $_GET['uid_shared_with']; +OC_Share::unshare($source, $uid_shared_with); + +?>
\ No newline at end of file diff --git a/apps/files_sharing/ajax/userautocomplete.php b/apps/files_sharing/ajax/userautocomplete.php new file mode 100644 index 00000000000..6da7afb659c --- /dev/null +++ b/apps/files_sharing/ajax/userautocomplete.php @@ -0,0 +1,28 @@ +<?php +$RUNTIME_NOAPPS = true; + +require_once('../../../lib/base.php'); + +if (!OC_User::isLoggedIn()) { + echo json_encode(array("status" => "error", "data" => array("message" => "Authentication error"))); + exit(); +} +$users = array(); +$ocusers = OC_User::getUsers(); +$self = OC_User::getUser(); +$groups = OC_Group::getUserGroups($self); +$users[] = "<optgroup label='Users'>"; +foreach ($ocusers as $user) { + if ($user != $self) { + $users[] = "<option value='".$user."'>".$user."</option>"; + } +} +$users[] = "</optgroup>"; +$users[] = "<optgroup label='Groups'>"; +foreach ($groups as $group) { + $users[] = "<option value='".$group."'>".$group."</option>"; +} +$users[] = "</optgroup>"; +echo json_encode($users); + +?> diff --git a/apps/files_sharing/appinfo/app.php b/apps/files_sharing/appinfo/app.php new file mode 100644 index 00000000000..6ff82563b80 --- /dev/null +++ b/apps/files_sharing/appinfo/app.php @@ -0,0 +1,17 @@ +<?php + +require_once('apps/files_sharing/lib_share.php'); +require_once('apps/files_sharing/sharedstorage.php'); + +OC_Filesystem::registerStorageType("shared", "OC_Filestorage_Shared", array("datadir"=>"string")); +OC_Util::addScript("files_sharing", "share"); +OC_Util::addScript("3rdparty", "chosen/chosen.jquery.min"); +OC_Util::addStyle( 'files_sharing', 'sharing' ); +OC_Util::addStyle("3rdparty", "chosen/chosen"); +OC_App::addNavigationEntry(array( + "id" => "files_sharing_list", + "order" => 2, + "href" => OC_Helper::linkTo( "files_sharing", "list.php" ), + "name" => "Shared")); + +?> diff --git a/apps/files_sharing/appinfo/database.xml b/apps/files_sharing/appinfo/database.xml new file mode 100644 index 00000000000..3378b6b09e5 --- /dev/null +++ b/apps/files_sharing/appinfo/database.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="ISO-8859-1" ?> +<database> + <name>*dbname*</name> + <create>true</create> + <overwrite>false</overwrite> + <charset>latin1</charset> + <table> + <name>*dbprefix*sharing</name> + <declaration> + <field> + <name>uid_owner</name> + <type>text</type> + <notnull>true</notnull> + <length>64</length> + </field> + <field> + <name>uid_shared_with</name> + <type>text</type> + <notnull>true</notnull> + <length>64</length> + </field> + <field> + <name>source</name> + <type>text</type> + <notnull>true</notnull> + <length>128</length> + </field> + <field> + <name>target</name> + <type>text</type> + <notnull>true</notnull> + <length>128</length> + </field> + <field> + <name>permissions</name> + <type>integer</type> + <notnull>true</notnull> + <length>1</length> + </field> + </declaration> + </table> +</database> diff --git a/apps/files_sharing/appinfo/info.xml b/apps/files_sharing/appinfo/info.xml new file mode 100644 index 00000000000..2fbb3300f69 --- /dev/null +++ b/apps/files_sharing/appinfo/info.xml @@ -0,0 +1,10 @@ +<?xml version="1.0"?> +<info> + <id>files_sharing</id> + <name>Share Files</name> + <description>File sharing between users</description> + <version>0.1</version> + <licence>AGPL</licence> + <author>Michael Gapczynski</author> + <require>2</require> +</info>
\ No newline at end of file diff --git a/apps/files_sharing/css/sharing.css b/apps/files_sharing/css/sharing.css new file mode 100644 index 00000000000..0759af2c274 --- /dev/null +++ b/apps/files_sharing/css/sharing.css @@ -0,0 +1,8 @@ +#dropdown { display:block; position:absolute; z-index:100; width:16em; right:0; margin-right:7em; background:#eee; padding:1em; +-moz-box-shadow:0 1px 1px #777; -webkit-box-shadow:0 1px 1px #777; box-shadow:0 1px 1px #777; +-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; } +#shared_list { padding:0.5em; list-style-type: none; } +#public { border-top:1px solid #ddd; padding-top:0.5em; } +a.unshare { float:right; display:inline; margin:0 .5em; padding:.3em .3em 0 .3em !important; opacity:.5; } +a.unshare:hover { opacity:1; }
\ No newline at end of file diff --git a/apps/files_sharing/js/list.js b/apps/files_sharing/js/list.js new file mode 100644 index 00000000000..41eabd1f4af --- /dev/null +++ b/apps/files_sharing/js/list.js @@ -0,0 +1,54 @@ +$(document).ready(function() { + $( "#source" ).autocomplete({ + source: "../../files/ajax/autocomplete.php", + minLength: 1 + }); + $( "#uid_shared_with" ).autocomplete({ + source: "ajax/userautocomplete.php", + minLength: 1 + }); + $("button.delete").live('click', function( event ) { + event.preventDefault(); +// var row=$(this); + var source=$(this).attr('data-source'); + var uid_shared_with=$(this).attr('data-uid_shared_with'); + var data='source='+encodeURIComponent(source)+'&uid_shared_with='+encodeURIComponent(uid_shared_with); + $.ajax({ + type: 'GET', + url: 'ajax/unshare.php', + cache: false, + data: data +// success: function(){ +// row.remove(); +// } + }); + }); + $('#share_item').submit(function( event ){ + event.preventDefault(); + var source=$('#source').val(); + var uid_shared_with=$('#uid_shared_with').val(); + var permissions=$('#permissions').val()||0; + var data='source='+source+'&uid_shared_with='+uid_shared_with+'&permissions='+permissions; + $.ajax({ + type: 'GET', + url: 'ajax/share.php', + cache: false, + data: data, +// success: function(token){ +// if(token){ +// var html="<tr class='link' id='"+token+"'>"; +// html+="<td class='path'>"+path+"</td>"; +// var expire=($('#expire').val())?$('#expire').val():'Never' +// html+="<td class='expire'>"+expire+"</td>" +// html+="<td class='link'><a href='get.php?token="+token+"'>"+$('#baseUrl').val()+"?token="+token+"</a></td>" +// html+="<td><button class='delete fancybutton' data-token='"+token+"'>Delete</button></td>" +// html+="</tr>" +// $(html).insertBefore($('#newlink_row')); +// $('#expire').val(''); +// $('#expire_time').val(''); +// $('#path').val(''); +// } +// } + }); + }); +});
\ No newline at end of file diff --git a/apps/files_sharing/js/share.js b/apps/files_sharing/js/share.js new file mode 100644 index 00000000000..f9500ffd000 --- /dev/null +++ b/apps/files_sharing/js/share.js @@ -0,0 +1,242 @@ +$(document).ready(function() { + if (typeof FileActions !== 'undefined') { + FileActions.register('all', 'Share', function(filename) { + var icon; + var file = $('#dir').val()+'/'+filename; + $.ajax({ + type: 'GET', + url: OC.linkTo('files_sharing', 'ajax/getitem.php'), + dataType: 'json', + data: 'source='+file, + async: false, + success: function(users) { + if (users.length > 0) { + icon = OC.imagePath('core', 'actions/shared'); + } else { + icon = OC.imagePath('core', 'actions/share'); + } + } + }); + $.ajax({ + type: 'GET', + url: OC.linkTo('files_publiclink', 'ajax/getlink.php'), + dataType: 'json', + data: 'path='+file, + async: false, + success: function(link) { + if (link.length > 0) { + icon = OC.imagePath('core', 'actions/public'); + } else { + icon = OC.imagePath('core', 'actions/share'); + } + } + }); + return icon; + }, function(filename) { + if (($('#dropdown').length > 0)) { + $('#dropdown').hide('blind', function() { + var dropdownFile = $('#dropdown').data('file') + var file = $('#dir').val()+'/'+filename; + $('#dropdown').remove(); + $('tr').removeClass('mouseOver'); + if (dropdownFile != file) { + createDropdown(filename, file); + } + }); + } else { + createDropdown(filename, $('#dir').val()+'/'+filename); + } + }); + }; + + $('.share').click(function(event) { + event.preventDefault(); + var filenames = getSelectedFiles('name'); + var length = filenames.length; + var files = ''; + for (var i = 0; i < length; i++) { + files += $('#dir').val()+'/'+filenames[i]+';'; + } + createDropdown(false, files); + }); + + $(this).click(function(event) { + if (!($(event.target).hasClass('drop')) && $(event.target).parents().index($('#dropdown')) == -1) { + if ($('#dropdown').is(':visible')) { + $('#dropdown').hide('blind', function() { + $('#dropdown').remove(); + $('tr').removeClass('mouseOver'); + }); + } + } + }); + + $('#share_with').live('change', function() { + var source = $('#dropdown').data('file'); + var uid_shared_with = $(this).val(); + var permissions = 0; + var data = 'sources='+encodeURIComponent(source)+'&uid_shared_with='+encodeURIComponent(uid_shared_with)+'&permissions='+encodeURIComponent(permissions); + $.ajax({ + type: 'POST', + url: OC.linkTo('files_sharing','ajax/share.php'), + cache: false, + data: data, + success: function(result) { + if (result !== 'false') { + addUser(uid_shared_with, permissions, false); + } + } + }); + }); + + $('#shared_list > li').live('mouseenter', function(event) { + $(':hidden', this).show(); + }); + + $('#shared_list > li').live('mouseleave', function(event) { + $('a', this).hide(); + if (!$('input:[type=checkbox]', this).is(':checked')) { + $('input:[type=checkbox]', this).hide(); + $('label', this).hide(); + } + }); + + $('.permissions').live('change', function() { + var permissions = (this.checked) ? 1 : 0; + var source = $('#dropdown').data('file'); + var uid_shared_with = $(this).parent().data('uid_shared_with'); + var data = 'source='+encodeURIComponent(source)+'&uid_shared_with='+encodeURIComponent(uid_shared_with)+'&permissions='+encodeURIComponent(permissions); + $.ajax({ + type: 'GET', + url: OC.linkTo('files_sharing','ajax/setpermissions.php'), + cache: false, + data: data + }); + }); + + $('.unshare').live('click', function(event) { + event.preventDefault(); + var user = $(this).parent(); + var source = $('#dropdown').data('file'); + var uid_shared_with = user.data('uid_shared_with'); + var data = 'source='+encodeURIComponent(source)+'&uid_shared_with='+encodeURIComponent(uid_shared_with); + $.ajax({ + type: 'GET', + url: OC.linkTo('files_sharing','ajax/unshare.php'), + cache: false, + data: data, + success: function() { + var option = "<option value='"+uid_shared_with+"'>"+uid_shared_with+"</option>"; + $(user).remove(); + $(option).appendTo('#share_with'); + $('#share_with').trigger('liszt:updated'); + } + }); + }); + + $('#makelink').live('change', function() { + if (this.checked) { + var source = $('#dropdown').data('file'); + var uid_shared_with = "public"; + var permissions = 0; + var data = 'sources='+encodeURIComponent(source)+'&uid_shared_with='+encodeURIComponent(uid_shared_with)+'&permissions='+encodeURIComponent(permissions); + $.ajax({ + type: 'POST', + url: OC.linkTo('files_sharing','ajax/share.php'), + cache: false, + data: data, + success: function(token) { + if (token) { + showPublicLink(token); + } + } + }); + } else { + var source = $('#dropdown').data('file'); + var uid_shared_with = "public"; + var data = 'source='+encodeURIComponent(source)+'&uid_shared_with='+encodeURIComponent(uid_shared_with); + $.ajax({ + type: 'GET', + url: OC.linkTo('files_sharing','ajax/unshare.php'), + cache: false, + data: data, + success: function(){ + $('#link').hide('blind'); + } + }); + } + }); + + $('#link').live('click', function() { + $(this).focus(); + $(this).select(); + }); +}); + +function createDropdown(filename, files) { + var html = "<div id='dropdown' class='drop' data-file='"+files+"'>"; + html += "<div id='private'>"; + html += "<select data-placeholder='User or Group' style='width:220px;' id='share_with' class='chzen-select'>"; + html += "<option value=''></option>"; + html += "</select>"; + html += "<ul id='shared_list'></ul>"; + html += "</div>"; + html += "<div id='public'>"; + html += "<input type='checkbox' name='makelink' id='makelink' value='1' /><label for='makelink'>make public</label>"; + //html += "<input type='checkbox' name='public_link_write' id='public_link_write' value='1' /><label for='public_link_write'>allow upload</label>"; + html += "<br />"; + html += "<input id='link' style='display:none; width:90%;' />"; + html += "</div>"; + if (filename) { + $('tr[data-file="'+filename+'"]').addClass('mouseOver'); + $(html).appendTo($('tr[data-file="'+filename+'"] td.filename')); + } else { + $(html).appendTo($('thead .share')); + } + $.getJSON(OC.linkTo('files_sharing', 'ajax/userautocomplete.php'), function(users) { + if (users) { + $.each(users, function(index, row) { + $(row).appendTo('#share_with'); + }); + $('#share_with').trigger('liszt:updated'); + } + }); + $.getJSON(OC.linkTo('files_sharing', 'ajax/getitem.php'), { source: files }, function(users) { + if (users) { + $.each(users, function(index, row) { + if (row.uid_shared_with == 'public') { + var token = 1234; + showPublicLink(token); + } else if (isNaN(index)) { + addUser(row.uid_shared_with, row.permissions, index.substr(0, index.lastIndexOf('-'))); + } else { + addUser(row.uid_shared_with, row.permissions, false); + } + }); + } + }); + $('#dropdown').show('blind'); + $('#share_with').chosen(); +} + +function addUser(uid_shared_with, permissions, parentFolder) { + if (parentFolder) { + var user = "<li>Parent folder "+parentFolder+" shared with "+uid_shared_with+"</li>"; + } else { + var checked = ((permissions > 0) ? "checked='checked'" : "style='display:none;'"); + var style = ((permissions == 0) ? "style='display:none;'" : ""); + var user = "<li data-uid_shared_with='"+uid_shared_with+"'>"+uid_shared_with; + user += "<input type='checkbox' name='permissions' id='"+uid_shared_with+"' class='permissions' "+checked+"/><label for='"+uid_shared_with+"' "+style+">can edit</label>"; + user += "<a href='' title='Unshare' class='unshare' style='display:none;'><img class='svg' src='"+OC.imagePath('core','actions/delete')+"'/></a></li>"; + } + $('#share_with option[value="'+uid_shared_with+'"]').remove(); + $('#share_with').trigger('liszt:updated'); + $(user).appendTo('#shared_list'); +} + +function showPublicLink(token) { + $('#makelink').attr('checked', true); + $('#link').data('token', token); + $('#link').val(parent.location.protocol+"//"+location.host+OC.linkTo('files_publiclink','get.php')+'?token='+token); + $('#link').show('blind'); +} diff --git a/apps/files_sharing/lib_share.php b/apps/files_sharing/lib_share.php new file mode 100644 index 00000000000..f682e1a30f1 --- /dev/null +++ b/apps/files_sharing/lib_share.php @@ -0,0 +1,396 @@ +<?php +/** + * ownCloud + * + * @author Michael Gapczynski + * @copyright 2011 Michael Gapczynski GapczynskiM@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 + * 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 <http://www.gnu.org/licenses/>. + * + */ + +OC_Hook::connect("OC_FILESYSTEM","post_delete", "OC_Share", "deleteItem"); +OC_Hook::connect("OC_FILESYSTEM","post_rename", "OC_Share", "renameItem"); + +/** + * This class manages shared items within the database. + */ +class OC_Share { + + const WRITE = 1; + const DELETE = 2; + const PUBLICLINK = "public"; + + private $token; + + /** + * Share an item, adds an entry into the database + * @param $source The source location of the item + * @param $uid_shared_with The user or group to share the item with + * @param $permissions The permissions, use the constants WRITE and DELETE + */ + public function __construct($source, $uid_shared_with, $permissions) { + $uid_owner = OC_User::getUser(); + $query = OC_DB::prepare("INSERT INTO *PREFIX*sharing VALUES(?,?,?,?,?)"); + if ($uid_shared_with == self::PUBLICLINK) { + $token = sha1("$uid_shared_with-$source"); + $query->execute(array($uid_owner, self::PUBLICLINK, $source, $token, $permissions)); + $this->token = $token; + } else { + if (OC_Group::groupExists($uid_shared_with)) { + $gid = $uid_shared_with; + $uid_shared_with = OC_Group::usersInGroup($gid); + // Remove the owner from the list of users in the group + $uid_shared_with = array_diff($uid_shared_with, array($uid_owner)); + } else if (OC_User::userExists($uid_shared_with)) { + $gid = null; + $uid_shared_with = array($uid_shared_with); + } else { + throw new Exception($uid_shared_with." is not a user"); + } + foreach ($uid_shared_with as $uid) { + // Check if this item is already shared with the user + $checkSource = OC_DB::prepare("SELECT source FROM *PREFIX*sharing WHERE source = ? AND uid_shared_with ".self::getUsersAndGroups($uid)); + $resultCheckSource = $checkSource->execute(array($source, $uid))->fetchAll(); + // TODO Check if the source is inside a folder + if (count($resultCheckSource) > 0 && !isset($gid)) { + throw new Exception("This item is already shared with ".$uid); + } + // Check if the target already exists for the user, if it does append a number to the name + $target = "/".$uid."/files/Shared/".basename($source); + if (self::getSource($target)) { + if ($pos = strrpos($target, ".")) { + $name = substr($target, 0, $pos); + $ext = substr($target, $pos); + } else { + $name = $target; + $ext = ""; + } + $counter = 1; + while ($checkTarget !== false) { + $newTarget = $name."_".$counter.$ext; + $checkTarget = self::getSource($newTarget); + $counter++; + } + $target = $newTarget; + } + if (isset($gid)) { + $uid = $uid."@".$gid; + } + $query->execute(array($uid_owner, $uid, $source, $target, $permissions)); + } + } + } + + /** + * Remove any duplicate or trailing '/' from the path + * @return A clean path + */ + private static function cleanPath($path) { + $path = rtrim($path, "/"); + return preg_replace('{(/)\1+}', "/", $path); + } + + /** + * Generate a string to be used for searching for uid_shared_with that handles both users and groups + * @param $uid (Optional) The uid to get the user groups for, a gid to get the users in a group, or if not set the current user + * @return An IN operator as a string + */ + private static function getUsersAndGroups($uid = null) { + $in = " IN("; + if (isset($uid) && OC_Group::groupExists($uid)) { + $users = OC_Group::usersInGroup($uid); + foreach ($users as $user) { + // Add a comma only if the the current element isn't the last + if ($user !== end($users)) { + $in .= "'".$user."@".$uid."', "; + } else { + $in .= "'".$user."@".$uid."'"; + } + } + } else if (isset($uid)) { + // TODO Check if this is necessary, only constructor needs it as IN. It would be better for other queries to just return =$uid + $in .= "'".$uid."'"; + $groups = OC_Group::getUserGroups($uid); + foreach ($groups as $group) { + $in .= ", '".$uid."@".$group."'"; + } + } else { + $uid = OC_User::getUser(); + $in .= "'".$uid."'"; + $groups = OC_Group::getUserGroups($uid); + foreach ($groups as $group) { + $in .= ", '".$uid."@".$group."'"; + } + } + $in .= ")"; + return $in; + } + + /** + * Create a new entry in the database for a file inside a shared folder + * + * $oldTarget and $newTarget may be the same value. $oldTarget exists in case the file is being moved outside of the folder + * + * @param $oldTarget The current target location + * @param $newTarget The new target location + */ + public static function pullOutOfFolder($oldTarget, $newTarget) { + $folders = self::getParentFolders($oldTarget); + $source = $folders['source'].substr($oldTarget, strlen($folders['target'])); + $item = self::getItem($folders['target']); + $query = OC_DB::prepare("INSERT INTO *PREFIX*sharing VALUES(?,?,?,?,?)"); + $query->execute(array($item[0]['uid_owner'], OC_User::getUser(), $source, $newTarget, $item[0]['permissions'])); + } + + /** + * Get the item with the specified target location + * @param $target The target location of the item + * @return An array with the item + */ + public static function getItem($target) { + $target = self::cleanPath($target); + $query = OC_DB::prepare("SELECT uid_owner, source, permissions FROM *PREFIX*sharing WHERE target = ? AND uid_shared_with = ? LIMIT 1"); + return $query->execute(array($target, OC_User::getUser()))->fetchAll(); + } + + /** + * Get the item with the specified source location + * @param $source The source location of the item + * @return An array with the users and permissions the item is shared with + */ + public static function getMySharedItem($source) { + $source = self::cleanPath($source); + $query = OC_DB::prepare("SELECT uid_shared_with, permissions FROM *PREFIX*sharing WHERE source = ? AND uid_owner = ?"); + return $query->execute(array($source, OC_User::getUser()))->fetchAll(); + } + /** + * Get all items the current user is sharing + * @return An array with all items the user is sharing + */ + public static function getMySharedItems() { + $query = OC_DB::prepare("SELECT uid_shared_with, source, permissions FROM *PREFIX*sharing WHERE uid_owner = ?"); + return $query->execute(array(OC_User::getUser()))->fetchAll(); + } + + /** + * Get the items within a shared folder that have their own entry for the purpose of name, location, or permissions that differ from the folder itself + * + * Works for both target and source folders. Can be used for getting all items shared with you e.g. pass '/MTGap/files' + * + * @param $folder The folder of the items to look for + * @return An array with all items in the database that are in the folder + */ + public static function getItemsInFolder($folder) { + $folder = self::cleanPath($folder); + // Append '/' in order to filter out the folder itself if not already there + if (substr($folder, -1) !== "/") { + $folder .= "/"; + } + $length = strlen($folder); + $query = OC_DB::prepare("SELECT uid_owner, source, target FROM *PREFIX*sharing WHERE SUBSTR(source, 1, ?) = ? OR SUBSTR(target, 1, ?) = ? AND uid_shared_with ".self::getUsersAndGroups()); + return $query->execute(array($length, $folder, $length, $folder))->fetchAll(); + } + + /** + * Get the source and target parent folders of the specified target location + * @param $target The target location of the item + * @return An array with the keys 'source' and 'target' with the values of the source and target parent folders + */ + public static function getParentFolders($target) { + $target = self::cleanPath($target); + $query = OC_DB::prepare("SELECT source FROM *PREFIX*sharing WHERE target = ? AND uid_shared_with".self::getUsersAndGroups()." LIMIT 1"); + // Prevent searching for user directory e.g. '/MTGap/files' + $userDirectory = substr($target, 0, strpos($target, "files") + 5); + $target = dirname($target); + $result = array(); + while ($target != "" && $target != "/" && $target != "." && $target != $userDirectory) { + // Check if the parent directory of this target location is shared + $result = $query->execute(array($target))->fetchAll(); + if (count($result) > 0) { + break; + } + $target = dirname($target); + } + if (count($result) > 0) { + // Return both the source folder and the target folder + return array("source" => $result[0]['source'], "target" => $target); + } else { + return false; + } + } + + /** + * Get the source location of the item at the specified target location + * @param $target The target location of the item + * @return Source location or false if target location is not valid + */ + public static function getSource($target) { + $target = self::cleanPath($target); + $query = OC_DB::prepare("SELECT source FROM *PREFIX*sharing WHERE target = ? AND uid_shared_with ".self::getUsersAndGroups()." LIMIT 1"); + $result = $query->execute(array($target))->fetchAll(); + if (count($result) > 0) { + return $result[0]['source']; + } else { + $folders = self::getParentFolders($target); + if ($folders == true) { + return $folders['source'].substr($target, strlen($folders['target'])); + } else { + return false; + } + } + } + + /** + * Get the user's permissions for the item at the specified target location + * @param $target The target location of the item + * @return The permissions, use bitwise operators to check against the constants WRITE and DELETE + */ + public static function getPermissions($target) { + $target = self::cleanPath($target); + $query = OC_DB::prepare("SELECT permissions FROM *PREFIX*sharing WHERE target = ? AND uid_shared_with ".self::getUsersAndGroups()." LIMIT 1"); + $result = $query->execute(array($target))->fetchAll(); + if (count($result) > 0) { + return $result[0]['permissions']; + } else { + $folders =self::getParentFolders($target); + if ($folders == true) { + $result = $query->execute(array($folders))->fetchAll(); + if (count($result) > 0) { + return $result[0]['permissions']; + } + } else { + return false; + } + } + } + + /** + * Get the token for a public link + * @return The token of the public link, a sha1 hash + */ + public function getToken() { + return $this->token; + } + + /** + * Get the token for a public link + * @param $source The source location of the item + * @return The token of the public link, a sha1 hash + */ + public static function getTokenFromSource($source) { + $query = OC_DB::prepare("SELECT target FROM *PREFIX*sharing WHERE source = ? AND uid_shared_with = ? AND uid_owner = ? LIMIT 1"); + $result = $query->execute(array($source, self::PUBLICLINK, OC_User::getUser()))->fetchAll(); + if (count($result) > 0) { + return $result[0]['target']; + } else { + return false; + } + } + + /** + * Set the source location to a new value + * @param $oldSource The current source location + * @param $newTarget The new source location + */ + public static function setSource($oldSource, $newSource) { + $oldSource = self::cleanPath($oldSource); + $newSource = self::cleanPath($newSource); + $query = OC_DB::prepare("UPDATE *PREFIX*sharing SET source = REPLACE(source, ?, ?) WHERE uid_owner = ?"); + $query->execute(array($oldSource, $newSource, OC_User::getUser())); + } + + /** + * Set the target location to a new value + * + * You must use the pullOutOfFolder() function to change the target location of a file inside a shared folder if the target location differs from the folder + * + * @param $oldTarget The current target location + * @param $newTarget The new target location + */ + public static function setTarget($oldTarget, $newTarget) { + $oldTarget = self::cleanPath($oldTarget); + $newTarget = self::cleanPath($newTarget); + $query = OC_DB::prepare("UPDATE *PREFIX*sharing SET target = REPLACE(target, ?, ?) WHERE uid_shared_with ".self::getUsersAndGroups()); + $query->execute(array($oldTarget, $newTarget)); + } + + /** + * Change the permissions for the specified item and user + * + * You must construct a new shared item to change the permissions of a file inside a shared folder if the permissions differ from the folder + * + * @param $source The source location of the item + * @param $uid_shared_with The user to change the permissions for + * @param $permissions The permissions, use the constants WRITE and DELETE + */ + public static function setPermissions($source, $uid_shared_with, $permissions) { + $source = self::cleanPath($source); + $query = OC_DB::prepare("UPDATE *PREFIX*sharing SET permissions = ? WHERE SUBSTR(source, 1, ?) = ? AND uid_owner = ? AND uid_shared_with ".self::getUsersAndGroups($uid_shared_with)); + $query->execute(array($permissions, strlen($source), $source, OC_User::getUser())); + } + + /** + * Unshare the item, removes it from all specified users + * + * You must use the pullOutOfFolder() function to unshare a file inside a shared folder and set $newTarget to nothing + * + * @param $source The source location of the item + * @param $uid_shared_with Array of users to unshare the item from + */ + public static function unshare($source, $uid_shared_with) { + $source = self::cleanPath($source); + $query = OC_DB::prepare("DELETE FROM *PREFIX*sharing WHERE SUBSTR(source, 1, ?) = ? AND uid_owner = ? AND uid_shared_with ".self::getUsersAndGroups($uid_shared_with)); + $query->execute(array(strlen($source), $source, OC_User::getUser())); + } + + /** + * Unshare the item from the current user, removes it only from the database and doesn't touch the source file + * + * You must use the pullOutOfFolder() function to unshare a file inside a shared folder and set $newTarget to nothing + * + * @param $target The target location of the item + */ + public static function unshareFromMySelf($target) { + $target = self::cleanPath($target); + $query = OC_DB::prepare("DELETE FROM *PREFIX*sharing WHERE SUBSTR(target, 1, ?) = ? AND uid_shared_with ".self::getUsersAndGroups()); + $query->execute(array(strlen($target), $target)); + } + + /** + * Remove the item from the database, the owner deleted the file + * @param $arguments Array of arguments passed from OC_HOOK + */ + public static function deleteItem($arguments) { + $source = "/".OC_User::getUser()."/files".$arguments['path']; + $source = self::cleanPath($source); + $query = OC_DB::prepare("DELETE FROM *PREFIX*sharing WHERE SUBSTR(source, 1, ?) = ? AND uid_owner = ?"); + $query->execute(array(strlen($source), $source, OC_User::getUser())); + } + + /** + * Rename the item in the database, the owner renamed the file + * @param $arguments Array of arguments passed from OC_HOOK + */ + public static function renameItem($arguments) { + $oldSource = "/".OC_User::getUser()."/files".$arguments['oldpath']; + $oldSource = self::cleanPath($oldSource); + $newSource = "/".OC_User::getUser()."/files".$arguments['newpath']; + $newSource = self::cleanPath($newSource); + self::setSource($oldSource, $newSource); + } + +} + +?> diff --git a/apps/files_sharing/list.php b/apps/files_sharing/list.php new file mode 100644 index 00000000000..0a11f438eb7 --- /dev/null +++ b/apps/files_sharing/list.php @@ -0,0 +1,39 @@ +<?php +/** + * ownCloud + * + * @author Michael Gapczynski + * @copyright 2011 Michael Gapczynski GapczynskiM@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 + * 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 <http://www.gnu.org/licenses/>. + * + */ + +require_once('../../lib/base.php'); +require_once('lib_share.php'); + +if (!OC_User::isLoggedIn()){ + header( "Location: ".OC_HELPER::linkTo( "index.php" )); + exit(); +} + +OC_App::setActiveNavigationEntry("files_sharing_list"); + +OC_Util::addScript("files_sharing", "list"); + +$tmpl = new OC_Template("files_sharing", "list", "user"); +$tmpl->assign("shared_items", OC_Share::getMySharedItems()); +$tmpl->printPage(); + +?>
\ No newline at end of file diff --git a/apps/files_sharing/sharedstorage.php b/apps/files_sharing/sharedstorage.php new file mode 100644 index 00000000000..271819782b7 --- /dev/null +++ b/apps/files_sharing/sharedstorage.php @@ -0,0 +1,574 @@ +<?php +/** + * ownCloud + * + * @author Michael Gapczynski + * @copyright 2011 Michael Gapczynski GapczynskiM@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 + * 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 <http://www.gnu.org/licenses/>. + * + */ + +require_once( 'lib_share.php' ); + +/** + * Convert target path to source path and pass the function call to the correct storage provider + */ +class OC_Filestorage_Shared extends OC_Filestorage { + + private $datadir; + private $sourcePaths = array(); + + public function __construct($arguments) { + $this->datadir = $arguments['datadir']; + if (!OC_Filesystem::is_dir($this->datadir)) { + OC_Filesystem::mkdir($this->datadir); + } + $this->datadir .= "/"; + } + + public function getInternalPath($path) { + $mountPoint = OC_Filesystem::getMountPoint($path); + $internalPath = substr($path, strlen($mountPoint)); + return $internalPath; + } + + public function getSource($target) { + $target = $this->datadir.$target; + if (array_key_exists($target, $this->sourcePaths)) { + return $this->sourcePaths[$target]; + } else { + $source = OC_Share::getSource($target); + $this->sourcePaths[$target] = $source; + return $source; + } + } + + public function mkdir($path) { + if ($path == "" || $path == "/") { + return false; + } else { + $source = $this->getSource($path); + if ($source) { + if ($this->is_writeable($path)) { + $storage = OC_Filesystem::getStorage($source); + return $storage->mkdir($this->getInternalPath($source)); + } + } + } + } + + public function rmdir($path) { + // The folder will be removed from the database, but won't be deleted from the owner's filesystem + OC_Share::unshareFromMySelf($this->datadir.$path); + $this->clearFolderSizeCache($path); + } + + public function opendir($path) { + if ($path == "" || $path == "/") { + global $FAKEDIRS; + $path = $this->datadir.$path; + $sharedItems = OC_Share::getItemsInFolder($path); + if (empty($sharedItems)) { + return false; + } + $files = array(); + foreach ($sharedItems as $item) { + // If item is in the root of the shared storage provider add it to the fakedirs + if (dirname($item['target'])."/" == $path) { + $files[] = basename($item['target']); + } + } + $FAKEDIRS['shared'] = $files; + return opendir('fakedir://shared'); + } else { + $source = $this->getSource($path); + if ($source) { + $storage = OC_Filesystem::getStorage($source); + $dh = $storage->opendir($this->getInternalPath($source)); + // Remove any duplicate or trailing '/' + $path = rtrim($this->datadir.$path, "/"); + $path = preg_replace('{(/)\1+}', "/", $path); + $modifiedItems = OC_Share::getItemsInFolder($source); + if ($modifiedItems && $dh) { + global $FAKEDIRS; + $sources = array(); + $targets = array(); + foreach ($modifiedItems as $item) { + // If the item is in the current directory and has a different name than the source, add it to the arrays + if (dirname($item['target']) == $path && basename($item['source']) != basename($item['target'])) { + $sources[] = basename($item['source']); + $targets[] = basename($item['target']); + // If the item was unshared from self, add it it to the arrays + } elseif ($item['target'] == "/") { + $sources[] = basename($item['source']); + $targets[] = ""; + } + } + // Don't waste time if there aren't any modified items in the current directory + if (empty($sources)) { + return $dh; + } else { + while (($filename = readdir($dh)) !== false) { + if ($filename != "." && $filename != "..") { + // If the file isn't in the sources array it isn't modified and can be added as is + if (!in_array($filename, $sources)) { + $files[] = $filename; + // The file has a different name than the source and is added to the fakedirs + } else { + $target = $targets[array_search($filename, $sources)]; + // Don't add the file if it was unshared from self by the user + if ($target != "") { + $files[] = $target; + } + } + } + } + $FAKEDIRS['shared'] = $files; + return opendir('fakedir://shared'); + } + } else { + return $dh; + } + } + } + } + + public function is_dir($path) { + if ($path == "" || $path == "/") { + return true; + } else { + $source = $this->getSource($path); + if ($source) { + $storage = OC_Filesystem::getStorage($source); + return $storage->is_dir($this->getInternalPath($source)); + } + } + } + + public function is_file($path) { + $source = $this->getSource($path); + if ($source) { + $storage = OC_Filesystem::getStorage($source); + return $storage->is_file($this->getInternalPath($source)); + } + } + + // TODO fill in other components of array + public function stat($path) { + if ($path == "" || $path == "/") { + $stat["dev"] = ""; + $stat["ino"] = ""; + $stat["mode"] = ""; + $stat["nlink"] = ""; + $stat["uid"] = ""; + $stat["gid"] = ""; + $stat["rdev"] = ""; + $stat["size"] = $this->filesize($path); + $stat["atime"] = $this->fileatime($path); + $stat["mtime"] = $this->filemtime($path); + $stat["ctime"] = $this->filectime($path); + $stat["blksize"] = ""; + $stat["blocks"] = ""; + return $stat; + } else { + $source = $this->getSource($path); + if ($source) { + $storage = OC_Filesystem::getStorage($source); + return $storage->stat($this->getInternalPath($source)); + } + } + } + + public function filetype($path) { + if ($path == "" || $path == "/") { + return "dir"; + } else { + $source = $this->getSource($path); + if ($source) { + $storage = OC_Filesystem::getStorage($source); + return $storage->filetype($this->getInternalPath($source)); + } + } + + } + + public function filesize($path) { + + if ($path == "" || $path == "/" || $this->is_dir($path)) { + return $this->getFolderSize($path); + } else { + $source = $this->getSource($path); + if ($source) { + $storage = OC_Filesystem::getStorage($source); + return $storage->filesize($this->getInternalPath($source)); + } + } + } + + public function getFolderSize($path) { + $dbpath = OC_User::getUser()."/files/Share/".$path."/"; + $query = OC_DB::prepare("SELECT size FROM *PREFIX*foldersize WHERE path=?"); + $size = $query->execute(array($dbpath))->fetchAll(); + if (count($size) > 0) { + return $size[0]['size']; + } else { + return $this->calculateFolderSize($path); + } + } + + public function calculateFolderSize($path) { + if ($this->is_file($path)) { + $path = dirname($path); + } + $path = str_replace("//", "/", $path); + if ($this->is_dir($path) && substr($path, -1) != "/") { + $path .= "/"; + } + $size = 0; + if ($dh = $this->opendir($path)) { + while (($filename = readdir($dh)) !== false) { + if ($filename != "." && $filename != "..") { + $subFile = $path.$filename; + if ($this->is_file($subFile)) { + $size += $this->filesize($subFile); + } else { + $size += $this->getFolderSize($subFile); + } + } + } + if ($size > 0) { + $dbpath = OC_User::getUser()."/files/Share/".$path; + $query = OC_DB::prepare("INSERT INTO *PREFIX*foldersize VALUES(?,?)"); + $result = $query->execute(array($dbpath, $size)); + } + } + return $size; + } + + public function clearFolderSizeCache($path){ + if ($this->is_file($path)) { + $path = dirname($path); + } + $path = str_replace("//", "/", $path); + if ($this->is_dir($path) && substr($path, -1) != "/") { + $path .= "/"; + } + $query = OC_DB::prepare("DELETE FROM *PREFIX*foldersize WHERE path = ?"); + $result = $query->execute(array($path)); + if ($path != "/" && $path != "") { + $parts = explode("/", $path); + $part = array_pop($parts); + if (empty($part)) { + array_pop($parts); + } + $parent = implode("/", $parts); + $this->clearFolderSizeCache($parent); + } + } + + public function is_readable($path) { + return true; + } + + public function is_writeable($path) { + if ($path == "" || $path == "/" || OC_Share::getPermissions($this->datadir.$path) & OC_Share::WRITE) { + return true; + } else { + return false; + } + } + + public function file_exists($path) { + if ($path == "" || $path == "/") { + return true; + } else { + $source = $this->getSource($path); + if ($source) { + $storage = OC_Filesystem::getStorage($source); + return $storage->file_exists($this->getInternalPath($source)); + } + } + } + + public function readfile($path) { + $source = $this->getSource($path); + if ($source) { + $storage = OC_Filesystem::getStorage($source); + return $storage->readfile($this->getInternalPath($source)); + } + } + + 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->getSource($path); + if ($source) { + $storage = OC_Filesystem::getStorage($source); + return $storage->filectime($this->getInternalPath($source)); + } + } + } + + public function filemtime($path) { + if ($path == "" || $path == "/") { + $mtime = 0; + if ($dh = $this->opendir($path)) { + while (($filename = readdir($dh)) !== false) { + $tempmtime = $this->filemtime($filename); + if ($tempmtime > $mtime) { + $mtime = $tempmtime; + } + } + return $mtime; + } + } else { + $source = $this->getSource($path); + if ($source) { + $storage = OC_Filesystem::getStorage($source); + return $storage->filemtime($this->getInternalPath($source)); + } + } + } + + public function fileatime($path) { + if ($path == "" || $path == "/") { + $atime = 0; + if ($dh = $this->opendir($path)) { + while (($filename = readdir($dh)) !== false) { + $tempatime = $this->fileatime($filename); + if ($tempatime > $atime) { + $atime = $tempatime; + } + } + return $atime; + } + } else { + $source = $this->getSource($path); + if ($source) { + $storage = OC_Filesystem::getStorage($source); + return $storage->fileatime($this->getInternalPath($source)); + } + } + } + + public function file_get_contents($path) { + $source = $this->getSource($path); + if ($source) { + $storage = OC_Filesystem::getStorage($source); + return $storage->file_get_contents($this->getInternalPath($source)); + } + } + + public function file_put_contents($path, $data) { + if ($this->is_writeable($path)) { + $source = $this->getSource($path); + if ($source) { + $storage = OC_Filesystem::getStorage($source); + return $storage->file_put_contents($this->getInternalPath($source), $data); + } + } + } + + public function unlink($path) { + $target = $this->datadir.$path; + // If the user has delete permission for the item, the source item will be deleted + if (OC_Share::getPermissions($target) & OC_Share::DELETE) { + $source = $this->getSource($path); + if ($source) { + $storage = OC_Filesystem::getStorage($source); + return $storage->unlink($this->getInternalPath($source)); + } + // The item will be removed from the database, but won't be touched on the owner's filesystem + } else { + // Check if the item is inside a shared folder + if (OC_Share::getParentFolders($target)) { + // If entry for item already exists + if (OC_Share::getItem($target)) { + OC_Share::setTarget($target, "/"); + } else { + OC_Share::pullOutOfFolder($target, "/"); + } + // Delete the database entry + } else { + OC_Share::unshareFromMySelf($target); + } + $this->clearFolderSizeCache($this->getInternalPath($target)); + } + return true; + } + + public function rename($path1, $path2) { + // If the user has write permission for the item, the source item will be renamed + if ($this->is_writeable($path1)) { + $source = $this->getSource($path1); + if ($source) { + $storage = OC_Filesystem::getStorage($source); + return $storage->rename($path1, $path2); + } + // The item will be renamed in the database, but won't be touched on the owner's filesystem + } else { + $oldTarget = $this->datadir.$path1; + $newTarget = $this->datadir.$path2; + if (OC_Share::getItem($oldTarget)) { + OC_Share::setTarget($oldTarget, $newTarget); + // There is no entry in the database for the item, it must be inside a shared folder + } else { + OC_Share::pullOutOfFolder($oldTarget, $newTarget); + // If this is a folder being renamed, call setTarget in case there are any database entries inside the folder + if (self::is_dir($path1)) { + OC_Share::setTarget($oldTarget, $newTarget); + } + } + $this->clearFolderSizeCache($this->getInternalPath($oldTarget)); + $this->clearFolderSizeCache($this->getInternalPath($newTarget)); + return true; + } + } + + public function copy($path1, $path2) { + if ($path2 == "" || $path2 == "/") { + // TODO Construct new shared item or should this not be allowed? + } else { + if ($this->is_writeable($path2)) { + $tmpFile = $this->toTmpFile($path1); + return $this->fromTmpFile($tmpFile, $path2); + } else { + return false; + } + } + } + + public function fopen($path, $mode) { + $source = $this->getSource($path); + if ($source) { + $storage = OC_Filesystem::getStorage($source); + return $storage->fopen($this->getInternalPath($source), $mode); + } + } + + public function toTmpFile($path) { + $source = $this->getSource($path); + if ($source) { + $storage = OC_Filesystem::getStorage($source); + return $storage->toTmpFile($this->getInternalPath($source)); + } + } + + public function fromTmpFile($tmpFile, $path) { + if ($this->is_writeable($path)) { + $source = $this->getSource($path); + if ($source) { + $storage = OC_Filesystem::getStorage($source); + return $storage->fromTmpFile($tmpFile, $this->getInternalPath($source)); + } + } else { + return false; + } + } + + public function fromUploadedFile($tmpPath, $path) { + $source = $this->getSource($tmpPath); + if ($source) { + $storage = OC_Filesystem::getStorage($source); + return $storage->fromUploadedFile($this->getInternalPath($source), $path); + } + } + + public function getMimeType($path) { + $source = $this->getSource($path); + if ($source) { + $storage = OC_Filesystem::getStorage($source); + return $storage->getMimeType($this->getInternalPath($source)); + } + } + + public function delTree($path) { + $target = $this->datadir.$path; + if (OC_Share::getPermissions($target) & OC_Share::DELETE) { + $source = $this->getSource($path); + if ($source) { + $storage = OC_Filesystem::getStorage($source); + return $storage->delTree($this->getInternalPath($source)); + } + } else { + // Check if the folder is inside a shared folder + if (OC_Share::getParentFolders($target)) { + // If entry for folder already exists + if (OC_Share::getItem($target)) { + OC_Share::setTarget($target, "/"); + } else { + OC_Share::pullOutOfFolder($target, "/"); + // Call setTarget in case there are any database entries for items inside this folder + OC_Share::setTarget($target, "/"); + } + // Delete the database entry + } else { + OC_Share::unshareFromMySelf($target); + } + $this->clearFolderSizeCache($this->getInternalPath($target)); + return true; + } + } + + public function find($path) { + $source = $this->getSource($path); + if ($source) { + $storage = OC_Filesystem::getStorage($source); + return $storage->find($this->getInternalPath($source)); + } + } + + public function getTree($path) { + $source = $this->getSource($path); + if ($source) { + $storage = OC_Filesystem::getStorage($source); + return $storage->getTree($this->getInternalPath($source)); + } + } + + public function hash($type, $path, $raw) { + $source = $this->getSource($path); + if ($source) { + $storage = OC_Filesystem::getStorage($source); + return $storage->hash($type, $this->getInternalPath($source), $raw); + } + } + + public function free_space($path) { + $source = $this->getSource($path); + if ($source) { + $storage = OC_Filesystem::getStorage($source); + return $storage->free_space($this->getInternalPath($source)); + } + } + + // TODO query all shared files? + public function search($query) { + + } + +} + +?>
\ No newline at end of file diff --git a/apps/files_sharing/templates/list.php b/apps/files_sharing/templates/list.php new file mode 100644 index 00000000000..7faf2cf4ba6 --- /dev/null +++ b/apps/files_sharing/templates/list.php @@ -0,0 +1,30 @@ +<fieldset> + <legend>Your Shared Files</legend> + <table id="itemlist"> + <thead> + <tr> + <th>Item</th> + <th>Shared With</th> + <th>Permissions</th> + </tr> + </thead> + <tbody> + <?php foreach($_['shared_items'] as $item):?> + <tr class="item"> + <td class="source"><?php echo substr($item['source'], strlen("/".$_SESSION['user_id']."/files/"));?></td> + <td class="uid_shared_with"><?php echo $item['uid_shared_with'];?></td> + <td class="permissions"><?php echo "Read"; echo($item['permissions'] & OC_SHARE::WRITE ? ", Edit" : ""); echo($item['permissions'] & OC_SHARE::DELETE ? ", Delete" : "");?></td> + <td><button class="delete" data-source="<?php echo $item['source'];?>" data-uid_shared_with="<?php echo $item['uid_shared_with'];?>">Delete</button></td> + </tr> + <?php endforeach;?> + <tr id="share_item_row"> + <form action="#" id="share_item"> + <td class="source"><input placeholder="Item" id="source" /></td> + <td class="uid_shared_with"><input placeholder="Share With" id="uid_shared_with" /></td> + <td class="permissions"><input placeholder="Permissions" id="permissions" /></td> + <td><input type="submit" value="Share" /></td> + </form> + </tr> + </tbody> + </table> +</fieldset> diff --git a/files/templates/index.php b/files/templates/index.php index cd061596a02..f5a212add48 100644 --- a/files/templates/index.php +++ b/files/templates/index.php @@ -29,7 +29,7 @@ <span class='name'><?php echo $l->t( 'Name' ); ?></span> <span class='selectedActions'> <a href="" title="<?php echo $l->t('Download')?>" class="download"><img class='svg' alt="Download" src="../core/img/actions/download.svg" /></a> - <!--<a href="" title="" class="share">Share</a>--> + <a href="" title="Share" class="share"><img class='svg' alt="Share" src="../core/img/actions/share.svg" /></a> </span> </th> <th id="headerSize"><?php echo $l->t( 'Size' ); ?></th> diff --git a/lib/filesystem.php b/lib/filesystem.php index 911a85e4a33..76032fae204 100644 --- a/lib/filesystem.php +++ b/lib/filesystem.php @@ -175,7 +175,7 @@ class OC_Filesystem{ * @param string path * @return OC_Filestorage */ - static private function getStorage($path){ + static public function getStorage($path){ $mountpoint=self::getMountPoint($path); if($mountpoint){ return self::$storages[$mountpoint]; @@ -189,7 +189,7 @@ class OC_Filesystem{ * @param string path * @return string */ - static private function getMountPoint($path){ + static public function getMountPoint($path){ if(!$path){ $path='/'; } @@ -214,6 +214,7 @@ class OC_Filesystem{ } return $foundMountPoint; } + /** * return the path to a local version of the file * we need this because we can't know if a file is stored local or not from outside the filestorage and for some purposes a local file is needed diff --git a/lib/util.php b/lib/util.php index 0fdfa012305..f4ca879a9bc 100644 --- a/lib/util.php +++ b/lib/util.php @@ -53,6 +53,10 @@ class OC_Util { // } OC_Filesystem::mount($rootStorage,'/'); + // TODO add this storage provider in a proper way + $sharedStorage = OC_Filesystem::createStorage('shared',array('datadir'=>'/'.OC_User::getUser().'/files/Shared')); + OC_Filesystem::mount($sharedStorage,'/'.OC_User::getUser().'/files/Shared/'); + $CONFIG_DATADIRECTORY = "$CONFIG_DATADIRECTORY_ROOT/$user/$root"; if( !is_dir( $CONFIG_DATADIRECTORY )){ mkdir( $CONFIG_DATADIRECTORY, 0755, true ); |