Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
c5956f1
`_login` partial
erinesullivan Jan 21, 2026
1558745
Changing `id` to `class`, and adding `text` local to `_login` partial.
erinesullivan Jan 22, 2026
3fc75d8
Hiding actions that require JavaScript if JavaScript is disabled.
erinesullivan Jan 22, 2026
f6d3912
Hiding `login` in header when JavaScript is disabled.
erinesullivan Jan 22, 2026
6f9a888
`_alert` partial, styles, scripts, and tests.
erinesullivan Jan 22, 2026
d7a3edd
`responseBody`, `fetchFormResponse`, and `emailAction` functions and …
erinesullivan Jan 23, 2026
39ddb12
`textAction` function and tests. Removing `shareForm` and `fetchFormR…
erinesullivan Jan 23, 2026
d9f34b6
`standardrb` fix.
erinesullivan Jan 23, 2026
490b793
Merge branch 'main' into email-action-messaging
erinesullivan Jan 23, 2026
7158576
`_login` partial
erinesullivan Jan 21, 2026
a1eadb3
Changing `id` to `class`, and adding `text` local to `_login` partial.
erinesullivan Jan 22, 2026
972f470
Hiding actions that require JavaScript if JavaScript is disabled.
erinesullivan Jan 22, 2026
73a253d
Hiding `login` in header when JavaScript is disabled.
erinesullivan Jan 22, 2026
1b1178d
`_alert` partial, styles, scripts, and tests.
erinesullivan Jan 22, 2026
0f09dba
`responseBody`, `fetchFormResponse`, and `emailAction` functions and …
erinesullivan Jan 23, 2026
d66828a
`textAction` function and tests. Removing `shareForm` and `fetchFormR…
erinesullivan Jan 23, 2026
16607c3
`standardrb` fix.
erinesullivan Jan 23, 2026
32dabf7
Merge branch 'email-action-messaging' of https://github.com/mlibrary/…
erinesullivan Jan 28, 2026
9a042f7
Adding hidden checkbox for full records. Adding locals to checkbox pa…
erinesullivan Jan 28, 2026
b9c3f20
Merge branch 'main' into email-action-messaging
erinesullivan Jan 28, 2026
86caadd
Merge branch 'main' into email-action-messaging
erinesullivan Jan 28, 2026
bc9ef71
Merge branch 'main' into email-action-messaging
erinesullivan Jan 28, 2026
43f732a
Merge branch 'main' into email-action-messaging
erinesullivan Jan 28, 2026
908da79
Merge branch 'main' into email-action-messaging
erinesullivan Jan 28, 2026
feb2362
Merge branch 'main' into email-action-messaging
erinesullivan Jan 28, 2026
d65a1bb
Merge branch 'main' into email-action-messaging
erinesullivan Jan 28, 2026
412e92b
refactor: methods for getting ht and online holdings
niquerio Jan 28, 2026
9332b03
refactor: holdings factory can build specific holdings
niquerio Jan 28, 2026
ce1cf1c
HathiTrust holdings can return only full text or only search only
niquerio Jan 29, 2026
8f4a389
Email holdings has too_many and filtered ht holdings
niquerio Jan 30, 2026
d120cdd
send email instead of brief
niquerio Jan 30, 2026
0c14d42
email has brief metadata
niquerio Jan 30, 2026
45cb371
add url method to full record
niquerio Jan 30, 2026
26927ad
add any? method to holdings
niquerio Jan 30, 2026
15b8f89
Updating data, and adding holdings. WIP.
erinesullivan Jan 30, 2026
04c68d7
"_border" partial. Removing `.to_h`.
erinesullivan Jan 30, 2026
2694989
Holdings partials WIP.
erinesullivan Jan 30, 2026
a4853ed
`_txt` metadata partials.
erinesullivan Feb 2, 2026
ab9a650
Metadata partials for HTML emails.
erinesullivan Feb 2, 2026
850d51a
Status icons and coloring for holdings.
erinesullivan Feb 2, 2026
f07440f
Using correct green.
erinesullivan Feb 2, 2026
47012af
Removing linked values and browse links to convert to just plain text.
erinesullivan Feb 2, 2026
05fe43e
Merge branch 'main' into email-action-messaging
niquerio Feb 5, 2026
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
4 changes: 2 additions & 2 deletions app.rb
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ def login
get "/email/record" do
datastore, id = params["record"].split(",")

@record = Search::Presenters::Record.for_datastore(datastore: datastore, id: id, size: "brief")
@record = Search::Presenters::Record.for_datastore(datastore: datastore, id: id, size: "email")

