Skip to content

Commit

Permalink
Move "User access list" to Settings (#6166)
Browse files Browse the repository at this point in the history
* renamed several React view to match current UI

* moved dataset access list to settings tab

* prevent another fetch for dataset

* restored admin access rights check

* updated CHANGELOG

* added info tooltip to user access list
  • Loading branch information
hotzenklotz authored Apr 26, 2022
1 parent d42694a commit 91a72b1
Show file tree
Hide file tree
Showing 12 changed files with 86 additions and 48 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
- Add filter functionality to "Access Permissions" column to filter for public datasets.
- Removed `isActive` and `isPublic` columns to save screen space.
- Changed data layer entries to display layer names instead of categories, e.g. "color" --> "axons".
- Moved the list of "user with access to the selected dataset" from the dashboard to the respective dataset's settings (Sharing Tab). [#6166](https://github.com/scalableminds/webknossos/pull/6166)
- Sped up initial requests for remote zarr dataset by using asynchronous caching. [#6165](https://github.com/scalableminds/webknossos/pull/6165)

### Fixed
Expand Down
4 changes: 2 additions & 2 deletions frontend/javascripts/admin/onboarding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import type { OxalisState } from "oxalis/store";
import Store from "oxalis/store";
import LinkButton from "components/link_button";
import { getDatastores, sendInvitesForOrganization } from "admin/admin_rest_api";
import DatasetImportView from "dashboard/dataset/dataset_import_view";
import DatasetSettingsView from "dashboard/dataset/dataset_settings_view";
import DatasetUploadView from "admin/dataset/dataset_upload_view";
import RegistrationForm from "admin/auth/registration_form";
import CreditsFooter from "components/credits_footer";
Expand Down Expand Up @@ -502,7 +502,7 @@ class OnboardingView extends React.PureComponent<Props, State> {
)}
{this.state.datasetNameToImport != null && (
<Modal visible width="85%" footer={null} maskClosable={false} onCancel={this.advanceStep}>
<DatasetImportView
<DatasetSettingsView
isEditingMode={false}
datasetId={{
name: this.state.datasetNameToImport || "",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { APIDataset, APIUser } from "types/api_flow_types";
import { getDatasetAccessList } from "admin/admin_rest_api";
import { handleGenericError } from "libs/error_handling";
import { stringToColor } from "libs/format_utils";

type Props = {
dataset: APIDataset;
};
Expand Down Expand Up @@ -66,7 +67,6 @@ export default class DatasetAccessListView extends React.PureComponent<Props, St
renderTable() {
return (
<div>
<h5>Users with Access Rights</h5>
<ul>
{this.state.datasetUsers.map((user: APIUser) => (
<li key={user.id}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,11 @@ import { getDatasetExtentAsString } from "oxalis/model/accessors/dataset_accesso
import { stringToColor, formatScale } from "libs/format_utils";
import { trackAction } from "oxalis/model/helpers/analytics";
import CategorizationLabel from "oxalis/view/components/categorization_label";
import DatasetAccessListView from "dashboard/advanced_dataset/dataset_access_list_view";
import DatasetActionView from "dashboard/advanced_dataset/dataset_action_view";
import EditableTextIcon from "oxalis/view/components/editable_text_icon";
import FixedExpandableTable from "components/fixed_expandable_table";
import FormattedDate from "components/formatted_date";
import * as Utils from "libs/utils";
import FixedExpandableTable from "components/fixed_expandable_table";

const { Column } = Table;
const typeHint: APIMaybeUnimportedDataset[] = [];
Expand All @@ -38,7 +37,6 @@ type Props = {
searchQuery: string;
searchTags: Array<string>;
isUserAdmin: boolean;
isUserTeamManager: boolean;
isUserDatasetManager: boolean;
datasetFilteringMode: DatasetFilteringMode;
reloadDataset: (arg0: APIDatasetId, arg1: Array<APIMaybeUnimportedDataset>) => Promise<void>;
Expand Down Expand Up @@ -184,7 +182,6 @@ class DatasetTable extends React.PureComponent<Props, State> {
}

render() {
const { isUserAdmin, isUserTeamManager } = this.props;
const filteredDataSource = this.getFilteredDatasets();
const { sortedInfo } = this.state;
const dataSourceSortedByRank = useLruRank
Expand Down Expand Up @@ -249,11 +246,6 @@ class DatasetTable extends React.PureComponent<Props, State> {
defaultPageSize: 50,
}}
onChange={this.handleChange}
expandedRowRender={
isUserAdmin || isUserTeamManager
? (dataset) => <DatasetAccessListView dataset={dataset} />
: undefined
}
locale={{
emptyText: this.renderEmptyText(),
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {

const FormItem = Form.Item;

export default function SimpleAdvancedDataForm({
export default function DatasetSettingsDataTab({
isReadOnlyDataset,
form,
activeDataSourceEditMode,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ import type { RouteComponentProps } from "react-router-dom";
import { withRouter } from "react-router-dom";
import { DatasetCacheContext } from "dashboard/dataset/dataset_cache_provider";
import { confirmAsync } from "./helper_components";

type Props = {
datasetId: APIDatasetId;
history: RouteComponentProps["history"];
};

const ImportDeleteComponent = ({ datasetId, history }: Props) => {
const DatasetSettingsDeleteTab = ({ datasetId, history }: Props) => {
const [isDeleting, setIsDeleting] = useState(false);
const [dataset, setDataset] = useState<APIDataset | null | undefined>(null);
const datasetContext = useContext(DatasetCacheContext);
Expand Down Expand Up @@ -66,4 +67,4 @@ const ImportDeleteComponent = ({ datasetId, history }: Props) => {
);
};

export default withRouter<RouteComponentProps & Props, any>(ImportDeleteComponent);
export default withRouter<RouteComponentProps & Props, any>(DatasetSettingsDeleteTab);
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Input, Col, Row, DatePicker } from "antd";
import React from "react";
import { FormItemWithInfo } from "./helper_components";
export default function ImportGeneralComponent() {

export default function DatasetSettingsMetadataTab() {
return (
<div>
<Row gutter={48}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,37 @@
import { Button, Input, Checkbox, Tooltip, FormInstance } from "antd";
import React, { useState, useEffect } from "react";
import type { APIDataset, APIDatasetId } from "types/api_flow_types";
import { RouteComponentProps, withRouter } from "react-router-dom";
import { connect } from "react-redux";
import { Button, Input, Checkbox, Tooltip, FormInstance, Collapse } from "antd";
import { CopyOutlined, InfoCircleOutlined, RetweetOutlined } from "@ant-design/icons";
import type { APIDataset, APIDatasetId, APIUser } from "types/api_flow_types";
import { AsyncButton } from "components/async_clickables";
import {
getDatasetSharingToken,
getDataset,
revokeDatasetSharingToken,
} from "admin/admin_rest_api";
import { getDatasetSharingToken, revokeDatasetSharingToken } from "admin/admin_rest_api";
import Toast from "libs/toast";
import features from "features";
import window from "libs/window";
import TeamSelectionComponent from "dashboard/dataset/team_selection_component";
import { CopyOutlined, RetweetOutlined } from "@ant-design/icons";
import DatasetAccessListView from "dashboard/advanced_dataset/dataset_access_list_view";
import { OxalisState } from "oxalis/store";
import { isUserAdminOrTeamManager } from "libs/utils";
import { FormItemWithInfo } from "./helper_components";

type Props = {
form: FormInstance | null;
datasetId: APIDatasetId;
dataset: APIDataset | null | undefined;
hasNoAllowedTeams: boolean;
activeUser: APIUser | null | undefined;
};
export default function ImportSharingComponent({ form, datasetId, hasNoAllowedTeams }: Props) {

function DatasetSettingsSharingTab({
form,
datasetId,
dataset,
hasNoAllowedTeams,
activeUser,
}: Props) {
const [sharingToken, setSharingToken] = useState("");
const [dataSet, setDataSet] = useState<APIDataset | null | undefined>(null);

const allowedTeamsComponent = (
<FormItemWithInfo
name={["dataset", "allowedTeams"]}
Expand All @@ -39,9 +50,7 @@ export default function ImportSharingComponent({ form, datasetId, hasNoAllowedTe

async function fetch() {
const newSharingToken = await getDatasetSharingToken(datasetId);
const newDataSet = await getDataset(datasetId);
setSharingToken(newSharingToken);
setDataSet(newDataSet);
}

useEffect(() => {
Expand Down Expand Up @@ -77,6 +86,7 @@ export default function ImportSharingComponent({ form, datasetId, hasNoAllowedTe

function getSharingLink() {
if (!form) return undefined;

const doesNeedToken = !form.getFieldValue("dataset.isPublic");
const tokenSuffix = `?token=${sharingToken}`;
return `${window.location.origin}/datasets/${datasetId.owningOrganization}/${
Expand All @@ -85,13 +95,35 @@ export default function ImportSharingComponent({ form, datasetId, hasNoAllowedTe
}

function getAllowUsageCode() {
if (dataSet != null) {
const dataStoreName = dataSet.dataStore.name;
const dataStoreURL = dataSet.dataStore.url;
if (dataset != null) {
const dataStoreName = dataset.dataStore.name;
const dataStoreURL = dataset.dataStore.url;
return `${dataStoreName}, ${dataStoreURL}, ${datasetId.name}`;
} else return "";
}

function getUserAccessList() {
if (!activeUser || !dataset) return undefined;
if (!isUserAdminOrTeamManager(activeUser)) return undefined;

const header = (
<span>
All users with access permission to work with this dataset{" "}
<Tooltip title="Based on the specified team permissions and individiual user roles. Any changes will only appear after pressing the Save button.">
<InfoCircleOutlined style={{ color: "gray" }} />
</Tooltip>
</span>
);

return (
<Collapse collapsible="header">
<Collapse.Panel header={header} key="1">
<DatasetAccessListView dataset={dataset} />
</Collapse.Panel>
</Collapse>
);
}

return form ? (
<div>
<FormItemWithInfo
Expand Down Expand Up @@ -179,6 +211,15 @@ export default function ImportSharingComponent({ form, datasetId, hasNoAllowedTe
</Input.Group>
</FormItemWithInfo>
)}

{getUserAccessList()}
</div>
) : null;
}

const mapStateToProps = (state: OxalisState) => ({
activeUser: state.activeUser,
});

const connector = connect(mapStateToProps);
export default connector(withRouter<RouteComponentProps & Props, any>(DatasetSettingsSharingTab));
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,11 @@ import features from "features";
import { isDatasourceJSONValid } from "types/validation";
import { enforceValidatedDatasetViewConfiguration } from "types/schemas/dataset_view_configuration_defaults";
import { Hideable, hasFormError, jsonEditStyle } from "./helper_components";
import DefaultConfigComponent from "./default_config_component";
import ImportGeneralComponent from "./import_general_component";
import ImportSharingComponent from "./import_sharing_component";
import ImportDeleteComponent from "./import_delete_component";
import SimpleAdvancedDataForm from "./simple_advanced_data_form";
import DatasetSettingsViewConfigTab from "./dataset_settings_viewconfig_tab";
import DatasetSettingsMetadataTab from "./dataset_settings_metadata_tab";
import DatasetSettingsSharingTab from "./dataset_settings_sharing_tab";
import DatasetSettingsDeleteTab from "./dataset_settings_delete_tab";
import DatasetSettingsDataTab from "./dataset_settings_data_tab";

const { TabPane } = Tabs;
const FormItem = Form.Item;
Expand Down Expand Up @@ -153,7 +153,7 @@ function ensureValidScaleOnInferredDataSource(
return inferredDataSourceClone;
}

class DatasetImportView extends React.PureComponent<PropsWithFormAndRouter, State> {
class DatasetSettingsView extends React.PureComponent<PropsWithFormAndRouter, State> {
formRef = React.createRef<FormInstance>();
unblock: UnregisterCallback | null | undefined;
blockTimeoutId: number | null | undefined;
Expand Down Expand Up @@ -866,7 +866,7 @@ class DatasetImportView extends React.PureComponent<PropsWithFormAndRouter, Stat
// to hidden form elements.
}
<Hideable hidden={this.state.activeTabKey !== "data"}>
<SimpleAdvancedDataForm
<DatasetSettingsDataTab
key="SimpleAdvancedDataForm"
isReadOnlyDataset={
this.state.dataset != null &&
Expand Down Expand Up @@ -903,17 +903,18 @@ class DatasetImportView extends React.PureComponent<PropsWithFormAndRouter, Stat
forceRender
>
<Hideable hidden={this.state.activeTabKey !== "sharing"}>
<ImportSharingComponent
<DatasetSettingsSharingTab
form={form}
datasetId={this.props.datasetId}
dataset={this.state.dataset}
hasNoAllowedTeams={hasNoAllowedTeams}
/>
</Hideable>
</TabPane>

<TabPane tab={<span>Metadata</span>} key="general" forceRender>
<Hideable hidden={this.state.activeTabKey !== "general"}>
<ImportGeneralComponent />
<DatasetSettingsMetadataTab />
</Hideable>
</TabPane>

Expand All @@ -923,15 +924,15 @@ class DatasetImportView extends React.PureComponent<PropsWithFormAndRouter, Stat
forceRender
>
<Hideable hidden={this.state.activeTabKey !== "defaultConfig"}>
<DefaultConfigComponent />
<DatasetSettingsViewConfigTab />
</Hideable>
</TabPane>

{isUserAdmin && features().allowDeleteDatasets ? (
<TabPane tab={<span> Delete Dataset </span>} key="deleteDataset" forceRender>
<Hideable hidden={this.state.activeTabKey !== "deleteDataset"}>
<DatasetCacheProvider>
<ImportDeleteComponent datasetId={this.props.datasetId} />
<DatasetSettingsDeleteTab datasetId={this.props.datasetId} />
</DatasetCacheProvider>
</Hideable>
</TabPane>
Expand Down Expand Up @@ -961,4 +962,4 @@ const mapStateToProps = (state: OxalisState): StateProps => ({
});

const connector = connect(mapStateToProps);
export default connector(withRouter<RouteComponentProps & OwnProps, any>(DatasetImportView));
export default connector(withRouter<RouteComponentProps & OwnProps, any>(DatasetSettingsView));
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import { getDefaultLayerViewConfiguration } from "types/schemas/dataset_view_con
import { layerViewConfigurations } from "messages";
import type { DatasetLayerConfiguration } from "oxalis/store";
import { FormItemWithInfo, jsonEditStyle } from "./helper_components";

const FormItem = Form.Item;
export default function DefaultConfigComponent() {

export default function DatasetSettingsViewConfigTab() {
const columns = [
{
title: "Name",
Expand Down
1 change: 0 additions & 1 deletion frontend/javascripts/dashboard/dataset_view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,6 @@ function DatasetView(props: Props) {
searchQuery={searchQuery}
searchTags={searchTags}
isUserAdmin={Utils.isUserAdmin(user)}
isUserTeamManager={Utils.isUserTeamManager(user)}
isUserDatasetManager={Utils.isUserDatasetManager(user)}
datasetFilteringMode={datasetFilteringMode}
updateDataset={context.updateCachedDataset}
Expand Down
6 changes: 3 additions & 3 deletions frontend/javascripts/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import AuthTokenView from "admin/auth/auth_token_view";
import ChangePasswordView from "admin/auth/change_password_view";
import DashboardView, { urlTokenToTabKeyMap } from "dashboard/dashboard_view";
import DatasetAddView from "admin/dataset/dataset_add_view";
import DatasetImportView from "dashboard/dataset/dataset_import_view";
import DatasetSettingsView from "dashboard/dataset/dataset_settings_view";
import DisableGenericDnd from "components/disable_generic_dnd";
import FinishResetPasswordView from "admin/auth/finish_reset_password_view";
import JobListView from "admin/job/job_list_view";
Expand Down Expand Up @@ -333,7 +333,7 @@ class ReactRouter extends React.Component<Props> {
isAuthenticated={isAuthenticated}
path="/datasets/:organizationName/:datasetName/import"
render={({ match }: ContextRouter) => (
<DatasetImportView
<DatasetSettingsView
isEditingMode={false}
datasetId={{
name: match.params.datasetName || "",
Expand All @@ -351,7 +351,7 @@ class ReactRouter extends React.Component<Props> {
isAuthenticated={isAuthenticated}
path="/datasets/:organizationName/:datasetName/edit"
render={({ match }: ContextRouter) => (
<DatasetImportView
<DatasetSettingsView
isEditingMode
datasetId={{
name: match.params.datasetName || "",
Expand Down

0 comments on commit 91a72b1

Please sign in to comment.