diff --git a/client/src/components/Common/PublishedItem.vue b/client/src/components/Common/PublishedItem.vue index 7f4aa88fc25..7be9e46c5f9 100644 --- a/client/src/components/Common/PublishedItem.vue +++ b/client/src/components/Common/PublishedItem.vue @@ -82,11 +82,11 @@ const { showActivityBar, showToolbox } = usePanels();

Related Pages

- All published {{ plural }}. + All published {{ plural }}
- Published {{ plural }} by {{ owner }}. + Published {{ plural }} by {{ owner }}
diff --git a/client/src/components/Grid/GridHistory.vue b/client/src/components/Grid/GridHistory.vue index 071ab611c15..fe61e4ab010 100644 --- a/client/src/components/Grid/GridHistory.vue +++ b/client/src/components/Grid/GridHistory.vue @@ -18,9 +18,10 @@ library.add(faPlus); interface Props { activeList?: "my" | "shared" | "published"; + username?: string; } -withDefaults(defineProps(), { +const props = withDefaults(defineProps(), { activeList: "my", }); @@ -66,6 +67,6 @@ withDefaults(defineProps(), { - + diff --git a/client/src/components/Grid/GridList.vue b/client/src/components/Grid/GridList.vue index 267cd0ec913..da08de2739c 100644 --- a/client/src/components/Grid/GridList.vue +++ b/client/src/components/Grid/GridList.vue @@ -37,6 +37,8 @@ interface Props { embedded?: boolean; // rows per page to be shown limit?: number; + // username for initial search + usernameSearch?: string; } const props = withDefaults(defineProps(), { @@ -213,6 +215,11 @@ function onSelectAll(current: boolean): void { * Initialize grid data */ onMounted(() => { + if (props.usernameSearch) { + const search_query = `user:${props.usernameSearch}`.trim(); + filterText.value = search_query; + onSearch(search_query); + } getGridData(); eventBus.on(onRouterPush); displayInitialMessage(); diff --git a/client/src/components/Grid/GridPage.vue b/client/src/components/Grid/GridPage.vue index e340c9eaecf..3508bba416f 100644 --- a/client/src/components/Grid/GridPage.vue +++ b/client/src/components/Grid/GridPage.vue @@ -17,9 +17,10 @@ library.add(faPlus); interface Props { activeList?: "my" | "published"; + username?: string; } -withDefaults(defineProps(), { +const props = withDefaults(defineProps(), { activeList: "my", }); @@ -47,6 +48,6 @@ withDefaults(defineProps(), { Public Pages - + diff --git a/client/src/components/History/Content/ContentItem.vue b/client/src/components/History/Content/ContentItem.vue index 0e5497d645c..70059b96f61 100644 --- a/client/src/components/History/Content/ContentItem.vue +++ b/client/src/components/History/Content/ContentItem.vue @@ -185,41 +185,54 @@ function onKeyDown(event: KeyboardEvent) { if (event.key === "Enter" || event.key === " ") { event.preventDefault(); - onClick(event); - } else if ((event.key === "ArrowUp" || event.key === "ArrowDown") && event.shiftKey) { + onClick(); + } else if ((event.key === "ArrowUp" || event.key === "ArrowDown") && !event.shiftKey) { event.preventDefault(); - emit("shift-select", event.key); - } else if (event.key === "ArrowUp" || event.key === "ArrowDown") { - event.preventDefault(); - emit("init-key-selection"); emit("arrow-navigate", event.key); - } else if (event.key === "Tab") { - emit("init-key-selection"); - } else if (event.key === "Delete" && !props.selected && !props.item.deleted) { - event.preventDefault(); - onDelete(event.shiftKey); - } else if (event.key === "Escape") { - event.preventDefault(); - emit("hide-selection"); - } else if (event.key === "a" && isSelectKey(event)) { - event.preventDefault(); - emit("select-all"); + } + + if (props.writable) { + if (event.key === "Tab") { + emit("init-key-selection"); + } else { + event.preventDefault(); + if ((event.key === "ArrowUp" || event.key === "ArrowDown") && event.shiftKey) { + emit("shift-select", event.key); + } else if (event.key === "ArrowUp" || event.key === "ArrowDown") { + emit("init-key-selection"); + } else if (event.key === "Delete" && !props.selected && !props.item.deleted) { + onDelete(event.shiftKey); + emit("arrow-navigate", "ArrowDown"); + } else if (event.key === "Escape") { + emit("hide-selection"); + } else if (event.key === "a" && isSelectKey(event)) { + emit("select-all"); + } + } } } -function onClick(e: Event) { +function onClick(e?: Event) { const event = e as KeyboardEvent; - if (event && event.shiftKey && isSelectKey(event)) { - emit("selected-to", false); - } else if (event && isSelectKey(event)) { - emit("init-key-selection"); - emit("update:selected", !props.selected); - } else if (event && event.shiftKey) { - emit("selected-to", true); - } else if (props.isPlaceholder) { - emit("init-key-selection"); - } else if (props.isDataset) { - emit("init-key-selection"); + if (event && props.writable) { + if (event.shiftKey && isSelectKey(event)) { + emit("selected-to", false); + return; + } else if (isSelectKey(event)) { + emit("init-key-selection"); + emit("update:selected", !props.selected); + return; + } else if (event.shiftKey) { + emit("selected-to", true); + return; + } else { + emit("init-key-selection"); + } + } + if (props.isPlaceholder) { + return; + } + if (props.isDataset) { emit("update:expand-dataset", !props.expandDataset); } else { emit("view-collection", props.item, props.name); diff --git a/client/src/components/History/CurrentHistory/HistoryPanel.vue b/client/src/components/History/CurrentHistory/HistoryPanel.vue index 8be2868a9c5..8b2062323ca 100644 --- a/client/src/components/History/CurrentHistory/HistoryPanel.vue +++ b/client/src/components/History/CurrentHistory/HistoryPanel.vue @@ -50,7 +50,6 @@ interface Props { history: HistorySummary; filter?: string; canEditHistory?: boolean; - shouldShowControls?: boolean; filterable?: boolean; isMultiViewItem?: boolean; } @@ -61,7 +60,6 @@ const props = withDefaults(defineProps(), { listOffset: 0, filter: "", canEditHistory: true, - shouldShowControls: true, filterable: false, isMultiViewItem: false, }); @@ -80,7 +78,7 @@ const querySelectionBreak = ref(false); const dragTarget = ref(null); const contentItemRefs = computed(() => { return historyItems.value.reduce((acc: ContentItemRef, item) => { - acc[`item-${item.id}`] = ref(null); + acc[itemUniqueKey(item)] = ref(null); return acc; }, {}); }); @@ -405,6 +403,10 @@ function getItemKey(item: HistoryItem) { return item.type_id; } +function itemUniqueKey(item: HistoryItem) { + return `${item.history_content_type}-${item.id}`; +} + onMounted(async () => { // `filterable` here indicates if this is the current history panel if (props.filterable && !props.filter) { @@ -413,9 +415,11 @@ onMounted(async () => { await loadHistoryItems(); // if there is a listOffset, we are coming from a collection view, so focus on item at that offset if (props.listOffset) { - const itemId = historyItems.value[props.listOffset]?.id; - const itemElement = contentItemRefs.value[`item-${itemId}`]?.value?.$el as HTMLElement; - itemElement?.focus(); + const itemAtOffset = historyItems.value[props.listOffset]; + if (itemAtOffset) { + const itemElement = contentItemRefs.value[itemUniqueKey(itemAtOffset)]?.value?.$el as HTMLElement; + itemElement?.focus(); + } } }); @@ -427,7 +431,7 @@ function arrowNavigate(item: HistoryItem, eventKey: string) { nextItem = historyItems.value[historyItems.value.indexOf(item) - 1]; } if (nextItem) { - const itemElement = contentItemRefs.value[`item-${nextItem.id}`]?.value?.$el as HTMLElement; + const itemElement = contentItemRefs.value[itemUniqueKey(nextItem)]?.value?.$el as HTMLElement; itemElement?.focus(); } return nextItem; @@ -510,13 +514,13 @@ function setItemDragstart( :history="history" :is-watching="isWatching" :last-checked="lastCheckedTime" - :show-controls="shouldShowControls" + :show-controls="canEditHistory" :filter-text.sync="filterText" :hide-reload="isMultiViewItem" @reloadContents="reloadContents" /> (); const history = ref({}); onMounted(async () => { - const result = await historyFetcher({ history_id: props.id }); - - history.value = result; + const { data } = await historyFetcher({ history_id: props.id }); + history.value = data; }); diff --git a/client/src/components/History/HistoryView.test.js b/client/src/components/History/HistoryView.test.js index 37fa9c79d49..f87a3324f5b 100644 --- a/client/src/components/History/HistoryView.test.js +++ b/client/src/components/History/HistoryView.test.js @@ -94,12 +94,15 @@ describe("History center panel View", () => { expect(tags.text()).toContain("tag_1"); expect(tags.text()).toContain("tag_2"); // HistoryCounter - expect(wrapper.find("[data-description='storage dashboard button']").attributes("disabled")).toBeTruthy(); expect(wrapper.find("[data-description='show active items button']").text()).toEqual("8"); expect(wrapper.find("[data-description='include deleted items button']").text()).toEqual("1"); expect(wrapper.find("[data-description='include hidden items button']").text()).toEqual("2"); } + function storageDashboardButtonDisabled(wrapper) { + return wrapper.find("[data-description='storage dashboard button']").attributes("disabled"); + } + it("current user's current history", async () => { const history = create_history("history_1", "user_1", false); const wrapper = await createWrapper(localVue, "user_1", history); @@ -117,6 +120,9 @@ describe("History center panel View", () => { // parts of the layout that should be similar for all cases expectCorrectLayout(wrapper); + // storage dashboard button should be enabled + expect(storageDashboardButtonDisabled(wrapper)).toBeFalsy(); + // make sure all history items show up const historyItems = wrapper.findAllComponents(ContentItem); expect(historyItems.length).toBe(10); @@ -138,6 +144,9 @@ describe("History center panel View", () => { expect(switchButton.exists()).toBe(false); expect(importButton.attributes("disabled")).toBeFalsy(); + // storage dashboard button should be disabled + expect(storageDashboardButtonDisabled(wrapper)).toBeTruthy(); + // parts of the layout that should be similar for all cases expectCorrectLayout(wrapper); }); @@ -153,6 +162,9 @@ describe("History center panel View", () => { expect(switchButton.attributes("disabled")).toBeFalsy(); expect(importButton.exists()).toBe(false); + // storage dashboard button should be enabled + expect(storageDashboardButtonDisabled(wrapper)).toBeFalsy(); + // parts of the layout that should be similar for all cases expectCorrectLayout(wrapper); }); @@ -168,6 +180,9 @@ describe("History center panel View", () => { expect(switchButton.attributes("disabled")).toBeTruthy(); expect(importButton.exists()).toBe(false); + // storage dashboard button should be disabled + expect(storageDashboardButtonDisabled(wrapper)).toBeTruthy(); + // instead we have an alert expect(wrapper.find("[data-description='history state info']").text()).toBe("This history has been purged."); }); @@ -183,6 +198,9 @@ describe("History center panel View", () => { expect(importButton.exists()).toBe(true); expect(importButton.attributes("disabled")).toBeFalsy(); + // storage dashboard button should be disabled + expect(storageDashboardButtonDisabled(wrapper)).toBeTruthy(); + expectCorrectLayout(wrapper); expect(wrapper.find("[data-description='history state info']").exists()).toBe(false); }); @@ -198,6 +216,9 @@ describe("History center panel View", () => { expect(switchButton.attributes("disabled")).toBeTruthy(); expect(importButton.exists()).toBe(false); + // storage dashboard button should be disabled + expect(storageDashboardButtonDisabled(wrapper)).toBeTruthy(); + expectCorrectLayout(wrapper); expect(wrapper.find("[data-description='history state info']").text()).toBe("This history has been archived."); }); diff --git a/client/src/components/History/HistoryView.vue b/client/src/components/History/HistoryView.vue index a6dbee74fd1..9fa3061b4e0 100644 --- a/client/src/components/History/HistoryView.vue +++ b/client/src/components/History/HistoryView.vue @@ -37,7 +37,6 @@ v-else :history="history" :can-edit-history="canEditHistory" - :should-show-controls="false" filterable @view-collection="onViewCollection" /> diff --git a/client/src/entry/analysis/router.js b/client/src/entry/analysis/router.js index 26268196303..87202bbd41c 100644 --- a/client/src/entry/analysis/router.js +++ b/client/src/entry/analysis/router.js @@ -281,9 +281,10 @@ export function getRouter(Galaxy) { { path: "histories/list_published", component: GridHistory, - props: { + props: (route) => ({ activeList: "published", - }, + username: route.query["f-username"], + }), }, { path: "histories/archived", @@ -381,9 +382,10 @@ export function getRouter(Galaxy) { { path: "pages/list_published", component: GridPage, - props: { + props: (route) => ({ activeList: "published", - }, + username: route.query["f-username"], + }), }, { path: "storage/history/:historyId", diff --git a/config/plugins/webhooks/news/config.yml b/config/plugins/webhooks/news/config.yml deleted file mode 100644 index cf49026fef9..00000000000 --- a/config/plugins/webhooks/news/config.yml +++ /dev/null @@ -1,7 +0,0 @@ -id: news -type: - - masthead -activate: true - -icon: fa-bullhorn -tooltip: See the Galaxy Release Notes diff --git a/config/plugins/webhooks/news/script.js b/config/plugins/webhooks/news/script.js deleted file mode 100644 index d5b7928e832..00000000000 --- a/config/plugins/webhooks/news/script.js +++ /dev/null @@ -1,111 +0,0 @@ -(function () { - function hideNewsOverlay() { - const container = document.getElementById("news-container"); - if (container) { - container.style.visibility = "hidden"; - } - } - - function newsSeen(currentGalaxyVersion) { - // When it's seen, remove the red indicator if it exists and store the current version. - const newsIndicator = document.getElementById("news-indicator"); - if (newsIndicator) { - newsIndicator.remove(); - } - window.localStorage.setItem("galaxy-news-seen-release", currentGalaxyVersion); - } - - function newsUnseen() { - // When there is news, add an red indicator to the icon. - const newsIconSpan = document.querySelector("#news .nav-link"); - newsIconSpan.insertAdjacentHTML("beforeend", ''); - } - - /* The masthead icon may not exist yet when this webhook executes; we need this to wait for that to happen. - * elementReady function from gist: - * https://gist.github.com/jwilson8767/db379026efcbd932f64382db4b02853e - */ - function elementReadyNews(selector) { - return new Promise((resolve, reject) => { - const el = document.querySelector(selector); - if (el) { - resolve(el); - } - new MutationObserver((mutationRecords, observer) => { - // Query for elements matching the specified selector - Array.from(document.querySelectorAll(selector)).forEach((element) => { - resolve(element); - //Once we have resolved we don't need the observer anymore. - observer.disconnect(); - }); - }).observe(document.documentElement, { - childList: true, - subtree: true, - }); - }); - } - - elementReadyNews("#news a").then((el) => { - // External stuff may also have attached a click handler here (vue-based masthead) - // replace with a clean copy of the node to remove all that cruft. - clean = el.cloneNode(true); - el.parentNode.replaceChild(clean, el); - - let currentGalaxyVersion = Galaxy.config.version_major; - const lastSeenVersion = window.localStorage.getItem("galaxy-news-seen-release"); - - // If we're at a deployed release candidate, just mark it seen and show - // the previous notes if someone clicks the link. RC notes won't exist. - if (Galaxy.config.version_minor.startsWith("rc")) { - // If we, for whatever reason, need to do this again just add - // another case here. It's not worth parsing and doing version - // math, and we should be able to drop preferring notifications - // framework moving forward in 23.2 - if (currentGalaxyVersion == "23.1") { - currentGalaxyVersion = "23.0"; - } - newsSeen(currentGalaxyVersion); - } else if (lastSeenVersion != currentGalaxyVersion) { - newsUnseen(); - } else { - newsSeen(currentGalaxyVersion); - } - - const releaseNotes = `https://docs.galaxyproject.org/en/latest/releases/${currentGalaxyVersion}_announce_user.html`; - - clean.addEventListener("click", (e) => { - e.preventDefault(); - e.stopPropagation(); - - // If element doesn't exist, add it. - if (document.getElementById("news-container") == null) { - document.querySelector("body.full-content").insertAdjacentHTML( - "afterbegin", - ` - ` - ); - // Clicking outside of GTN closes it - document.getElementById("news-screen").addEventListener("click", () => { - hideNewsOverlay(); - }); - } - document.getElementById("news-container").style.visibility = "visible"; - newsSeen(currentGalaxyVersion); - }); - }); - - // Remove the overlay on escape button click - document.addEventListener("keydown", (e) => { - // Check for escape button - "27" - if (e.which === 27 || e.keyCode === 27) { - hideNewsOverlay(); - } - }); -})(); diff --git a/config/plugins/webhooks/news/styles.css b/config/plugins/webhooks/news/styles.css deleted file mode 100644 index d343e02312c..00000000000 --- a/config/plugins/webhooks/news/styles.css +++ /dev/null @@ -1,38 +0,0 @@ -#news-screen { - position: fixed; - z-index: 5100; - width: 100%; - height: 100%; -} - -#news-screen-overlay { - position: fixed; - top: 0; - left: 0; - background: rgba(224, 224, 224, 0.75); - z-index: 5000; - width: 100%; - height: 100%; - opacity: 2; -} - -#news-header { - position: fixed; - height: 100%; - width: 100%; - z-index: 5200; - display: flex; - justify-content: center; - flex-direction: column; - align-items: center; -} - -#news-indicator { - position: absolute; - top: 0.5rem; - right: 0.2rem; - width: 0.6rem; - height: 0.6rem; - border-radius: 50%; - background: #e31a1e; -}