if params["content_type"] == "txt"
content_type "text/plain"
Expand All @@ -259,7 +259,7 @@ def login
@records = Hash.new { |hash, key| hash[key] = [] }
params["record"].map do |r|
datastore, id = r.split(",")
@records[datastore.capitalize].push Search::Presenters::Record.for_datastore(datastore: datastore, id: id, size: "brief")
@records[datastore.capitalize].push Search::Presenters::Record.for_datastore(datastore: datastore, id: id, size: "email")
end

if params["content_type"] == "txt"
Expand Down
8 changes: 5 additions & 3 deletions assets/scripts/datastores/list/scripts.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { getTemporaryList, temporaryList } from './layout.js';
import { shareForm, tabControl } from '../partials/_actions.js';
import { downloadTemporaryListRIS } from '../partials/actions/action/_ris.js';
import { emailAction } from '../partials/actions/action/_email.js';
import { initializeCitations } from '../partials/actions/action/_citation.js';
import { removeSelected } from '../partials/actions/action/_remove-selected.js';
import { selectAll } from './partials/_select-all.js';
import { tabControl } from '../partials/_actions.js';
import { textAction } from '../partials/actions/action/_text.js';

// Get the temporary list from session storage
const list = getTemporaryList();
Expand All @@ -12,10 +14,10 @@ const list = getTemporaryList();
tabControl('.actions');

// Email
shareForm('#actions__email--tabpanel');
emailAction();

// Text
shareForm('#actions__text--tabpanel');
textAction();

// Citations
initializeCitations();
Expand Down
33 changes: 0 additions & 33 deletions assets/scripts/datastores/partials/_actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,37 +59,6 @@ const disableActionTabs = () => {
});
};

const fetchFormResults = async (form) => {
const formData = new FormData(form);

const response = await fetch(form.action, {
body: formData,
headers: {
Accept: 'application/json'
},
method: form.method
});

return response;
};

const shareForm = (panel, formResults = fetchFormResults) => {
const form = document.querySelector(`${panel} form:not(.login__form)`);

// Return if form not found because the user is not logged in
if (!form) {
return;
}

form.addEventListener('submit', async (event) => {
event.preventDefault();
changeAlert({
alert: document.querySelector(`${panel} .alert`),
response: await formResults(form)
});
});
};

