Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
2c1a9b5
feat(git): add “Share to GitHub” flow
wanling0000 Aug 10, 2025
755b84b
add module-info
wanling0000 Aug 10, 2025
9f4f675
Resolve conflicts
wanling0000 Aug 10, 2025
66d2e49
fix(git): add missing imports
wanling0000 Aug 10, 2025
79720ee
test: init repo in SlrGitHandlerTest; fix localization keys
wanling0000 Aug 10, 2025
f090c38
fix: fail fast on null/blank parameters
wanling0000 Aug 10, 2025
e880044
fix: Avoid silent failure in GitPreferences
wanling0000 Aug 10, 2025
667d317
refactor: avoid null init
wanling0000 Aug 10, 2025
a8cbdb9
Merge `upstream/main` into `feat/gsoc-git-share-to-github`
wanling0000 Aug 11, 2025
8864b63
add GitHandler class
wanling0000 Aug 11, 2025
1f0b3f6
fix: keep RuntimeException catch but using log WARN
wanling0000 Aug 11, 2025
cc01903
Test Stacked branch
wanling0000 Aug 11, 2025
0e3c9b1
fix: checkstyle error
wanling0000 Aug 11, 2025
16bc319
Merge `upstream/main` into `feat/gsoc-git-share-to-github`
wanling0000 Aug 11, 2025
609aae3
fix: add missing imports
wanling0000 Aug 11, 2025
cc55735
Use BackgroundTask and field Validator
wanling0000 Aug 15, 2025
d9cda75
Refactor GitPreferences align with ProxyPreferences
wanling0000 Aug 15, 2025
746eb72
Add https-only validator
wanling0000 Aug 15, 2025
2d05b88
Add TODOs and usage of StringUtil.isBlank
koppor Aug 15, 2025
afa356c
WIP - save intermediate GitPreferences refactor
wanling0000 Aug 15, 2025
8d2f3bc
WIP - save intermediate GitPreferences refactor
wanling0000 Aug 15, 2025
9f7e8f5
WIP
koppor Aug 17, 2025
809cbe1
refactor: GitHub share dialog
wanling0000 Aug 18, 2025
0316c2b
Merge remote-tracking branch 'upstream/main' into clean/gsoc-git-shar…
wanling0000 Aug 18, 2025
1ec62d9
Compile fix
koppor Aug 18, 2025
9b16562
Merge branch 'feat/gsoc-git-share-to-github' of github.com:wanling000…
koppor Aug 18, 2025
f3bf354
Convert GitPreferences to record (as suggested by IntelliJ)
koppor Aug 18, 2025
1e0a344
Fix git PAT storage
wanling0000 Aug 18, 2025
984d9c4
Merge branch 'feat/gsoc-git-share-to-github' of github.com:wanling000…
wanling0000 Aug 18, 2025
dae0377
Add TODO note
koppor Aug 18, 2025
cf5d724
Merge branch 'feat/gsoc-git-share-to-github' of github.com:wanling000…
koppor Aug 18, 2025
59adde0
Changes by subhr
koppor Aug 18, 2025
5848b1a
Changes by fwl
koppor Aug 18, 2025
8a137ac
Revert "Convert GitPreferences to record (as suggested by IntelliJ)"
koppor Aug 18, 2025
12b5d74
Compile fix
koppor Aug 18, 2025
4472d81
Disable non-working test
koppor Aug 18, 2025
46cf427
Add ing to ADR-0016
koppor Aug 18, 2025
a4d667f
Rewrite Optional to ""
koppor Aug 18, 2025
87353e1
Fix markdown lint
koppor Aug 18, 2025
3f8a864
Link to ADR-0016
koppor Aug 18, 2025
5a2ce1e
Fix casing
koppor Aug 18, 2025
1af29d3
Fix architecture
koppor Aug 18, 2025
c49a1cf
Fix typo
koppor Aug 18, 2025
01684be
Refine comments
koppor Aug 18, 2025
3602e72
Remove TODO
koppor Aug 18, 2025
160c398
More language cleanup
koppor Aug 18, 2025
9ec8879
Fix empty line
koppor Aug 18, 2025
752b7fe
Cleanup language again
koppor Aug 18, 2025
614a0ff
Remove TODO
koppor Aug 18, 2025
1f89a5a
Fix localization
koppor Aug 18, 2025
d7a2ff6
Remove separator
koppor Aug 18, 2025
5ec253e
Git is first
koppor Aug 18, 2025
ed1ae89
Fix conversion
koppor Aug 18, 2025
095f076
Use "notify" instead of a popup
koppor Aug 18, 2025
2df3788
Remove obsolete string
koppor Aug 18, 2025
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
6 changes: 6 additions & 0 deletions docs/decisions/0016-mutable-preferences-objects.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,9 @@ To create an immutable preferences object every time seems to be a waste of time
Chosen option: "Alter the exiting object", because the preferences objects are just wrappers around the basic preferences framework of JDK. They
should be mutable on-the-fly similar to objects with a Builder inside and to be stored immediately again in the
preferences.

