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

task/WC-186: Serve published projects on a world-readable page #1046

Open
wants to merge 1 commit into
base: task/digital-rocks
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ CombinedBreadcrumbs.propTypes = {
path: PropTypes.string.isRequired,
section: PropTypes.string.isRequired,
isPublic: PropTypes.bool,
basePath: PropTypes.string,
className: PropTypes.string,
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const BreadcrumbsDropdown = ({
scheme,
system,
path,
basePath,
section,
isPublic,
}) => {
Expand All @@ -36,7 +37,7 @@ const BreadcrumbsDropdown = ({
: null;

const handleNavigation = (targetPath) => {
const basePath = isPublic ? '/public-data' : '/workbench/data';
if (!basePath) basePath = isPublic ? '/public-data' : '/workbench/data';
let url;

if (scheme === 'projects' && targetPath === systemName) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ const DataFilesListing = ({
path,
isPublic,
rootSystem,
basePath,
}) => {
// Redux hooks
const location = useLocation();
Expand All @@ -54,7 +55,7 @@ const DataFilesListing = ({
);
const sharedWorkspaces = systems.find((e) => e.scheme === 'projects');
const isPortalProject = scheme === 'projects';
const hideSearchBar = isPortalProject && sharedWorkspaces.hideSearchBar;
const hideSearchBar = isPortalProject && sharedWorkspaces?.hideSearchBar;

const showViewPath = useSelector(
(state) =>
Expand Down Expand Up @@ -101,6 +102,7 @@ const DataFilesListing = ({
scheme={scheme}
href={row.original._links.self.href}
isPublic={isPublic}
basePath={basePath}
length={row.original.length}
metadata={row.original.metadata}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export const FileNavCell = React.memo(
scheme,
href,
isPublic,
basePath,
length,
metadata,
rootSystem,
Expand All @@ -80,7 +81,7 @@ export const FileNavCell = React.memo(
});
};

const basePath = isPublic ? '/public-data' : '/workbench/data';
if (!basePath) basePath = isPublic ? '/public-data' : '/workbench/data';

return (
<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ const DataFilesProjectFileListing = ({ rootSystem, system, path }) => {
metadata.members
.filter((member) =>
member.user
? member.user.username === state.authenticatedUser.user.username
? member.user.username === state.authenticatedUser?.user?.username
: { access: null }
)
.map((currentUser) => currentUser.access === 'owner')[0]
Expand Down Expand Up @@ -172,6 +172,7 @@ const DataFilesProjectFileListing = ({ rootSystem, system, path }) => {
scheme="projects"
system={system}
path={path || '/'}
basePath="/publications"
rootSystem={rootSystem}
/>
</SectionTableWrapper>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ import './DataFilesPublicationsList.scss';
import Searchbar from '_common/Searchbar';
import { formatDate, formatDateTimeFromValue } from 'utils/timeFormat';

const DataFilesPublicationsList = ({ rootSystem }) => {
const DataFilesPublicationsList = ({ rootSystem, basePath }) => {
const { error, loading, publications } = useSelector(
(state) => state.publications.listing
);

const _basePath = basePath ?? '/workbench/data';

const query = queryStringParser.parse(useLocation().search);

const systems = useSelector(
Expand All @@ -38,7 +40,7 @@ const DataFilesPublicationsList = ({ rootSystem }) => {
type: 'PUBLICATIONS_GET_PUBLICATIONS',
payload: {
queryString: query.query_string,
system: selectedSystem.system,
system: selectedSystem?.system,
},
});
}, [dispatch, query.query_string]);
Expand All @@ -60,7 +62,7 @@ const DataFilesPublicationsList = ({ rootSystem }) => {
Cell: (el) => (
<Link
className="data-files-nav-link"
to={`/workbench/data/tapis/projects/${rootSystem}/${el.row.original.id}`}
to={`${_basePath}/tapis/projects/${selectedSystem?.system}/${el.row.original.id}`}
>
{el.value}
</Link>
Expand Down Expand Up @@ -141,6 +143,7 @@ const DataFilesPublicationsList = ({ rootSystem }) => {
isLoading={loading}
noDataText={noDataText}
className="publications-listing"
columnMemoProps={[selectedSystem]}
/>
</div>
</SectionTableWrapper>
Expand Down
43 changes: 43 additions & 0 deletions client/src/components/Publications/PublicationDetailPublicView.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from 'react';
import CombinedBreadcrumbs from '../DataFiles/CombinedBreadcrumbs/CombinedBreadcrumbs';
import DataFilesPreviewModal from '../DataFiles/DataFilesModals/DataFilesPreviewModal';
import DataFilesProjectCitationModal from '../DataFiles/DataFilesModals/DataFilesProjectCitationModal';
import DataFilesProjectTreeModal from '../DataFiles/DataFilesModals/DataFilesProjectTreeModal';
import DataFilesPublicationAuthorsModal from '../DataFiles/DataFilesModals/DataFilesPublicationAuthorsModal';
import DataFilesShowPathModal from '../DataFiles/DataFilesModals/DataFilesShowPathModal';
import DataFilesViewDataModal from '../DataFiles/DataFilesModals/DataFilesViewDataModal';
import DataFilesProjectFileListing from '../DataFiles/DataFilesProjectFileListing/DataFilesProjectFileListing';

function PublicationDetailPublicView({params}) {
return (
<div
style={{
display: 'flex',
flexDirection: 'column',
padding: '10px',
}}
>
<CombinedBreadcrumbs
api="tapis"
scheme="projects"
system={params.system}
path={params.path || ''}
section="FilesListing"
basePath="/publications"
/>
<DataFilesProjectFileListing
rootSystem={params.root_system}
system={params.system}
path={params.path || '/'}
/>
<DataFilesPreviewModal />
<DataFilesShowPathModal />
<DataFilesProjectTreeModal />
<DataFilesPublicationAuthorsModal />
<DataFilesProjectCitationModal />
<DataFilesViewDataModal />
</div>
);
}

export default PublicationDetailPublicView;
14 changes: 14 additions & 0 deletions client/src/components/Publications/PublicationsPublicView.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react';
import DataFilesPublicationsList from '../DataFiles/DataFilesPublicationsList/DataFilesPublicationsList';
import DataFilesProjectDescriptionModal from '../DataFiles/DataFilesModals/DataFilesProjectDescriptionModal';

function PublicationsPublicView() {
return (
<div>
<DataFilesPublicationsList basePath="/publications" />
<DataFilesProjectDescriptionModal />
</div>
);
}

export default PublicationsPublicView
15 changes: 14 additions & 1 deletion client/src/components/Workbench/AppRouter.jsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useSystems } from 'hooks/datafiles';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import { BrowserRouter as Router, Route, Redirect } from 'react-router-dom';
import Workbench from './Workbench';
import * as ROUTES from '../../constants/routes';
import TicketStandaloneCreate from '../Tickets/TicketStandaloneCreate';
import PublicData from '../PublicData/PublicData';
import RequestAccess from '../RequestAccess/RequestAccess';
import GoogleDrivePrivacyPolicy from '../ManageAccount/GoogleDrivePrivacyPolicy';
import SiteSearch from '../SiteSearch';
import PublicationsPublicView from '../Publications/PublicationsPublicView';
import PublicationDetailPublicView from '../Publications/PublicationDetailPublicView';


function AppRouter() {
const dispatch = useDispatch();
Expand All @@ -17,7 +20,7 @@
(state) => state.authenticatedUser.user
);
const hasCustomSagas = useSelector(
(state) => state.workbench.config.hasCustomSagas

Check failure on line 23 in client/src/components/Workbench/AppRouter.jsx

View workflow job for this annotation

GitHub Actions / Client_Side_Unit_Tests

src/components/Workbench/index.test.jsx > AppRouter > renders AppRouter and dispatches events

TypeError: Cannot read properties of undefined (reading 'config') ❯ src/components/Workbench/AppRouter.jsx:23:32 ❯ useSelectorWithStoreAndSubscription node_modules/react-redux/lib/hooks/useSelector.js:39:30 ❯ Proxy.useSelector node_modules/react-redux/lib/hooks/useSelector.js:139:25 ❯ AppRouter src/components/Workbench/AppRouter.jsx:22:26 ❯ renderWithHooks node_modules/react-dom/cjs/react-dom.development.js:14985:18 ❯ mountIndeterminateComponent node_modules/react-dom/cjs/react-dom.development.js:17811:13 ❯ beginWork node_modules/react-dom/cjs/react-dom.development.js:19049:16 ❯ HTMLUnknownElement.callCallback node_modules/react-dom/cjs/react-dom.development.js:3945:14 ❯ HTMLUnknownElement.callTheUserObjectsOperation node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30 ❯ innerInvokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:338:25
);

useEffect(() => {
Expand Down Expand Up @@ -45,6 +48,16 @@
<Route path={ROUTES.WORKBENCH} component={Workbench} />
<Route path="/tickets/new" component={TicketStandaloneCreate} />
<Route path="/public-data" component={PublicData} />
<Route path="/publications" exact component={PublicationsPublicView}/>
<Route path="/publications/tapis/projects/:root_system" exact>
<Redirect to="/publications" />
</Route>
<Route
path={`/publications/tapis/projects/:root_system/:system/:path*`}
render={({ match: { params } }) => {
return <PublicationDetailPublicView params={params}/>;
}}
/>
<Route path="/request-access" component={RequestAccess} />
<Route
path="/googledrive-privacy-policy"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const DataFilesProjectFileListingAddon = ({ rootSystem, system }) => {
const { canEditDataset, canRequestPublication, canReviewPublication } =
useSelector((state) => {
const { members } = state.projects.metadata;
const { username } = state.authenticatedUser.user;
const { username } = state.authenticatedUser?.user ?? {};
const currentUser = members.find(
(member) => member.user?.username === username
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const DataFilesProjectFileListingMetadataTitleAddon = ({
const userAccess = state.projects.metadata.members
.filter((member) =>
member.user
? member.user.username === state.authenticatedUser.user.username
? member.user.username === state.authenticatedUser?.user?.username
: { access: null }
)
.map((currentUser) => {
Expand Down
2 changes: 1 addition & 1 deletion server/conf/nginx/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ http {
alias /srv/www/portal/server/docs;
}

location ~ ^/(core|auth|workbench|tickets|googledrive-privacy-policy|public-data|request-access|accounts|api|login|webhooks|search) {
location ~ ^/(core|auth|workbench|tickets|googledrive-privacy-policy|public-data|publications|request-access|accounts|api|login|webhooks|search) {
proxy_pass http://portal_core;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
Expand Down
6 changes: 5 additions & 1 deletion server/portal/apps/datafiles/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def get(self, request):
response['default_host'] = system_def.host
response['default_system'] = system_id
else:
response['system_list'] = [sys for sys in portal_systems if sys['scheme'] == 'public']
response['system_list'] = [sys for sys in portal_systems if sys['scheme'] == 'public' or sys['system'] == settings.PORTAL_PROJECTS_PUBLISHED_ROOT_SYSTEM_NAME]

return JsonResponse(response)

Expand All @@ -71,6 +71,8 @@ def get(self, request, systemId):
public_sys = next((sys for sys in settings.PORTAL_DATAFILES_STORAGE_SYSTEMS if sys['scheme'] == 'public'), None)
if public_sys and public_sys['system'] == systemId:
client = service_account()
if systemId.startswith(settings.PORTAL_PROJECTS_PUBLISHED_SYSTEM_PREFIX):
client = service_account()
else:
return JsonResponse({'message': 'Unauthorized'}, status=401)
system_def = client.systems.getSystem(systemId=systemId)
Expand All @@ -92,6 +94,8 @@ def get(self, request, operation=None, scheme=None, system=None, path='/'):
public_sys = next((sys for sys in settings.PORTAL_DATAFILES_STORAGE_SYSTEMS if sys['scheme'] == 'public'), None)
if public_sys and public_sys['system'] == system and path.startswith(public_sys['homeDir'].strip('/')):
client = service_account()
if system and system.startswith(settings.PORTAL_PROJECTS_PUBLISHED_SYSTEM_PREFIX):
client = service_account()
else:
return JsonResponse(
{'message': 'This data requires authentication to view.'},
Expand Down
9 changes: 6 additions & 3 deletions server/portal/apps/projects/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from django.conf import settings
from django.http import JsonResponse
from django.utils.decorators import method_decorator
from portal.libs.agave.utils import service_account
from portal.utils.decorators import agave_jwt_login
from portal.exceptions.api import ApiException
from portal.views.base import BaseApiView
Expand Down Expand Up @@ -157,7 +158,6 @@ def post(self, request): # pylint: disable=no-self-use


@method_decorator(agave_jwt_login, name='dispatch')
@method_decorator(login_required, name='dispatch')
class ProjectInstanceApiView(BaseApiView):
"""Project Instance API view.

Expand All @@ -178,8 +178,11 @@ def get(self, request, project_id=None, system_id=None):
# Based on url mapping, either system_id or project_id is always available.
if system_id is not None:
project_id = system_id.split(f"{settings.PORTAL_PROJECTS_SYSTEM_PREFIX}.")[1]

client = request.user.tapis_oauth.client

if system_id and system_id.startswith(settings.PORTAL_PROJECTS_PUBLISHED_SYSTEM_PREFIX):
client = service_account()
else:
client = request.user.tapis_oauth.client

prj = get_project(client, project_id)

Expand Down
1 change: 1 addition & 0 deletions server/portal/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@
namespace='googledrive-privacy-policy')),
path('workbench/', include('portal.apps.workbench.urls', namespace='workbench')),
path('public-data/', include('portal.apps.public_data.urls', namespace='public')),
path('publications/', include('portal.apps.public_data.urls', namespace='publications')),
path('request-access/', include('portal.apps.request_access.urls', namespace='request_access')),
path('search/', include('portal.apps.site_search.urls', namespace='site_search')),

Expand Down
Loading