const copyToClipboard = ({ alert, text }) => {
if (alert) {
alert.style.display = 'block';
Expand All @@ -101,9 +70,7 @@ export {
changeAlert,
copyToClipboard,
disableActionTabs,
fetchFormResults,
getTabPanel,
isSelected,
shareForm,
tabControl
};
52 changes: 52 additions & 0 deletions assets/scripts/datastores/partials/actions/action/_email.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { changeAlert } from '../_alert.js';
import { viewingFullRecord } from '../../../record/layout.js';

const responseBody = ({ elements }) => {
const params = new URLSearchParams();
// Convert form elements to URL-encoded string
Array.from(elements).forEach((element) => {
if (element.name && !element.disabled) {
params.append(element.name, element.value);
}
});
// Return the URL-encoded string
return params.toString();
};

const fetchFormResponse = async ({ body = responseBody, form, isFullRecord = viewingFullRecord(), url }) => {
const { action, elements, method } = form;
return await fetch(
isFullRecord ? action : url,
{
body: body({ elements }),
headers: {
Accept: 'application/json',
'Content-Type': 'application/x-www-form-urlencoded'
},
method
}
);
};

const emailAction = ({ emailResponse = fetchFormResponse, showAlert = changeAlert } = {}) => {
const form = document.querySelector('form.action__email--form');

// Return early if the form is not found because the user is not logged in
if (!form) {
return;
}

form.addEventListener('submit', async (event) => {
event.preventDefault();
showAlert({
alert: document.querySelector('#actions__email--tabpanel .alert'),
response: await emailResponse({ form, url: '/everything/list/email' })
});
});
};

export {
emailAction,
fetchFormResponse,
responseBody
};
21 changes: 21 additions & 0 deletions assets/scripts/datastores/partials/actions/action/_text.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { changeAlert } from '../_alert.js';
import { fetchFormResponse } from './_email.js';

const textAction = ({ showAlert = changeAlert, textResponse = fetchFormResponse } = {}) => {
const form = document.querySelector('form.action__text--form');

// Return early if the form is not found because the user is not logged in
if (!form) {
return;
}

form.addEventListener('submit', async (event) => {
event.preventDefault();
showAlert({
alert: document.querySelector('#actions__text--tabpanel .alert'),
response: await textResponse({ form, url: '/everything/list/sms' })
});
});
};

export { textAction };
8 changes: 5 additions & 3 deletions assets/scripts/datastores/record/scripts.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { shareForm, tabControl } from '../partials/_actions.js';
import { addToList } from '../list/partials/_add-to.js';
import copyLink from '../partials/actions/action/_link.js';
import { emailAction } from '../partials/actions/action/_email.js';
import { getTemporaryList } from '../list/layout.js';
import { initializeCitations } from '../partials/actions/action/_citation.js';
import shelfBrowse from './partials/_shelf-browse.js';
import { tabControl } from '../partials/_actions.js';
import { textAction } from '../partials/actions/action/_text.js';
import toggleItems from '../partials/_toggle.js';
import toggleMARCData from './partials/_marc.js';
import toggleTruncatedText from './partials/_title.js';
Expand All @@ -15,10 +17,10 @@ const list = getTemporaryList();
tabControl('.actions');

// Email
shareForm('#actions__email--tabpanel');
emailAction();

// Text
shareForm('#actions__text--tabpanel');
textAction();

// Citations
initializeCitations();
Expand Down
2 changes: 1 addition & 1 deletion lib/search/email.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def self.for(id)
end

def initialize(id)
@record = Search::Presenters::Record.for_datastore(datastore: "catalog", id: id, size: "brief")
@record = Search::Presenters::Record.for_datastore(datastore: "catalog", id: id, size: "email")
end

def subject
Expand Down
22 changes: 21 additions & 1 deletion lib/search/models/record/catalog/holdings/hathi_trust.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,28 @@ def count
@count ||= @hathi_trust_items.count
end

def search_only_count
@search_only_count ||= search_only_items.count
end

def full_text_count
@full_text_count ||= full_text_items.count
end

def has_description?
@hathi_trust_items.any? { |x| x["description"].present? }
end

def items
@hathi_trust_items.map { |item| Item.new(item) }
@items ||= @hathi_trust_items.map { |item| Item.new(item) }
end

def full_text_items
@full_text_items ||= items.select { |item| item.full_text? }
end

def search_only_items
@search_only_items ||= items.select { |item| !item.full_text? }
end

class Item
Expand All @@ -26,6 +42,10 @@ def initialize(item)
end
end

def full_text?
status.match?("Full text")
end

def url
"http://hdl.handle.net/2027/#{id}"
end
Expand Down
7 changes: 6 additions & 1 deletion lib/search/presenters/record/catalog.rb
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ def id
@record.bib.id
end

def url
"#{S.base_url}/catalog/record/#{id}"
end

def title
result = [
OpenStruct.new(text: @record.bib.title.original.text, css_class: "title-primary")
Expand Down Expand Up @@ -367,7 +371,7 @@ def to_h
transliterated: f.values&.first&.transliterated&.text
}
end,
url: "#{S.base_url}/catalog/record/#{id}",
url: url,
citation: {
ris: ris,
csl: csl
Expand Down Expand Up @@ -414,3 +418,4 @@ def each(&block)
end
end
end
require_relative "catalog/email"
12 changes: 12 additions & 0 deletions lib/search/presenters/record/catalog/email.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module Search::Presenters::Record::Catalog
class Email < Full
METADATA_METHODS = [
:main_author,
:published,
:series
]
def holdings
EmailHoldings.new(@record)
end
end
end
50 changes: 50 additions & 0 deletions lib/search/presenters/record/catalog/email_holdings.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
class Search::Presenters::Record::Catalog::EmailHoldings
#
# <Description>
#
# @param [Search::Models::Record::Catalog] data is the catalog record model
#
def initialize(data)
@data = data
@holdings = Search::Presenters::Record::Catalog::Holdings.new(@data)
end

def too_many?
non_ht_search_only_item_count > 3 || (non_ht_search_only_item_count == 0 && @holdings.hathi_trust.search_only_count > 3)
end

def any?
list.any?
end

def list
@list ||= [
hathi_trust_list,
@holdings.online,
@holdings.finding_aids,
* @holdings.physical
].reject { |x| x.empty? }
end

private

def hathi_trust_list
if non_ht_search_only_item_count == 0
@holdings.hathi_trust
else
Search::Presenters::Record::Catalog::Holdings::HathiTrustFullText.new(@data.holdings.hathi_trust)
end
end

def non_ht_search_only_item_count
@non_ht_search_only_item_count ||=
physical_item_count +
@holdings.online.count +
@holdings.finding_aids.count +
@holdings.hathi_trust.full_text_count
end

def physical_item_count
@holdings.physical.map { |x| x.count }.sum
end
end
Loading