diff --git a/changelog/unreleased/quicklinks.md b/changelog/unreleased/quicklinks.md new file mode 100644 index 0000000000..8633e5bf6e --- /dev/null +++ b/changelog/unreleased/quicklinks.md @@ -0,0 +1,4 @@ +Enhancement: Add support for quicklinks for public shares + +https://github.com/cs3org/reva/pull/3163 +https://github.com/cs3org/reva/pull/2715 diff --git a/go.mod b/go.mod index 5eedc21016..c7f1bbb3c4 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/cheggaaa/pb v1.0.29 github.com/coreos/go-oidc v2.2.1+incompatible github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e - github.com/cs3org/go-cs3apis v0.0.0-20220330081745-2ad58f5932b9 + github.com/cs3org/go-cs3apis v0.0.0-20220719130120-361e9f987d64 github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8 github.com/dgraph-io/ristretto v0.1.0 github.com/disintegration/imaging v1.6.2 diff --git a/go.sum b/go.sum index 299cd94873..bebfdf206a 100644 --- a/go.sum +++ b/go.sum @@ -226,6 +226,8 @@ github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e h1:tqSPWQeueWTKnJVMJff github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e/go.mod h1:XJEZ3/EQuI3BXTp/6DUzFr850vlxq11I6satRtz0YQ4= github.com/cs3org/go-cs3apis v0.0.0-20220330081745-2ad58f5932b9 h1:SuPu5Mc2mpz+J059XML+cMd0i5FZR4t/kROS3SaIsnU= github.com/cs3org/go-cs3apis v0.0.0-20220330081745-2ad58f5932b9/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY= +github.com/cs3org/go-cs3apis v0.0.0-20220719130120-361e9f987d64 h1:cFnankJOCWndnOns4sKRG7yzH61ammK2Am6rEGWCK40= +github.com/cs3org/go-cs3apis v0.0.0-20220719130120-361e9f987d64/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY= github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8 h1:Z9lwXumT5ACSmJ7WGnFl+OMLLjpz5uR2fyz7dC255FI= github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8/go.mod h1:4abs/jPXcmJzYoYGF91JF9Uq9s/KL5n1jvFDix8KcqY= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= diff --git a/internal/http/services/owncloud/ocs/conversions/main.go b/internal/http/services/owncloud/ocs/conversions/main.go index c8cb64e801..c0239e20c6 100644 --- a/internal/http/services/owncloud/ocs/conversions/main.go +++ b/internal/http/services/owncloud/ocs/conversions/main.go @@ -144,6 +144,7 @@ type ShareData struct { Attributes string `json:"attributes,omitempty" xml:"attributes,omitempty"` // PasswordProtected represents a public share is password protected // PasswordProtected bool `json:"password_protected,omitempty" xml:"password_protected,omitempty"` + Quicklink bool `json:"quicklink,omitempty" xml:"quicklink,omitempty"` } // ShareeData holds share recipient search results @@ -215,6 +216,7 @@ func PublicShare2ShareData(share *link.PublicShare, r *http.Request, publicURL s URL: publicURL + path.Join("/", "s/"+share.Token), UIDOwner: LocalUserIDToString(share.Creator), UIDFileOwner: LocalUserIDToString(share.Owner), + Quicklink: share.Quicklink, } if share.Id != nil { sd.ID = share.Id.OpaqueId diff --git a/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/public.go b/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/public.go index 81deec02dc..6305f8550f 100644 --- a/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/public.go +++ b/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/public.go @@ -36,6 +36,7 @@ import ( "github.com/cs3org/reva/internal/http/services/owncloud/ocs/conversions" "github.com/cs3org/reva/internal/http/services/owncloud/ocs/response" "github.com/cs3org/reva/pkg/appctx" + "github.com/cs3org/reva/pkg/publicshare" "github.com/cs3org/reva/pkg/rgrpc/todo/pool" "github.com/pkg/errors" ) @@ -56,6 +57,37 @@ func (h *Handler) createPublicLinkShare(w http.ResponseWriter, r *http.Request, return } + if quicklink, _ := strconv.ParseBool(r.FormValue("quicklink")); quicklink { + res, err := c.ListPublicShares(ctx, &link.ListPublicSharesRequest{ + Filters: []*link.ListPublicSharesRequest_Filter{ + publicshare.ResourceIDFilter(statInfo.Id), + }, + }) + if err != nil { + response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "could not list public links", err) + return + } + if res.Status.Code != rpc.Code_CODE_OK { + response.WriteOCSError(w, r, int(res.Status.GetCode()), "could not list public links", nil) + return + } + + for _, l := range res.GetShare() { + if l.Quicklink { + s := conversions.PublicShare2ShareData(l, r, h.publicURL) + err = h.addFileInfo(ctx, s, statInfo) + if err != nil { + response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "error enhancing response with share data", err) + return + } + h.mapUserIds(ctx, c, s) + response.WriteOCSSuccess(w, r, s) + return + } + } + + } + newPermissions, err := permissionFromRequest(r, h) if err != nil { response.WriteOCSError(w, r, response.MetaBadRequest.StatusCode, "Could not read permission from request", err) @@ -108,7 +140,8 @@ func (h *Handler) createPublicLinkShare(w http.ResponseWriter, r *http.Request, // set displayname and password protected as arbitrary metadata req.ResourceInfo.ArbitraryMetadata = &provider.ArbitraryMetadata{ Metadata: map[string]string{ - "name": r.FormValue("name"), + "name": r.FormValue("name"), + "quicklink": r.FormValue("quicklink"), // "password": r.FormValue("password"), }, } diff --git a/pkg/cbox/publicshare/sql/sql.go b/pkg/cbox/publicshare/sql/sql.go index 5c8082cb3f..fe94d9ec7e 100644 --- a/pkg/cbox/publicshare/sql/sql.go +++ b/pkg/cbox/publicshare/sql/sql.go @@ -133,6 +133,8 @@ func (m *manager) CreatePublicShare(ctx context.Context, u *user.User, rInfo *pr tkn := utils.RandString(15) now := time.Now().Unix() + quicklink, _ := strconv.ParseBool(rInfo.ArbitraryMetadata.Metadata["quicklink"]) + displayName, ok := rInfo.ArbitraryMetadata.Metadata["name"] if !ok { displayName = tkn @@ -154,8 +156,8 @@ func (m *manager) CreatePublicShare(ctx context.Context, u *user.User, rInfo *pr fileSource = 0 } - query := "insert into oc_share set share_type=?,uid_owner=?,uid_initiator=?,item_type=?,fileid_prefix=?,item_source=?,file_source=?,permissions=?,stime=?,token=?,share_name=?" - params := []interface{}{publicShareType, owner, creator, itemType, prefix, itemSource, fileSource, permissions, now, tkn, displayName} + query := "insert into oc_share set share_type=?,uid_owner=?,uid_initiator=?,item_type=?,fileid_prefix=?,item_source=?,file_source=?,permissions=?,stime=?,token=?,share_name=?,quicklink=?" + params := []interface{}{publicShareType, owner, creator, itemType, prefix, itemSource, fileSource, permissions, now, tkn, displayName, quicklink} var passwordProtected bool password := g.Password @@ -203,6 +205,7 @@ func (m *manager) CreatePublicShare(ctx context.Context, u *user.User, rInfo *pr PasswordProtected: passwordProtected, Expiration: g.Expiration, DisplayName: displayName, + Quicklink: quicklink, }, nil } @@ -264,8 +267,8 @@ func (m *manager) UpdatePublicShare(ctx context.Context, u *user.User, req *link func (m *manager) getByToken(ctx context.Context, token string, u *user.User) (*link.PublicShare, string, error) { s := conversions.DBShare{Token: token} - query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, coalesce(item_type, '') as item_type, coalesce(expiration, '') as expiration, coalesce(share_name, '') as share_name, id, stime, permissions FROM oc_share WHERE (orphan = 0 or orphan IS NULL) AND share_type=? AND token=?" - if err := m.db.QueryRow(query, publicShareType, token).Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.Prefix, &s.ItemSource, &s.ItemType, &s.Expiration, &s.ShareName, &s.ID, &s.STime, &s.Permissions); err != nil { + query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, coalesce(item_type, '') as item_type, coalesce(expiration, '') as expiration, coalesce(share_name, '') as share_name, id, stime, permissions, quicklink FROM oc_share WHERE (orphan = 0 or orphan IS NULL) AND share_type=? AND token=?" + if err := m.db.QueryRow(query, publicShareType, token).Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.Prefix, &s.ItemSource, &s.ItemType, &s.Expiration, &s.ShareName, &s.ID, &s.STime, &s.Permissions, &s.Quicklink); err != nil { if err == sql.ErrNoRows { return nil, "", errtypes.NotFound(token) } @@ -277,8 +280,8 @@ func (m *manager) getByToken(ctx context.Context, token string, u *user.User) (* func (m *manager) getByID(ctx context.Context, id *link.PublicShareId, u *user.User) (*link.PublicShare, string, error) { uid := conversions.FormatUserID(u.Id) s := conversions.DBShare{ID: id.OpaqueId} - query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, coalesce(item_type, '') as item_type, coalesce(token,'') as token, coalesce(expiration, '') as expiration, coalesce(share_name, '') as share_name, stime, permissions FROM oc_share WHERE (orphan = 0 or orphan IS NULL) AND share_type=? AND id=? AND (uid_owner=? OR uid_initiator=?)" - if err := m.db.QueryRow(query, publicShareType, id.OpaqueId, uid, uid).Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.Prefix, &s.ItemSource, &s.ItemType, &s.Token, &s.Expiration, &s.ShareName, &s.STime, &s.Permissions); err != nil { + query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, coalesce(item_type, '') as item_type, coalesce(token,'') as token, coalesce(expiration, '') as expiration, coalesce(share_name, '') as share_name, stime, permissions, quicklink FROM oc_share WHERE (orphan = 0 or orphan IS NULL) AND share_type=? AND id=? AND (uid_owner=? OR uid_initiator=?)" + if err := m.db.QueryRow(query, publicShareType, id.OpaqueId, uid, uid).Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.Prefix, &s.ItemSource, &s.ItemType, &s.Token, &s.Expiration, &s.ShareName, &s.STime, &s.Permissions, &s.Quicklink); err != nil { if err == sql.ErrNoRows { return nil, "", errtypes.NotFound(id.OpaqueId) } @@ -321,7 +324,7 @@ func (m *manager) GetPublicShare(ctx context.Context, u *user.User, ref *link.Pu } func (m *manager) ListPublicShares(ctx context.Context, u *user.User, filters []*link.ListPublicSharesRequest_Filter, md *provider.ResourceInfo, sign bool) ([]*link.PublicShare, error) { - query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, coalesce(item_type, '') as item_type, coalesce(token,'') as token, coalesce(expiration, '') as expiration, coalesce(share_name, '') as share_name, id, stime, permissions FROM oc_share WHERE (orphan = 0 or orphan IS NULL) AND (share_type=?)" + query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, coalesce(item_type, '') as item_type, coalesce(token,'') as token, coalesce(expiration, '') as expiration, coalesce(share_name, '') as share_name, id, stime, permissions, quicklink FROM oc_share WHERE (orphan = 0 or orphan IS NULL) AND (share_type=?)" var resourceFilters, ownerFilters, creatorFilters string var resourceParams, ownerParams, creatorParams []interface{} params := []interface{}{publicShareType} @@ -379,7 +382,7 @@ func (m *manager) ListPublicShares(ctx context.Context, u *user.User, filters [] var s conversions.DBShare shares := []*link.PublicShare{} for rows.Next() { - if err := rows.Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.Prefix, &s.ItemSource, &s.ItemType, &s.Token, &s.Expiration, &s.ShareName, &s.ID, &s.STime, &s.Permissions); err != nil { + if err := rows.Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.Prefix, &s.ItemSource, &s.ItemType, &s.Token, &s.Expiration, &s.ShareName, &s.ID, &s.STime, &s.Permissions, &s.Quicklink); err != nil { continue } cs3Share := conversions.ConvertToCS3PublicShare(s) @@ -438,8 +441,8 @@ func (m *manager) RevokePublicShare(ctx context.Context, u *user.User, ref *link func (m *manager) GetPublicShareByToken(ctx context.Context, token string, auth *link.PublicShareAuthentication, sign bool) (*link.PublicShare, error) { s := conversions.DBShare{Token: token} - query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, coalesce(item_type, '') as item_type, coalesce(expiration, '') as expiration, coalesce(share_name, '') as share_name, id, stime, permissions FROM oc_share WHERE share_type=? AND token=?" - if err := m.db.QueryRow(query, publicShareType, token).Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.Prefix, &s.ItemSource, &s.ItemType, &s.Expiration, &s.ShareName, &s.ID, &s.STime, &s.Permissions); err != nil { + query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, coalesce(item_type, '') as item_type, coalesce(expiration, '') as expiration, coalesce(share_name, '') as share_name, id, stime, permissions, quicklink FROM oc_share WHERE share_type=? AND token=?" + if err := m.db.QueryRow(query, publicShareType, token).Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.Prefix, &s.ItemSource, &s.ItemType, &s.Expiration, &s.ShareName, &s.ID, &s.STime, &s.Permissions, &s.Quicklink); err != nil { if err == sql.ErrNoRows { return nil, errtypes.NotFound(token) } diff --git a/pkg/cbox/utils/conversions.go b/pkg/cbox/utils/conversions.go index 41b8e78a44..7309b48349 100644 --- a/pkg/cbox/utils/conversions.go +++ b/pkg/cbox/utils/conversions.go @@ -48,6 +48,7 @@ type DBShare struct { STime int FileTarget string State int + Quicklink bool } // FormatGrantee formats a CS3API grantee to a string @@ -250,5 +251,6 @@ func ConvertToCS3PublicShare(s DBShare) *link.PublicShare { Expiration: expires, Ctime: ts, Mtime: ts, + Quicklink: s.Quicklink, } }