Skip to content

Commit

Permalink
Perms on pins
Browse files Browse the repository at this point in the history
  • Loading branch information
snyaggarwal committed Dec 16, 2020
1 parent 596e670 commit f389106
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 42 deletions.
36 changes: 33 additions & 3 deletions src/common/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import alertifyjs from 'alertifyjs';
import moment from 'moment';
import {
filter, difference, compact, find, reject, intersectionBy, size, keys, omitBy, isEmpty,
get, includes, map,
get, includes, map, isArray, values
} from 'lodash';
import { DATE_FORMAT, DATETIME_FORMAT } from './constants';

Expand Down Expand Up @@ -124,7 +124,7 @@ export const getCurrentUser = () => {
};

export const getCurrentUserOrgs = () => {
return get(getCurrentUser(), 'supported_orgs');
return get(getCurrentUser(), 'subscribed_orgs');
};

export const getCurrentUserUsername = () => {
Expand All @@ -136,5 +136,35 @@ export const nonEmptyCount = (object, attributes) => {
}

export const isCurrentUserMemberOf = orgId => {
return includes(map(getCurrentUserOrgs(), 'id'), orgId);
return orgId && includes(map(getCurrentUserOrgs(), 'id'), orgId);
}

export const defaultCreatePin = (resourceType, resourceId, service, callback) => {
if(service) {
service.post({resource_type: resourceType, resource_id: resourceId}).then(response => {
if(get(response, 'error')) {
let error;
if(isArray(response.error) && !isEmpty(compact(response.error)))
error = compact(response.error)[0];
else
error = get(values(response.error), '0') || 'Something bad happened.';
alertifyjs.error(error);
} else if(callback && get(response, 'status') === 201)
callback(response.data);
});
}
}

export const defaultDeletePin = (service, callback) => {
if(service) {
service.delete().then(response => {
if(callback && get(response, 'status') === 204)
callback();
});
}
}

export const isAdminUser = () => {
const currentUser = getCurrentUser();
return get(currentUser, 'is_staff') || get(currentUser, 'is_superuser');
}
6 changes: 2 additions & 4 deletions src/components/common/Pins.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
AccountTreeRounded as TreeIcon
} from '@material-ui/icons';
import { map, isEmpty, get, last, compact, orderBy } from 'lodash';
import { isLoggedIn } from '../../common/utils';
import { ORANGE, GREEN } from '../../common/constants';
import PinIcon from '../common/PinIcon';

Expand All @@ -30,8 +29,7 @@ const getGridDivision = pinsCount => {
return Math.floor(division/pinsCount);
}

