Merge pull request #10218 from nextcloud/share-comments
allow to add a personal note to a share
This commit is contained in:
commit
b41d0d32e2
|
@ -121,6 +121,7 @@ nbproject
|
|||
/build/jsdocs/
|
||||
/npm-debug.log
|
||||
/PhantomJS_*
|
||||
/build/package-lock.json
|
||||
|
||||
# puphpet
|
||||
puphpet
|
||||
|
|
|
@ -7,12 +7,6 @@
|
|||
clear: both;
|
||||
}
|
||||
|
||||
#app-sidebar .mainFileInfoView {
|
||||
margin-right: 20px; /* accommodate for close icon */
|
||||
float:left;
|
||||
display:block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#app-sidebar .mainFileInfoView .icon {
|
||||
display: inline-block;
|
||||
|
|
|
@ -94,7 +94,8 @@
|
|||
@include icon-color('star-dark', 'files', $color-black, 2, true);
|
||||
}
|
||||
.nav-icon-sharingin,
|
||||
.nav-icon-sharingout {
|
||||
.nav-icon-sharingout,
|
||||
.nav-icon-shareoverview {
|
||||
@include icon-color('share', 'files', $color-black);
|
||||
}
|
||||
.nav-icon-sharinglinks {
|
||||
|
|
|
@ -169,3 +169,8 @@ thead {
|
|||
opacity: .57;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
#note {
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
}
|
||||
|
|
|
@ -2,143 +2,242 @@
|
|||
min-height: 100px;
|
||||
}
|
||||
|
||||
.shareTabView .oneline {
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
.share-autocomplete-item {
|
||||
display: flex;
|
||||
.autocomplete-item-text {
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
line-height: 32px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.shareTabView .shareWithLoading {
|
||||
padding-left: 10px;
|
||||
right: 35px;
|
||||
top: 0px;
|
||||
.shareTabView {
|
||||
.oneline {
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
}
|
||||
.shareWithLoading {
|
||||
padding-left: 10px;
|
||||
right: 35px;
|
||||
top: 0px;
|
||||
}
|
||||
.shareWithConfirm,
|
||||
.clipboardButton,
|
||||
.linkPass .icon-loading-small {
|
||||
position: absolute;
|
||||
right: 2px;
|
||||
top: 6px;
|
||||
padding: 14px;
|
||||
}
|
||||
.shareWithConfirm {
|
||||
opacity: 0.5;
|
||||
}
|
||||
.shareWithField:focus ~ .shareWithConfirm {
|
||||
opacity: 1;
|
||||
}
|
||||
.linkMore {
|
||||
position: absolute;
|
||||
right: -7px;
|
||||
top: -4px;
|
||||
padding: 14px;
|
||||
}
|
||||
.popovermenu {
|
||||
.datepicker {
|
||||
margin-left: 35px;
|
||||
}
|
||||
.clipboardButton {
|
||||
position: relative;
|
||||
top: initial;
|
||||
right: initial;
|
||||
padding: 0;
|
||||
}
|
||||
.share-add {
|
||||
input.share-note-delete {
|
||||
display: none;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
width: 44px !important;
|
||||
padding: 0;
|
||||
flex: 0 0 44px;
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
// note
|
||||
.share-note-form {
|
||||
span.icon-note {
|
||||
position: relative;
|
||||
}
|
||||
textarea.share-note {
|
||||
margin: 0;
|
||||
width: 200px;
|
||||
min-height: 70px;
|
||||
resize: none;
|
||||
+ input.share-note-submit {
|
||||
position: absolute;
|
||||
width: 44px !important;
|
||||
height: 44px;
|
||||
bottom: 0px;
|
||||
right: 10px;
|
||||
margin: 0;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
opacity: .7;
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
// fix for popover link share
|
||||
&.share-note-link {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.linkPass .icon-loading-small {
|
||||
margin-right: 0px;
|
||||
}
|
||||
.icon {
|
||||
background-size: 16px 16px;
|
||||
}
|
||||
.shareWithList .icon-loading-small:not(.hidden) + span,
|
||||
.linkShareView .icon-loading-small:not(.hidden) + input + label:before {
|
||||
/* Hide if loader is visible */
|
||||
display: none !important;
|
||||
}
|
||||
input {
|
||||
&[type='checkbox'] {
|
||||
margin: 0 3px 0 8px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
&[type='text'] {
|
||||
&.shareWithField,
|
||||
&.emailField {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding-right: 32px;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
&[type='text'].linkText
|
||||
&[type='password'].linkPassText,
|
||||
&[type='password'].passwordField {
|
||||
width: 180px !important;
|
||||
}
|
||||
}
|
||||
form {
|
||||
font-size: 100%;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
// share note on the sidebar
|
||||
.share-note {
|
||||
border-radius: var(--border-radius);
|
||||
margin-bottom: 10px;
|
||||
margin-left: 37px;
|
||||
}
|
||||
}
|
||||
|
||||
.shareTabView .shareWithConfirm,
|
||||
.shareTabView .clipboardButton,
|
||||
.shareTabView .linkPass .icon-loading-small {
|
||||
position: absolute;
|
||||
right: -7px;
|
||||
top: -2px;
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
.shareTabView .shareWithConfirm {
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
.shareTabView .shareWithField:focus ~ .shareWithConfirm {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.shareTabView .linkMore {
|
||||
position: absolute;
|
||||
right: -7px;
|
||||
top: -4px;
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
/* fix the popup menu because the button is shifted and then the menu is not aligned */
|
||||
.shareTabView .popovermenu.socialSharingMenu {
|
||||
right: -7px;
|
||||
}
|
||||
|
||||
.shareTabView .popovermenu .clipboardButton {
|
||||
position: relative;
|
||||
top: initial;
|
||||
right: initial;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.shareTabView label {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.shareTabView input[type="checkbox"] {
|
||||
margin: 0 3px 0 8px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.shareTabView input[type="text"].shareWithField,
|
||||
.shareTabView input[type="text"].emailField,
|
||||
.shareTabView input[type="text"].linkText,
|
||||
.shareTabView input[type="password"] {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding-right: 32px;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.shareTabView form {
|
||||
font-size: 100%;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
#shareWithList {
|
||||
// Sharing tab users list
|
||||
.shareWithList {
|
||||
list-style-type: none;
|
||||
padding: 0 0 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
> li {
|
||||
height: 44px;
|
||||
white-space: normal;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
.avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background-color: var(--color-background-darker);
|
||||
}
|
||||
}
|
||||
.unshare img {
|
||||
vertical-align: text-bottom;
|
||||
/* properly align icons */
|
||||
}
|
||||
.sharingOptionsGroup {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
// can edit label
|
||||
> .shareOption > label {
|
||||
padding: 13px;
|
||||
padding-right: 0;
|
||||
}
|
||||
// more menu
|
||||
> .share-menu {
|
||||
position: relative;
|
||||
display: block;
|
||||
.icon-more {
|
||||
padding: 14px;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
opacity: .5;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
}
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
.icon-more {
|
||||
opacity: .7;;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.username {
|
||||
padding: 0 8px;
|
||||
}
|
||||
}
|
||||
|
||||
#shareWithList > li {
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
white-space: normal;
|
||||
.ui-autocomplete {
|
||||
/* limit dropdown height to 4 1/2 entries */
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
z-index: 1550 !important;
|
||||
}
|
||||
|
||||
.notCreatable {
|
||||
padding-left: 12px;
|
||||
padding-top: 12px;
|
||||
color: var(--color-text-lighter);
|
||||
}
|
||||
|
||||
.contactsmenu-popover {
|
||||
left: -6px;
|
||||
right: auto;
|
||||
padding: 3px 6px;
|
||||
top: 100%;
|
||||
margin-top: 0;
|
||||
li.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
&:after {
|
||||
left: 8px;
|
||||
right: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.reshare,
|
||||
#link label,
|
||||
#expiration label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
.avatar {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
#shareWithList .unshare img {
|
||||
vertical-align: text-bottom; /* properly align icons */
|
||||
}
|
||||
|
||||
#shareWithList .sharingOptionsGroup > a .icon {
|
||||
padding: 7px;
|
||||
vertical-align: middle;
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
#shareWithList .sharingOptionsGroup .popovermenu:after {
|
||||
right: 3px;
|
||||
}
|
||||
|
||||
#shareWithList label input[type=checkbox] {
|
||||
margin-left: 0;
|
||||
.resharerInfoView.subView {
|
||||
position: relative;
|
||||
}
|
||||
#shareWithList .username {
|
||||
padding-right: 8px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
vertical-align: middle;
|
||||
}
|
||||
#shareWithList li .sharingOptionsGroup > .shareOption > label {
|
||||
padding: 6px;
|
||||
margin-right: 8px;
|
||||
vertical-align: text-top;
|
||||
}
|
||||
|
||||
.shareTabView .icon-loading-small {
|
||||
display: inline-block;
|
||||
z-index: 1;
|
||||
vertical-align: text-top;
|
||||
}
|
||||
|
||||
.shareTabView .shareWithList .icon-loading-small:not(.hidden) + span,
|
||||
.shareTabView .linkShareView .icon-loading-small:not(.hidden) + input + label:before {
|
||||
/* Hide if loader is visible */
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.linkShareView {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.shareTabView .linkPass .icon-loading-small {
|
||||
margin-right: 0px;
|
||||
}
|
||||
|
||||
.shareTabView .icon {
|
||||
background-size: 16px 16px;
|
||||
}
|
||||
}
|
|
@ -144,6 +144,7 @@ class ShareAPIController extends OCSController {
|
|||
'expiration' => null,
|
||||
'token' => null,
|
||||
'uid_file_owner' => $share->getShareOwner(),
|
||||
'note' => $share->getNote(),
|
||||
'displayname_file_owner' => $shareOwner !== null ? $shareOwner->getDisplayName() : $share->getShareOwner(),
|
||||
];
|
||||
|
||||
|
@ -698,17 +699,21 @@ class ShareAPIController extends OCSController {
|
|||
* @param string $password
|
||||
* @param string $publicUpload
|
||||
* @param string $expireDate
|
||||
* @param string $note
|
||||
* @return DataResponse
|
||||
* @throws OCSNotFoundException
|
||||
* @throws LockedException
|
||||
* @throws NotFoundException
|
||||
* @throws OCSBadRequestException
|
||||
* @throws OCSForbiddenException
|
||||
* @throws OCSNotFoundException
|
||||
*/
|
||||
public function updateShare(
|
||||
string $id,
|
||||
int $permissions = null,
|
||||
string $password = null,
|
||||
string $publicUpload = null,
|
||||
string $expireDate = null
|
||||
string $expireDate = null,
|
||||
string $note = null
|
||||
): DataResponse {
|
||||
try {
|
||||
$share = $this->getShareById($id);
|
||||
|
@ -722,10 +727,14 @@ class ShareAPIController extends OCSController {
|
|||
throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
|
||||
}
|
||||
|
||||
if ($permissions === null && $password === null && $publicUpload === null && $expireDate === null) {
|
||||
if ($permissions === null && $password === null && $publicUpload === null && $expireDate === null && $note === null) {
|
||||
throw new OCSBadRequestException($this->l->t('Wrong or no update parameter given'));
|
||||
}
|
||||
|
||||
if($note !== null) {
|
||||
$share->setNote($note);
|
||||
}
|
||||
|
||||
/*
|
||||
* expirationdate, password and publicUpload only make sense for link shares
|
||||
*/
|
||||
|
|
|
@ -262,6 +262,7 @@ class ShareController extends AuthPublicShareController {
|
|||
$shareTmpl['owner'] = $share->getShareOwner();
|
||||
$shareTmpl['filename'] = $share->getNode()->getName();
|
||||
$shareTmpl['directory_path'] = $share->getTarget();
|
||||
$shareTmpl['note'] = $share->getNote();
|
||||
$shareTmpl['mimetype'] = $share->getNode()->getMimetype();
|
||||
$shareTmpl['previewSupported'] = $this->previewManager->isMimeSupported($share->getNode()->getMimetype());
|
||||
$shareTmpl['dirToken'] = $this->getToken();
|
||||
|
|
|
@ -29,6 +29,12 @@ $maxUploadFilesize = min($upload_max_filesize, $post_max_size);
|
|||
<input type="hidden" name="filesize" value="<?php p($_['nonHumanFileSize']); ?>" id="filesize">
|
||||
<?php endif; ?>
|
||||
<input type="hidden" name="maxSizeAnimateGif" value="<?php p($_['maxSizeAnimateGif']); ?>" id="maxSizeAnimateGif">
|
||||
<?php if (isset($_['note']) && $_['note'] !== '') : ?>
|
||||
<div id="note">
|
||||
<?php p($l->t('Note:')); p(' ' . $_['note']); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!isset($_['hideFileList']) || (isset($_['hideFileList']) && $_['hideFileList'] === false)) { ?>
|
||||
<div id="files-public-content">
|
||||
<div id="preview">
|
||||
|
|
|
@ -238,7 +238,7 @@ class ShareAPIControllerTest extends TestCase {
|
|||
*/
|
||||
|
||||
public function createShare($id, $shareType, $sharedWith, $sharedBy, $shareOwner, $path, $permissions,
|
||||
$shareTime, $expiration, $parent, $target, $mail_send, $token=null,
|
||||
$shareTime, $expiration, $parent, $target, $mail_send, $note = '', $token=null,
|
||||
$password=null) {
|
||||
$share = $this->getMockBuilder(IShare::class)->getMock();
|
||||
$share->method('getId')->willReturn($id);
|
||||
|
@ -248,6 +248,7 @@ class ShareAPIControllerTest extends TestCase {
|
|||
$share->method('getShareOwner')->willReturn($shareOwner);
|
||||
$share->method('getNode')->willReturn($path);
|
||||
$share->method('getPermissions')->willReturn($permissions);
|
||||
$share->method('getNote')->willReturn($note);
|
||||
$time = new \DateTime();
|
||||
$time->setTimestamp($shareTime);
|
||||
$share->method('getShareTime')->willReturn($time);
|
||||
|
@ -310,7 +311,8 @@ class ShareAPIControllerTest extends TestCase {
|
|||
null,
|
||||
6,
|
||||
'target',
|
||||
0
|
||||
0,
|
||||
'personal note'
|
||||
);
|
||||
$expected = [
|
||||
'id' => 100,
|
||||
|
@ -334,6 +336,7 @@ class ShareAPIControllerTest extends TestCase {
|
|||
'storage' => 101,
|
||||
'mail_send' => 0,
|
||||
'uid_file_owner' => 'ownerId',
|
||||
'note' => 'personal note',
|
||||
'displayname_file_owner' => 'ownerDisplay',
|
||||
'mimetype' => 'myMimeType',
|
||||
];
|
||||
|
@ -352,7 +355,8 @@ class ShareAPIControllerTest extends TestCase {
|
|||
null,
|
||||
6,
|
||||
'target',
|
||||
0
|
||||
0,
|
||||
'personal note'
|
||||
);
|
||||
$expected = [
|
||||
'id' => 101,
|
||||
|
@ -376,6 +380,7 @@ class ShareAPIControllerTest extends TestCase {
|
|||
'storage' => 101,
|
||||
'mail_send' => 0,
|
||||
'uid_file_owner' => 'ownerId',
|
||||
'note' => 'personal note',
|
||||
'displayname_file_owner' => 'ownerDisplay',
|
||||
'mimetype' => 'myFolderMimeType',
|
||||
];
|
||||
|
@ -396,6 +401,7 @@ class ShareAPIControllerTest extends TestCase {
|
|||
6,
|
||||
'target',
|
||||
0,
|
||||
'personal note',
|
||||
'token',
|
||||
'password'
|
||||
);
|
||||
|
@ -422,6 +428,7 @@ class ShareAPIControllerTest extends TestCase {
|
|||
'mail_send' => 0,
|
||||
'url' => 'url',
|
||||
'uid_file_owner' => 'ownerId',
|
||||
'note' => 'personal note',
|
||||
'displayname_file_owner' => 'ownerDisplay',
|
||||
'mimetype' => 'myFolderMimeType',
|
||||
];
|
||||
|
@ -455,7 +462,7 @@ class ShareAPIControllerTest extends TestCase {
|
|||
->willReturn(true);
|
||||
|
||||
$this->shareManager
|
||||
->expects($this->once())
|
||||
->expects($this->any())
|
||||
->method('getShareById')
|
||||
->with($share->getFullId(), 'currentUser')
|
||||
->willReturn($share);
|
||||
|
@ -501,6 +508,8 @@ class ShareAPIControllerTest extends TestCase {
|
|||
['group', $group],
|
||||
]));
|
||||
|
||||
$d = $ocs->getShare($share->getId())->getData()[0];
|
||||
|
||||
$this->assertEquals($result, $ocs->getShare($share->getId())->getData()[0]);
|
||||
}
|
||||
|
||||
|
@ -1810,9 +1819,10 @@ class ShareAPIControllerTest extends TestCase {
|
|||
->setNode($file)
|
||||
->setShareTime(new \DateTime('2000-01-01T00:01:02'))
|
||||
->setTarget('myTarget')
|
||||
->setNote('personal note')
|
||||
->setId(42);
|
||||
|
||||
/* User backend down */
|
||||
// User backend down
|
||||
$result[] = [
|
||||
[
|
||||
'id' => 42,
|
||||
|
@ -1836,12 +1846,12 @@ class ShareAPIControllerTest extends TestCase {
|
|||
'file_target' => 'myTarget',
|
||||
'share_with' => 'recipient',
|
||||
'share_with_displayname' => 'recipient',
|
||||
'note' => 'personal note',
|
||||
'mail_send' => 0,
|
||||
'mimetype' => 'myMimeType',
|
||||
], $share, [], false
|
||||
];
|
||||
|
||||
/* User backend up */
|
||||
// User backend up
|
||||
$result[] = [
|
||||
[
|
||||
'id' => 42,
|
||||
|
@ -1855,6 +1865,7 @@ class ShareAPIControllerTest extends TestCase {
|
|||
'token' => null,
|
||||
'uid_file_owner' => 'owner',
|
||||
'displayname_file_owner' => 'ownerDN',
|
||||
'note' => 'personal note',
|
||||
'path' => 'file',
|
||||
'item_type' => 'file',
|
||||
'storage_id' => 'storageId',
|
||||
|
@ -1883,9 +1894,9 @@ class ShareAPIControllerTest extends TestCase {
|
|||
->setNode($file)
|
||||
->setShareTime(new \DateTime('2000-01-01T00:01:02'))
|
||||
->setTarget('myTarget')
|
||||
->setNote('personal note')
|
||||
->setId(42);
|
||||
|
||||
/* User backend down */
|
||||
// User backend down
|
||||
$result[] = [
|
||||
[
|
||||
'id' => 42,
|
||||
|
@ -1899,6 +1910,7 @@ class ShareAPIControllerTest extends TestCase {
|
|||
'token' => null,
|
||||
'uid_file_owner' => 'owner',
|
||||
'displayname_file_owner' => 'owner',
|
||||
'note' => 'personal note',
|
||||
'path' => 'file',
|
||||
'item_type' => 'file',
|
||||
'storage_id' => 'storageId',
|
||||
|
@ -1915,6 +1927,7 @@ class ShareAPIControllerTest extends TestCase {
|
|||
];
|
||||
|
||||
// with existing group
|
||||
|
||||
$share = \OC::$server->getShareManager()->newShare();
|
||||
$share->setShareType(\OCP\Share::SHARE_TYPE_GROUP)
|
||||
->setSharedWith('recipientGroup')
|
||||
|
@ -1924,6 +1937,7 @@ class ShareAPIControllerTest extends TestCase {
|
|||
->setNode($file)
|
||||
->setShareTime(new \DateTime('2000-01-01T00:01:02'))
|
||||
->setTarget('myTarget')
|
||||
->setNote('personal note')
|
||||
->setId(42);
|
||||
|
||||
$result[] = [
|
||||
|
@ -1939,6 +1953,7 @@ class ShareAPIControllerTest extends TestCase {
|
|||
'token' => null,
|
||||
'uid_file_owner' => 'owner',
|
||||
'displayname_file_owner' => 'owner',
|
||||
'note' => 'personal note',
|
||||
'path' => 'file',
|
||||
'item_type' => 'file',
|
||||
'storage_id' => 'storageId',
|
||||
|
@ -1964,6 +1979,7 @@ class ShareAPIControllerTest extends TestCase {
|
|||
->setNode($file)
|
||||
->setShareTime(new \DateTime('2000-01-01T00:01:02'))
|
||||
->setTarget('myTarget')
|
||||
->setNote('personal note')
|
||||
->setId(42);
|
||||
$result[] = [
|
||||
[
|
||||
|
@ -1978,6 +1994,7 @@ class ShareAPIControllerTest extends TestCase {
|
|||
'token' => null,
|
||||
'uid_file_owner' => 'owner',
|
||||
'displayname_file_owner' => 'owner',
|
||||
'note' => 'personal note',
|
||||
'path' => 'file',
|
||||
'item_type' => 'file',
|
||||
'storage_id' => 'storageId',
|
||||
|
@ -2004,6 +2021,7 @@ class ShareAPIControllerTest extends TestCase {
|
|||
->setPassword('mypassword')
|
||||
->setExpirationDate(new \DateTime('2001-01-02T00:00:00'))
|
||||
->setToken('myToken')
|
||||
->setNote('personal note')
|
||||
->setId(42);
|
||||
|
||||
$result[] = [
|
||||
|
@ -2019,6 +2037,7 @@ class ShareAPIControllerTest extends TestCase {
|
|||
'token' => 'myToken',
|
||||
'uid_file_owner' => 'owner',
|
||||
'displayname_file_owner' => 'owner',
|
||||
'note' => 'personal note',
|
||||
'path' => 'file',
|
||||
'item_type' => 'file',
|
||||
'storage_id' => 'storageId',
|
||||
|
@ -2044,6 +2063,7 @@ class ShareAPIControllerTest extends TestCase {
|
|||
->setNode($folder)
|
||||
->setShareTime(new \DateTime('2000-01-01T00:01:02'))
|
||||
->setTarget('myTarget')
|
||||
->setNote('personal note')
|
||||
->setId(42);
|
||||
|
||||
$result[] = [
|
||||
|
@ -2059,6 +2079,7 @@ class ShareAPIControllerTest extends TestCase {
|
|||
'token' => null,
|
||||
'uid_file_owner' => 'owner',
|
||||
'displayname_file_owner' => 'owner',
|
||||
'note' => 'personal note',
|
||||
'path' => 'folder',
|
||||
'item_type' => 'folder',
|
||||
'storage_id' => 'storageId',
|
||||
|
@ -2101,6 +2122,7 @@ class ShareAPIControllerTest extends TestCase {
|
|||
'token' => null,
|
||||
'uid_file_owner' => 'owner',
|
||||
'displayname_file_owner' => 'owner',
|
||||
'note' => '',
|
||||
'path' => 'folder',
|
||||
'item_type' => 'folder',
|
||||
'storage_id' => 'storageId',
|
||||
|
@ -2142,6 +2164,7 @@ class ShareAPIControllerTest extends TestCase {
|
|||
'token' => null,
|
||||
'uid_file_owner' => 'owner',
|
||||
'displayname_file_owner' => 'owner',
|
||||
'note' => '',
|
||||
'path' => 'folder',
|
||||
'item_type' => 'folder',
|
||||
'storage_id' => 'storageId',
|
||||
|
@ -2183,6 +2206,7 @@ class ShareAPIControllerTest extends TestCase {
|
|||
'token' => null,
|
||||
'uid_file_owner' => 'owner',
|
||||
'displayname_file_owner' => 'owner',
|
||||
'note' => '',
|
||||
'path' => 'folder',
|
||||
'item_type' => 'folder',
|
||||
'storage_id' => 'storageId',
|
||||
|
@ -2207,6 +2231,7 @@ class ShareAPIControllerTest extends TestCase {
|
|||
->setPermissions(\OCP\Constants::PERMISSION_READ)
|
||||
->setShareTime(new \DateTime('2000-01-01T00:01:02'))
|
||||
->setTarget('myTarget')
|
||||
->setNote('personal note')
|
||||
->setId(42);
|
||||
|
||||
$result[] = [
|
||||
|
@ -2238,6 +2263,7 @@ class ShareAPIControllerTest extends TestCase {
|
|||
'token' => null,
|
||||
'uid_file_owner' => 'owner',
|
||||
'displayname_file_owner' => 'owner',
|
||||
'note' => '',
|
||||
'path' => 'folder',
|
||||
'item_type' => 'folder',
|
||||
'storage_id' => 'storageId',
|
||||
|
|
|
@ -192,6 +192,9 @@ class ShareControllerTest extends \Test\TestCase {
|
|||
|
||||
|
||||
public function testShowShare() {
|
||||
|
||||
$note = 'personal note';
|
||||
|
||||
$this->shareController->setToken('token');
|
||||
|
||||
$owner = $this->getMockBuilder(IUser::class)->getMock();
|
||||
|
@ -210,6 +213,7 @@ class ShareControllerTest extends \Test\TestCase {
|
|||
$share->setPassword('password')
|
||||
->setShareOwner('ownerUID')
|
||||
->setNode($file)
|
||||
->setNote($note)
|
||||
->setTarget('/file1.txt');
|
||||
|
||||
$this->session->method('exists')->with('public_link_authenticated')->willReturn(true);
|
||||
|
@ -283,6 +287,7 @@ class ShareControllerTest extends \Test\TestCase {
|
|||
'shareUrl' => null,
|
||||
'previewImage' => null,
|
||||
'previewURL' => 'downloadURL',
|
||||
'note' => $note
|
||||
);
|
||||
|
||||
$csp = new \OCP\AppFramework\Http\ContentSecurityPolicy();
|
||||
|
|
|
@ -506,6 +506,61 @@ class ShareByMailProvider implements IShareProvider {
|
|||
return true;
|
||||
}
|
||||
|
||||
protected function sendNote(IShare $share) {
|
||||
|
||||
$recipient = $share->getSharedWith();
|
||||
|
||||
|
||||
$filename = $share->getNode()->getName();
|
||||
$initiator = $share->getSharedBy();
|
||||
$note = $share->getNote();
|
||||
|
||||
$initiatorUser = $this->userManager->get($initiator);
|
||||
$initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
|
||||
$initiatorEmailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null;
|
||||
|
||||
$plainHeading = $this->l->t('%1$s shared »%2$s« with you and wants to add:', [$initiatorDisplayName, $filename]);
|
||||
$htmlHeading = $this->l->t('%1$s shared »%2$s« with you and wants to add', [$initiatorDisplayName, $filename]);
|
||||
|
||||
$message = $this->mailer->createMessage();
|
||||
|
||||
$emailTemplate = $this->mailer->createEMailTemplate('shareByMail.sendNote');
|
||||
|
||||
$emailTemplate->setSubject($this->l->t('»%s« added a note to a file shared with you', [$initiatorDisplayName]));
|
||||
$emailTemplate->addHeader();
|
||||
$emailTemplate->addHeading(htmlspecialchars($htmlHeading), $plainHeading);
|
||||
$emailTemplate->addBodyText(htmlspecialchars($note), $note);
|
||||
|
||||
$link = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare',
|
||||
['token' => $share->getToken()]);
|
||||
$emailTemplate->addBodyButton(
|
||||
$this->l->t('Open »%s«', [$filename]),
|
||||
$link
|
||||
);
|
||||
|
||||
// The "From" contains the sharers name
|
||||
$instanceName = $this->defaults->getName();
|
||||
$senderName = $this->l->t(
|
||||
'%1$s via %2$s',
|
||||
[
|
||||
$initiatorDisplayName,
|
||||
$instanceName
|
||||
]
|
||||
);
|
||||
$message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
|
||||
if ($initiatorEmailAddress !== null) {
|
||||
$message->setReplyTo([$initiatorEmailAddress => $initiatorDisplayName]);
|
||||
$emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan());
|
||||
} else {
|
||||
$emailTemplate->addFooter();
|
||||
}
|
||||
|
||||
$message->setTo([$recipient]);
|
||||
$message->useTemplate($emailTemplate);
|
||||
$this->mailer->send($message);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* send auto generated password to the owner. This happens if the admin enforces
|
||||
* a password for mail shares and forbid to send the password by mail to the recipient
|
||||
|
@ -662,8 +717,13 @@ class ShareByMailProvider implements IShareProvider {
|
|||
->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
|
||||
->set('password', $qb->createNamedParameter($share->getPassword()))
|
||||
->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
|
||||
->set('note', $qb->createNamedParameter($share->getNote()))
|
||||
->execute();
|
||||
|
||||
if ($originalShare->getNote() !== $share->getNote() && $share->getNote() !== '') {
|
||||
$this->sendNote($share);
|
||||
}
|
||||
|
||||
return $share;
|
||||
}
|
||||
|
||||
|
@ -904,6 +964,7 @@ class ShareByMailProvider implements IShareProvider {
|
|||
->setPermissions((int)$data['permissions'])
|
||||
->setTarget($data['file_target'])
|
||||
->setMailSend((bool)$data['mail_send'])
|
||||
->setNote($data['note'])
|
||||
->setToken($data['token']);
|
||||
|
||||
$shareTime = new \DateTime();
|
||||
|
|
|
@ -342,15 +342,17 @@ class ShareByMailProviderTest extends TestCase {
|
|||
$uidOwner = 'user2';
|
||||
$permissions = 1;
|
||||
$token = 'token';
|
||||
$note = 'personal note';
|
||||
|
||||
|
||||
$instance = $this->getInstance();
|
||||
|
||||
$id = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token);
|
||||
$id = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token, $note);
|
||||
|
||||
$this->share->expects($this->once())->method('getPermissions')->willReturn($permissions + 1);
|
||||
$this->share->expects($this->once())->method('getShareOwner')->willReturn($uidOwner);
|
||||
$this->share->expects($this->once())->method('getSharedBy')->willReturn($sharedBy);
|
||||
$this->share->expects($this->any())->method('getNote')->willReturn($note);
|
||||
$this->share->expects($this->atLeastOnce())->method('getId')->willReturn($id);
|
||||
|
||||
$this->assertSame($this->share,
|
||||
|
@ -372,6 +374,7 @@ class ShareByMailProviderTest extends TestCase {
|
|||
$this->assertSame($uidOwner, $result[0]['uid_owner']);
|
||||
$this->assertSame($permissions + 1, (int)$result[0]['permissions']);
|
||||
$this->assertSame($token, $result[0]['token']);
|
||||
$this->assertSame($note, $result[0]['note']);
|
||||
}
|
||||
|
||||
public function testDelete() {
|
||||
|
@ -478,7 +481,7 @@ class ShareByMailProviderTest extends TestCase {
|
|||
$instance = $this->getInstance(['createShareObject']);
|
||||
|
||||
$idMail = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token);
|
||||
$idPublic = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token, \OCP\Share::SHARE_TYPE_LINK);
|
||||
$idPublic = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token, '', \OCP\Share::SHARE_TYPE_LINK);
|
||||
|
||||
$this->assertTrue($idMail !== $idPublic);
|
||||
|
||||
|
@ -490,9 +493,9 @@ class ShareByMailProviderTest extends TestCase {
|
|||
}
|
||||
);
|
||||
|
||||
$this->assertInstanceOf('OCP\Share\IShare',
|
||||
$instance->getShareByToken('token')
|
||||
);
|
||||
$result = $instance->getShareByToken('token');
|
||||
|
||||
$this->assertInstanceOf('OCP\Share\IShare', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -511,7 +514,7 @@ class ShareByMailProviderTest extends TestCase {
|
|||
$instance = $this->getInstance(['createShareObject']);
|
||||
|
||||
$idMail = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token);
|
||||
$idPublic = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, "token2", \OCP\Share::SHARE_TYPE_LINK);
|
||||
$idPublic = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, "token2", '', \OCP\Share::SHARE_TYPE_LINK);
|
||||
|
||||
$this->assertTrue($idMail !== $idPublic);
|
||||
|
||||
|
@ -631,7 +634,7 @@ class ShareByMailProviderTest extends TestCase {
|
|||
$this->invokePrivate($instance, 'getRawShare', [$id+1]);
|
||||
}
|
||||
|
||||
private function createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token, $shareType = \OCP\Share::SHARE_TYPE_EMAIL) {
|
||||
private function createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token, $note='', $shareType = \OCP\Share::SHARE_TYPE_EMAIL) {
|
||||
$qb = $this->connection->getQueryBuilder();
|
||||
$qb->insert('share')
|
||||
->setValue('share_type', $qb->createNamedParameter($shareType))
|
||||
|
@ -643,6 +646,7 @@ class ShareByMailProviderTest extends TestCase {
|
|||
->setValue('uid_initiator', $qb->createNamedParameter($sharedBy))
|
||||
->setValue('permissions', $qb->createNamedParameter($permissions))
|
||||
->setValue('token', $qb->createNamedParameter($token))
|
||||
->setValue('note', $qb->createNamedParameter($note))
|
||||
->setValue('stime', $qb->createNamedParameter(time()));
|
||||
|
||||
/*
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2018 Bjoern Schiessle <bjoern@schiessle.org>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OC\Core\Migrations;
|
||||
|
||||
use OCP\DB\ISchemaWrapper;
|
||||
use OCP\Migration\SimpleMigrationStep;
|
||||
|
||||
/**
|
||||
* add column for share notes
|
||||
*
|
||||
* Class Version14000Date20180712153140
|
||||
*/
|
||||
class Version14000Date20180712153140 extends SimpleMigrationStep {
|
||||
public function changeSchema(\OCP\Migration\IOutput $output, \Closure $schemaClosure, array $options) {
|
||||
|
||||
/** @var ISchemaWrapper $schema */
|
||||
$schema = $schemaClosure();
|
||||
|
||||
$table = $schema->getTable('share');
|
||||
$table->addColumn('note', 'text', ['notnull' => false]);
|
||||
|
||||
return $schema;
|
||||
}
|
||||
}
|
|
@ -74,6 +74,13 @@ kbd {
|
|||
/* Navigation: folder like structure */
|
||||
#app-navigation {
|
||||
width: $navigation-width;
|
||||
position: sticky;
|
||||
top: $header-height;
|
||||
left: 0;
|
||||
z-index: 1500;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
height: calc(100vh - #{$header-height});
|
||||
box-sizing: border-box;
|
||||
background-color: var(--color-main-background);
|
||||
-webkit-user-select: none;
|
||||
|
@ -320,9 +327,6 @@ kbd {
|
|||
&.hidden {
|
||||
display: none;
|
||||
}
|
||||
&.without-app-settings {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Button styling for menu, edit and undo
|
||||
|
@ -581,12 +585,7 @@ kbd {
|
|||
padding-top: $header-height;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
display: flex;
|
||||
/* trick: scroll #app-content and not the body
|
||||
* to avoid double scrollbar with sidebar
|
||||
*/
|
||||
max-height: 100vh;
|
||||
}
|
||||
|
||||
/* APP-CONTENT AND WRAPPER ------------------------------------------ */
|
||||
|
@ -637,17 +636,19 @@ kbd {
|
|||
min-width: $sidebar-min-width;
|
||||
max-width: $sidebar-max-width;
|
||||
display: block;
|
||||
position: relative;
|
||||
position: sticky;
|
||||
top: $header-height;
|
||||
right:0;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
z-index: 1500;
|
||||
height: calc(100vh - #{$header-height});
|
||||
background: var(--color-main-background);
|
||||
border-left: 1px solid var(--color-border);
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
flex-shrink: 0;
|
||||
transition: 300ms width ease-in-out,
|
||||
300ms min-width ease-in-out;
|
||||
// no animations possible, use OC.Apps.showAppSidebar
|
||||
&.disappear {
|
||||
width: 0;
|
||||
min-width: 0;
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -880,6 +881,11 @@ $popovericon-size: 16px;
|
|||
li {
|
||||
display: flex;
|
||||
flex: 0 0 auto;
|
||||
|
||||
&.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
> button,
|
||||
> a,
|
||||
> .menuitem {
|
||||
|
@ -895,6 +901,7 @@ $popovericon-size: 16px;
|
|||
box-shadow: none;
|
||||
width: 100%;
|
||||
color: var(--color-main-text);
|
||||
white-space: nowrap;
|
||||
/* Override the app-navigation li opacity */
|
||||
opacity: .7 !important;
|
||||
span[class^='icon-'],
|
||||
|
@ -943,6 +950,7 @@ $popovericon-size: 16px;
|
|||
width: 150px;
|
||||
line-height: 1.6em;
|
||||
padding: 8px 0;
|
||||
white-space: normal;
|
||||
}
|
||||
> select {
|
||||
margin: 0;
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
#app-navigation,
|
||||
#app-sidebar {
|
||||
position: fixed !important;
|
||||
}
|
||||
#app-content {
|
||||
width: $navigation-width !important;
|
||||
}
|
||||
#app-sidebar.disappear {
|
||||
right: -$sidebar-max-width !important;
|
||||
}
|
|
@ -1,204 +0,0 @@
|
|||
/**
|
||||
* @copyright Copyright (c) 2016, John Molakvoæ <skjnldsv@protonmail.com>
|
||||
* @copyright Copyright (c) 2016, Morris Jobke <hey@morrisjobke.de>
|
||||
* @copyright Copyright (c) 2016, Julia Bode <julia.bode@lulisaur.us>
|
||||
* @copyright Copyright (c) 2016, Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
* @copyright Copyright (c) 2015, Hendrik Leppelsack <hendrik@leppelsack.de>
|
||||
* @copyright Copyright (c) 2015, Jan-Christoph Borchardt <hey@jancborchardt.net>
|
||||
* @copyright Copyright (c) 2015, Vincent Petry <pvince81@owncloud.com>
|
||||
* @copyright Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
|
||||
* @copyright Copyright (c) 2015, Roeland Jago Douma <roeland@famdouma.nl>
|
||||
* @copyright Copyright (c) 2015, Morris Jobke <hey@morrisjobke.de>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
*/
|
||||
|
||||
/* SHARE TAB STYLING -------------------------------------------------------- */
|
||||
.shareTabView {
|
||||
.unshare.icon-loading-small {
|
||||
margin-top: 1px;
|
||||
}
|
||||
.shareWithLoading, .linkShare .icon-loading-small {
|
||||
display: inline-block !important;
|
||||
padding-left: 10px;
|
||||
}
|
||||
.shareWithLoading {
|
||||
position: relative;
|
||||
right: 70px;
|
||||
top: 2px;
|
||||
}
|
||||
.icon-loading-small.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
.avatar {
|
||||
margin-right: 8px;
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
vertical-align: middle;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
label {
|
||||
font-weight: 400;
|
||||
white-space: nowrap;
|
||||
}
|
||||
input[type='radio'].radio + label {
|
||||
margin-left: -1px;
|
||||
}
|
||||
input[type='checkbox'] {
|
||||
margin: 0 3px 0 8px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
input[type='submit'] {
|
||||
margin-left: 7px;
|
||||
}
|
||||
form {
|
||||
font-size: 100%;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
.error {
|
||||
color: var(--color-error);
|
||||
border-color: var(--color-error);
|
||||
}
|
||||
.mailView .icon-mail {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
.share-autocomplete-item {
|
||||
display: flex;
|
||||
.autocomplete-item-text {
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
line-height: 32px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-autocomplete .autocomplete-note {
|
||||
padding: 5px 10px;
|
||||
color: rgba(0, 0, 0, .3);
|
||||
}
|
||||
|
||||
#shareWithList {
|
||||
list-style-type: none;
|
||||
padding: 8px;
|
||||
> li {
|
||||
position: relative;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
font-weight: bold;
|
||||
line-height: 21px;
|
||||
white-space: normal;
|
||||
width: 100%;
|
||||
}
|
||||
.sharingOptionsGroup {
|
||||
flex-shrink: 0;
|
||||
position: relative;
|
||||
.popovermenu {
|
||||
right: -11px;
|
||||
top: 35px;
|
||||
}
|
||||
}
|
||||
|
||||
.shareOption {
|
||||
white-space: nowrap;
|
||||
display: inline-block;
|
||||
}
|
||||
.unshare img {
|
||||
vertical-align: text-bottom;
|
||||
/* properly align icons */
|
||||
}
|
||||
label input[type=checkbox] {
|
||||
margin-left: 0;
|
||||
position: relative;
|
||||
}
|
||||
.username {
|
||||
padding-right: 8px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
vertical-align: middle;
|
||||
flex-grow: 5;
|
||||
}
|
||||
}
|
||||
|
||||
#link {
|
||||
border-top: 1px solid var(--color-border);
|
||||
padding-top: 8px;
|
||||
#showPassword img {
|
||||
padding-left: 5px;
|
||||
width: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.reshare,
|
||||
#link label,
|
||||
#expiration label {
|
||||
display: inline-block;
|
||||
padding: 6px 4px;
|
||||
}
|
||||
|
||||
.resharerInfoView.subView {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#defaultExpireMessage, .reshare {
|
||||
/* fix shared by text going out of box */
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
#defaultExpireMessage {
|
||||
/* show message on new line */
|
||||
display: block;
|
||||
padding-left: 4px;
|
||||
/* TODO: style the dropdown in a proper way - border-box, etc. */
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.ui-autocomplete {
|
||||
/* limit dropdown height to 4 1/2 entries */
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.notCreatable {
|
||||
padding-left: 12px;
|
||||
padding-top: 12px;
|
||||
color: var(--color-text-lighter);
|
||||
}
|
||||
|
||||
.contactsmenu-popover {
|
||||
left: -6px;
|
||||
right: auto;
|
||||
padding: 3px 6px;
|
||||
top: 100%;
|
||||
margin-top: 0;
|
||||
li.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
&:after {
|
||||
left: 8px;
|
||||
right: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.popovermenu .datepicker {
|
||||
margin-left: 35px;
|
||||
}
|
||||
|
||||
.popovermenu .passwordField {
|
||||
margin-left: 35px;
|
||||
width: inherit !important;
|
||||
}
|
||||
|
||||
.ui-datepicker {
|
||||
z-index: 1111 !important;
|
||||
}
|
|
@ -530,6 +530,7 @@ code {
|
|||
width: auto;
|
||||
border-radius: var(--border-radius);
|
||||
border: none;
|
||||
z-index: 500 !important;
|
||||
|
||||
.ui-state-default,
|
||||
.ui-widget-content .ui-state-default,
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 16 16" height="16" width="16"><path fill="#fff" d="m9.2363 2.166-3.1816 3.1836c-0.7071 0.7072-1.0378 1.6182-0.9883 2.457 0.05 0.8389 0.4333 1.5841 0.9883 2.1387l1.4121-1.416c-0.5672-0.5672-0.5444-1.2192 0.002-1.7656l3.1812-3.1817c0.52536-0.52536 1.2507-0.52318 1.772-0.002 0.48245 0.5556 0.52732 1.2382-0.004 1.7695l-0.82 0.8203c0.555 0.785 0.645 1.3663 0.593 2.2344l1.641-1.6406c1.2374-1.2374 1.2371-3.3645 0-4.6016-1.236-1.2361-3.342-1.2113-4.5957 0.004zm0.7071 3.8848-1.4141 1.418c0 0 0.003-00 0.004 0 0.55 0.55 0.50736 1.2582-0.004 1.7695l-3.1816 3.1817c-0.696 0.59192-1.2985 0.47105-1.7696 0-0.62636-0.62636-0.5-1.2681 0-1.768l0.85-0.8473c-0.556-0.7835-0.6484-1.365-0.5976-2.2324l-1.666 1.666c-1.2393 1.2393-1.2357 3.36 0 4.5957 1.2353 1.2353 3.362 1.2356 4.5976 0l3.1817-3.182c0.7086-0.7083 1.0396-1.6184 0.9906-2.4586-0.048-0.8401-0.432-1.5864-0.9887-2.1407z"/></svg>
|
After Width: | Height: | Size: 942 B |
|
@ -27,8 +27,9 @@
|
|||
*/
|
||||
exports.Apps.showAppSidebar = function($el) {
|
||||
var $appSidebar = $el || $('#app-sidebar');
|
||||
$appSidebar.removeClass('disappear');
|
||||
$('#content').addClass('with-app-sidebar').trigger(new $.Event('appresized'));
|
||||
$appSidebar.removeClass('disappear')
|
||||
.show('slide', { direction: 'right' }, 300);
|
||||
$('#app-content').trigger(new $.Event('appresized'));
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -39,8 +40,11 @@
|
|||
*/
|
||||
exports.Apps.hideAppSidebar = function($el) {
|
||||
var $appSidebar = $el || $('#app-sidebar');
|
||||
$appSidebar.addClass('disappear');
|
||||
$('#content').removeClass('with-app-sidebar').trigger(new $.Event('appresized'));
|
||||
$appSidebar.hide('slide', { direction: 'right' }, 300,
|
||||
function() {
|
||||
$appSidebar.addClass('disappear');
|
||||
});
|
||||
$('#app-content').trigger(new $.Event('appresized'));
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -37,7 +37,6 @@
|
|||
"shareconfigmodel.js",
|
||||
"shareitemmodel.js",
|
||||
"sharedialogview.js",
|
||||
"sharedialogexpirationview.js",
|
||||
"sharedialoglinkshareview.js",
|
||||
"sharedialogresharerinfoview.js",
|
||||
"sharedialogshareelistview.js",
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
[
|
||||
"shareconfigmodel.js",
|
||||
"shareitemmodel.js",
|
||||
"sharesocialmanager.js",
|
||||
"sharedialogresharerinfoview.js",
|
||||
"sharedialoglinkshareview.js",
|
||||
"sharedialogexpirationview.js",
|
||||
"sharedialogshareelistview.js",
|
||||
"sharedialogview.js",
|
||||
"share.js"
|
||||
"shareconfigmodel.js",
|
||||
"shareitemmodel.js",
|
||||
"sharesocialmanager.js",
|
||||
"sharedialogresharerinfoview.js",
|
||||
"sharedialoglinkshareview.js",
|
||||
"sharedialogshareelistview.js",
|
||||
"sharedialogview.js",
|
||||
"share.js"
|
||||
]
|
||||
|
|
|
@ -1,209 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2015
|
||||
*
|
||||
* This file is licensed under the Affero General Public License version 3
|
||||
* or later.
|
||||
*
|
||||
* See the COPYING-README file.
|
||||
*
|
||||
*/
|
||||
|
||||
/* global moment, Handlebars */
|
||||
|
||||
(function() {
|
||||
if (!OC.Share) {
|
||||
OC.Share = {};
|
||||
}
|
||||
|
||||
var TEMPLATE =
|
||||
// currently expiration is only effective for link share.
|
||||
// this is about to change in future. Therefore this is not included
|
||||
// in the LinkShareView to ease reusing it in future. Then,
|
||||
// modifications (getting rid of IDs) are still necessary.
|
||||
'{{#if isLinkShare}}' +
|
||||
'<input type="checkbox" name="expirationCheckbox" class="expirationCheckbox checkbox" id="expirationCheckbox-{{cid}}" value="1" ' +
|
||||
'{{#if isExpirationSet}}checked="checked"{{/if}} {{#if disableCheckbox}}disabled="disabled"{{/if}} />' +
|
||||
'<label for="expirationCheckbox-{{cid}}">{{setExpirationLabel}}</label>' +
|
||||
'<div class="expirationDateContainer {{#unless isExpirationSet}}hidden{{/unless}}">' +
|
||||
' <label for="expirationDate" class="hidden-visually" value="{{expirationDate}}">{{expirationLabel}}</label>' +
|
||||
' <input id="expirationDate" class="datepicker" type="text" placeholder="{{expirationDatePlaceholder}}" value="{{expirationValue}}" />' +
|
||||
'</div>' +
|
||||
' {{#if isExpirationEnforced}}' +
|
||||
// originally the expire message was shown when a default date was set, however it never had text
|
||||
'<em id="defaultExpireMessage">{{defaultExpireMessage}}</em>' +
|
||||
' {{/if}}' +
|
||||
'{{/if}}'
|
||||
;
|
||||
|
||||
/**
|
||||
* @class OCA.Share.ShareDialogExpirationView
|
||||
* @member {OC.Share.ShareItemModel} model
|
||||
* @member {jQuery} $el
|
||||
* @memberof OCA.Sharing
|
||||
* @classdesc
|
||||
*
|
||||
* Represents the expiration part in the GUI of the share dialogue
|
||||
*
|
||||
*/
|
||||
var ShareDialogExpirationView = OC.Backbone.View.extend({
|
||||
/** @type {string} **/
|
||||
id: 'shareDialogLinkShare',
|
||||
|
||||
/** @type {OC.Share.ShareConfigModel} **/
|
||||
configModel: undefined,
|
||||
|
||||
/** @type {Function} **/
|
||||
_template: undefined,
|
||||
|
||||
/** @type {boolean} **/
|
||||
showLink: true,
|
||||
|
||||
className: 'hidden',
|
||||
|
||||
events: {
|
||||
'change .expirationCheckbox': '_onToggleExpiration',
|
||||
'change .datepicker': '_onChangeExpirationDate'
|
||||
},
|
||||
|
||||
initialize: function(options) {
|
||||
if(!_.isUndefined(options.configModel)) {
|
||||
this.configModel = options.configModel;
|
||||
} else {
|
||||
throw 'missing OC.Share.ShareConfigModel';
|
||||
}
|
||||
|
||||
var view = this;
|
||||
this.configModel.on('change:isDefaultExpireDateEnforced', function() {
|
||||
view.render();
|
||||
});
|
||||
|
||||
this.model.on('change:itemType', function() {
|
||||
view.render();
|
||||
});
|
||||
|
||||
this.model.on('change:linkShare', function() {
|
||||
view.render();
|
||||
});
|
||||
},
|
||||
|
||||
_onToggleExpiration: function(event) {
|
||||
var $checkbox = $(event.target);
|
||||
var state = $checkbox.prop('checked');
|
||||
// TODO: slide animation
|
||||
this.$el.find('.expirationDateContainer').toggleClass('hidden', !state);
|
||||
if (!state) {
|
||||
// discard expiration date
|
||||
this.model.get('linkShare').expiration = '';
|
||||
this.model.saveLinkShare({
|
||||
expireDate: ''
|
||||
});
|
||||
} else {
|
||||
this.$el.find('#expirationDate').focus();
|
||||
}
|
||||
},
|
||||
|
||||
_onChangeExpirationDate: function(event) {
|
||||
var $target = $(event.target);
|
||||
$target.tooltip('hide');
|
||||
$target.removeClass('error');
|
||||
|
||||
var expiration = moment($target.val(), 'DD-MM-YYYY').format('YYYY-MM-DD');
|
||||
this.model.get('linkShare').expiration = expiration;
|
||||
this.model.saveLinkShare({
|
||||
expiration: expiration
|
||||
}, {
|
||||
error: function(model, message) {
|
||||
if (!message) {
|
||||
$target.attr('title', t('core', 'Error setting expiration date'));
|
||||
} else {
|
||||
$target.attr('title', message);
|
||||
}
|
||||
$target.tooltip({gravity: 'n'});
|
||||
$target.tooltip('show');
|
||||
$target.addClass('error');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var defaultExpireMessage = '';
|
||||
var defaultExpireDays = this.configModel.get('defaultExpireDate');
|
||||
var isExpirationEnforced = this.configModel.get('isDefaultExpireDateEnforced');
|
||||
|
||||
if( (this.model.isFolder() || this.model.isFile())
|
||||
&& isExpirationEnforced) {
|
||||
defaultExpireMessage = t(
|
||||
'core',
|
||||
'The public link will expire no later than {days} days after it is created',
|
||||
{'days': defaultExpireDays }
|
||||
);
|
||||
}
|
||||
|
||||
var isExpirationSet = !!this.model.get('linkShare').expiration || isExpirationEnforced;
|
||||
|
||||
var expiration;
|
||||
if (isExpirationSet) {
|
||||
expiration = moment(this.model.get('linkShare').expiration, 'YYYY-MM-DD').format('DD-MM-YYYY');
|
||||
}
|
||||
|
||||
this.$el.html(this.template({
|
||||
cid: this.cid,
|
||||
setExpirationLabel: t('core', 'Set expiration date'),
|
||||
expirationLabel: t('core', 'Expiration'),
|
||||
expirationDatePlaceholder: t('core', 'Expiration date'),
|
||||
defaultExpireMessage: defaultExpireMessage,
|
||||
isLinkShare: this.model.get('linkShare').isLinkShare,
|
||||
isExpirationSet: isExpirationSet,
|
||||
isExpirationEnforced: isExpirationEnforced,
|
||||
disableCheckbox: isExpirationEnforced && isExpirationSet,
|
||||
expirationValue: expiration
|
||||
}));
|
||||
|
||||
// what if there is another date picker on that page?
|
||||
var minDate = new Date();
|
||||
var maxDate = null;
|
||||
// min date should always be the next day
|
||||
minDate.setDate(minDate.getDate()+1);
|
||||
|
||||
if(isExpirationSet) {
|
||||
if(isExpirationEnforced) {
|
||||
// TODO: hack: backend returns string instead of integer
|
||||
var shareTime = this.model.get('linkShare').stime;
|
||||
if (_.isNumber(shareTime)) {
|
||||
shareTime = new Date(shareTime * 1000);
|
||||
}
|
||||
if (!shareTime) {
|
||||
shareTime = new Date(); // now
|
||||
}
|
||||
shareTime = OC.Util.stripTime(shareTime).getTime();
|
||||
maxDate = new Date(shareTime + defaultExpireDays * 24 * 3600 * 1000);
|
||||
}
|
||||
}
|
||||
$.datepicker.setDefaults({
|
||||
minDate: minDate,
|
||||
maxDate: maxDate
|
||||
});
|
||||
|
||||
this.$el.find('.datepicker').datepicker({dateFormat : 'dd-mm-yy'});
|
||||
|
||||
this.delegateEvents();
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* @returns {Function} from Handlebars
|
||||
* @private
|
||||
*/
|
||||
template: function (data) {
|
||||
if (!this._template) {
|
||||
this._template = Handlebars.compile(TEMPLATE);
|
||||
}
|
||||
return this._template(data);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
OC.Share.ShareDialogExpirationView = ShareDialogExpirationView;
|
||||
|
||||
})();
|
|
@ -21,71 +21,101 @@
|
|||
|
||||
var TEMPLATE =
|
||||
'{{#if shareAllowed}}' +
|
||||
'<span class="icon-loading-small hidden"></span>' +
|
||||
'<input type="checkbox" name="linkCheckbox" id="linkCheckbox-{{cid}}" class="checkbox linkCheckbox" value="1" {{#if isLinkShare}}checked="checked"{{/if}} />' +
|
||||
'<label for="linkCheckbox-{{cid}}">{{linkShareLabel}}</label>' +
|
||||
'<br />' +
|
||||
'<div class="oneline">' +
|
||||
'<label for="linkText-{{cid}}" class="hidden-visually">{{urlLabel}}</label>' +
|
||||
'<input id="linkText-{{cid}}" class="linkText {{#unless isLinkShare}}hidden{{/unless}}" type="text" readonly="readonly" value="{{shareLinkURL}}" />' +
|
||||
'{{#if singleAction}}' +
|
||||
'<a class="{{#unless isLinkShare}}hidden-visually{{/unless}} clipboardButton icon icon-clippy" data-clipboard-target="#linkText-{{cid}}"></a>' +
|
||||
'{{else}}' +
|
||||
'<a class="{{#unless isLinkShare}}hidden-visually{{/unless}}" href="#"><span class="linkMore icon icon-more"></span></a>' +
|
||||
'{{{popoverMenu}}}' +
|
||||
'{{/if}}' +
|
||||
'</div>' +
|
||||
'{{#if publicUpload}}' +
|
||||
'<div>' +
|
||||
'<span class="icon-loading-small hidden"></span>' +
|
||||
'<input type="radio" name="publicUpload" value="{{publicUploadRValue}}" id="sharingDialogAllowPublicUpload-r-{{cid}}" class="radio publicUploadRadio" {{{publicUploadRChecked}}} />' +
|
||||
'<label for="sharingDialogAllowPublicUpload-r-{{cid}}">{{publicUploadRLabel}}</label>' +
|
||||
'</div>' +
|
||||
'<div>' +
|
||||
'<span class="icon-loading-small hidden"></span>' +
|
||||
'<input type="radio" name="publicUpload" value="{{publicUploadRWValue}}" id="sharingDialogAllowPublicUpload-rw-{{cid}}" class="radio publicUploadRadio" {{{publicUploadRWChecked}}} />' +
|
||||
'<label for="sharingDialogAllowPublicUpload-rw-{{cid}}">{{publicUploadRWLabel}}</label>' +
|
||||
'</div>' +
|
||||
'<div>' +
|
||||
'<span class="icon-loading-small hidden"></span>' +
|
||||
'<input type="radio" name="publicUpload" value="{{publicUploadWValue}}" id="sharingDialogAllowPublicUpload-w-{{cid}}" class="radio publicUploadRadio" {{{publicUploadWChecked}}} />' +
|
||||
'<label for="sharingDialogAllowPublicUpload-w-{{cid}}">{{publicUploadWLabel}}</label>' +
|
||||
'</div>' +
|
||||
'{{/if}}' +
|
||||
' {{#if publicEditing}}' +
|
||||
'<div id="allowPublicEditingWrapper">' +
|
||||
' <span class="icon-loading-small hidden"></span>' +
|
||||
' <input type="checkbox" value="1" name="allowPublicEditing" id="sharingDialogAllowPublicEditing-{{cid}}" class="checkbox publicEditingCheckbox" {{{publicEditingChecked}}} />' +
|
||||
'<label for="sharingDialogAllowPublicEditing-{{cid}}">{{publicEditingLabel}}</label>' +
|
||||
'</div>' +
|
||||
' {{/if}}' +
|
||||
' {{#if showPasswordCheckBox}}' +
|
||||
'<input type="checkbox" name="showPassword" id="showPassword-{{cid}}" class="checkbox showPasswordCheckbox" {{#if isPasswordSet}}checked="checked"{{/if}} value="1" />' +
|
||||
'<label for="showPassword-{{cid}}">{{enablePasswordLabel}}</label>' +
|
||||
' {{/if}}' +
|
||||
'<div id="linkPass" class="oneline linkPass {{#unless isPasswordSet}}hidden{{/unless}}">' +
|
||||
' <label for="linkPassText-{{cid}}" class="hidden-visually">{{passwordLabel}}</label>' +
|
||||
' {{#if showPasswordCheckBox}}' +
|
||||
' <input id="linkPassText-{{cid}}" class="linkPassText" type="password" placeholder="{{passwordPlaceholder}}" autocomplete="new-password" />' +
|
||||
' {{else}}' +
|
||||
' <input id="linkPassText-{{cid}}" class="linkPassText" type="password" placeholder="{{passwordPlaceholderInitial}}" autocomplete="new-password" />' +
|
||||
' {{/if}}' +
|
||||
' <span class="icon icon-loading-small hidden"></span>' +
|
||||
'</div>' +
|
||||
'<ul id="shareLink" class="shareWithList">' +
|
||||
' <li data-share-id="{{cid}}">' +
|
||||
' <div class="avatar icon-public-white"></div><span class="username" title="{{linkShareLabel}}">{{linkShareLabel}}</span>' +
|
||||
' <span class="sharingOptionsGroup">' +
|
||||
' <span class="shareOption"> ' +
|
||||
' <span class="icon-loading-small hidden"></span>' +
|
||||
' <input id="linkCheckbox-{{cid}}" {{#if isLinkShare}}checked="checked"{{/if}} type="checkbox" name="linkCheckbox" class="linkCheckbox permissions checkbox">' +
|
||||
' <label for="linkCheckbox-{{cid}}">{{linkShareEnableLabel}}</label>' +
|
||||
' </span>' +
|
||||
' {{#if isLinkShare}}' +
|
||||
' <div class="share-menu" tabindex="0"><span class="icon icon-more"></span>' +
|
||||
' {{{popoverMenu}}}' +
|
||||
' </div>' +
|
||||
' {{/if}}' +
|
||||
' </span>' +
|
||||
' </li>' +
|
||||
'</ul>' +
|
||||
'{{else}}' +
|
||||
// FIXME: this doesn't belong in this view
|
||||
'{{#if noSharingPlaceholder}}<input id="shareWith-{{cid}}" class="shareWithField" type="text" placeholder="{{noSharingPlaceholder}}" disabled="disabled"/>{{/if}}' +
|
||||
'{{/if}}'
|
||||
;
|
||||
var TEMPLATE_POPOVER_MENU =
|
||||
'<div class="popovermenu bubble hidden menu socialSharingMenu">' +
|
||||
'<div class="popovermenu menu">' +
|
||||
'<ul>' +
|
||||
'<li>' +
|
||||
'<a href="#" class="shareOption menuitem clipboardButton" data-clipboard-target="#linkText-{{cid}}">' +
|
||||
'<a href="#" class="menuitem clipboardButton" data-clipboard-text="{{shareLinkURL}}">' +
|
||||
'<span class="icon icon-clippy" ></span>' +
|
||||
'<span>{{copyLabel}}</span>' +
|
||||
'</a>' +
|
||||
'</li>' +
|
||||
'<li class="hidden linkTextMenu">' +
|
||||
'<span class="menuitem icon-link-text">' +
|
||||
'<input id="linkText-{{cid}}" class="linkText" type="text" readonly="readonly" value="{{shareLinkURL}}" />' +
|
||||
'</span>' +
|
||||
'</li>' +
|
||||
'{{#if publicUpload}}' +
|
||||
'<li><span class="shareOption menuitem">' +
|
||||
'<span class="icon-loading-small hidden"></span>' +
|
||||
'<input type="radio" name="publicUpload" value="{{publicUploadRValue}}" id="sharingDialogAllowPublicUpload-r-{{cid}}" class="radio publicUploadRadio" {{{publicUploadRChecked}}} />' +
|
||||
'<label for="sharingDialogAllowPublicUpload-r-{{cid}}">{{publicUploadRLabel}}</label>' +
|
||||
'</span></li>' +
|
||||
'<li><span class="shareOption menuitem">' +
|
||||
'<span class="icon-loading-small hidden"></span>' +
|
||||
'<input type="radio" name="publicUpload" value="{{publicUploadRWValue}}" id="sharingDialogAllowPublicUpload-rw-{{cid}}" class="radio publicUploadRadio" {{{publicUploadRWChecked}}} />' +
|
||||
'<label for="sharingDialogAllowPublicUpload-rw-{{cid}}">{{publicUploadRWLabel}}</label>' +
|
||||
'</span></li>' +
|
||||
'<li><span class="shareOption menuitem">' +
|
||||
'<span class="icon-loading-small hidden"></span>' +
|
||||
'<input type="radio" name="publicUpload" value="{{publicUploadWValue}}" id="sharingDialogAllowPublicUpload-w-{{cid}}" class="radio publicUploadRadio" {{{publicUploadWChecked}}} />' +
|
||||
'<label for="sharingDialogAllowPublicUpload-w-{{cid}}">{{publicUploadWLabel}}</label>' +
|
||||
'</span></li>' +
|
||||
'{{/if}}' +
|
||||
'{{#if publicEditing}}' +
|
||||
' <li id="allowPublicEditingWrapper"><span class="shareOption menuitem">' +
|
||||
' <span class="icon-loading-small hidden"></span>' +
|
||||
' <input type="checkbox" name="allowPublicEditing" id="sharingDialogAllowPublicEditing-{{cid}}" class="checkbox publicEditingCheckbox" {{{publicEditingChecked}}} />' +
|
||||
' <label for="sharingDialogAllowPublicEditing-{{cid}}">{{publicEditingLabel}}</label>' +
|
||||
' </span></li>' +
|
||||
'{{/if}}' +
|
||||
'{{#if showPasswordCheckBox}}' +
|
||||
' <li><span class="shareOption menuitem">' +
|
||||
' <input type="checkbox" name="showPassword" id="showPassword-{{cid}}" class="checkbox showPasswordCheckbox" {{#if isPasswordSet}}checked="checked"{{/if}} value="1" />' +
|
||||
' <label for="showPassword-{{cid}}">{{enablePasswordLabel}}</label>' +
|
||||
' </span></li>' +
|
||||
' <li class="{{#unless isPasswordSet}}hidden{{/unless}} linkPassMenu"><span class="shareOption menuitem icon-share-pass">' +
|
||||
' <input id="linkPassText-{{cid}}" class="linkPassText" type="password" placeholder="{{passwordPlaceholder}}" autocomplete="new-password" />' +
|
||||
' <span class="icon icon-loading-small hidden"></span>' +
|
||||
' </span></li>' +
|
||||
'{{/if}}' +
|
||||
'<li>' +
|
||||
'<span class="shareOption menuitem">' +
|
||||
'<input id="expireDate-{{cid}}" type="checkbox" name="expirationDate" class="expireDate checkbox" {{#if hasExpireDate}}checked="checked"{{/if}}" />' +
|
||||
'<label for="expireDate-{{cid}}">{{expireDateLabel}}</label>' +
|
||||
'</li>' +
|
||||
'<li class="{{#unless hasExpireDate}}hidden{{/unless}}">' +
|
||||
'<span class="menuitem icon-expiredate expirationDateContainer-{{cid}}">' +
|
||||
' <label for="expirationDatePicker-{{cid}}" class="hidden-visually" value="{{expirationDate}}">{{expirationLabel}}</label>' +
|
||||
' <input id="expirationDatePicker-{{cid}}" class="datepicker" type="text" placeholder="{{expirationDatePlaceholder}}" value="{{#if hasExpireDate}}{{expireDate}}{{else}}{{defaultExpireDate}}{{/if}}" />' +
|
||||
'</span>' +
|
||||
'</span>' +
|
||||
'</li>' +
|
||||
'<li>' +
|
||||
'<a href="#" class="share-add"><span class="icon-loading-small hidden"></span>' +
|
||||
' <span class="icon icon-edit"></span>' +
|
||||
' <span>{{addNoteLabel}}</span>' +
|
||||
' <input type="button" class="share-note-delete icon-delete">' +
|
||||
'</a>' +
|
||||
'</li>' +
|
||||
'<li class="share-note-form share-note-link hidden">' +
|
||||
'<span class="menuitem icon-note">' +
|
||||
' <textarea class="share-note">{{shareNote}}</textarea>' +
|
||||
' <input type="submit" class="icon-confirm share-note-submit" value="" id="add-note-{{shareId}}" />' +
|
||||
'</span>' +
|
||||
'</li>' +
|
||||
'{{#each social}}' +
|
||||
'<li>' +
|
||||
'<a href="#" class="shareOption menuitem pop-up" data-url="{{url}}" data-window="{{newWindow}}">' +
|
||||
|
@ -131,9 +161,15 @@
|
|||
'click .linkText': 'onLinkTextClick',
|
||||
'change .publicEditingCheckbox': 'onAllowPublicEditingChange',
|
||||
'click .showPasswordCheckbox': 'onShowPasswordClick',
|
||||
'click .icon-more': 'onToggleMenu',
|
||||
'click .share-menu .icon-more': 'onToggleMenu',
|
||||
'click .pop-up': 'onPopUpClick',
|
||||
'change .publicUploadRadio': 'onPublicUploadChange'
|
||||
'change .publicUploadRadio': 'onPublicUploadChange',
|
||||
'click .expireDate' : 'onExpireDateChange',
|
||||
'change .datepicker': 'onChangeExpirationDate',
|
||||
'click .datepicker' : 'showDatePicker',
|
||||
'click .share-add': 'showNoteForm',
|
||||
'click .share-note-delete': 'deleteNote',
|
||||
'click .share-note-submit': 'updateNote'
|
||||
},
|
||||
|
||||
initialize: function(options) {
|
||||
|
@ -165,37 +201,26 @@
|
|||
throw 'missing OC.Share.ShareConfigModel';
|
||||
}
|
||||
|
||||
_.bindAll(
|
||||
this,
|
||||
'onLinkCheckBoxChange',
|
||||
'onPasswordEntered',
|
||||
'onPasswordKeyUp',
|
||||
'onLinkTextClick',
|
||||
'onShowPasswordClick',
|
||||
'onAllowPublicEditingChange',
|
||||
'onPublicUploadChange'
|
||||
);
|
||||
|
||||
var clipboard = new Clipboard('.clipboardButton');
|
||||
clipboard.on('success', function(e) {
|
||||
var $input = $(e.trigger);
|
||||
$input.tooltip('hide')
|
||||
var $menu = $(e.trigger);
|
||||
var $linkTextMenu = $menu.parent().next('li.linkTextMenu')
|
||||
|
||||
$menu.tooltip('hide')
|
||||
.attr('data-original-title', t('core', 'Copied!'))
|
||||
.tooltip('fixTitle')
|
||||
.tooltip({placement: 'bottom', trigger: 'manual'})
|
||||
.tooltip('show');
|
||||
_.delay(function() {
|
||||
$input.tooltip('hide');
|
||||
if (OC.Share.Social.Collection.size() == 0) {
|
||||
$input.attr('data-original-title', t('core', 'Copy'))
|
||||
.tooltip('fixTitle');
|
||||
} else {
|
||||
$input.tooltip("destroy");
|
||||
}
|
||||
$menu.tooltip('hide');
|
||||
$menu.tooltip('destroy');
|
||||
}, 3000);
|
||||
});
|
||||
clipboard.on('error', function (e) {
|
||||
var $input = $(e.trigger);
|
||||
var $menu = $(e.trigger);
|
||||
var $linkTextMenu = $menu.parent().next('li.linkTextMenu')
|
||||
var $input = $linkTextMenu.find('.linkText');
|
||||
|
||||
var actionMsg = '';
|
||||
if (/iPhone|iPad/i.test(navigator.userAgent)) {
|
||||
actionMsg = t('core', 'Not supported!');
|
||||
|
@ -205,6 +230,8 @@
|
|||
actionMsg = t('core', 'Press Ctrl-C to copy.');
|
||||
}
|
||||
|
||||
$linkTextMenu.removeClass('hidden');
|
||||
$input.select();
|
||||
$input.tooltip('hide')
|
||||
.attr('data-original-title', actionMsg)
|
||||
.tooltip('fixTitle')
|
||||
|
@ -212,15 +239,10 @@
|
|||
.tooltip('show');
|
||||
_.delay(function () {
|
||||
$input.tooltip('hide');
|
||||
if (OC.Share.Social.Collection.size() == 0) {
|
||||
$input.attr('data-original-title', t('core', 'Copy'))
|
||||
.tooltip('fixTitle');
|
||||
} else {
|
||||
$input.tooltip("destroy");
|
||||
}
|
||||
$input.attr('data-original-title', t('core', 'Copy'))
|
||||
.tooltip('fixTitle');
|
||||
}, 3000);
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
onLinkCheckBoxChange: function() {
|
||||
|
@ -257,6 +279,7 @@
|
|||
|
||||
onShowPasswordClick: function() {
|
||||
this.$el.find('.linkPass').slideToggle(OC.menuSpeed);
|
||||
this.$el.find('.linkPassMenu').toggleClass('hidden');
|
||||
if(!this.$el.find('.showPasswordCheckbox').is(':checked')) {
|
||||
this.model.saveLinkShare({
|
||||
password: ''
|
||||
|
@ -275,7 +298,7 @@
|
|||
},
|
||||
|
||||
onPasswordEntered: function() {
|
||||
var $loading = this.$el.find('.linkPass .icon-loading-small');
|
||||
var $loading = this.$el.find('.linkPassMenu .icon-loading-small');
|
||||
if (!$loading.hasClass('hidden')) {
|
||||
// still in process
|
||||
return;
|
||||
|
@ -334,12 +357,95 @@
|
|||
},
|
||||
|
||||
|
||||
onPublicUploadChange: function(e) {
|
||||
onPublicUploadChange: function(e) {
|
||||
var permissions = e.currentTarget.value;
|
||||
this.model.saveLinkShare({
|
||||
permissions: permissions
|
||||
});
|
||||
},
|
||||
|
||||
showNoteForm: function(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
var self = this;
|
||||
var $element = $(event.target);
|
||||
var $li = $element.closest('li[data-share-id]');
|
||||
var $menu = $element.closest('li');
|
||||
var $form = $menu.next('li.share-note-form');
|
||||
|
||||
// show elements
|
||||
$menu.find('.share-note-delete').toggle();
|
||||
$form.toggleClass('hidden');
|
||||
$form.find('textarea').focus();
|
||||
},
|
||||
|
||||
deleteNote: function(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
var self = this;
|
||||
var $element = $(event.target);
|
||||
var $li = $element.closest('li[data-share-id]');
|
||||
var shareId = $li.data('share-id');
|
||||
var $menu = $element.closest('li');
|
||||
var $form = $menu.next('li.share-note-form');
|
||||
|
||||
console.log($form.find('.share-note'));
|
||||
$form.find('.share-note').val('');
|
||||
|
||||
$form.addClass('hidden');
|
||||
$menu.find('.share-note-delete').hide();
|
||||
|
||||
self.sendNote('', shareId, $menu);
|
||||
},
|
||||
|
||||
updateNote: function(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
var self = this;
|
||||
var $element = $(event.target);
|
||||
var $li = $element.closest('li[data-share-id]');
|
||||
var shareId = $li.data('share-id');
|
||||
var $form = $element.closest('li.share-note-form');
|
||||
var $menu = $form.prev('li');
|
||||
var message = $form.find('.share-note').val().trim();
|
||||
|
||||
if (message.length < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.sendNote(message, shareId, $menu);
|
||||
},
|
||||
|
||||
sendNote: function(note, shareId, $menu) {
|
||||
var $form = $menu.next('li.share-note-form');
|
||||
var $submit = $form.find('input.share-note-submit');
|
||||
var $error = $form.find('input.share-note-error');
|
||||
|
||||
$submit.prop('disabled', true);
|
||||
$menu.find('.icon-loading-small').removeClass('hidden');
|
||||
$menu.find('.icon-edit').hide();
|
||||
|
||||
var complete = function() {
|
||||
$submit.prop('disabled', false);
|
||||
$menu.find('.icon-loading-small').addClass('hidden');
|
||||
$menu.find('.icon-edit').show();
|
||||
};
|
||||
var error = function() {
|
||||
$error.show();
|
||||
setTimeout(function() {
|
||||
$error.hide();
|
||||
}, 3000);
|
||||
};
|
||||
|
||||
// send data
|
||||
$.ajax({
|
||||
method: 'PUT',
|
||||
url: OC.linkToOCS('apps/files_sharing/api/v1/shares',2) + shareId + '?' + OC.buildQueryString({format: 'json'}),
|
||||
data: { note: note },
|
||||
complete : complete,
|
||||
error: error
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var linkShareTemplate = this.template();
|
||||
|
@ -412,18 +518,48 @@
|
|||
});
|
||||
});
|
||||
|
||||
var popover = this.popoverMenuTemplate({
|
||||
cid: this.cid,
|
||||
copyLabel: t('core', 'Copy'),
|
||||
social: social
|
||||
var defaultExpireDays = this.configModel.get('defaultExpireDate');
|
||||
var isExpirationEnforced = this.configModel.get('isDefaultExpireDateEnforced');
|
||||
var hasExpireDate = !!this.model.get('linkShare').expiration || isExpirationEnforced;
|
||||
|
||||
var expireDate;
|
||||
if (hasExpireDate) {
|
||||
expireDate = moment(this.model.get('linkShare').expiration, 'YYYY-MM-DD').format('DD-MM-YYYY');
|
||||
}
|
||||
|
||||
// what if there is another date picker on that page?
|
||||
var minDate = new Date();
|
||||
var maxDate = null;
|
||||
// min date should always be the next day
|
||||
minDate.setDate(minDate.getDate()+1);
|
||||
|
||||
if(hasExpireDate) {
|
||||
if(isExpirationEnforced) {
|
||||
// TODO: hack: backend returns string instead of integer
|
||||
var shareTime = this.model.get('linkShare').stime;
|
||||
if (_.isNumber(shareTime)) {
|
||||
shareTime = new Date(shareTime * 1000);
|
||||
}
|
||||
if (!shareTime) {
|
||||
shareTime = new Date(); // now
|
||||
}
|
||||
shareTime = OC.Util.stripTime(shareTime).getTime();
|
||||
maxDate = new Date(shareTime + defaultExpireDays * 24 * 3600 * 1000);
|
||||
}
|
||||
}
|
||||
$.datepicker.setDefaults({
|
||||
minDate: minDate,
|
||||
maxDate: maxDate
|
||||
});
|
||||
|
||||
this.$el.html(linkShareTemplate({
|
||||
cid: this.cid,
|
||||
shareAllowed: true,
|
||||
isLinkShare: isLinkShare,
|
||||
this.$el.find('.datepicker').datepicker({dateFormat : 'dd-mm-yy'});
|
||||
|
||||
var popover = this.popoverMenuTemplate({
|
||||
cid: this.model.get('linkShare').id,
|
||||
copyLabel: t('core', 'Copy URL'),
|
||||
social: social,
|
||||
|
||||
shareLinkURL: this.model.get('linkShare').link,
|
||||
linkShareLabel: t('core', 'Share link'),
|
||||
urlLabel: t('core', 'Link'),
|
||||
enablePasswordLabel: t('core', 'Password protect'),
|
||||
passwordLabel: t('core', 'Password'),
|
||||
|
@ -437,8 +573,6 @@
|
|||
publicEditingLabel: t('core', 'Allow editing'),
|
||||
mailPrivatePlaceholder: t('core', 'Email link to person'),
|
||||
mailButtonText: t('core', 'Send'),
|
||||
singleAction: OC.Share.Social.Collection.size() == 0,
|
||||
popoverMenu: popover,
|
||||
publicUploadRWLabel: t('core', 'Allow upload and editing'),
|
||||
publicUploadRLabel: t('core', 'Read only'),
|
||||
publicUploadWLabel: t('core', 'File drop (upload only)'),
|
||||
|
@ -447,19 +581,32 @@
|
|||
publicUploadWValue: OC.PERMISSION_CREATE,
|
||||
publicUploadRWChecked: publicUploadRWChecked,
|
||||
publicUploadRChecked: publicUploadRChecked,
|
||||
publicUploadWChecked: publicUploadWChecked
|
||||
publicUploadWChecked: publicUploadWChecked,
|
||||
expireDateLabel: t('core', 'Set expiration date'),
|
||||
expirationLabel: t('core', 'Expiration'),
|
||||
expirationDatePlaceholder: t('core', 'Expiration date'),
|
||||
hasExpireDate: hasExpireDate,
|
||||
isExpirationEnforced: isExpirationEnforced,
|
||||
expireDate: expireDate,
|
||||
defaultExpireDate: moment().add(1, 'day').format('DD-MM-YYYY'), // Can't expire today
|
||||
shareNote: this.model.get('linkShare').note,
|
||||
addNoteLabel: t('core', 'Set share note'),
|
||||
});
|
||||
|
||||
this.$el.html(linkShareTemplate({
|
||||
cid: this.model.get('linkShare').id,
|
||||
shareAllowed: true,
|
||||
isLinkShare: isLinkShare,
|
||||
linkShareLabel: t('core', 'Share link'),
|
||||
linkShareEnableLabel: t('core', 'Enable'),
|
||||
popoverMenu: popover,
|
||||
}));
|
||||
|
||||
if (OC.Share.Social.Collection.size() == 0) {
|
||||
this.$el.find('.clipboardButton').tooltip({
|
||||
placement: 'bottom',
|
||||
title: t('core', 'Copy'),
|
||||
trigger: 'hover'
|
||||
});
|
||||
}
|
||||
|
||||
this.delegateEvents();
|
||||
|
||||
// new note autosize
|
||||
autosize(this.$el.find('.share-note-form .share-note'));
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
|
@ -467,8 +614,8 @@
|
|||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
var $element = $(event.target);
|
||||
var $li = $element.closest('.oneline');
|
||||
var $menu = $li.find('.popovermenu');
|
||||
var $li = $element.closest('li[data-share-id]');
|
||||
var $menu = $li.find('.sharingOptionsGroup .popovermenu');
|
||||
|
||||
OC.showMenu(null, $menu);
|
||||
this._menuOpen = $li.data('share-id');
|
||||
|
@ -517,7 +664,52 @@
|
|||
window.location.href = url;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onExpireDateChange: function(event) {
|
||||
var $element = $(event.target);
|
||||
var li = $element.closest('li[data-share-id]');
|
||||
var shareId = li.data('share-id');
|
||||
var expirationDatePicker = '#expirationDateContainer-' + shareId;
|
||||
var datePicker = $(expirationDatePicker);
|
||||
var state = $element.prop('checked');
|
||||
datePicker.toggleClass('hidden', !state);
|
||||
|
||||
if (!state) {
|
||||
// disabled, let's hide the input and
|
||||
// set the expireDate to nothing
|
||||
$element.closest('li').next('li').addClass('hidden');
|
||||
this.setExpirationDate('');
|
||||
} else {
|
||||
// enabled, show the input and the datepicker
|
||||
$element.closest('li').next('li').removeClass('hidden');
|
||||
this.showDatePicker(event);
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
showDatePicker: function(event) {
|
||||
var $element = $(event.target);
|
||||
var li = $element.closest('li[data-share-id]');
|
||||
var shareId = li.data('share-id');
|
||||
var expirationDatePicker = '#expirationDatePicker-' + shareId;
|
||||
var self = this;
|
||||
|
||||
$(expirationDatePicker).datepicker({
|
||||
dateFormat : 'dd-mm-yy',
|
||||
onSelect: function (expireDate) {
|
||||
self.setExpirationDate(expireDate);
|
||||
}
|
||||
});
|
||||
console.log(event, $(expirationDatePicker));
|
||||
$(expirationDatePicker).datepicker('show');
|
||||
$(expirationDatePicker).focus();
|
||||
|
||||
},
|
||||
|
||||
setExpirationDate: function(expireDate) {
|
||||
this.model.saveLinkShare({expireDate: expireDate});
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
|
|
|
@ -19,7 +19,8 @@
|
|||
'<span class="reshare">' +
|
||||
' <div class="avatar" data-userName="{{reshareOwner}}"></div>' +
|
||||
' {{sharedByText}}' +
|
||||
'</span><br/>'
|
||||
'</span>' +
|
||||
'{{#if hasShareNote}}<div class="share-note">{{shareNote}}</div>{{/if}}'
|
||||
;
|
||||
|
||||
/**
|
||||
|
@ -72,7 +73,10 @@
|
|||
|
||||
var reshareTemplate = this.template();
|
||||
var ownerDisplayName = this.model.getReshareOwnerDisplayname();
|
||||
var shareNote = this.model.getReshareNote();
|
||||
|
||||
var sharedByText = '';
|
||||
|
||||
if (this.model.getReshareType() === OC.Share.SHARE_TYPE_GROUP) {
|
||||
sharedByText = t(
|
||||
'core',
|
||||
|
@ -105,9 +109,13 @@
|
|||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
this.$el.html(reshareTemplate({
|
||||
reshareOwner: this.model.getReshareOwner(),
|
||||
sharedByText: sharedByText
|
||||
sharedByText: sharedByText,
|
||||
shareNote: shareNote,
|
||||
hasShareNote: shareNote !== ''
|
||||
}));
|
||||
|
||||
this.$el.find('.avatar').each(function() {
|
||||
|
|
|
@ -34,8 +34,9 @@
|
|||
'<label for="canEdit-{{cid}}-{{shareId}}">{{canEditLabel}}</label>' +
|
||||
'</span>' +
|
||||
'{{/if}}' +
|
||||
'<a href="#"><span class="icon icon-more"></span></a>' +
|
||||
'{{{popoverMenu}}}' +
|
||||
'<div tabindex="0" class="share-menu"><span class="icon icon-more"></span>' +
|
||||
'{{{popoverMenu}}}' +
|
||||
'</div>' +
|
||||
'</span>' +
|
||||
'</li>' +
|
||||
'{{/each}}' +
|
||||
|
@ -102,11 +103,13 @@
|
|||
'<span class="shareOption menuitem">' +
|
||||
'<input id="password-{{cid}}-{{shareId}}" type="checkbox" name="password" class="password checkbox" {{#if isPasswordSet}}checked="checked"{{/if}}{{#if isPasswordSet}}{{#if isPasswordForMailSharesRequired}}disabled=""{{/if}}{{/if}}" />' +
|
||||
'<label for="password-{{cid}}-{{shareId}}">{{passwordLabel}}</label>' +
|
||||
'<div class="passwordContainer-{{cid}}-{{shareId}} {{#unless isPasswordSet}}hidden{{/unless}}">' +
|
||||
' <label for="passwordField-{{cid}}-{{shareId}}" class="hidden-visually" value="{{password}}">{{passwordLabel}}</label>' +
|
||||
' <input id="passwordField-{{cid}}-{{shareId}}" class="passwordField" type="password" placeholder="{{passwordPlaceholder}}" value="{{passwordValue}}" autocomplete="new-password" />' +
|
||||
' <span class="icon-loading-small hidden"></span>' +
|
||||
'</div>' +
|
||||
'</span>' +
|
||||
'</li>' +
|
||||
'<li class="passwordMenu-{{cid}}-{{shareId}} {{#unless isPasswordSet}}hidden{{/unless}}">' +
|
||||
'<span class="passwordContainer-{{cid}}-{{shareId}} icon-passwordmail menuitem">' +
|
||||
' <label for="passwordField-{{cid}}-{{shareId}}" class="hidden-visually" value="{{password}}">{{passwordLabel}}</label>' +
|
||||
' <input id="passwordField-{{cid}}-{{shareId}}" class="passwordField" type="password" placeholder="{{passwordPlaceholder}}" value="{{passwordValue}}" autocomplete="new-password" />' +
|
||||
' <span class="icon-loading-small hidden"></span>' +
|
||||
'</span>' +
|
||||
'</li>' +
|
||||
'{{/if}}' +
|
||||
|
@ -114,12 +117,29 @@
|
|||
'<span class="shareOption menuitem">' +
|
||||
'<input id="expireDate-{{cid}}-{{shareId}}" type="checkbox" name="expirationDate" class="expireDate checkbox" {{#if hasExpireDate}}checked="checked"{{/if}}" />' +
|
||||
'<label for="expireDate-{{cid}}-{{shareId}}">{{expireDateLabel}}</label>' +
|
||||
'<div class="expirationDateContainer-{{cid}}-{{shareId}} {{#unless hasExpireDate}}hidden{{/unless}}">' +
|
||||
' <label for="expirationDatePicker-{{cid}}-{{shareId}}" class="hidden-visually" value="{{expirationDate}}">{{expirationLabel}}</label>' +
|
||||
' <input id="expirationDatePicker-{{cid}}-{{shareId}}" class="datepicker" type="text" placeholder="{{expirationDatePlaceholder}}" value="{{#if hasExpireDate}}{{expireDate}}{{else}}{{defaultExpireDate}}{{/if}}" />' +
|
||||
'</div>' +
|
||||
'</span>' +
|
||||
'</li>' +
|
||||
'<li class="expirationDateMenu-{{cid}}-{{shareId}} {{#unless hasExpireDate}}hidden{{/unless}}">' +
|
||||
'<span class="expirationDateContainer-{{cid}}-{{shareId}} icon-expiredate menuitem">' +
|
||||
' <label for="expirationDatePicker-{{cid}}-{{shareId}}" class="hidden-visually" value="{{expirationDate}}">{{expirationLabel}}</label>' +
|
||||
' <input id="expirationDatePicker-{{cid}}-{{shareId}}" class="datepicker" type="text" placeholder="{{expirationDatePlaceholder}}" value="{{#if hasExpireDate}}{{expireDate}}{{else}}{{defaultExpireDate}}{{/if}}" />' +
|
||||
'</span>' +
|
||||
'</li>' +
|
||||
'{{#if isNoteAvailable}}' +
|
||||
'<li>' +
|
||||
'<a href="#" class="share-add"><span class="icon-loading-small hidden"></span>' +
|
||||
' <span class="icon icon-edit"></span>' +
|
||||
' <span>{{addNoteLabel}}</span>' +
|
||||
' <input type="button" class="share-note-delete icon-delete">' +
|
||||
'</a>' +
|
||||
'</li>' +
|
||||
'<li class="share-note-form hidden">' +
|
||||
'<span class="menuitem icon-note">' +
|
||||
' <textarea class="share-note">{{shareNote}}</textarea>' +
|
||||
' <input type="submit" class="icon-confirm share-note-submit" value="" id="add-note-{{shareId}}" />' +
|
||||
'</span>' +
|
||||
'</li>' +
|
||||
'{{/if}}' +
|
||||
'<li>' +
|
||||
'<a href="#" class="unshare"><span class="icon-loading-small hidden"></span><span class="icon icon-delete"></span><span>{{unshareLabel}}</span></a>' +
|
||||
'</li>' +
|
||||
|
@ -156,7 +176,10 @@
|
|||
|
||||
events: {
|
||||
'click .unshare': 'onUnshare',
|
||||
'click .icon-more': 'onToggleMenu',
|
||||
'click .share-add': 'showNoteForm',
|
||||
'click .share-note-delete': 'deleteNote',
|
||||
'click .share-note-submit': 'updateNote',
|
||||
'click .share-menu .icon-more': 'onToggleMenu',
|
||||
'click .permissions': 'onPermissionChange',
|
||||
'click .expireDate' : 'onExpireDateChange',
|
||||
'click .password' : 'onMailSharePasswordProtectChange',
|
||||
|
@ -255,12 +278,14 @@
|
|||
modSeed: shareType !== OC.Share.SHARE_TYPE_USER && (shareType !== OC.Share.SHARE_TYPE_CIRCLE || shareWithAvatar),
|
||||
isRemoteShare: shareType === OC.Share.SHARE_TYPE_REMOTE,
|
||||
isRemoteGroupShare: shareType === OC.Share.SHARE_TYPE_REMOTE_GROUP,
|
||||
isNoteAvailable: shareType !== OC.Share.SHARE_TYPE_REMOTE && shareType !== OC.Share.SHARE_TYPE_REMOTE_GROUP,
|
||||
isMailShare: shareType === OC.Share.SHARE_TYPE_EMAIL,
|
||||
isCircleShare: shareType === OC.Share.SHARE_TYPE_CIRCLE,
|
||||
isFileSharedByMail: shareType === OC.Share.SHARE_TYPE_EMAIL && !this.model.isFolder(),
|
||||
isPasswordSet: hasPassword,
|
||||
secureDropMode: !this.model.hasReadPermission(shareIndex),
|
||||
hasExpireDate: this.model.getExpireDate(shareIndex) !== null,
|
||||
shareNote: this.model.getNote(shareIndex),
|
||||
expireDate: moment(this.model.getExpireDate(shareIndex), 'YYYY-MM-DD').format('DD-MM-YYYY'),
|
||||
passwordPlaceholder: hasPassword ? PASSWORD_PLACEHOLDER : PASSWORD_PLACEHOLDER_MESSAGE,
|
||||
});
|
||||
|
@ -269,6 +294,7 @@
|
|||
getShareProperties: function() {
|
||||
return {
|
||||
unshareLabel: t('core', 'Unshare'),
|
||||
addNoteLabel: t('core', 'Set share note'),
|
||||
canShareLabel: t('core', 'Can reshare'),
|
||||
canEditLabel: t('core', 'Can edit'),
|
||||
createPermissionLabel: t('core', 'Can create'),
|
||||
|
@ -435,6 +461,9 @@
|
|||
|
||||
this._renderPermissionChange = false;
|
||||
|
||||
// new note autosize
|
||||
autosize(this.$el.find('.share-note-form .share-note'));
|
||||
|
||||
this.delegateEvents();
|
||||
|
||||
return this;
|
||||
|
@ -470,6 +499,88 @@
|
|||
return this._popoverMenuTemplate(data);
|
||||
},
|
||||
|
||||
showNoteForm: function(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
var $element = $(event.target);
|
||||
var $menu = $element.closest('li');
|
||||
var $form = $menu.next('li.share-note-form');
|
||||
|
||||
// show elements
|
||||
$menu.find('.share-note-delete').toggle();
|
||||
$form.toggleClass('hidden');
|
||||
$form.find('textarea').focus();
|
||||
},
|
||||
|
||||
deleteNote: function(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
var self = this;
|
||||
var $element = $(event.target);
|
||||
var $li = $element.closest('li[data-share-id]');
|
||||
var shareId = $li.data('share-id');
|
||||
var $menu = $element.closest('li');
|
||||
var $form = $menu.next('li.share-note-form');
|
||||
|
||||
console.log($form.find('.share-note'));
|
||||
$form.find('.share-note').val('');
|
||||
|
||||
$form.addClass('hidden');
|
||||
$menu.find('.share-note-delete').hide();
|
||||
|
||||
self.sendNote('', shareId, $menu);
|
||||
},
|
||||
|
||||
updateNote: function(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
var self = this;
|
||||
var $element = $(event.target);
|
||||
var $li = $element.closest('li[data-share-id]');
|
||||
var shareId = $li.data('share-id');
|
||||
var $form = $element.closest('li.share-note-form');
|
||||
var $menu = $form.prev('li');
|
||||
var message = $form.find('.share-note').val().trim();
|
||||
|
||||
if (message.length < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.sendNote(message, shareId, $menu);
|
||||
|
||||
},
|
||||
|
||||
sendNote: function(note, shareId, $menu) {
|
||||
var $form = $menu.next('li.share-note-form');
|
||||
var $submit = $form.find('input.share-note-submit');
|
||||
var $error = $form.find('input.share-note-error');
|
||||
|
||||
$submit.prop('disabled', true);
|
||||
$menu.find('.icon-loading-small').removeClass('hidden');
|
||||
$menu.find('.icon-edit').hide();
|
||||
|
||||
var complete = function() {
|
||||
$submit.prop('disabled', false);
|
||||
$menu.find('.icon-loading-small').addClass('hidden');
|
||||
$menu.find('.icon-edit').show();
|
||||
};
|
||||
var error = function() {
|
||||
$error.show();
|
||||
setTimeout(function() {
|
||||
$error.hide();
|
||||
}, 3000);
|
||||
};
|
||||
|
||||
// send data
|
||||
$.ajax({
|
||||
method: 'PUT',
|
||||
url: OC.linkToOCS('apps/files_sharing/api/v1/shares',2) + shareId + '?' + OC.buildQueryString({format: 'json'}),
|
||||
data: { note: note },
|
||||
complete : complete,
|
||||
error: error
|
||||
});
|
||||
},
|
||||
|
||||
onUnshare: function(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
@ -513,16 +624,21 @@
|
|||
},
|
||||
|
||||
onExpireDateChange: function(event) {
|
||||
var element = $(event.target);
|
||||
var li = element.closest('li[data-share-id]');
|
||||
var $element = $(event.target);
|
||||
var li = $element.closest('li[data-share-id]');
|
||||
var shareId = li.data('share-id');
|
||||
var datePickerClass = '.expirationDateContainer-' + this.cid + '-' + shareId;
|
||||
var datePicker = $(datePickerClass);
|
||||
var state = element.prop('checked');
|
||||
var state = $element.prop('checked');
|
||||
datePicker.toggleClass('hidden', !state);
|
||||
if (!state) {
|
||||
// disabled, let's hide the input and
|
||||
// set the expireDate to nothing
|
||||
$element.closest('li').next('li').addClass('hidden');
|
||||
this.setExpirationDate(shareId, '');
|
||||
} else {
|
||||
// enabled, show the input and the datepicker
|
||||
$element.closest('li').next('li').removeClass('hidden');
|
||||
this.showDatePicker(event);
|
||||
|
||||
}
|
||||
|
@ -552,7 +668,7 @@
|
|||
var element = $(event.target);
|
||||
var li = element.closest('li[data-share-id]');
|
||||
var shareId = li.data('share-id');
|
||||
var passwordContainerClass = '.passwordContainer-' + this.cid + '-' + shareId;
|
||||
var passwordContainerClass = '.passwordMenu-' + this.cid + '-' + shareId;
|
||||
var passwordContainer = $(passwordContainerClass);
|
||||
var loading = this.$el.find(passwordContainerClass + ' .icon-loading-small');
|
||||
var inputClass = '#passwordField-' + this.cid + '-' + shareId;
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
'{{/if}}' +
|
||||
'<div class="shareeListView subView"></div>' +
|
||||
'<div class="linkShareView subView"></div>' +
|
||||
'<div class="expirationView subView"></div>' +
|
||||
'<div class="loading hidden" style="height: 50px"></div>';
|
||||
|
||||
/**
|
||||
|
@ -59,9 +58,6 @@
|
|||
/** @type {object} **/
|
||||
linkShareView: undefined,
|
||||
|
||||
/** @type {object} **/
|
||||
expirationView: undefined,
|
||||
|
||||
/** @type {object} **/
|
||||
shareeListView: undefined,
|
||||
|
||||
|
@ -111,7 +107,6 @@
|
|||
var subViews = {
|
||||
resharerInfoView: 'ShareDialogResharerInfoView',
|
||||
linkShareView: 'ShareDialogLinkShareView',
|
||||
expirationView: 'ShareDialogExpirationView',
|
||||
shareeListView: 'ShareDialogShareeListView'
|
||||
};
|
||||
|
||||
|
@ -671,9 +666,6 @@
|
|||
this.linkShareView.$el = this.$el.find('.linkShareView');
|
||||
this.linkShareView.render();
|
||||
|
||||
this.expirationView.$el = this.$el.find('.expirationView');
|
||||
this.expirationView.render();
|
||||
|
||||
this.shareeListView.$el = this.$el.find('.shareeListView');
|
||||
this.shareeListView.render();
|
||||
|
||||
|
|
|
@ -340,6 +340,13 @@
|
|||
return this.get('reshare').displayname_owner;
|
||||
},
|
||||
|
||||
/**
|
||||
* @returns {string}
|
||||
*/
|
||||
getReshareNote: function() {
|
||||
return this.get('reshare').note;
|
||||
},
|
||||
|
||||
/**
|
||||
* @returns {string}
|
||||
*/
|
||||
|
@ -366,6 +373,10 @@
|
|||
return this._shareExpireDate(shareIndex);
|
||||
},
|
||||
|
||||
getNote: function(shareIndex) {
|
||||
return this._shareNote(shareIndex);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns all share entries that only apply to the current item
|
||||
* (file/folder)
|
||||
|
@ -502,6 +513,15 @@
|
|||
return date2;
|
||||
},
|
||||
|
||||
|
||||
_shareNote: function(shareIndex) {
|
||||
var share = this.get('shares')[shareIndex];
|
||||
if(!_.isObject(share)) {
|
||||
throw "Unknown Share";
|
||||
}
|
||||
return share.note;
|
||||
},
|
||||
|
||||
/**
|
||||
* @return {int}
|
||||
*/
|
||||
|
|
|
@ -92,7 +92,7 @@ describe('OC.Share.ShareDialogLinkShareView', function () {
|
|||
|
||||
var $passwordDiv = view.$el.find('#linkPass');
|
||||
$passwordText = view.$el.find('.linkPassText');
|
||||
$workingIcon = view.$el.find('.linkPass .icon-loading-small');
|
||||
$workingIcon = view.$el.find('.linkPassMenu .icon-loading-small');
|
||||
|
||||
sinon.stub(shareModel, 'saveLinkShare');
|
||||
|
||||
|
|
|
@ -214,188 +214,6 @@ describe('OC.Share.ShareDialogView', function() {
|
|||
focusStub.restore();
|
||||
selectStub.restore();
|
||||
});
|
||||
describe('password', function() {
|
||||
var slideToggleStub;
|
||||
|
||||
beforeEach(function() {
|
||||
$('#allowShareWithLink').val('yes');
|
||||
configModel.set({
|
||||
enforcePasswordForPublicLink: false
|
||||
});
|
||||
|
||||
slideToggleStub = sinon.stub($.fn, 'slideToggle');
|
||||
});
|
||||
afterEach(function() {
|
||||
slideToggleStub.restore();
|
||||
});
|
||||
|
||||
it('enforced but toggled does not fire request', function() {
|
||||
configModel.set('enforcePasswordForPublicLink', true);
|
||||
dialog.render();
|
||||
|
||||
dialog.$el.find('.linkCheckbox').click();
|
||||
|
||||
// The password linkPass field is shown (slideToggle is called).
|
||||
// No request is made yet
|
||||
expect(slideToggleStub.callCount).toEqual(1);
|
||||
expect(slideToggleStub.getCall(0).thisValue.eq(0).attr('id')).toEqual('linkPass');
|
||||
expect(fakeServer.requests.length).toEqual(0);
|
||||
|
||||
// Now untoggle share by link
|
||||
dialog.$el.find('.linkCheckbox').click();
|
||||
dialog.render();
|
||||
|
||||
// Password field disappears and no ajax requests have been made
|
||||
expect(fakeServer.requests.length).toEqual(0);
|
||||
expect(slideToggleStub.callCount).toEqual(2);
|
||||
expect(slideToggleStub.getCall(1).thisValue.eq(0).attr('id')).toEqual('linkPass');
|
||||
});
|
||||
});
|
||||
describe('expiration date', function() {
|
||||
var shareData;
|
||||
var shareItem;
|
||||
var clock;
|
||||
var expectedMinDate;
|
||||
|
||||
beforeEach(function() {
|
||||
// pick a fake date
|
||||
clock = sinon.useFakeTimers(new Date(2014, 0, 20, 14, 0, 0).getTime());
|
||||
expectedMinDate = new Date(2014, 0, 21, 14, 0, 0);
|
||||
|
||||
configModel.set({
|
||||
enforcePasswordForPublicLink: false,
|
||||
isDefaultExpireDateEnabled: false,
|
||||
isDefaultExpireDateEnforced: false,
|
||||
defaultExpireDate: 7
|
||||
});
|
||||
|
||||
shareModel.set('linkShare', {
|
||||
isLinkShare: true,
|
||||
token: 'tehtoken',
|
||||
permissions: OC.PERMISSION_READ,
|
||||
expiration: null
|
||||
});
|
||||
});
|
||||
afterEach(function() {
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
it('does not check expiration date checkbox when no date was set', function() {
|
||||
shareModel.get('linkShare').expiration = null;
|
||||
dialog.render();
|
||||
|
||||
expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(false);
|
||||
expect(dialog.$el.find('.datepicker').val()).toEqual('');
|
||||
});
|
||||
it('does not check expiration date checkbox for new share', function() {
|
||||
dialog.render();
|
||||
|
||||
expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(false);
|
||||
expect(dialog.$el.find('.datepicker').val()).toEqual('');
|
||||
});
|
||||
it('checks expiration date checkbox and populates field when expiration date was set', function() {
|
||||
shareModel.get('linkShare').expiration = '2014-02-01 00:00:00';
|
||||
dialog.render();
|
||||
expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(true);
|
||||
expect(dialog.$el.find('.datepicker').val()).toEqual('01-02-2014');
|
||||
});
|
||||
it('sets default date when default date setting is enabled', function() {
|
||||
configModel.set('isDefaultExpireDateEnabled', true);
|
||||
dialog.render();
|
||||
dialog.$el.find('.linkCheckbox').click();
|
||||
// here fetch would be called and the server returns the expiration date
|
||||
shareModel.get('linkShare').expiration = '2014-1-27 00:00:00';
|
||||
dialog.render();
|
||||
|
||||
// enabled by default
|
||||
expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(true);
|
||||
expect(dialog.$el.find('.datepicker').val()).toEqual('27-01-2014');
|
||||
|
||||
// disabling is allowed
|
||||
dialog.$el.find('[name=expirationCheckbox]').click();
|
||||
expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(false);
|
||||
});
|
||||
it('enforces default date when enforced date setting is enabled', function() {
|
||||
configModel.set({
|
||||
isDefaultExpireDateEnabled: true,
|
||||
isDefaultExpireDateEnforced: true
|
||||
});
|
||||
dialog.render();
|
||||
dialog.$el.find('.linkCheckbox').click();
|
||||
// here fetch would be called and the server returns the expiration date
|
||||
shareModel.get('linkShare').expiration = '2014-1-27 00:00:00';
|
||||
dialog.render();
|
||||
|
||||
expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(true);
|
||||
expect(dialog.$el.find('.datepicker').val()).toEqual('27-01-2014');
|
||||
|
||||
// disabling is not allowed
|
||||
expect(dialog.$el.find('[name=expirationCheckbox]').prop('disabled')).toEqual(true);
|
||||
dialog.$el.find('[name=expirationCheckbox]').click();
|
||||
expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(true);
|
||||
});
|
||||
it('enforces default date when enforced date setting is enabled and password is enforced', function() {
|
||||
configModel.set({
|
||||
enforcePasswordForPublicLink: true,
|
||||
isDefaultExpireDateEnabled: true,
|
||||
isDefaultExpireDateEnforced: true
|
||||
});
|
||||
dialog.render();
|
||||
dialog.$el.find('.linkCheckbox').click();
|
||||
// here fetch would be called and the server returns the expiration date
|
||||
shareModel.get('linkShare').expiration = '2014-1-27 00:00:00';
|
||||
dialog.render();
|
||||
|
||||
//Enter password
|
||||
dialog.$el.find('.linkPassText').val('foo');
|
||||
dialog.$el.find('.linkPassText').trigger(new $.Event('keyup', {keyCode: 13}));
|
||||
fakeServer.requests[0].respond(
|
||||
200,
|
||||
{ 'Content-Type': 'application/json' },
|
||||
JSON.stringify({data: {token: 'xyz'}, status: 'success'})
|
||||
);
|
||||
|
||||
expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(true);
|
||||
expect(dialog.$el.find('.datepicker').val()).toEqual('27-01-2014');
|
||||
|
||||
// disabling is not allowed
|
||||
expect(dialog.$el.find('[name=expirationCheckbox]').prop('disabled')).toEqual(true);
|
||||
dialog.$el.find('[name=expirationCheckbox]').click();
|
||||
expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(true);
|
||||
});
|
||||
it('sets picker minDate to today and no maxDate by default', function() {
|
||||
dialog.render();
|
||||
dialog.$el.find('.linkCheckbox').click();
|
||||
dialog.$el.find('[name=expirationCheckbox]').click();
|
||||
expect($.datepicker._defaults.minDate).toEqual(expectedMinDate);
|
||||
expect($.datepicker._defaults.maxDate).toEqual(null);
|
||||
});
|
||||
it('limits the date range to X days after share time when enforced', function() {
|
||||
configModel.set({
|
||||
isDefaultExpireDateEnabled: true,
|
||||
isDefaultExpireDateEnforced: true
|
||||
});
|
||||
dialog.render();
|
||||
dialog.$el.find('.linkCheckbox').click();
|
||||
expect($.datepicker._defaults.minDate).toEqual(expectedMinDate);
|
||||
expect($.datepicker._defaults.maxDate).toEqual(new Date(2014, 0, 27, 0, 0, 0, 0));
|
||||
});
|
||||
it('limits the date range to X days after share time when enforced, even when redisplayed the next days', function() {
|
||||
// item exists, was created two days ago
|
||||
var shareItem = shareModel.get('linkShare');
|
||||
shareItem.expiration = '2014-1-27';
|
||||
// share time has time component but must be stripped later
|
||||
shareItem.stime = new Date(2014, 0, 20, 11, 0, 25).getTime() / 1000;
|
||||
configModel.set({
|
||||
isDefaultExpireDateEnabled: true,
|
||||
isDefaultExpireDateEnforced: true
|
||||
});
|
||||
dialog.render();
|
||||
expect($.datepicker._defaults.minDate).toEqual(expectedMinDate);
|
||||
expect($.datepicker._defaults.maxDate).toEqual(new Date(2014, 0, 27, 0, 0, 0, 0));
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
describe('check for avatar', function() {
|
||||
beforeEach(function() {
|
||||
|
@ -455,8 +273,8 @@ describe('OC.Share.ShareDialogView', function() {
|
|||
it('test correct function calls', function() {
|
||||
expect(avatarStub.calledThrice).toEqual(true);
|
||||
expect(placeholderStub.callCount).toEqual(4);
|
||||
expect(dialog.$('.shareWithList').children().length).toEqual(5);
|
||||
expect(dialog.$('.avatar').length).toEqual(6);
|
||||
expect(dialog.$('.shareWithList').children().length).toEqual(6);
|
||||
expect(dialog.$('.avatar').length).toEqual(7);
|
||||
});
|
||||
|
||||
it('test avatar owner', function() {
|
||||
|
|
|
@ -611,6 +611,7 @@ return array(
|
|||
'OC\\Core\\Migrations\\Version14000Date20180518120534' => $baseDir . '/core/Migrations/Version14000Date20180518120534.php',
|
||||
'OC\\Core\\Migrations\\Version14000Date20180522074438' => $baseDir . '/core/Migrations/Version14000Date20180522074438.php',
|
||||
'OC\\Core\\Migrations\\Version14000Date20180626223656' => $baseDir . '/core/Migrations/Version14000Date20180626223656.php',
|
||||
'OC\\Core\\Migrations\\Version14000Date20180712153140' => $baseDir . '/core/Migrations/Version14000Date20180712153140.php',
|
||||
'OC\\DB\\Adapter' => $baseDir . '/lib/private/DB/Adapter.php',
|
||||
'OC\\DB\\AdapterMySQL' => $baseDir . '/lib/private/DB/AdapterMySQL.php',
|
||||
'OC\\DB\\AdapterOCI8' => $baseDir . '/lib/private/DB/AdapterOCI8.php',
|
||||
|
|
|
@ -641,6 +641,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
|
|||
'OC\\Core\\Migrations\\Version14000Date20180518120534' => __DIR__ . '/../../..' . '/core/Migrations/Version14000Date20180518120534.php',
|
||||
'OC\\Core\\Migrations\\Version14000Date20180522074438' => __DIR__ . '/../../..' . '/core/Migrations/Version14000Date20180522074438.php',
|
||||
'OC\\Core\\Migrations\\Version14000Date20180626223656' => __DIR__ . '/../../..' . '/core/Migrations/Version14000Date20180626223656.php',
|
||||
'OC\\Core\\Migrations\\Version14000Date20180712153140' => __DIR__ . '/../../..' . '/core/Migrations/Version14000Date20180712153140.php',
|
||||
'OC\\DB\\Adapter' => __DIR__ . '/../../..' . '/lib/private/DB/Adapter.php',
|
||||
'OC\\DB\\AdapterMySQL' => __DIR__ . '/../../..' . '/lib/private/DB/AdapterMySQL.php',
|
||||
'OC\\DB\\AdapterOCI8' => __DIR__ . '/../../..' . '/lib/private/DB/AdapterOCI8.php',
|
||||
|
|
|
@ -86,7 +86,6 @@ class Share extends Constants {
|
|||
);
|
||||
if(count(self::$backendTypes) === 1) {
|
||||
Util::addScript('core', 'merged-share-backend');
|
||||
\OC_Util::addStyle('core', 'share');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -30,8 +30,14 @@
|
|||
namespace OC\Share20;
|
||||
|
||||
use OC\Files\Cache\Cache;
|
||||
use OCP\Defaults;
|
||||
use OCP\Files\Folder;
|
||||
use OCP\IL10N;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\IUser;
|
||||
use OCP\Mail\IMailer;
|
||||
use OCP\Share\IShare;
|
||||
use OCP\Share\IShareHelper;
|
||||
use OCP\Share\IShareProvider;
|
||||
use OC\Share20\Exception\InvalidShare;
|
||||
use OC\Share20\Exception\ProviderException;
|
||||
|
@ -67,6 +73,18 @@ class DefaultShareProvider implements IShareProvider {
|
|||
/** @var IRootFolder */
|
||||
private $rootFolder;
|
||||
|
||||
/** @var IMailer */
|
||||
private $mailer;
|
||||
|
||||
/** @var Defaults */
|
||||
private $defaults;
|
||||
|
||||
/** @var IL10N */
|
||||
private $l;
|
||||
|
||||
/** @var IURLGenerator */
|
||||
private $urlGenerator;
|
||||
|
||||
/**
|
||||
* DefaultShareProvider constructor.
|
||||
*
|
||||
|
@ -74,16 +92,28 @@ class DefaultShareProvider implements IShareProvider {
|
|||
* @param IUserManager $userManager
|
||||
* @param IGroupManager $groupManager
|
||||
* @param IRootFolder $rootFolder
|
||||
* @param IMailer $mailer ;
|
||||
* @param Defaults $defaults
|
||||
* @param IL10N $l
|
||||
* @param IURLGenerator $urlGenerator
|
||||
*/
|
||||
public function __construct(
|
||||
IDBConnection $connection,
|
||||
IUserManager $userManager,
|
||||
IGroupManager $groupManager,
|
||||
IRootFolder $rootFolder) {
|
||||
IRootFolder $rootFolder,
|
||||
IMailer $mailer,
|
||||
Defaults $defaults,
|
||||
IL10N $l,
|
||||
IURLGenerator $urlGenerator) {
|
||||
$this->dbConn = $connection;
|
||||
$this->userManager = $userManager;
|
||||
$this->groupManager = $groupManager;
|
||||
$this->rootFolder = $rootFolder;
|
||||
$this->mailer = $mailer;
|
||||
$this->defaults = $defaults;
|
||||
$this->l = $l;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -197,6 +227,9 @@ class DefaultShareProvider implements IShareProvider {
|
|||
* @return \OCP\Share\IShare The share object
|
||||
*/
|
||||
public function update(\OCP\Share\IShare $share) {
|
||||
|
||||
$originalShare = $this->getShareById($share->getId());
|
||||
|
||||
if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
|
||||
/*
|
||||
* We allow updating the recipient on user shares.
|
||||
|
@ -211,6 +244,7 @@ class DefaultShareProvider implements IShareProvider {
|
|||
->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
|
||||
->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
|
||||
->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
|
||||
->set('note', $qb->createNamedParameter($share->getNote()))
|
||||
->execute();
|
||||
} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
|
||||
$qb = $this->dbConn->getQueryBuilder();
|
||||
|
@ -222,6 +256,7 @@ class DefaultShareProvider implements IShareProvider {
|
|||
->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
|
||||
->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
|
||||
->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
|
||||
->set('note', $qb->createNamedParameter($share->getNote()))
|
||||
->execute();
|
||||
|
||||
/*
|
||||
|
@ -235,6 +270,7 @@ class DefaultShareProvider implements IShareProvider {
|
|||
->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
|
||||
->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
|
||||
->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
|
||||
->set('note', $qb->createNamedParameter($share->getNote()))
|
||||
->execute();
|
||||
|
||||
/*
|
||||
|
@ -259,9 +295,15 @@ class DefaultShareProvider implements IShareProvider {
|
|||
->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
|
||||
->set('token', $qb->createNamedParameter($share->getToken()))
|
||||
->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
|
||||
->set('note', $qb->createNamedParameter($share->getNote()))
|
||||
->execute();
|
||||
}
|
||||
|
||||
if ($originalShare->getNote() !== $share->getNote() && $share->getNote() !== '') {
|
||||
$this->propagateNote($share);
|
||||
}
|
||||
|
||||
|
||||
return $share;
|
||||
}
|
||||
|
||||
|
@ -875,6 +917,7 @@ class DefaultShareProvider implements IShareProvider {
|
|||
->setShareType((int)$data['share_type'])
|
||||
->setPermissions((int)$data['permissions'])
|
||||
->setTarget($data['file_target'])
|
||||
->setNote($data['note'])
|
||||
->setMailSend((bool)$data['mail_send']);
|
||||
|
||||
$shareTime = new \DateTime();
|
||||
|
@ -1227,4 +1270,96 @@ class DefaultShareProvider implements IShareProvider {
|
|||
|
||||
return $best;
|
||||
}
|
||||
|
||||
/**
|
||||
* propagate notes to the recipients
|
||||
*
|
||||
* @param IShare $share
|
||||
* @throws \OCP\Files\NotFoundException
|
||||
*/
|
||||
private function propagateNote(IShare $share) {
|
||||
if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
|
||||
$user = $this->userManager->get($share->getSharedWith());
|
||||
$this->sendNote([$user], $share);
|
||||
} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
|
||||
$group = $this->groupManager->get($share->getSharedWith());
|
||||
$groupMembers = $group->getUsers();
|
||||
$this->sendNote($groupMembers, $share);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* send note by mail
|
||||
*
|
||||
* @param array $recipients
|
||||
* @param IShare $share
|
||||
* @throws \OCP\Files\NotFoundException
|
||||
*/
|
||||
private function sendNote(array $recipients, IShare $share) {
|
||||
|
||||
$toList = [];
|
||||
|
||||
foreach ($recipients as $recipient) {
|
||||
/** @var IUser $recipient */
|
||||
$email = $recipient->getEMailAddress();
|
||||
if ($email) {
|
||||
$toList[$email] = $recipient->getDisplayName();
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($toList)) {
|
||||
|
||||
$filename = $share->getNode()->getName();
|
||||
$initiator = $share->getSharedBy();
|
||||
$note = $share->getNote();
|
||||
|
||||
$initiatorUser = $this->userManager->get($initiator);
|
||||
$initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
|
||||
$initiatorEmailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null;
|
||||
$plainHeading = $this->l->t('%1$s shared »%2$s« with you and wants to add:', [$initiatorDisplayName, $filename]);
|
||||
$htmlHeading = $this->l->t('%1$s shared »%2$s« with you and wants to add', [$initiatorDisplayName, $filename]);
|
||||
$message = $this->mailer->createMessage();
|
||||
|
||||
$emailTemplate = $this->mailer->createEMailTemplate('defaultShareProvider.sendNote');
|
||||
|
||||
$emailTemplate->setSubject($this->l->t('»%s« added a note to a file shared with you', [$initiatorDisplayName]));
|
||||
$emailTemplate->addHeader();
|
||||
$emailTemplate->addHeading($htmlHeading, $plainHeading);
|
||||
$emailTemplate->addBodyText(htmlspecialchars($note), $note);
|
||||
|
||||
$link = $this->urlGenerator->linkToRouteAbsolute('files.viewcontroller.showFile', ['fileid' => $share->getNode()->getId()]);
|
||||
$emailTemplate->addBodyButton(
|
||||
$this->l->t('Open »%s«', [$filename]),
|
||||
$link
|
||||
);
|
||||
|
||||
|
||||
// The "From" contains the sharers name
|
||||
$instanceName = $this->defaults->getName();
|
||||
$senderName = $this->l->t(
|
||||
'%1$s via %2$s',
|
||||
[
|
||||
$initiatorDisplayName,
|
||||
$instanceName
|
||||
]
|
||||
);
|
||||
$message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
|
||||
if ($initiatorEmailAddress !== null) {
|
||||
$message->setReplyTo([$initiatorEmailAddress => $initiatorDisplayName]);
|
||||
$emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan());
|
||||
} else {
|
||||
$emailTemplate->addFooter();
|
||||
}
|
||||
|
||||
if (count($toList) === 1) {
|
||||
$message->setTo($toList);
|
||||
} else {
|
||||
$message->setTo([]);
|
||||
$message->setBcc($toList);
|
||||
}
|
||||
$message->useTemplate($emailTemplate);
|
||||
$this->mailer->send($message);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,7 +81,11 @@ class ProviderFactory implements IProviderFactory {
|
|||
$this->serverContainer->getDatabaseConnection(),
|
||||
$this->serverContainer->getUserManager(),
|
||||
$this->serverContainer->getGroupManager(),
|
||||
$this->serverContainer->getLazyRootFolder()
|
||||
$this->serverContainer->getLazyRootFolder(),
|
||||
$this->serverContainer->getMailer(),
|
||||
$this->serverContainer->query(Defaults::class),
|
||||
$this->serverContainer->getL10N('sharing'),
|
||||
$this->serverContainer->getURLGenerator()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -57,6 +57,8 @@ class Share implements \OCP\Share\IShare {
|
|||
private $shareOwner;
|
||||
/** @var int */
|
||||
private $permissions;
|
||||
/** @var string */
|
||||
private $note = '';
|
||||
/** @var \DateTime */
|
||||
private $expireDate;
|
||||
/** @var string */
|
||||
|
@ -308,6 +310,24 @@ class Share implements \OCP\Share\IShare {
|
|||
return $this->permissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function setNote($note) {
|
||||
$this->note = $note;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getNote() {
|
||||
if (is_string($this->note)) {
|
||||
return $this->note;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
|
|
|
@ -42,6 +42,7 @@ use OC\Template\JSCombiner;
|
|||
use OC\Template\JSConfigHelper;
|
||||
use OC\Template\SCSSCacher;
|
||||
use OCP\Defaults;
|
||||
use OC\AppFramework\Http\Request;
|
||||
|
||||
class TemplateLayout extends \OC_Template {
|
||||
|
||||
|
@ -61,6 +62,9 @@ class TemplateLayout extends \OC_Template {
|
|||
// yes - should be injected ....
|
||||
$this->config = \OC::$server->getConfig();
|
||||
|
||||
if(\OCP\Util::isIE()) {
|
||||
\OC_Util::addStyle('ie');
|
||||
}
|
||||
|
||||
// Decide which page we show
|
||||
if($renderAs == 'user') {
|
||||
|
|
|
@ -64,6 +64,7 @@ use OCP\IConfig;
|
|||
use OCP\IGroupManager;
|
||||
use OCP\ILogger;
|
||||
use OCP\IUser;
|
||||
use OC\AppFramework\Http\Request;
|
||||
|
||||
class OC_Util {
|
||||
public static $scripts = array();
|
||||
|
@ -1528,4 +1529,13 @@ class OC_Util {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* is this Internet explorer ?
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function isIe() {
|
||||
return preg_match(Request::USER_AGENT_IE, $_SERVER['HTTP_USER_AGENT']) === 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -222,6 +222,24 @@ interface IShare {
|
|||
*/
|
||||
public function getPermissions();
|
||||
|
||||
/**
|
||||
* Attach a note to a share
|
||||
*
|
||||
* @param string $note
|
||||
* @return \OCP\Share\IShare The modified object
|
||||
* @since 14.0.0
|
||||
*/
|
||||
public function setNote($note);
|
||||
|
||||
/**
|
||||
* Get note attached to a share
|
||||
*
|
||||
* @return string
|
||||
* @since 14.0.0
|
||||
*/
|
||||
public function getNote();
|
||||
|
||||
|
||||
/**
|
||||
* Set the expiration date
|
||||
*
|
||||
|
|
|
@ -558,4 +558,14 @@ class Util {
|
|||
}
|
||||
return self::$needUpgradeCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* is this Internet explorer ?
|
||||
*
|
||||
* @return boolean
|
||||
* @since 14.0.0
|
||||
*/
|
||||
public static function isIe() {
|
||||
return \OC_Util::isIe();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -187,21 +187,49 @@ class FilesAppContext implements Context, ActorAwareInterface {
|
|||
/**
|
||||
* @return Locator
|
||||
*/
|
||||
public static function shareLinkCheckbox() {
|
||||
// forThe()->checkbox("Share link") can not be used here; that would
|
||||
// return the checkbox itself, but the element that the user interacts
|
||||
// with is the label.
|
||||
return Locator::forThe()->xpath("//label[normalize-space() = 'Share link']")->
|
||||
public static function shareLinkRow() {
|
||||
return Locator::forThe()->id("shareLink")->
|
||||
descendantOf(self::detailsView())->
|
||||
describedAs("Share link row in the details view in Files app");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Locator
|
||||
*/
|
||||
public static function shareLinkCheckbox() {
|
||||
// forThe()->checkbox("Enable") can not be used here; that would return
|
||||
// the checkbox itself, but the element that the user interacts with is
|
||||
// the label.
|
||||
return Locator::forThe()->xpath("//label[normalize-space() = 'Enable']")->
|
||||
descendantOf(self::shareLinkRow())->
|
||||
describedAs("Share link checkbox in the details view in Files app");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Locator
|
||||
*/
|
||||
public static function shareLinkField() {
|
||||
return Locator::forThe()->css(".linkText")->descendantOf(self::detailsView())->
|
||||
describedAs("Share link field in the details view in Files app");
|
||||
public static function shareLinkMenuButton() {
|
||||
return Locator::forThe()->css(".share-menu > .icon")->
|
||||
descendantOf(self::shareLinkRow())->
|
||||
describedAs("Share link menu button in the details view in Files app");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Locator
|
||||
*/
|
||||
public static function shareLinkMenu() {
|
||||
return Locator::forThe()->css(".share-menu > .menu")->
|
||||
descendantOf(self::shareLinkRow())->
|
||||
describedAs("Share link menu in the details view in Files app");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Locator
|
||||
*/
|
||||
public static function copyUrlMenuItem() {
|
||||
return Locator::forThe()->xpath("//a[normalize-space() = 'Copy URL']")->
|
||||
descendantOf(self::shareLinkMenu())->
|
||||
describedAs("Copy URL menu item in the share link menu in the details view in Files app");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -212,7 +240,7 @@ class FilesAppContext implements Context, ActorAwareInterface {
|
|||
// that would return the radio button itself, but the element that the
|
||||
// user interacts with is the label.
|
||||
return Locator::forThe()->xpath("//label[normalize-space() = 'Allow upload and editing']")->
|
||||
descendantOf(self::detailsView())->
|
||||
descendantOf(self::shareLinkMenu())->
|
||||
describedAs("Allow upload and editing radio button in the details view in Files app");
|
||||
}
|
||||
|
||||
|
@ -224,7 +252,7 @@ class FilesAppContext implements Context, ActorAwareInterface {
|
|||
// would return the checkbox itself, but the element that the user
|
||||
// interacts with is the label.
|
||||
return Locator::forThe()->xpath("//label[normalize-space() = 'Password protect']")->
|
||||
descendantOf(self::detailsView())->
|
||||
descendantOf(self::shareLinkMenu())->
|
||||
describedAs("Password protect checkbox in the details view in Files app");
|
||||
}
|
||||
|
||||
|
@ -232,7 +260,7 @@ class FilesAppContext implements Context, ActorAwareInterface {
|
|||
* @return Locator
|
||||
*/
|
||||
public static function passwordProtectField() {
|
||||
return Locator::forThe()->css(".linkPassText")->descendantOf(self::detailsView())->
|
||||
return Locator::forThe()->css(".linkPassText")->descendantOf(self::shareLinkMenu())->
|
||||
describedAs("Password protect field in the details view in Files app");
|
||||
}
|
||||
|
||||
|
@ -240,7 +268,7 @@ class FilesAppContext implements Context, ActorAwareInterface {
|
|||
* @return Locator
|
||||
*/
|
||||
public static function passwordProtectWorkingIcon() {
|
||||
return Locator::forThe()->css(".linkPass .icon-loading-small")->descendantOf(self::detailsView())->
|
||||
return Locator::forThe()->css(".linkPassMenu .icon-loading-small")->descendantOf(self::shareLinkMenu())->
|
||||
describedAs("Password protect working icon in the details view in Files app");
|
||||
}
|
||||
|
||||
|
@ -278,17 +306,14 @@ class FilesAppContext implements Context, ActorAwareInterface {
|
|||
* @Given I write down the shared link
|
||||
*/
|
||||
public function iWriteDownTheSharedLink() {
|
||||
// The shared link field always exists in the DOM (once the "Sharing"
|
||||
// tab is loaded), but its value is the actual shared link only when it
|
||||
// is visible.
|
||||
if (!WaitFor::elementToBeEventuallyShown(
|
||||
$this->actor,
|
||||
self::shareLinkField(),
|
||||
$timeout = 10 * $this->actor->getFindTimeoutMultiplier())) {
|
||||
PHPUnit_Framework_Assert::fail("The shared link was not shown yet after $timeout seconds");
|
||||
}
|
||||
$this->showShareLinkMenuIfNeeded();
|
||||
|
||||
$this->actor->getSharedNotebook()["shared link"] = $this->actor->find(self::shareLinkField())->getValue();
|
||||
$this->actor->find(self::copyUrlMenuItem(), 2)->click();
|
||||
|
||||
// Clicking on the menu item copies the link to the clipboard, but it is
|
||||
// not possible to access that value from the acceptance tests. Due to
|
||||
// this the value of the attribute that holds the URL is used instead.
|
||||
$this->actor->getSharedNotebook()["shared link"] = $this->actor->find(self::copyUrlMenuItem(), 2)->getWrappedElement()->getAttribute("data-clipboard-text");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -313,14 +338,18 @@ class FilesAppContext implements Context, ActorAwareInterface {
|
|||
* @When I set the shared link as editable
|
||||
*/
|
||||
public function iSetTheSharedLinkAsEditable() {
|
||||
$this->actor->find(self::allowUploadAndEditingRadioButton(), 10)->click();
|
||||
$this->showShareLinkMenuIfNeeded();
|
||||
|
||||
$this->actor->find(self::allowUploadAndEditingRadioButton(), 2)->click();
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I protect the shared link with the password :password
|
||||
*/
|
||||
public function iProtectTheSharedLinkWithThePassword($password) {
|
||||
$this->actor->find(self::passwordProtectCheckbox(), 10)->click();
|
||||
$this->showShareLinkMenuIfNeeded();
|
||||
|
||||
$this->actor->find(self::passwordProtectCheckbox(), 2)->click();
|
||||
|
||||
$this->actor->find(self::passwordProtectField(), 2)->setValue($password . "\r");
|
||||
}
|
||||
|
@ -460,4 +489,17 @@ class FilesAppContext implements Context, ActorAwareInterface {
|
|||
$this->iSeeThatTheWorkingIconForPasswordProtectIsEventuallyNotShown();
|
||||
}
|
||||
|
||||
private function showShareLinkMenuIfNeeded() {
|
||||
// In some cases the share menu is hidden after clicking on an action of
|
||||
// the menu. Therefore, if the menu is visible, wait a little just in
|
||||
// case it is in the process of being hidden due to a previous action,
|
||||
// in which case it is shown again.
|
||||
if (WaitFor::elementToBeEventuallyNotShown(
|
||||
$this->actor,
|
||||
self::shareLinkMenu(),
|
||||
$timeout = 2 * $this->actor->getFindTimeoutMultiplier())) {
|
||||
$this->actor->find(self::shareLinkMenuButton(), 10)->click();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -22,15 +22,19 @@
|
|||
namespace Test\Share20;
|
||||
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\Defaults;
|
||||
use OCP\Files\File;
|
||||
use OCP\Files\Folder;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\IGroup;
|
||||
use OCP\IL10N;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\IUser;
|
||||
use OCP\IUserManager;
|
||||
use OCP\IGroupManager;
|
||||
use OCP\Files\IRootFolder;
|
||||
use OC\Share20\DefaultShareProvider;
|
||||
use OCP\Mail\IMailer;
|
||||
use OCP\Share\IShare;
|
||||
|
||||
/**
|
||||
|
@ -56,11 +60,27 @@ class DefaultShareProviderTest extends \Test\TestCase {
|
|||
/** @var DefaultShareProvider */
|
||||
protected $provider;
|
||||
|
||||
/** @var \PHPUnit_Framework_MockObject_MockObject|IMailer */
|
||||
protected $mailer;
|
||||
|
||||
/** @var \PHPUnit_Framework_MockObject_MockObject|IL10N */
|
||||
protected $l10n;
|
||||
|
||||
/** @var \PHPUnit_Framework_MockObject_MockObject|Defaults */
|
||||
protected $defaults;
|
||||
|
||||
/** @var \PHPUnit_Framework_MockObject_MockObject|IURLGenerator */
|
||||
protected $urlGenerator;
|
||||
|
||||
public function setUp() {
|
||||
$this->dbConn = \OC::$server->getDatabaseConnection();
|
||||
$this->userManager = $this->createMock(IUserManager::class);
|
||||
$this->groupManager = $this->createMock(IGroupManager::class);
|
||||
$this->rootFolder = $this->createMock(IRootFolder::class);
|
||||
$this->mailer = $this->createMock(IMailer::class);
|
||||
$this->l10n = $this->createMock(IL10N::class);
|
||||
$this->defaults = $this->getMockBuilder(Defaults::class)->disableOriginalConstructor()->getMock();
|
||||
$this->urlGenerator = $this->createMock(IURLGenerator::class);
|
||||
|
||||
$this->userManager->expects($this->any())->method('userExists')->willReturn(true);
|
||||
|
||||
|
@ -71,7 +91,11 @@ class DefaultShareProviderTest extends \Test\TestCase {
|
|||
$this->dbConn,
|
||||
$this->userManager,
|
||||
$this->groupManager,
|
||||
$this->rootFolder
|
||||
$this->rootFolder,
|
||||
$this->mailer,
|
||||
$this->defaults,
|
||||
$this->l10n,
|
||||
$this->urlGenerator
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -403,6 +427,10 @@ class DefaultShareProviderTest extends \Test\TestCase {
|
|||
$this->userManager,
|
||||
$this->groupManager,
|
||||
$this->rootFolder,
|
||||
$this->mailer,
|
||||
$this->defaults,
|
||||
$this->l10n,
|
||||
$this->urlGenerator
|
||||
])
|
||||
->setMethods(['getShareById'])
|
||||
->getMock();
|
||||
|
@ -493,6 +521,10 @@ class DefaultShareProviderTest extends \Test\TestCase {
|
|||
$this->userManager,
|
||||
$this->groupManager,
|
||||
$this->rootFolder,
|
||||
$this->mailer,
|
||||
$this->defaults,
|
||||
$this->l10n,
|
||||
$this->urlGenerator
|
||||
])
|
||||
->setMethods(['getShareById'])
|
||||
->getMock();
|
||||
|
@ -2368,7 +2400,11 @@ class DefaultShareProviderTest extends \Test\TestCase {
|
|||
$this->dbConn,
|
||||
$userManager,
|
||||
$groupManager,
|
||||
$rootFolder
|
||||
$rootFolder,
|
||||
$this->mailer,
|
||||
$this->defaults,
|
||||
$this->l10n,
|
||||
$this->urlGenerator
|
||||
);
|
||||
|
||||
$password = md5(time());
|
||||
|
@ -2461,7 +2497,11 @@ class DefaultShareProviderTest extends \Test\TestCase {
|
|||
$this->dbConn,
|
||||
$userManager,
|
||||
$groupManager,
|
||||
$rootFolder
|
||||
$rootFolder,
|
||||
$this->mailer,
|
||||
$this->defaults,
|
||||
$this->l10n,
|
||||
$this->urlGenerator
|
||||
);
|
||||
|
||||
$u1 = $userManager->createUser('testShare1', 'test');
|
||||
|
@ -2552,7 +2592,11 @@ class DefaultShareProviderTest extends \Test\TestCase {
|
|||
$this->dbConn,
|
||||
$userManager,
|
||||
$groupManager,
|
||||
$rootFolder
|
||||
$rootFolder,
|
||||
$this->mailer,
|
||||
$this->defaults,
|
||||
$this->l10n,
|
||||
$this->urlGenerator
|
||||
);
|
||||
|
||||
$u1 = $userManager->createUser('testShare1', 'test');
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
// between betas, final and RCs. This is _not_ the public version number. Reset minor/patchlevel
|
||||
// when updating major/minor version number.
|
||||
|
||||
$OC_Version = array(14, 0, 0, 10);
|
||||
$OC_Version = array(14, 0, 0, 11);
|
||||
|
||||
// The human readable string
|
||||
$OC_VersionString = '14.0.0 alpha';
|
||||
|
|
Loading…
Reference in New Issue