Skip to content

Add entry from url - #16435

Open
dvinit wants to merge 11 commits into
JabRef:mainfrom
dvinit:add-entry-from-url
Open

Add entry from url#16435
dvinit wants to merge 11 commits into
JabRef:mainfrom
dvinit:add-entry-from-url

Conversation

@dvinit

@dvinit dvinit commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Related issues and pull requests

Closes #15411

PR Description

Until now, JabRef could create a new entry from a DOI, ISBN, or arXiv ID — but not from a plain web link. This PR adds a new "Enter URL" tab to the New Entry dialog: paste any link, and JabRef creates an entry for it automatically, using the linked page's title when it can be reached, and recording the date it was added.

While testing this, we also found and fixed a small existing bug: pasting a link with an extra space at the start or end (which easily happens when copying a URL from a browser) was incorrectly rejected as invalid.

User documentation to follow in a separate PR.

invalid_url ritual_15411 valid_url

Steps to test

  1. Open a library and open the "New Entry" dialog (Cmd/Ctrl+N, or Library menu → "Add entry using...").
  2. Click the new "Enter URL" tab.
  3. Paste in a link, for example https://gi-radar.de/397-coding-unterstuetzung-im-lauf-der-zeit/, and click "Create".
  4. A new entry should appear, with the link, a title (taken from the page, or the link itself if the page couldn't be reached), and today's date.
  5. Try typing something that isn't a real link (e.g. not a url) — the Create button should stay disabled and show an error.
  6. Paste a link with a space before or after it (as if pasted from a browser's address bar) — it should now be accepted.

AI usage

Claude Code was used as a helper for exploring the codebase, resolving git issues and suggesting style fixes. The core feature plan and implementation for this task was done manually.

AI CHECKLIST.md walkthrough

Updated after automated (Qodo) review: 4 of its 6 findings were fixed (@NullMarked, the non-http title-fetch bug, an explicit timeout, and one Localization.lang concatenation) and folded into the existing commits; 2 were deliberately left as-is with reasoning given in review-thread replies. This walkthrough reflects the current, post-fix state of the code.

1. Code self-review

Nullability and control flow

  • No == null / != null checks — GenericUrlBasedFetcher.performSearch originally had one (url == null ? null : url.trim()); removed it to match the sibling fetchers in this same package (RfcFetcher, DoiFetcher, IssnFetcher), none of which null-check their String parameter — they let it propagate naturally. NewEntryView.switchEnterUrl()'s if (urlText != null) is unchanged from the file's existing convention (every sibling switchXxx() method has the identical guard) — not a new pattern.
  • No Objects.requireNonNull(...)
  • New classes annotated @NullMarked — added to both UrlBasedFetcher and GenericUrlBasedFetcher in response to automated review. I'd originally left this out reasoning it mirrors the unannotated sibling IdBasedFetcher, but AGENTS.md states the rule unconditionally for new classes, with no carve-out for matching an unannotated sibling — so I fixed it rather than keep the exception. IdBasedFetcher itself is pre-existing code, untouched by this PR.
  • Optional consumed with map/orElsefetchTitle(...).orElse(trimmedUrl) uses a real, meaningful fallback value, not a discarded one.
  • StringUtil.isBlank(...) — used in WorkerEnterUrl.call().

Exceptions

  • No catch (Exception e) — only IOException (title fetch) and FetcherException/its subtypes.
  • No throw new RuntimeException(...)/IllegalStateException(...)
  • Logged exceptions passed as last logger argument

Style and idioms

  • New BibEntry objects built with withers (.withField(...))
  • Modern Java used (List.of(entry))
  • [/] Precompiled Pattern — no new regex introduced
  • [/] Background work uses org.jabref.logic.util.BackgroundTask — uses javafx.concurrent.Task via the shared taskExecutor instead, matching this exact file's existing convention for every sibling tab (WorkerLookupId, WorkerInterpretCitations, WorkerSpecifyBibtex all extend Task, not BackgroundTask) — followed the established in-file pattern rather than introducing a different one for only this tab.
  • No commented-out code, no trivial comments, no AI-disclosure comments in source
  • Markdown Javadoc (///) — fixed two spots that initially used old-style {@link}/{@code} tags in my own new files (UrlBasedFetcher.java, GenericUrlBasedFetcher.java); now use [ClassName]/backtick style throughout. The doc comment added later on fetchTitle (explaining the http/https restriction) follows the same style (`ftp://`, [Jsoup.connect], [URLUtil.isURL]) — confirmed it resolves cleanly with zero javadoc warnings. (Pre-existing {@link} usages elsewhere in URLUtil.java and a plain // comment in NewEntryView.java predate this PR and are untouched.)

User-facing text

  • All user-facing text localized (Localization.lang in Java, % prefix in FXML)
  • Sentence case; no trailing !; no trailing : on labels. Tab title "Enter URL" is Title Case, matching every existing sibling tab title ("Enter Identifier", "Interpret Citations", "Specify Bib(La)TeX") — a UI-navigation-label convention, not body copy.
  • Variance via placeholders ("...: %0"), not concatenation

Security

  • [/] HTML-escaping — not applicable; no HTML/HTTP response output involved (desktop GUI only)

Tests

  • org.jabref.logic behavior changes have tests — GenericUrlBasedFetcherTest (7 tests: valid URL, unreachable-URL fallback, whitespace trimming, non-http scheme skips title fetch instead of throwing, invalid/blank input, getName()) and 3 new whitespace cases in URLUtilTest. The dialog-wiring changes live in org.jabref.gui, outside this rule's stated scope (same situation as prior GUI-layer PRs) — verified manually instead (see Steps to test).
  • Tests assert with assertEquals/assertTrue/assertThrows, plain JUnit, no @DisplayName, don't catch exceptions, no manual temp dirs

2. Verification commands

  • ./gradlew :jablib:fetcherTest --tests GenericUrlBasedFetcherTest — 7/7 pass (includes a live network call to the issue's own test URL, plus a regression test for the ftp:// scheme bug found in review)
  • ./gradlew :jablib:test --tests URLUtilTest — 62/62 pass (includes the new whitespace cases)
  • ./gradlew :jablib:test --tests LocalizationConsistencyTest — passes with the 10 new keys
  • ./gradlew :jablib:checkstyleMain :jablib:checkstyleTest :jabgui:checkstyleMain :jabgui:checkstyleTest — clean
  • ./gradlew modernizer — clean
  • ./gradlew --no-configuration-cache :rewriteDryRun — "Applying recipes would make no changes" (one pre-existing, unrelated parse warning on a file I didn't touch)
  • ./gradlew :jablib:javadoc :jabgui:javadoc — builds successfully; zero warnings on any file this PR touches (100 warnings total, all on pre-existing files elsewhere)
  • npx markdownlint-cli2 "CHANGELOG.md" — 0 issues
  • ./gradlew check (full aggregate, all modules) — not run; ran the targeted equivalents above instead given the time cost of the full suite. Happy to run it if a maintainer wants that explicit confirmation.
  • CI's format check (IntelliJ-based formatter, runs via Docker in CI) — initially failed on indentation in the new Jsoup.connect(...) method chain, since none of the local commands above cover that specific formatter (Docker daemon wasn't available locally to run it myself, ghcr.io/leventebajczi/intellij-format from CHECKLIST.md's own fallback instructions). Fixed to match CI's reported diff exactly and confirmed passing in a subsequent CI run. Flagging this honestly as a real gap in my local verification coverage, not something the commands above would have caught.

3. Documentation

  • CHANGELOG.md entry added under ### Added, linked to Add entry using URL #15411
  • Searched jabref/issues — Add entry using URL #15411 was the assigned issue, identified directly
  • [/] Requirement added to docs/requirements/<area>.md — not added. The new tab's text field reuses the existing documented requirement req~textinput.clipboard.autofocus~1 (docs/requirements/dialog-with-text-input.md), tagged accordingly ([impl->req~textinput.clipboard.autofocus~1] in NewEntryView.initializeEnterUrl()). The more specific req~newentry.clipboard.autofocus~1 (auto-switch-tab-on-clipboard-detection, docs/requirements/focus.md) deliberately does not apply — auto-switching to this tab on clipboard content was explicitly decided against, to keep this PR scoped. No existing doc enumerates "which tabs exist" as a trackable requirement, so there's no natural place to add "Enter URL" as a new entry; open to adding one if a maintainer wants it.
  • [/] Developer documentation under docs/ — checked; nothing under docs/ references the New Entry dialog's tab set that would go stale.

4. Pull request

  • PR body built from .github/PULL_REQUEST_TEMPLATE.md, every legitimate section filled
  • All checklist items kept, marked [x]/[ ]/[/]
  • All HTML comments removed from the PR body
  • PR to be created with gh pr create --body-file <file>
  • [/] CHANGELOG TODO placeholder — not applicable, the real issue link (Add entry using URL #15411) was used from the start

Checklist

  • I own the copyright of the code submitted and I license it under the MIT license
  • If AI tools were used, I disclosed them in the "AI usage" section and reviewed, understood, and take full ownership of all AI-generated code
  • I manually tested my changes in running JabRef (always required)
  • I added JUnit tests for changes (if applicable)
  • I added screenshots in the PR description
  • I added a screenshot in the PR description showing a library with a single entry with me as author and as title the issue number
  • I described the change in CHANGELOG.md in a way that can be understood by the average user (if change is visible to the user)
  • I checked the user documentation for up to dateness and submitted a pull request to our user documentation repository

dvinit added 4 commits July 30, 2026 19:11
GenericUrlBasedFetcher wraps an arbitrary URL into a @misc BibEntry,
populating url, a best-effort scraped page title (falling back to the
URL itself if the fetch fails), and urldate.

Part of JabRef#15411.
Wires GenericUrlBasedFetcher into a new tab, placed after "Enter
Identifier", following the same async fetch -> duplicate-check ->
import pattern used by the Interpret Citations and Specify BibTeX
tabs.

Fixes JabRef#15411.
isURL() matched the raw string against the URL pattern before
trimming, so a pasted URL with a leading/trailing space (e.g. from a
clipboard paste) was incorrectly reported as invalid, even though
URLUtil.create() right after it already trims. Trim once up front,
matching create()'s behavior.

This also fixes the same latent issue in the library-table paste
handler and the URL field in the entry editor, which share isURL().
@dvinit
dvinit marked this pull request as draft July 30, 2026 13:51
@github-actions github-actions Bot added good first issue An issue intended for project-newcomers. Varies in difficulty. component: fetcher labels Jul 30, 2026
@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Add “Enter URL” tab to create @misc entries from pasted links

✨ Enhancement 🐞 Bug fix 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Add a new “Enter URL” tab that creates a @Misc entry from a pasted link.
• Fetch the target page title when reachable and store the access date automatically.
• Fix URL validation to accept URLs with leading/trailing whitespace and add tests.
Diagram

graph TD
  UI["New Entry dialog"] --> VM["NewEntryViewModel"] --> F["GenericUrlBasedFetcher"] --> U["URLUtil.isURL"]
  F --> W{{"Web page"}}
  VM --> IH["ImportHandler"] --> LIB["Library"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Route URLs through a centralized fetcher registry
  • ➕ Enables future specialized URL handlers (publisher pages, Semantic Scholar links) without changing the GUI
  • ➕ Keeps selection logic consistent with other fetcher selection patterns
  • ➖ Adds extra abstraction and configuration work for a single generic URL path
  • ➖ May require broader refactoring of existing fetcher wiring
2. Avoid network fetch at creation time (lazy title retrieval)
  • ➕ No UI latency and fewer failure modes during entry creation
  • ➕ Avoids network access surprises in environments with restricted connectivity
  • ➖ Title stays as raw URL until a later refresh step is implemented
  • ➖ Requires an additional background updater mechanism and UX for refresh
3. Use URLDownload-only and parse without Jsoup
  • ➕ Reduces dependency surface and avoids Jsoup scheme limitations
  • ➕ Can better control timeouts and redirects with existing networking utilities
  • ➖ HTML parsing becomes brittle without a real parser
  • ➖ More custom parsing code to maintain

Recommendation: The PR’s approach (a simple GenericUrlBasedFetcher directly wired into the New Entry dialog) is a pragmatic, low-risk implementation that matches existing async-import patterns. If URL handling expands to multiple specialized strategies later, consider introducing a small registry/selector so the GUI stays stable while fetcher selection evolves.

Files changed (11) +403 / -2

Enhancement (6) +285 / -0
NewEntryDialogTab.javaAdd ENTER_URL tab identifier +1/-0

Add ENTER_URL tab identifier

• Extends the dialog tab enum to include ENTER_URL so the tab can be selected and persisted as the latest approach.

jabgui/src/main/java/org/jabref/gui/newentry/NewEntryDialogTab.java

NewEntryView.javaWire new “Enter URL” tab into New Entry dialog behavior +53/-0

Wire new “Enter URL” tab into New Entry dialog behavior

• Adds JavaFX fields and initialization for the URL tab, including clipboard prefill, validator-driven error visibility, and focus handling. Updates tab switching and execution to run the URL workflow and show a “Fetching...” state.

jabgui/src/main/java/org/jabref/gui/newentry/NewEntryView.java

NewEntryViewModel.javaImplement background URL-to-entry creation and import flow +95/-0

Implement background URL-to-entry creation and import flow

• Introduces URL text state, URL validation via URLUtil.isURL, and a WorkerEnterUrl Task calling GenericUrlBasedFetcher. On success, imports the created entry with duplicate checking; on failure, displays a localized error dialog and logs the exception.

jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java

NewEntry.fxmlAdd FXML layout for “Enter URL” tab +38/-0

Add FXML layout for “Enter URL” tab

• Adds the new tab UI containing a URL text field, tooltip, and a validation error label, using localization keys for all user-facing text.

jabgui/src/main/resources/org/jabref/gui/newentry/NewEntry.fxml

UrlBasedFetcher.javaIntroduce UrlBasedFetcher interface +18/-0

Introduce UrlBasedFetcher interface

• Adds a new WebFetcher subtype representing fetchers that search based on a URL and return BibEntry results.

jablib/src/main/java/org/jabref/logic/importer/UrlBasedFetcher.java

GenericUrlBasedFetcher.javaCreate GenericUrlBasedFetcher for URL → minimal @Misc entry +80/-0

Create GenericUrlBasedFetcher for URL → minimal @misc entry

• Implements UrlBasedFetcher to validate/trim input, create a @Misc entry with url + urldate, and best-effort scrape the page title (http/https only) with a timeout and a safe fallback to using the URL as title.

jablib/src/main/java/org/jabref/logic/importer/fetcher/GenericUrlBasedFetcher.java

Bug fix (1) +4 / -2
URLUtil.javaFix URL validation to accept surrounding whitespace +4/-2

Fix URL validation to accept surrounding whitespace

• Updates isURL to trim input before regex matching and URL creation, preventing valid pasted URLs with leading/trailing spaces from being rejected.

jablib/src/main/java/org/jabref/logic/util/URLUtil.java

Tests (2) +103 / -0
GenericUrlBasedFetcherTest.javaAdd tests for GenericUrlBasedFetcher behavior and edge cases +91/-0

Add tests for GenericUrlBasedFetcher behavior and edge cases

• Adds FetcherTest coverage for title/urldate population, unreachable-host fallback, whitespace trimming, non-http(s) scheme behavior, and invalid/blank input handling.

jablib/src/test/java/org/jabref/logic/importer/fetcher/GenericUrlBasedFetcherTest.java

URLUtilTest.javaAdd regression tests for URLUtil whitespace handling +12/-0

Add regression tests for URLUtil whitespace handling

• Adds parameterized cases ensuring URLUtil.isURL accepts otherwise-valid URLs with leading/trailing spaces.

jablib/src/test/java/org/jabref/logic/net/URLUtilTest.java

Documentation (1) +1 / -0
CHANGELOG.mdDocument the new “Enter URL” entry-creation workflow +1/-0

Document the new “Enter URL” entry-creation workflow

• Adds a changelog entry describing the new New Entry dialog tab that creates a @Misc entry from a URL, including title retrieval and access date recording.

CHANGELOG.md

Other (1) +10 / -0
JabRef_en.propertiesAdd localization strings for URL entry creation +10/-0

Add localization strings for URL entry creation

• Introduces new i18n keys for the “Enter URL” tab text, validation messages, and the URL fetch error dialog/button state strings.

jablib/src/main/resources/l10n/JabRef_en.properties

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (2) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Trimmed URL validation mismatch ✓ Resolved 🐞 Bug ≡ Correctness
Description
URLUtil.isURL now validates url.trim(), so whitespace-surrounded URLs become “valid”, but
several existing callers still pass the original untrimmed string into strict URI/URL consumers
(e.g., new URI(link) and browser opening), which can fail and break flows like PDF URL handling or
opening links. This regression is introduced by the isURL contract change rather than the new
URL-entry fetcher (which trims internally).
Code

jablib/src/main/java/org/jabref/logic/util/URLUtil.java[R84-87]

+        String trimmedUrl = url.trim();
+
   // Check if the URL has a protocol (http://, https://, ftp://)
-        if (!URL_PATTERN.matcher(url).matches()) {
+        if (!URL_PATTERN.matcher(trimmedUrl).matches()) {
Evidence
The PR changes URLUtil.isURL to trim before matching/creating a URL, but downstream code often
still consumes the original untrimmed string (constructing a URI from it or passing it to
browser-opening logic), which can reject leading/trailing whitespace and return empty/throw, causing
user-visible failures.

jablib/src/main/java/org/jabref/logic/util/URLUtil.java[79-96]
jabgui/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java[568-585]
jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[142-151]
jabgui/src/main/java/org/jabref/gui/mergeentries/threewaymerge/cell/OpenExternalLinkAction.java[28-42]
jabgui/src/main/java/org/jabref/gui/desktop/os/NativeDesktop.java[279-305]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`URLUtil.isURL` now trims before validating, but callers often continue using the *original* (potentially whitespace-surrounded) string when constructing `URI`s, downloading, or opening the browser. This creates a validate/consume mismatch where `isURL(" https://...")` is true but downstream operations fail.
## Issue Context
The PR changed `URLUtil.isURL` to accept surrounding whitespace. Existing code paths (PDF URL handling and link opening) were previously protected because `isURL` rejected such strings.
## Fix Focus Areas
- Update URL consumers to use a normalized value (e.g., `trim()` or `strip()`) once `isURL(...)` is true.
- Prefer central normalization in helpers to avoid repeated fixes.
### Suggested edits
- Trim in URL filename extraction:
- jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java[142-151]
- Trim before PDF URL handling / filename detection:
- jabgui/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java[572-584]
- Trim before opening in browser (either at call sites or centrally in `NativeDesktop.openBrowser`):
- jabgui/src/main/java/org/jabref/gui/desktop/os/NativeDesktop.java[279-305]
- jabgui/src/main/java/org/jabref/gui/mergeentries/threewaymerge/cell/OpenExternalLinkAction.java[28-42]
- jabgui/src/main/java/org/jabref/gui/fieldeditors/UrlEditorViewModel.java[49-58]
## Notes
Consider using `strip()` (Unicode whitespace) instead of `trim()` if the goal is robust copy/paste handling.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Title fetch aborts entry ✓ Resolved 🐞 Bug ≡ Correctness
Description
GenericUrlBasedFetcher.fetchTitle is documented as “should never prevent the entry from being
created”, but it only catches IOException, so other failures from the HTML fetch/parsing can
escape and fail the whole URL-to-entry flow instead of falling back to using the URL as the title.
Code

jablib/src/main/java/org/jabref/logic/importer/fetcher/GenericUrlBasedFetcher.java[R55-62]

+    private Optional<String> fetchTitle(String url) {
+        try {
+            String title = Jsoup.connect(url).userAgent(URLDownload.USER_AGENT).get().title();
+            return title.isBlank() ? Optional.empty() : Optional.of(title);
+        } catch (IOException e) {
+            LOGGER.debug("Could not fetch title for '{}', falling back to the URL as title.", url, e);
+            return Optional.empty();
+        }
Evidence
The fetcher’s own comment says title fetching must not block entry creation, but the implementation
only swallows IOException. At the same time, URL validation explicitly allows ftp:// via regex
and the new URL tab’s validator uses that same isURL, so non-IOException failures in Jsoup can
reach the UI as task failures.

jablib/src/main/java/org/jabref/logic/importer/fetcher/GenericUrlBasedFetcher.java[52-63]
jablib/src/main/java/org/jabref/logic/importer/fetcher/GenericUrlBasedFetcher.java[33-45]
jablib/src/main/java/org/jabref/logic/util/URLUtil.java[21-27]
jablib/src/main/java/org/jabref/logic/util/URLUtil.java[73-97]
jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java[145-151]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`GenericUrlBasedFetcher.fetchTitle(...)` promises that failures in title scraping must not prevent entry creation, but it only catches `IOException`. If Jsoup throws other exceptions (e.g., `IllegalArgumentException`/runtime parsing failures), they propagate and make `performSearch`/`executeEnterUrl` fail instead of falling back to `trimmedUrl`.
### Issue Context
- `URLUtil.isURL(...)` accepts multiple schemes (regex includes `ftp`), and the UI validator uses `URLUtil::isURL`, so inputs can pass validation and still trigger non-`IOException` failures inside `fetchTitle`.
### Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/importer/fetcher/GenericUrlBasedFetcher.java[33-45]
- jablib/src/main/java/org/jabref/logic/importer/fetcher/GenericUrlBasedFetcher.java[52-63]
- jablib/src/main/java/org/jabref/logic/util/URLUtil.java[21-27]
- jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java[145-151]
### Suggested fix
- Make `fetchTitle` truly best-effort by catching `RuntimeException` (or the specific non-IO exceptions Jsoup can throw) in addition to `IOException`, returning `Optional.empty()`.
- Optionally, restrict title fetching to `http/https` only (e.g., check scheme before calling Jsoup), while still allowing entry creation for other valid URL schemes by skipping the title fetch.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Network-dependent GenericUrlBasedFetcherTest 📘 Rule violation ☼ Reliability
Description
performSearchWithValidUrlReturns... / GenericUrlBasedFetcherTest uses a real third-party URL to
assert a non-blank title, which makes the fetcher tests slow and non-deterministic when CI has
restricted outbound network access or the remote site is down/slow. Tests should remain
deterministic and fast by using a local in-process HTTP server or a stubbed/injected title-fetch
implementation rather than reaching the public internet.
Code

jablib/src/test/java/org/jabref/logic/importer/fetcher/GenericUrlBasedFetcherTest.java[R26-29]

+        String url = "https://gi-radar.de/397-coding-unterstuetzung-im-lauf-der-zeit/";
+        // Captured before performSearch, which internally calls LocalDate.now() itself during its (real, network-
+        // dependent) title fetch -- capturing after the call risks the two calls straddling a midnight rollover.
+        String expectedUrlDate = new Date(LocalDate.now()).getNormalized();
Evidence
PR Compliance ID 27 requires tests to be deterministic and fast, but the new test explicitly targets
a real external URL (e.g., https://gi-radar.de/...) and invokes performSearch, which ultimately
calls GenericUrlBasedFetcher.fetchTitle. That production path performs an actual HTTP GET using
Jsoup.connect(url).timeout(CONNECT_TIMEOUT_MILLIS).get() with a 30s timeout, so the test’s runtime
and outcome depend on external network conditions and the remote site’s availability. Since the
project has a dedicated fetcherTest task for tests tagged FetcherTest, this test will run as
part of that suite and can therefore introduce intermittent failures or long timeouts.

AGENTS.md: Testing quality: add/update tests for behavior changes; keep tests deterministic/fast; do not disable or weaken assertions: AGENTS.md: Testing quality: add/update tests for behavior changes; keep tests deterministic/fast; do not disable or weaken assertions: AGENTS.md: Testing quality: add/update tests for behavior changes; keep tests deterministic/fast; do not disable or weaken assertions: AGENTS.md: Testing quality: add/update tests for behavior changes; keep tests deterministic/fast; do not disable or weaken assertions: AGENTS.md: Testing quality: add/update tests for behavior changes; keep tests deterministic/fast; do not disable or weaken assertions: AGENTS.md: Testing quality: add/update tests for behavior changes; keep tests deterministic/fast; do not disable or weaken assertions: AGENTS.md: Testing quality: add/update tests for behavior changes; keep tests deterministic/fast; do not disable or weaken assertions: AGENTS.md: Testing quality: add/update tests for behavior changes; keep tests deterministic/fast; do not disable or weaken assertions
jablib/src/test/java/org/jabref/logic/importer/fetcher/GenericUrlBasedFetcherTest.java[25-32]
jablib/src/main/java/org/jabref/logic/importer/fetcher/GenericUrlBasedFetcher.java[69-74]
jablib/src/test/java/org/jabref/logic/importer/fetcher/GenericUrlBasedFetcherTest.java[24-32]
jablib/build.gradle.kts[282-313]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A newly added fetcher test calls `performSearch` with a real external URL and asserts it returns a non-blank title, but `GenericUrlBasedFetcher` performs a real HTTP request (with a 30s timeout), making CI runs slower and potentially flaky/non-deterministic when outbound internet is restricted or the third-party site is slow/unavailable.
## Issue Context
- The test currently depends on live behavior of a third-party website (e.g., `https://gi-radar.de/...`) and on the network to retrieve an HTML `<title>`.
- `GenericUrlBasedFetcher.fetchTitle` performs an actual request via `Jsoup.connect(url).timeout(CONNECT_TIMEOUT_MILLIS).get()`, so failures/timeouts are influenced by external factors.
- There is a dedicated `fetcherTest` task for `FetcherTest`-tagged tests, so this will run as part of that suite.
- PR Compliance ID 27 expects tests to remain deterministic and fast.
Suggested fix options:
1) Replace the external dependency with a local in-process HTTP server in the test (e.g., JDK `com.sun.net.httpserver.HttpServer`) that serves a minimal HTML page with a known `<title>`, and assert the exact title.
2) Refactor `GenericUrlBasedFetcher` to allow injecting a “document/title fetch” strategy so tests can provide a fake implementation (no network).
Optionally keep a separate test that validates fallback/error behavior using an unroutable/invalid host, but avoid relying on third-party domains for the success case.
## Fix Focus Areas
- jablib/src/test/java/org/jabref/logic/importer/fetcher/GenericUrlBasedFetcherTest.java[24-39]
- jablib/src/main/java/org/jabref/logic/importer/fetcher/GenericUrlBasedFetcher.java[69-75]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Fetching label resets immediately 🐞 Bug ≡ Correctness
Description
In NewEntryView.execute’s ENTER_URL branch, the button text is set to “Fetching...” but then
switchEnterUrl immediately resets it to “Create,” so users don’t see progress for the URL fetch.
This is introduced by the new URL tab wiring.
Code

jabgui/src/main/java/org/jabref/gui/newentry/NewEntryView.java[R551-553]

+                generateButton.setText(Localization.lang("Fetching..."));
+                viewModel.executeEnterUrl();
+                switchEnterUrl();
Evidence
execute() sets the button to “Fetching...” then immediately calls switchEnterUrl(), and
switchEnterUrl() unconditionally sets the text to “Create,” overwriting the progress label.

jabgui/src/main/java/org/jabref/gui/newentry/NewEntryView.java[537-554]
jabgui/src/main/java/org/jabref/gui/newentry/NewEntryView.java[463-483]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The ENTER_URL execution path updates the button text to "Fetching..." but then calls `switchEnterUrl()`, which overwrites the text back to "Create" while the background task is still running.
## Issue Context
This is especially visible for URL fetching since it can involve network access. The dialog is disabled via `executingProperty`, but the button label no longer communicates that work is ongoing.
## Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/newentry/NewEntryView.java[537-554]
- jabgui/src/main/java/org/jabref/gui/newentry/NewEntryView.java[463-483]
## Suggested fix
- In the ENTER_URL case, do not call `switchEnterUrl()` after starting the task (it’s meant for tab-selection initialization, not for post-click state).
- Or: adjust `switchEnterUrl()` to avoid calling `generateButton.setText("Create")` while `viewModel.executingProperty()` is true.
- Optionally, restore the button text to "Create" in the URL worker’s success/failure handlers (on the UI thread) once the task finishes.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Logs full failing URL ✓ Resolved 🐞 Bug ⛨ Security
Description
NewEntryViewModel.executeEnterUrl logs the user-provided URL verbatim at error level on fetch
failure, which can leak credentials or signed query tokens embedded in URLs into application logs.
This is unnecessary for diagnosing most failures and increases exposure of sensitive data.
Code

jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java[R419-422]

+            final Throwable exception = urlWorker.getException();
+            final String exceptionMessage = exception.getMessage();
+            LOGGER.error("An exception occurred with the URL fetcher when resolving '{}'.", urlText.getValue(), exception);
+
Evidence
The new URL-entry flow logs urlText.getValue() directly on failure; since this feature accepts
arbitrary URLs, that value can include secrets and will be persisted in logs.

jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java[412-429]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The URL-entry failure path logs the full user-provided URL at `ERROR` level. URLs may contain credentials (userinfo) or sensitive query parameters (tokens), which should not be written to logs.
## Issue Context
This is in the new Enter URL workflow’s `urlWorker.setOnFailed` handler.
## Fix Focus Areas
- Sanitize/redact before logging:
- jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java[418-427]
## Suggested approach
- Log only scheme + host (and maybe path), dropping userinfo and query/fragment.
- If parsing fails, log a constant like "<invalid url>".
- Keep the exception throwable for stack trace, but avoid including the raw URL string.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View medium (6)
6. Duplicate valid-URL l10n keys ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
Two near-duplicate user-facing localization strings were introduced for the same validation case
(You must provide a valid URL. vs You must specify a valid URL.), which increases translator
workload and risks inconsistent UI wording. Prefer reusing a single key for both the FXML label and
the validator message.
Code

jablib/src/main/resources/l10n/JabRef_en.properties[R3172-3176]

+You\ must\ provide\ a\ valid\ URL.=You must provide a valid URL.
You\ must\ select\ an\ identifier\ type.=You must select an identifier type.
You\ must\ specify\ a\ Bib(La)TeX\ source.=You must specify a Bib(La)TeX source.
You\ must\ specify\ an\ identifier.=You must specify an identifier.
+You\ must\ specify\ a\ valid\ URL.=You must specify a valid URL.
Evidence
PR Compliance ID 22 requires localizing user-facing strings while avoiding unnecessary
near-duplicate keys. The PR adds two separate keys for the same invalid-URL validation message,
which violates the “reuse existing keys / avoid near-duplicates” criterion.

AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow: AGENTS.md: Localization: localize all user-facing strings and follow JabRef localization workflow
jablib/src/main/resources/l10n/JabRef_en.properties[3171-3177]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Two new localization keys express the same “invalid URL” error with only minor wording differences. This creates unnecessary duplication and can lead to inconsistent UI text.
## Issue Context
- FXML uses `%You must provide a valid URL.`
- ViewModel validator uses `Localization.lang("You must specify a valid URL.")`
## Fix Focus Areas
- jablib/src/main/resources/l10n/JabRef_en.properties[3172-3176]
- jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java[145-151]
- jabgui/src/main/resources/org/jabref/gui/newentry/NewEntry.fxml[202-204]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. Missing OpenFastTrace requirement entry 📘 Rule violation ⚙ Maintainability
Description
This PR adds a new end-user feature (“Enter URL” tab and URL-to-entry creation flow) but does not
add a corresponding OpenFastTrace requirement entry under docs/requirements/. This breaks
requirements-to-implementation traceability expected for new features.
Code

jabgui/src/main/resources/org/jabref/gui/newentry/NewEntry.fxml[R174-210]

+            <Tab fx:id="tabEnterUrl"
+                 text="%Enter URL"
+                 closable="false">
+                <VBox spacing="10.0">
+                    <padding>
+                        <Insets top="10.0"/>
+                    </padding>
+                    <Label text="%Enter a URL to create a new entry for that link.">
+                        <font>
+                            <Font name="System Italic"
+                                  size="13.0"/>
+                        </font>
+                        <padding>
+                            <Insets bottom="5.0"/>
+                        </padding>
+                    </Label>
+                    <HBox alignment="CENTER_LEFT"
+                          spacing="10.0">
+                        <Label text="%URL"/>
+                        <TextField
+                                fx:id="urlText"
+                                prefHeight="30.0"
+                                HBox.hgrow="ALWAYS">
+                            <tooltip>
+                                <Tooltip text="%Specify the URL to create an entry for."/>
+                            </tooltip>
+                        </TextField>
+                    </HBox>
+                    <Label fx:id="urlErrorInvalidText"
+                           text="%You must provide a valid URL.">
+                        <font>
+                            <Font name="System Italic"
+                                  size="13.0"/>
+                        </font>
+                    </Label>
+                </VBox>
+            </Tab>
Evidence
PR Compliance ID 27 requires adding an OpenFastTrace requirement entry for new features/significant
fixes. The diff clearly introduces a new UI workflow (Enter URL tab) and execution flow, but no
new requirement is added in the requirements documentation (e.g., docs/requirements/focus.md
contains only the existing new-entry autofocus requirement).

AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes: AGENTS.md: Requirements tracing with OpenFastTrace for new features or significant bug fixes
jabgui/src/main/resources/org/jabref/gui/newentry/NewEntry.fxml[174-210]
docs/requirements/focus.md[14-32]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A new feature was added (creating a new entry from a pasted URL), but there is no corresponding `req~...~<n>` requirement added in `docs/requirements/<area>.md`.
## Issue Context
PR introduces a new "Enter URL" tab and supporting logic/fetcher integration.
## Fix Focus Areas
- docs/requirements/focus.md[14-32]
- jabgui/src/main/resources/org/jabref/gui/newentry/NewEntry.fxml[174-210]
- jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java[412-459]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


8. Enum ordinal prefs break ✓ Resolved 🐞 Bug ≡ Correctness
Description
ENTER_URL is inserted into the middle of NewEntryDialogTab, but JabRefGuiPreferences persists
latestApproach as the index into NewEntryDialogTab.values(). Existing stored indices will now
resolve to different tabs after upgrade, silently changing users’ last-selected New Entry tab.
Code

jabgui/src/main/java/org/jabref/gui/newentry/NewEntryDialogTab.java[6]

+    ENTER_URL,
Evidence
The enum order changed by inserting ENTER_URL, while preferences explicitly store/read the selected
approach by indexing into NewEntryDialogTab.values(), so existing stored indices will map to
different constants after this insertion.

jabgui/src/main/java/org/jabref/gui/newentry/NewEntryDialogTab.java[3-9]
jabgui/src/main/java/org/jabref/gui/preferences/JabRefGuiPreferences.java[1164-1168]
jabgui/src/main/java/org/jabref/gui/preferences/JabRefGuiPreferences.java[1182-1186]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`NewEntryDialogTab` added `ENTER_URL` between existing enum constants. `JabRefGuiPreferences` persists the latest approach as an **index into** `NewEntryDialogTab.values()`, so inserting a new constant shifts ordinals and breaks existing stored preferences.
### Issue Context
Users who previously had `INTERPRET_CITATIONS` (or `SPECIFY_BIBTEX`) stored will now open a different tab after upgrading.
### Fix Focus Areas
- Move the new enum constant to the end to preserve existing ordinals, **or** change persistence to store a stable identifier (e.g., enum name) with migration.
- jabgui/src/main/java/org/jabref/gui/newentry/NewEntryDialogTab.java[3-9]
- jabgui/src/main/java/org/jabref/gui/preferences/JabRefGuiPreferences.java[1164-1168]
- jabgui/src/main/java/org/jabref/gui/preferences/JabRefGuiPreferences.java[1182-1186]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


9. Flaky LocalDate assertions ✓ Resolved 🐞 Bug ☼ Reliability
Description
GenericUrlBasedFetcherTest computes the expected URLDATE using LocalDate.now() at assertion time,
but the production code also uses LocalDate.now() during performSearch. If the date rolls over
between those calls (especially with network delays), the test can fail nondeterministically.
Code

jablib/src/test/java/org/jabref/logic/importer/fetcher/GenericUrlBasedFetcherTest.java[R34-35]

+        assertEquals(new Date(LocalDate.now()).getNormalized(), entry.getField(StandardField.URLDATE).orElse(null));
+        assertTrue(entry.getField(StandardField.TITLE).map(title -> !title.isBlank()).orElse(false));
Evidence
The test uses LocalDate.now() in assertions after the fetcher call; because the fetcher also uses
LocalDate.now() internally, these two calls can straddle midnight and disagree.

jablib/src/test/java/org/jabref/logic/importer/fetcher/GenericUrlBasedFetcherTest.java[24-36]
jablib/src/test/java/org/jabref/logic/importer/fetcher/GenericUrlBasedFetcherTest.java[39-49]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The tests assert against `new Date(LocalDate.now())` after calling `performSearch(...)`. If execution crosses midnight between fetcher execution and the assertion (network call / DNS timeout), the expected and actual URLDATE can differ, making the test flaky.
### Issue Context
This affects at least the assertions in the “valid URL” and “unreachable URL” tests.
### Fix Focus Areas
- Capture `LocalDate expected = LocalDate.now();` **before** calling `performSearch(...)` and assert against `new Date(expected).getNormalized()`.
- (Optional stronger approach) inject a `Clock` into `GenericUrlBasedFetcher` and use a fixed clock in tests.
- jablib/src/test/java/org/jabref/logic/importer/fetcher/GenericUrlBasedFetcherTest.java[24-49]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


10. Localization.lang uses concatenation ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
User-facing dialog messages are built via string concatenation inside Localization.lang(...),
which makes localization maintenance harder and violates the placeholder-based localization pattern.
This affects the new URL entry flow error/warning dialogs.
Code

jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java[R426-430]

+                    Localization.lang(
+                            "Failed to fetch the URL.\n" +
+                                    "The following error was encountered:\n" +
+                                    "%0",
+                            exceptionMessage));
Evidence
PR Compliance ID 34 forbids building user-facing localized strings via concatenation. In
executeEnterUrl, the dialog body passed to Localization.lang(...) is assembled with `"..." +
"..."`.

jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java[423-430]
jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java[438-443]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`Localization.lang(...)` is called with concatenated string literals, which violates the localization pattern requiring a single, placeholder-based message template.
## Issue Context
This occurs in the new URL-entry workflow dialogs and should be expressed as single localization keys/templates (with `%0` placeholders where needed) without `+` concatenation.
## Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java[423-430]
- jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java[438-443]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


11. switchEnterUrl runLater unguarded ✓ Resolved 📘 Rule violation ☼ Reliability
Description
Platform.runLater requests focus without validating the tab/state is still current when the
callback runs, which can cause stale UI updates (focus jumping) if the user switches tabs quickly.
This violates the deferred JavaFX callback stale-state guard pattern.
Code

jabgui/src/main/java/org/jabref/gui/newentry/NewEntryView.java[R472-474]

+        if (urlText != null) {
+            Platform.runLater(() -> urlText.requestFocus());
+        }
Evidence
PR Compliance ID 33 requires guarding deferred FX-thread callbacks against stale state. The newly
added Platform.runLater(() -> urlText.requestFocus()) has no generation/token or re-check inside
the callback.

jabgui/src/main/java/org/jabref/gui/newentry/NewEntryView.java[463-474]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A deferred JavaFX callback (`Platform.runLater`) mutates UI state (focus) without a stale-state guard inside the callback.
## Issue Context
Even though `switchEnterUrl()` checks selection before scheduling, the selection can change before the FX callback executes; the callback should re-check a token/generation or tab selection inside the runnable.
## Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/newentry/NewEntryView.java[463-474]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI gener...

Comment thread jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java Outdated
Comment thread jabgui/src/main/java/org/jabref/gui/newentry/NewEntryView.java
@github-actions github-actions Bot added the status: changes-required Pull requests that are not yet complete label Jul 30, 2026
- fetchTitle now skips non-http(s) URLs (e.g. ftp://) before calling
  Jsoup, which only supports http/https and threw an uncaught
  IllegalArgumentException for anything else, aborting entry creation
  entirely. Added a regression test for this.
- Add an explicit 30s connect timeout, matching the existing
  DoiResolution.java convention, so a stalled host can't hang the
  background task indefinitely.
- Annotate UrlBasedFetcher and GenericUrlBasedFetcher with
  @NullMarked, per AGENTS.md's rule for new classes.
- Drop the string concatenation in the one Localization.lang(...)
  call that's new to this PR; left the identical pre-existing
  "An unknown error has occurred..." message (shared verbatim with
  two other tabs in the same file) untouched to avoid making three
  copies inconsistent.
@dvinit
dvinit force-pushed the add-entry-from-url branch from aadb485 to d867d56 Compare July 30, 2026 17:27
Per Qodo review: Platform.runLater(() -> urlText.requestFocus()) could
fire after the user switches away from the Enter URL tab, stealing
focus back to a field that's no longer visible. Re-check
tabEnterUrl.isSelected() inside the callback before requesting focus.

Scoped to only this new tab -- the three sibling tabs
(switchLookupIdentifier, switchInterpretCitations, switchSpecifyBibtex)
have the identical unguarded pattern already, pre-existing and out of
this PR's scope.
@github-actions github-actions Bot added status: no-bot-comments and removed status: changes-required Pull requests that are not yet complete labels Jul 30, 2026
@dvinit
dvinit marked this pull request as ready for review July 30, 2026 19:41
Comment thread jablib/src/main/resources/l10n/JabRef_en.properties Outdated
Comment thread jabgui/src/main/resources/org/jabref/gui/newentry/NewEntry.fxml
Comment thread jabgui/src/main/java/org/jabref/gui/newentry/NewEntryDialogTab.java
@qodo-free-for-open-source-projects

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit b2458fa

- Move ENTER_URL to the end of NewEntryDialogTab. JabRefGuiPreferences
  persists the last-used tab as the ordinal index into values();
  inserting it in the middle shifted every later constant's ordinal,
  silently remapping existing users' stored tab preference. The tab's
  visual position (right after Enter Identifier) is unaffected -- that
  comes from NewEntry.fxml's element order, not the enum's declaration
  order.
- Capture the expected urldate before calling performSearch in
  GenericUrlBasedFetcherTest, not after. The production code also
  calls LocalDate.now() internally during its (real, network-bound)
  title fetch, so asserting with a post-call LocalDate.now() risked a
  midnight rollover making the test flaky.
- Add req~fetchers.generic-url~1 (docs/requirements/fetchers.md) with
  a matching [impl->...] tag, closing an OpenFastTrace coverage gap
  for this new feature. Verified via ./gradlew traceRequirements.
@github-actions github-actions Bot added status: changes-required Pull requests that are not yet complete and removed status: no-bot-comments labels Jul 31, 2026
@dvinit

dvinit commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

/review

Comment thread jablib/src/main/java/org/jabref/logic/util/URLUtil.java Outdated
Comment thread jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java
@qodo-free-for-open-source-projects

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 6fa64f8

@pluto-han pluto-han left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

quick scan

Comment on lines +8 to +13
// Appended after the pre-existing constants (rather than grouped next to ENTER_IDENTIFIER, which is where it
// appears in NewEntry.fxml's tab order -- unrelated to this enum's declaration order) because
// JabRefGuiPreferences persists the last-used tab as the ordinal index into values(). Inserting a constant in
// the middle would silently shift every later constant's ordinal, remapping existing users' stored preference
// to the wrong tab.
ENTER_URL,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the comment.

Comment on lines +84 to +92
String trimmedUrl = url.trim();

// Check if the URL has a protocol (http://, https://, ftp://)
if (!URL_PATTERN.matcher(url).matches()) {
if (!URL_PATTERN.matcher(trimmedUrl).matches()) {
return false;
}

try {
create(url);
create(trimmedUrl);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bad naming, why not make it inline

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've reverted the helper change to avoid changing 3 existing callers of isURL(unrelated to this issue).

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

The requested changes were not addressed for 3 days. Please follow-up in the next 7 days or your PR will be automatically closed. You can check the contributing guidelines for hints on the pull request process.

@github-actions github-actions Bot added the status: stale Issues marked by a bot as "stale". All issues need to be investigated manually. label Aug 9, 2026
- Revert URLUtil.isURL() trimming: widening it to accept surrounding
  whitespace created a validate/consume mismatch for existing callers
  that pass the raw string to strict URI consumers. Trimming is now
  scoped to the Enter URL tab's own validator; the fetcher already
  trims before use.
- Log only scheme and host of a failing URL, since userinfo and query
  parameters can embed credentials or access tokens.
- Remove the enum ordering comment from NewEntryDialogTab (ENTER_URL
  stays declared last; the last-used tab is persisted by ordinal).
@dvinit

dvinit commented Aug 9, 2026

Copy link
Copy Markdown
Contributor Author

/review

@github-actions github-actions Bot added status: no-bot-comments and removed status: changes-required Pull requests that are not yet complete status: stale Issues marked by a bot as "stale". All issues need to be investigated manually. labels Aug 9, 2026
@dvinit

dvinit commented Aug 9, 2026

Copy link
Copy Markdown
Contributor Author

@LoayTarek5 @pluto-han I've tried to address qodo comments, and reverted the isURL helper change.

Comment thread jabgui/src/main/java/org/jabref/gui/newentry/NewEntryView.java
@qodo-free-for-open-source-projects

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 38156be

@dvinit
dvinit requested a review from pluto-han August 9, 2026 21:28
@LoayTarek5

Copy link
Copy Markdown
Collaborator

@LoayTarek5 @pluto-han I've tried to address qodo comments, and reverted the isURL helper change.

Ok, i will review it by today or tomorrow at the latest

private final GenericUrlBasedFetcher fetcher = new GenericUrlBasedFetcher();

@Test
void performSearchWithValidUrlReturnsMiscEntryWithTitleAndUrldate() throws FetcherException {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test can not fail for the reason it exists, bec GenericUrlBasedFetcher sets the title to fetchTitle.orElse(trimmedUrl), and the URL is always non blank

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup, fixed. Now the URL-as-title fallback no longer passes as a scraped title.

import static org.junit.jupiter.api.Assertions.assertTrue;

@FetcherTest
class GenericUrlBasedFetcherTest {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The invalid URL, blank input and getName tests touch no network but are excluded from the default test task, so tag the four network tests instead

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved to method level for the 3 tests, 1 test is now offline.


private class WorkerEnterUrl extends Task<Optional<List<BibEntry>>> {
@Override
protected Optional<List<BibEntry>> call() throws FetcherException {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think the WorkerEnterUrl.call() and result.isEmpty() below, these guards can not trigger, bec performSearch always return List.of(entry), so entries.isEmpty() is never true, and the blank, invalid recheck duplicates urlTextValidator, maybe call() can return List directly, right?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes call can return list directly.

validator.configureValidation(viewModel.duplicateDoiValidatorStatus(), textInput);
}

private void initializeEnterUrl() {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i try to think about that, but uncnditional clipboard prefill means the tab opens showing a red "You must provide a valid URL." for ordinary clipboard text,right? try to see the identifier tab guards this at NewEntryView

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, now clipboard invalid urls won't prefill and instead the tab would remain empty.


/// Reduces a user-provided URL to its scheme and host for logging, since the rest of the URL (userinfo, path,
/// query, fragment) commonly carries credentials or access tokens that must not end up in application logs.
private static String sanitizeUrlForLogging(String url) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

try to see FetcherException already handles this, so getLocalizedMessage runs getRedactedUrl which strips userinfo and API key query params, use a URL-carrying constructor instead

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, dropped the sanitizer. using redacted url and localized message now.

Comment on lines +57 to +64
/// Best-effort fetch of the target page's `<title>`. A failure here (unreachable host, timeout, no
/// title tag, ...) should never prevent the entry from being created, so this swallows the error and lets the
/// caller fall back to using the URL itself as the title.
///
/// Only attempted for `http`/`https` URLs — [Jsoup.connect] only supports those schemes and throws
/// `IllegalArgumentException` for anything else (e.g. `ftp://`, which [URLUtil.isURL] otherwise accepts).
private Optional<String> fetchTitle(String url) {
String scheme = url.toLowerCase(Locale.ROOT);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Jsoup.connect throws IllegalArgumentException for non-http schemes, but jsoup throws MalformedURLException, which is an IOException and already caught below, right

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, the guard was redundant. fixed.

urlTextValidator = new FunctionBasedValidator<>(
urlText,
input -> input != null && URLUtil.isURL(input.trim()),
ValidationMessage.error(Localization.lang("You must specify a valid URL.")));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think it is never rendered, only the FXML string shows

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the validator message was never displayed. It now reuses the FXML label's string.

Comment on lines +13 to +18
/// Looks for bibliographic information for the resource located at the given URL.
///
/// @param url a string containing the URL of the resource
/// @return a list of [BibEntry], one entry per hit found for the URL
List<BibEntry> performSearch(String url) throws FetcherException;
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the javadoc describes the method but not what implementations must guarantee

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Included in javadoc : implementations own validation (throw FetcherException for unhandled URLs), must degrade gracefully on optional-data failures, and return a possibly-empty but never-null list.

Comment on lines +27 to +29
/// added, and (best-effort) the target page's title.
@NullMarked
// [impl->req~fetchers.generic-url~1]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this sits between @NullMarked and the class declaration?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved.

@github-actions github-actions Bot added status: changes-required Pull requests that are not yet complete and removed status: no-bot-comments labels Aug 13, 2026

@LoayTarek5 LoayTarek5 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have questions also, @koppor
1- The URL tab ignores identifiers, so if you paste somthing like https://dl.acm.org/doi/epdf/10.1145/... into the new tab, you get a plain @misc entry with just the link and the page title, and paste the same link into the Enter Identifier tab next to it, and you get a full entry with authors, journal and year.

2- is it should @misc or @online? i see the issue asks for @misc, but in NewEntryView file says Online is the type for web sites, and this PR also sets urldate, which is a biblatex field, so i suggest @online looks like the better fit for a plain web link, right?

@pluto-han

Copy link
Copy Markdown
Collaborator

is it should @misc or @online? i see the issue asks for @misc, but in NewEntryView file says Online is the type for web sites, and this PR also sets urldate, which is a biblatex field, so i suggest @online looks like the better fit for a plain web link, right?

I think because @online is only for BibLaTeX

@LoayTarek5

Copy link
Copy Markdown
Collaborator

I think because @online is only for BibLaTeX

Yeah, so it would break plain BibTeX libraries, and urldate is BibLaTeX only too,right? so @misc + urldate makes sense

- Document the UrlBasedFetcher contract (multi-hit, validation
  ownership, graceful degradation on optional data)
- Remove the redundant non-http scheme guard: jsoup rejects such
  URLs with a MalformedURLException before opening a connection,
  which the existing IOException handling already covers
- Strengthen the live-scrape test so the URL-as-title fallback no
  longer passes as a scraped title; tag only the three tests that
  touch the network as @FetcherTest so the rest run per-PR
- Simplify WorkerEnterUrl to Task<List<BibEntry>>; a single
  empty-result check remains in onSucceeded per the interface
  contract
- Use FetcherException's URL redaction for log, dialog, and the
  invalid-URL message instead of a hand-rolled sanitizer
- Only prefill the Enter URL field when the clipboard holds a URL,
  mirroring the Enter Identifier tab's guard
- Drop the never-rendered duplicate "valid URL" localisation string
@github-actions github-actions Bot added status: no-bot-comments and removed status: changes-required Pull requests that are not yet complete labels Aug 14, 2026
@dvinit
dvinit requested a review from LoayTarek5 August 14, 2026 22:47
@dvinit

dvinit commented Aug 14, 2026

Copy link
Copy Markdown
Contributor Author

@LoayTarek5 I've done the required changes. code is a lot cleaner now.

LoayTarek5
LoayTarek5 previously approved these changes Aug 15, 2026

@LoayTarek5 LoayTarek5 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, great work @dvinit

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component: fetcher good first issue An issue intended for project-newcomers. Varies in difficulty. status: awaiting-second-review For non-trivial changes status: no-bot-comments

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add entry using URL

4 participants