Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Current shared collection refactoring changes #2016

Open
wants to merge 10 commits into
base: updated-shared-sync-collection-refactoring
Choose a base branch
from
2 changes: 1 addition & 1 deletion app/api/chemotion/collection_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class CollectionAPI < Grape::API
desc "Return all collections for the current user"
get do
collections = Collection.owned_by(current_user.id).includes(collection_acls: :user)
shared = Collection.shared_with(current_user.id).includes(:user)
shared = Collection.shared_with(user_ids).includes(:user)

{
collections: Entities::CollectionOwnedEntity.represent(collections),
Expand Down
15 changes: 7 additions & 8 deletions app/api/chemotion/share_collection_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,16 @@ class ShareCollectionAPI < Grape::API
error!('401 Unauthorized import from current collection', 401) unless from_collection
to_collection_id = fetch_collection_id_for_assign(params, 4)
error!('401 Unauthorized assignment to collection', 401) unless to_collection_id
if !params[:user_ids].blank?
if params[:user_ids].blank?
create_or_move_collection(params[:action], from_collection, to_collection_id, params[:ui_state])
# create_generic_elements(params, from_collection, to_collection_id)
else
params[:user_ids].each do |user|
create_or_move_collection(params[:action], from_collection, to_collection_id, params[:ui_state])
# create_generic_elements(params, from_collection, to_collection_id)
to_collection_id = to_collection_id ? to_collection_id : from_collection&.id
create_acl_collection(user[:value], to_collection_id, params, from_collection&.label)
to_collection_id ||= from_collection&.id
create_acl_collection(user[:value], to_collection_id, params)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style/NegatedIfElseCondition: Invert the negated condition and swap the if-else branches.

end
else
create_or_move_collection(params[:action], from_collection, to_collection_id, params[:ui_state])
# create_generic_elements(params, from_collection, to_collection_id)
end

status 204
Expand Down Expand Up @@ -72,11 +72,10 @@ class ShareCollectionAPI < Grape::API
delete do
collection_acl = CollectionAcl.includes(:collection).find_by(id: params[:id])
error!('404 Share collection id not found', 404) unless collection_acl
unless user_ids.include?(user_ids) || user_ids.include?(collection_acl.collection.user_id)
unless (collection_acl.user_id == current_user.id) || (collection_acl.collection.user_id == current_user.id)
error!('401 Unauthorized delete share collection', 401)
end
collection_acl.destroy!
status 204
end
end

Expand Down
5 changes: 2 additions & 3 deletions app/api/helpers/collection_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def check_permission_level_compatibility(collection, permission_level)
collection_id = collection.id
collection_permission_level =
CollectionAcl.find_by(collection_id: collection_id, user_id: current_user.id)&.permission_level
permission_level && collection_permission_level > permission_level
permission_level && collection_permission_level >= permission_level
end
# return the collection if current_user is associated to it (owned) or if acl exists
# return nil if no association
Expand Down Expand Up @@ -109,15 +109,14 @@ def set_var(c_id = params[:collection_id])
@dl_cl = @dl[:celllinesample_detail_level]
end

def create_acl_collection(user_id, collection_id, params, root_col_label)
def create_acl_collection(user_id, collection_id, params)
current_collection = params['ui_state']['currentCollection']
label = params[:newCollection] || current_collection['label']

c_acl = CollectionAcl.find_or_create_by(
user_id: user_id,
collection_id: collection_id,
)
label = root_col_label if label.nil?
c_acl.update!(
label: label,
permission_level: params['ui_state']['currentCollection']['permission_level'],
Expand Down
2 changes: 1 addition & 1 deletion app/models/concerns/taggable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def pubchem_check
def collection_id
klass = "collections_#{self.class.name.underscore.pluralize}"
return unless respond_to?(klass)
send(klass).pluck(:id)
send(klass).pluck(:collection_id)
end

def grouped_analyses
Expand Down
2 changes: 1 addition & 1 deletion app/models/concerns/tagging.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def update_tag
element = 'cellline_sample'
end

element && send(element)&.update_tag!(args)
element && send(element)&.update_tag!(**args)
end
end
# rubocop: enable Metrics/CyclomaticComplexity
14 changes: 9 additions & 5 deletions app/packs/src/apps/mydb/collections/SharedWithMeCollections.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Tree from 'react-ui-tree';
import { Button, ButtonGroup, FormControl, OverlayTrigger, Popover } from 'react-bootstrap';
import CollectionStore from 'src/stores/alt/stores/CollectionStore';
import CollectionActions from 'src/stores/alt/actions/CollectionActions';
import UserStore from 'src/stores/alt/stores/UserStore';

export default class SharedWithMeCollections extends React.Component {
constructor(props) {
Expand Down Expand Up @@ -44,10 +45,10 @@ export default class SharedWithMeCollections extends React.Component {
label(node) {
if(node.label == "Shared with me Collections") {
return (
<FormControl
value ="Synchronized with me Collections"
type="text"
className="root-label"
<FormControl
value ="Shared with me Collections"
type="text"
className="root-label"
disabled/>);
} else {
return (
Expand Down Expand Up @@ -79,7 +80,10 @@ export default class SharedWithMeCollections extends React.Component {
);

if (!node.is_locked && node.label !== 'Shared with me Collections') {
if (typeof (node.shared_to) === 'undefined' || !node.shared_to) {
const { currentUser } = UserStore.getState();
const aclUserIds = node.collection_acls.map((acl) => acl.user_id);

if (aclUserIds.includes(currentUser.id)) {
return (
<ButtonGroup className="actions">
<OverlayTrigger
Expand Down
32 changes: 23 additions & 9 deletions app/packs/src/apps/mydb/elements/labels/ElementCollectionLabels.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Label, OverlayTrigger, Popover, Button } from 'react-bootstrap';
import uuid from 'uuid';
import { AviatorNavigation } from 'src/utilities/routesUtils';
import CollectionStore from 'src/stores/alt/stores/CollectionStore';
import UserStore from 'src/stores/alt/stores/UserStore';
import SharedByIcon from 'src/components/common/SharedByIcon';
import _ from 'lodash';

Expand All @@ -31,10 +32,11 @@ export default class ElementCollectionLabels extends React.Component {
}

formatLabels(collections) {
const currentUser = (UserStore.getState() && UserStore.getState().currentUser) || {};
return collections.map((collection) => (
<span className="collection-label" key={uuid.v4()}>
<Button
disabled={(collection?.acl?.length > 0) && collection?.ownedByMe()}
disabled={this.isDisabledTag(collection, currentUser)}
bsStyle="default" bsSize="xs"
onClick={(e) => this.handleOnClick(collection, e)}
>
Expand All @@ -47,6 +49,16 @@ export default class ElementCollectionLabels extends React.Component {
));
}

isDisabledTag(collection, currentUser) {
const collectionAcls = collection?.acl || [];

const noUserAcl = collectionAcls.some((acl) => acl.user_id !== currentUser?.id);

const isNotOwnedByMe = !collection.ownedByMe();

return noUserAcl && isNotOwnedByMe;
}

renderCollectionsLabels(collectionName, labels) {
if (labels.length == 0) return <span />;

Expand All @@ -63,7 +75,7 @@ export default class ElementCollectionLabels extends React.Component {
render() {
const { element } = this.state;

if (!element.tag || !element.tag.taggable_data || !element.tag.taggable_data.collection_labels) {
if (!element.tag || !element.tag.taggable_data || !element.tag.taggable_data.collection_ids) {
return (<span />);
}

Expand All @@ -76,13 +88,15 @@ export default class ElementCollectionLabels extends React.Component {

let shared_labels = [];
let labels = [];
collections.map((collection) => {
if (collection?.ownedByMe()) {
labels.push(collection);
} else {
shared_labels.push(collection);
}
});
collections
.filter((collection) => collection != null)
.forEach((collection) => {
if (collection?.ownedByMe()) {
labels.push(collection);
} else {
shared_labels.push(collection);
}
});

if (labels.length == 0 && shared_labels.length == 0)
return (<span />);
Expand Down
15 changes: 13 additions & 2 deletions app/packs/src/components/managingActions/ManagingModalSharing.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ export default class ManagingModalSharing extends React.Component {
wellplateDetailLevel: props.wellplateDetailLevel,
screenDetailLevel: props.screenDetailLevel,
elementDetailLevel: props.elementDetailLevel,
label: props.label,
label: props.label || '',
selectedUsers: null,
errors: {},
}

this.onUserChange = this.onUserChange.bind(this);
Expand Down Expand Up @@ -203,6 +204,11 @@ export default class ManagingModalSharing extends React.Component {
wellplateDetailLevel, screenDetailLevel, elementDetailLevel
} = this.state;

if (!label || !label.trim()) {
this.setState({ errors: { label: 'Label is required' } });
return;
}

const current_collection = {
permission_level: permissionLevel,
sample_detail_level: sampleDetailLevel,
Expand Down Expand Up @@ -439,11 +445,16 @@ export default class ManagingModalSharing extends React.Component {
</FormControl>
</FormGroup>
<FormGroup controlId="label">
<ControlLabel>Label</ControlLabel>
<ControlLabel>Label*</ControlLabel>
<FormControl required id="label" type="text" placeholder="Label" name="label"
value={this.state.label}
onChange={(e) => { this.handleLabelChange(e); }}
/>
{this.state.errors.label && (
<div className="error" style={{ color: 'red' }}>
{this.state.errors.label}
</div>
)}
</FormGroup>
{this.selectUsers()}
<br />
Expand Down
16 changes: 11 additions & 5 deletions app/policies/element_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,27 @@ def self.update?(user, record)
# 2. there exists a shared collection, containing the sample, which he owns and where the user has
# the required permission_level
def read?
maximum_permission_level(user_collections, acl_collections) >= 0
user_is_owner(user_collections) || maximum_permission_level(user_collections, acl_collections) >= 0
end

def update?
maximum_permission_level(user_collections, acl_collections) >= 1
user_is_owner(user_collections) || maximum_permission_level(user_collections, acl_collections) >= 1
end

def copy?
maximum_element_permission_level(user_collections) >= 1 || maximum_element_permission_level(acl_collections) >= 1
user_is_owner(user_collections) ||
maximum_element_permission_level(user_collections) >= 1 ||
maximum_element_permission_level(acl_collections) >= 1
end

def share?
return true unless record

maximum_permission_level(user_collections, acl_collections) >= 2
user_is_owner(user_collections) || maximum_permission_level(user_collections, acl_collections) >= 2
end

def destroy?
maximum_permission_level(user_collections, acl_collections) >= 3
user_is_owner(user_collections) || maximum_permission_level(user_collections, acl_collections) >= 3
end

def scope
Expand All @@ -55,6 +57,10 @@ def resolve

private

def user_is_owner(user_collections)
user_collections.pluck(:user_id).include?(user.id)
end

def maximum_permission_level(collections, collection_acl)
(collections.pluck(:permission_level) + collection_acl.pluck(:permission_level)).max || -1
end
Expand Down
2 changes: 1 addition & 1 deletion app/usecases/sharing/take_ownership.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def execute!
cols = Collection.where([' id = ? or ancestry = ? or ancestry like ? or ancestry like ? or ancestry like ? ',
col_id, col_id.to_s, "%/#{col_id}", "#{col_id}/%", "%/#{col_id}/%"])
cols.update_all(user_id: new_owner_id)
acl.destroy
acl.update(user_id: previous_owner_id, permission_level: 4)

user = User.find_by(id: new_owner_id)
Message.create_msg_notification(
Expand Down
2 changes: 1 addition & 1 deletion db/migrate/20240119141814_migrate_element_tag_data.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def up
klass = "collections_#{element_tag.taggable_type.underscore.pluralize}"
collection_ids = element.respond_to?(klass) ? element.send(klass).pluck(:collection_id) : []
taggable_data = element_tag.taggable_data&.except('collection_labels') || {}
taggable_data[:collection_ids] = collection_ids
taggable_data['collection_ids'] = collection_ids
element_tag.update_columns(taggable_data: taggable_data)
end
end
Expand Down
Loading