### Consequences

* Import logic will be more hard as exising preferences objects have to be altered; and it is very hard to know which preference objects exactly are needed to be modified.
* Cached variables need to be observables, too. AKA The cache needs to be observable.
* There is NO "real" factory pattern for the preferences objects, as they are mutable --> they are passed via the constructor and long-lived
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,13 @@ public enum StandardActions implements Action {
GROUP_SUBGROUP_RENAME(Localization.lang("Rename subgroup"), KeyBinding.GROUP_SUBGROUP_RENAME),
GROUP_ENTRIES_REMOVE(Localization.lang("Remove selected entries from this group")),

CLEAR_EMBEDDINGS_CACHE(Localization.lang("Clear embeddings cache"));
CLEAR_EMBEDDINGS_CACHE(Localization.lang("Clear embeddings cache")),

GIT(Localization.lang("Git"), IconTheme.JabRefIcons.GIT_SYNC),
GIT_PULL(Localization.lang("Pull")),
GIT_PUSH(Localization.lang("Push")),
GIT_COMMIT(Localization.lang("Commit")),
GIT_SHARE(Localization.lang("Share this library to GitHub"));

private String text;
private final String description;
Expand Down
10 changes: 10 additions & 0 deletions jabgui/src/main/java/org/jabref/gui/frame/MainMenu.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import org.jabref.gui.externalfiles.AutoLinkFilesAction;
import org.jabref.gui.externalfiles.DownloadFullTextAction;
import org.jabref.gui.externalfiles.FindUnlinkedFilesAction;
import org.jabref.gui.git.GitShareToGitHubAction;
import org.jabref.gui.help.AboutAction;
import org.jabref.gui.help.ErrorConsoleAction;
import org.jabref.gui.help.HelpAction;
Expand Down Expand Up @@ -180,10 +181,19 @@ private void createMenu() {

new SeparatorMenuItem(),

// region: Sharing of the library

// TODO: Should be only enabled if not yet shared.
factory.createSubMenu(StandardActions.GIT,
factory.createMenuItem(StandardActions.GIT_SHARE, new GitShareToGitHubAction(dialogService, stateManager))
),

factory.createSubMenu(StandardActions.REMOTE_DB,
factory.createMenuItem(StandardActions.CONNECT_TO_SHARED_DB, new ConnectToSharedDatabaseCommand(frame, dialogService)),
factory.createMenuItem(StandardActions.PULL_CHANGES_FROM_SHARED_DB, new PullChangesFromSharedAction(stateManager))),

// endregion

new SeparatorMenuItem(),

factory.createMenuItem(StandardActions.SHOW_PREFS, new ShowPreferencesAction(frame, dialogService)),
Expand Down
4 changes: 2 additions & 2 deletions jabgui/src/main/java/org/jabref/gui/git/GitPullViewModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ public GitPullViewModel(GitSyncService syncService, GitStatusViewModel gitStatus
public MergeResult pull() throws IOException, GitAPIException, JabRefException {
Optional<BibDatabaseContext> databaseContextOpt = gitStatusViewModel.getDatabaseContext();
if (databaseContextOpt.isEmpty()) {
throw new JabRefException(Localization.lang("Cannot pull: No active BibDatabaseContext."));
throw new JabRefException(Localization.lang("No library selected"));
}

BibDatabaseContext localBibDatabaseContext = databaseContextOpt.get();
Path bibFilePath = localBibDatabaseContext.getDatabasePath().orElseThrow(() ->
new JabRefException(Localization.lang("Cannot pull: .bib file path missing in BibDatabaseContext."))
new JabRefException(Localization.lang("Cannot pull: Please save the library to a file first."))
);

MergeResult result = syncService.fetchAndMerge(localBibDatabaseContext, bibFilePath);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package org.jabref.gui.git;

import javafx.beans.binding.BooleanExpression;

import org.jabref.gui.DialogService;
import org.jabref.gui.StateManager;
import org.jabref.gui.actions.SimpleCommand;

import static org.jabref.gui.actions.ActionHelper.needsDatabase;

public class GitShareToGitHubAction extends SimpleCommand {
private final DialogService dialogService;
private final StateManager stateManager;

public GitShareToGitHubAction(
DialogService dialogService,
StateManager stateManager) {
this.dialogService = dialogService;
this.stateManager = stateManager;

this.executable.bind(this.enabledGitShare());
}

@Override
public void execute() {
dialogService.showCustomDialogAndWait(new GitShareToGitHubDialogView());
}

private BooleanExpression enabledGitShare() {
// TODO: Determine the correct condition for enabling "Git Share". This currently only requires an open library.
// In the future, this may need to check whether:
// - the repo is initialized (because without a repository, the current implementation does not work -> future work)
// - etc.
// Can be called independent if a remote is configured or not -- it will be done in the dialog
// HowTo: Inject the observables (or maybe the stateManager) containing these constraints
return needsDatabase(stateManager);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package org.jabref.gui.git;

import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.scene.control.ButtonType;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.scene.control.Tooltip;

import org.jabref.gui.DialogService;
import org.jabref.gui.StateManager;
import org.jabref.gui.desktop.os.NativeDesktop;
import org.jabref.gui.preferences.GuiPreferences;
import org.jabref.gui.util.BaseDialog;
import org.jabref.gui.util.IconValidationDecorator;
import org.jabref.logic.l10n.Localization;
import org.jabref.logic.util.TaskExecutor;

import com.airhacks.afterburner.views.ViewLoader;
import de.saxsys.mvvmfx.utils.validation.visualization.ControlsFxVisualizer;
import jakarta.inject.Inject;

public class GitShareToGitHubDialogView extends BaseDialog<Void> {
private static final String GITHUB_PAT_DOCS_URL =
"https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens";

private static final String GITHUB_NEW_REPO_URL = "https://github.com/new";

@FXML private TextField repositoryUrl;
@FXML private TextField username;
@FXML private PasswordField personalAccessToken;
@FXML private ButtonType shareButton;
@FXML private Label patHelpIcon;
@FXML private Tooltip patHelpTooltip;
@FXML private CheckBox rememberSettingsCheck;
@FXML private Label repoHelpIcon;
@FXML private Tooltip repoHelpTooltip;

private GitShareToGitHubDialogViewModel viewModel;

@Inject
private DialogService dialogService;

@Inject
private StateManager stateManager;

@Inject
private TaskExecutor taskExecutor;

@Inject
private GuiPreferences preferences;

private final ControlsFxVisualizer visualizer = new ControlsFxVisualizer();

public GitShareToGitHubDialogView() {
ViewLoader.view(this)
.load()
.setAsDialogPane(this);
}

@FXML
private void initialize() {
this.viewModel = new GitShareToGitHubDialogViewModel(preferences.getGitPreferences(), stateManager, dialogService, taskExecutor);

this.setTitle(Localization.lang("Share this Library to GitHub"));

// See "javafx.md"
this.setResultConverter(button -> {
if (button != ButtonType.CANCEL) {
// We do not want to use "OK", but we want to use a custom text instead.
// JavaFX does not allow to alter the text of the "OK" button.
// Therefore, we used another button type.
// Since we have only two buttons, we can check for non-cancel here.
shareToGitHub();
}
return null;
});

patHelpTooltip.setText(
Localization.lang("Click to open GitHub Personal Access Token documentation")
);

username.setPromptText(Localization.lang("Your GitHub username"));
personalAccessToken.setPromptText(Localization.lang("PAT with repo access"));

repoHelpTooltip.setText(
Localization.lang("Create an empty repository on GitHub, then copy the HTTPS URL (ends with .git). Click to open GitHub.")
);
Tooltip.install(repoHelpIcon, repoHelpTooltip);
repoHelpIcon.setOnMouseClicked(e ->
NativeDesktop.openBrowserShowPopup(
GITHUB_NEW_REPO_URL,
dialogService,
preferences.getExternalApplicationsPreferences()
)
);

Tooltip.install(patHelpIcon, patHelpTooltip);
patHelpIcon.setOnMouseClicked(e ->
NativeDesktop.openBrowserShowPopup(
GITHUB_PAT_DOCS_URL,
dialogService,
preferences.getExternalApplicationsPreferences()
)
);

repositoryUrl.textProperty().bindBidirectional(viewModel.repositoryUrlProperty());
username.textProperty().bindBidirectional(viewModel.usernameProperty());
personalAccessToken.textProperty().bindBidirectional(viewModel.patProperty());
rememberSettingsCheck.selectedProperty().bindBidirectional(viewModel.rememberPatProperty());

viewModel.setValues();

Platform.runLater(() -> {
visualizer.setDecoration(new IconValidationDecorator());

visualizer.initVisualization(viewModel.repositoryUrlValidation(), repositoryUrl, true);
visualizer.initVisualization(viewModel.githubUsernameValidation(), username, true);
visualizer.initVisualization(viewModel.githubPatValidation(), personalAccessToken, true);
});
}

@FXML
private void shareToGitHub() {
viewModel.shareToGitHub(() -> this.close());
}
}
Loading