const Pins = ({ pins, onDelete }) => {
const isAuthenticated = isLoggedIn();
const Pins = ({ pins, onDelete, canDelete }) => {
let gridDivision = getGridDivision(pins.length);
const gridClassName = `col-md-${gridDivision}`
return (
Expand Down Expand Up @@ -59,7 +57,7 @@ const Pins = ({ pins, onDelete }) => {
</Avatar>
}
action={
isAuthenticated &&
canDelete &&
<IconButton size='small' onClick={() => onDelete(pin.id)}>
<DeleteIcon fontSize='small' style={{width: '20px'}}/>
</IconButton>
Expand Down
9 changes: 8 additions & 1 deletion src/components/orgs/OrgHome.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import { CircularProgress } from '@material-ui/core';
import { reject, get } from 'lodash';
import APIService from '../../services/APIService';
import { isCurrentUserMemberOf } from '../../common/utils';
import Pins from '../common/Pins';
import OrgHomeHeader from './OrgHomeHeader';
import OrgHomeTabs from './OrgHomeTabs';
Expand Down Expand Up @@ -119,14 +120,19 @@ class OrgHome extends React.Component {
render() {
const { org, isLoading, tab, pins } = this.state;
const url = this.getURLFromPath()
const isCurrentUserMemberOfOrg = isCurrentUserMemberOf(this.getOrgId());
return (
<div style={isLoading ? {textAlign: 'center', marginTop: '40px'} : {}}>
{
isLoading ?
<CircularProgress color='primary' /> :
<div className='col-md-12 home-container no-side-padding'>
<OrgHomeHeader org={org} url={url} />
<Pins pins={pins} onDelete={this.deletePin} />
<Pins
pins={pins}
onDelete={this.deletePin}
canDelete={isCurrentUserMemberOfOrg}
/>
<OrgHomeTabs
tab={tab}
onTabChange={this.onTabChange}
Expand All @@ -136,6 +142,7 @@ class OrgHome extends React.Component {
pins={pins}
onPinCreate={this.createPin}
onPinDelete={this.deletePin}
showPin={isCurrentUserMemberOfOrg}
/>
</div>
}
Expand Down
6 changes: 5 additions & 1 deletion src/components/orgs/OrgHomeTabs.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import AboutAccordian from '../common/AboutAccordian';
import OrgHomeChildrenList from './OrgHomeChildrenList';

const OrgHomeTabs = props => {
const { tab, org, location, url, pins, onTabChange, onPinCreate, onPinDelete} = props;
const {
tab, org, location, url, pins, showPin, onTabChange, onPinCreate, onPinDelete,
} = props;
const about = get(org, 'extras.about')

return (
Expand All @@ -24,6 +26,7 @@ const OrgHomeTabs = props => {
location={location}
url={url}
pins={pins}
showPin={showPin}
onPinCreate={onPinCreate}
onPinDelete={onPinDelete}
resource='sources'
Expand All @@ -36,6 +39,7 @@ const OrgHomeTabs = props => {
location={location}
url={url}
pins={pins}
showPin={showPin}
onPinCreate={onPinCreate}
onPinDelete={onPinDelete}
resource='collections'
Expand Down
55 changes: 41 additions & 14 deletions src/components/search/ResultsTable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ import {
BLUE, WHITE, DARKGRAY, COLOR_ROW_SELECTED, ORANGE, GREEN, EMPTY_VALUE
} from '../../common/constants';
import {
formatDate, formatDateTime, headFirst, isLoggedIn
formatDate, formatDateTime, headFirst, isLoggedIn, defaultCreatePin, defaultDeletePin,
getCurrentUserUsername, isCurrentUserMemberOf,
} from '../../common/utils';
import ReferenceChip from '../common/ReferenceChip';
import OwnerChip from '../common/OwnerChip';
Expand Down Expand Up @@ -264,7 +265,7 @@ const LocalesTable = ({ locales, isDescription }) => {
const ExpandibleRow = props => {
const {
item, resourceDefinition, resource, isSelected, isSelectable, onPinCreate, onPinDelete, pins,
nested
nested, showPin
} = props;
const [mappings, setMappings] = React.useState([]);
const [versions, setVersions] = React.useState([]);
Expand All @@ -276,13 +277,12 @@ const ExpandibleRow = props => {
const [selected, setSelected] = React.useState(isSelected);
const isConceptContainer = includes(['sources', 'collections'], resource);
const isPublic = includes(['view', 'edit'], get(item, 'public_access', '').toLowerCase()) && isConceptContainer;
const shouldShowPin = isLoggedIn() && resourceDefinition.pinnable;
const pinId = get(find(pins, {resource_uri: item.url}), 'id');

const columnsCount = get(resourceDefinition, 'columns.length', 1) +
(isConceptContainer ? 1 : 0) + //public column
(isSelectable ? 1 : 0) + // select column
((resourceDefinition.expandible || shouldShowPin) ? 1 : 0) + // expand icon column
((resourceDefinition.expandible || showPin) ? 1 : 0) + // expand icon column
(resourceDefinition.tags ? 1 : 0); //tags column

React.useEffect(() => {
Expand Down Expand Up @@ -310,18 +310,44 @@ const ExpandibleRow = props => {
})
}

const getPinService = pinId => {
const username = get(props, 'match.params.user') || getCurrentUserUsername();
const orgId = get(props, 'match.params.org');
let service = null;
if(orgId && isCurrentUserMemberOf(orgId))
service = APIService.orgs(orgId)
else if(username && isLoggedIn())
service = APIService.users(username)

if(service) {
if(pinId)
return service.pins(pinId)
return service.pins()
}
}

const createPin = (resourceType, resourceId) => {
if(onPinCreate)
onPinCreate(resourceType, resourceId)
else
defaultCreatePin(resourceType, resourceId, getPinService())
}

const deletePin = pinId => {
if(onPinDelete)
onPinDelete(pinId)
else
defaultDeletePin(getPinService(pinId))
}

const onPinClick = event => {
if(!shouldShowPin)
if(!showPin)
return;

event.stopPropagation()
event.preventDefault()

const newPinState = !pinned;
if(newPinState)
onPinCreate(item.type, item.uuid)
else
onPinDelete(pinId)
pinned ? deletePin(pinId) : createPin(item.type, item.uuid);

return false;
}
Expand Down Expand Up @@ -461,7 +487,7 @@ const ExpandibleRow = props => {
</TableCell>
}
{
(resourceDefinition.expandible || shouldShowPin) &&
(resourceDefinition.expandible || showPin) &&
<TableCell align={nested ? 'center' : 'left'}>
{
resourceDefinition.expandible &&
Expand All @@ -470,7 +496,7 @@ const ExpandibleRow = props => {
</IconButton>
}
{
shouldShowPin &&
showPin &&
<IconButton aria-label="expand row" size="small" onClick={onPinClick}>
{<PinIcon fontSize="small" pinned={pinned ? pinned.toString() : undefined} />}
</IconButton>
Expand Down Expand Up @@ -535,7 +561,7 @@ const ExpandibleRow = props => {
const ResultsTable = (
{
resource, results, onPageChange, onSortChange, sortParams,
onPinCreate, onPinDelete, pins, nested
onPinCreate, onPinDelete, pins, nested, showPin
}
) => {
const resourceDefinition = RESOURCE_DEFINITIONS[resource];
Expand All @@ -546,7 +572,7 @@ const ResultsTable = (
border: `1px solid ${theadBgColor}`,
}
const isConceptContainer = includes(['sources', 'collections'], resource);
const shouldShowPin = isLoggedIn() && resourceDefinition.pinnable;
const shouldShowPin = showPin && resourceDefinition.pinnable;
const columnsCount = get(resourceDefinition, 'columns.length', 1) + ((resourceDefinition.expandible || shouldShowPin) ? 2 : 1) + (isConceptContainer ? 1 : 0);
const canRender = results.total && resourceDefinition;
const defaultOrderBy = get(find(resourceDefinition.columns, {sortOn: get(values(sortParams), '0', 'last_update')}), 'id', 'UpdateOn');
Expand Down Expand Up @@ -667,6 +693,7 @@ const ResultsTable = (
onPinDelete={onPinDelete}
pins={pins}
nested={nested}
showPin={shouldShowPin}
/>
))
}
Expand Down
3 changes: 2 additions & 1 deletion src/components/search/Search.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ class Search extends React.Component {
}

render() {
const { nested, pins, onPinCreate, onPinDelete } = this.props;
const { nested, pins, onPinCreate, onPinDelete, showPin } = this.props;
const {
resource, results, isLoading, limit, sortParams, openFacetsDrawer, isTable, isInfinite
} = this.state;
Expand Down Expand Up @@ -418,6 +418,7 @@ class Search extends React.Component {
onPinDelete={onPinDelete}
pins={pins}
nested={nested}
showPin={showPin}
/>
}
{
Expand Down
34 changes: 16 additions & 18 deletions src/components/users/UserHome.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import React from 'react';
import { reject, get } from 'lodash';
import APIService from '../../services/APIService';
import {
defaultCreatePin, defaultDeletePin, getCurrentUserUsername, isAdminUser
} from '../../common/utils';
import Pins from '../common/Pins';
import UserHomeDetails from './UserHomeDetails';
import UserHomeTabs from './UserHomeTabs';
Expand Down Expand Up @@ -89,44 +92,39 @@ class UserHome extends React.Component {

createPin = (resourceType, resourceId) => {
const service = this.getPinsService()
if(service) {
service
.post({resource_type: resourceType, resource_id: resourceId})
.then(response => {
if(get(response, 'status') === 201) {
this.setState({pins: [...this.state.pins, response.data]})
}
})
}
defaultCreatePin(resourceType, resourceId, service, createdPin => {
this.setState({pins: [...this.state.pins, createdPin]})
})
}

deletePin = pinId => {
const service = this.getPinsService(pinId)
if(service) {
service
.delete()
.then(response => {
if(get(response, 'status') === 204)
this.setState({pins: reject(this.state.pins, {id: pinId})})
})
}
defaultDeletePin(service, () => {
this.setState({pins: reject(this.state.pins, {id: pinId})})
})
}

canActOnPins() {
return isAdminUser() || (getCurrentUserUsername() === this.getUsername())
}

render() {
const { user, pins } = this.state;
const canActOnPins = this.canActOnPins()
return (
<div className="col-md-12">
<div className="col-md-3 no-right-padding">
<UserHomeDetails user={user} />
</div>
<div className='col-md-9 no-left-padding'>
<Pins pins={pins} onDelete={this.deletePin} />
<Pins pins={pins} onDelete={this.deletePin} canDelete={canActOnPins} />
<UserHomeTabs
{...this.state}
{...this.props}
onTabChange={this.onTabChange}
onPinCreate={this.createPin}
onPinDelete={this.deletePin}
showPin={canActOnPins}
/>
</div>
</div>
Expand Down

0 comments on commit f389106

Please sign in to comment.