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

Better handling of public pages and workflows authored by deleted users #19394

Merged
merged 7 commits into from
Jan 27, 2025
Merged
15 changes: 15 additions & 0 deletions client/src/api/schema/schema.ts
Original file line number Diff line number Diff line change
@@ -14717,6 +14717,11 @@ export interface components {
PageContentFormat: "markdown" | "html";
/** PageDetails */
PageDetails: {
/**
* Author deleted
* @description Whether the author of this Page has been deleted.
*/
author_deleted: boolean;
/**
* Content
* @description Raw text contents of the last page revision (type dependent on content_format).
@@ -14815,6 +14820,11 @@ export interface components {
};
/** PageSummary */
PageSummary: {
/**
* Author deleted
* @description Whether the author of this Page has been deleted.
*/
author_deleted: boolean;
/**
* Create Time
* Format: date-time
@@ -16364,6 +16374,11 @@ export interface components {
creator?:
| (components["schemas"]["Person"] | components["schemas"]["galaxy__schema__schema__Organization"])[]
| null;
/**
* Creator deleted
* @description Whether the creator of this Workflow has been deleted.
*/
creator_deleted: boolean;
/**
* Deleted
* @description Whether this item is marked as deleted.
11 changes: 9 additions & 2 deletions client/src/components/Common/PublishedItem.vue
Original file line number Diff line number Diff line change
@@ -12,6 +12,7 @@ interface Item {
owner?: string;
username?: string;
email_hash?: string;
author_deleted?: boolean;
tags?: string[];
title?: string;
}
@@ -37,8 +38,14 @@ const plural = computed(() => {
return `${modelTitle.value}s`;
});

const owner = computed(() => {
if (props.item?.author_deleted) {
return "Archived author";
}
return props.item?.owner ?? props.item?.username ?? "Unavailable";
});

const gravatarSource = computed(() => `https://secure.gravatar.com/avatar/${props.item?.email_hash}?d=identicon`);
const owner = computed(() => props.item?.owner ?? props.item?.username ?? "Unavailable");
const pluralPath = computed(() => plural.value.toLowerCase());
const publishedByUser = computed(() => `/${pluralPath.value}/list_published?f-username=${owner.value}`);
const urlAll = computed(() => `/${pluralPath.value}/list_published`);
@@ -76,7 +83,7 @@ const urlAll = computed(() => `/${pluralPath.value}/list_published`);
<router-link :to="urlAll">All published {{ plural }}</router-link>
</div>

<div>
<div v-if="!props.item?.author_deleted">
<router-link :to="publishedByUser"> Published {{ plural }} by {{ owner }}</router-link>
</div>
</div>
14 changes: 12 additions & 2 deletions client/src/components/Workflow/Published/WorkflowInformation.vue
Original file line number Diff line number Diff line change
@@ -43,6 +43,13 @@ const fullLink = computed(() => {
const userOwned = computed(() => {
return userStore.matchesCurrentUsername(props.workflowInfo.owner);
});

const owner = computed(() => {
if (props.workflowInfo?.creator_deleted) {
return "Archived author";
}
return props.workflowInfo.owner;
});
</script>

<template>
@@ -58,12 +65,15 @@ const userOwned = computed(() => {
<div class="workflow-info-box">
<hgroup class="mb-2">
<Heading h3 size="md" class="mb-0">Author</Heading>
<span class="ml-2">{{ workflowInfo.owner }}</span>
<span class="ml-2">{{ owner }}</span>
</hgroup>

<img alt="User Avatar" :src="gravatarSource" class="mb-2" />

<RouterLink :to="publishedByUser" :target="props.embedded ? '_blank' : ''">
<RouterLink
v-if="!props.workflowInfo?.creator_deleted"
:to="publishedByUser"
:target="props.embedded ? '_blank' : ''">
All published Workflows by {{ workflowInfo.owner }}
</RouterLink>
</div>
4 changes: 4 additions & 0 deletions lib/galaxy/managers/pages.py
Original file line number Diff line number Diff line change
@@ -158,6 +158,10 @@ def index_query(

stmt = select(self.model_class)

# Do not include pages authored by deleted users
if show_published:
stmt = stmt.join(Page.user).where(User.deleted == false())

filters = []
if show_own or (not show_published and not show_shared and not is_admin):
filters = [self.model_class.user == user]
5 changes: 5 additions & 0 deletions lib/galaxy/managers/workflows.py
Original file line number Diff line number Diff line change
@@ -197,6 +197,11 @@ def index_query(
filters.append(StoredWorkflow.published == true())

stmt = select(StoredWorkflow)

# Do not include workflows authored by deleted users
if show_published or show_shared:
stmt = stmt.join(StoredWorkflow.user).where(User.deleted == false())

if show_shared:
stmt = stmt.outerjoin(StoredWorkflow.users_shared_with)
stmt = stmt.outerjoin(StoredWorkflow.tags)
7 changes: 7 additions & 0 deletions lib/galaxy/model/__init__.py
Original file line number Diff line number Diff line change
@@ -7859,6 +7859,7 @@ def to_dict(self, view="collection", value_mapper=None):
rval["latest_workflow_uuid"] = (lambda uuid: str(uuid) if self.latest_workflow.uuid else None)(
self.latest_workflow.uuid
)
rval["creator_deleted"] = self.user.deleted
return rval


@@ -10491,6 +10492,7 @@ class Page(Base, HasTags, Dictifiable, RepresentById, UsesCreateAndUpdateTime):
"deleted",
"username",
"email_hash",
"author_deleted",
"create_time",
"update_time",
]
@@ -10517,6 +10519,11 @@ def username(self):
def email_hash(self):
return md5_hash_str(self.user.email)

# needed to determine how to display page details
@property
def author_deleted(self):
return self.user.deleted


class PageRevision(Base, Dictifiable, RepresentById):
__tablename__ = "page_revision"
5 changes: 5 additions & 0 deletions lib/galaxy/schema/schema.py
Original file line number Diff line number Diff line change
@@ -3788,6 +3788,11 @@ class PageSummary(PageSummaryBase, WithModelClass):
title="Encoded email",
description="The encoded email of the user.",
)
author_deleted: bool = Field(
..., # Required
title="Author deleted",
description="Whether the author of this Page has been deleted.",
)
published: bool = Field(
..., # Required
title="Published",
5 changes: 5 additions & 0 deletions lib/galaxy/schema/workflows.py
Original file line number Diff line number Diff line change
@@ -225,6 +225,11 @@ class StoredWorkflowDetailed(StoredWorkflowSummary):
title="Creator",
description=("Additional information about the creator (or multiple creators) of this workflow."),
)
creator_deleted: bool = Field(
...,
title="Creator deleted",
description="Whether the creator of this Workflow has been deleted.",
)
steps: Dict[
int,
Annotated[

Unchanged files with check annotations Beta

<template>

Check warning on line 1 in client/src/components/Alert.vue

GitHub Actions / client-unit-test (18)

Component name "Alert" should always be multi-word
<b-alert :variant="galaxyKwdToBootstrap" :show="showP" v-bind="$props">
<!-- @slot Message to display in alert -->
<slot> {{ message }} </slot>
/** Display a close button in the alert that allows it to be dismissed */
dismissible: Boolean,
/** Label for the close button, for aria */
dismissLabel: String,

Check warning on line 35 in client/src/components/Alert.vue

GitHub Actions / client-unit-test (18)

Prop 'dismissLabel' requires default value to be set
/** If a number, number of seconds to show before dismissing */
show: [Boolean, Number],

Check warning on line 37 in client/src/components/Alert.vue

GitHub Actions / client-unit-test (18)

Prop 'show' requires default value to be set
/** Should the alert fade out */
fade: Boolean,
},
<template>

Check warning on line 1 in client/src/components/Annotation.vue

GitHub Actions / client-unit-test (18)

Component name "Annotation" should always be multi-word
<ClickToEdit
ref="annotationInput"
v-slot="{ toggleEdit, placeholder, stateValidator }"
<template>
<div>
{{ prefix }}
<span v-html="citationHtml" />

Check warning on line 41 in client/src/components/Citation/CitationItem.vue

GitHub Actions / client-unit-test (18)

'v-html' directive can lead to XSS attack
<a v-if="link" :href="link" target="_blank">
Visit Citation
</template>
<div v-if="source === 'histories'" class="infomessage">
<div v-html="config?.citations_export_message_html"></div>

Check warning on line 68 in client/src/components/Citation/CitationsList.vue

GitHub Actions / client-unit-test (18)

'v-html' directive can lead to XSS attack
</div>
<div class="citations-formatted">
extensions?: string[];
extensionsToggle?: boolean;
noItems?: boolean;
collectionType?: string;

Check warning on line 48 in client/src/components/Collections/common/CollectionCreator.vue

GitHub Actions / client-unit-test (18)

Prop 'collectionType' requires default value to be set
showUpload: boolean;
}
<script setup lang="ts">

Check warning on line 1 in client/src/components/Common/Abbreviation.vue

GitHub Actions / client-unit-test (18)

Component name "Abbreviation" should always be multi-word
interface Props {
explanation: string;
}
<slot />
</div>
</Popper>
<div class="context-menu-overlay" @click="emit('hide')" />

Check warning on line 47 in client/src/components/Common/ContextMenu.vue

GitHub Actions / client-unit-test (18)

Visible, non-interactive elements with click handlers must have at least one keyboard listener

Check warning on line 47 in client/src/components/Common/ContextMenu.vue

GitHub Actions / client-unit-test (18)

Visible, non-interactive elements should not have an interactive handler
</div>
</template>