Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Programming exercises: Add clone in IDE buttons #8735

Merged
merged 23 commits into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
601fb0f
`Programming exercises`: Add clone in IntelliJ button
Strohgelaender Jun 4, 2024
1f89807
Merge branch 'develop' into feature/programming/clone-in-intellij
Strohgelaender Jun 4, 2024
627e05e
fix file name
Strohgelaender Jun 4, 2024
c6f4862
Update src/main/webapp/app/shared/components/clone-repo-button/clone-…
Strohgelaender Jun 4, 2024
098ec61
Merge branch 'develop' of https://github.com/ls1intum/Artemis into fe…
Strohgelaender Jun 5, 2024
6475b91
translations
Strohgelaender Jun 5, 2024
88fbd39
remove help icon
Strohgelaender Jun 5, 2024
f6571ed
support more jetbrains ideas
Strohgelaender Jun 5, 2024
3cfdb86
Merge branch 'develop' of https://github.com/ls1intum/Artemis into fe…
Strohgelaender Jun 6, 2024
829833e
review + client tests
Strohgelaender Jun 6, 2024
25e5057
don't use self-closed tags (compilation error)
Strohgelaender Jun 6, 2024
8dc617e
fix test by expecting encoded uri
Strohgelaender Jun 6, 2024
db52115
Merge branch 'develop' of https://github.com/ls1intum/Artemis into fe…
Strohgelaender Jun 9, 2024
bc47a1c
revert to only IntelliJ
Strohgelaender Jun 9, 2024
9a965c6
Update src/main/webapp/i18n/de/exercise-actions.json
Strohgelaender Jun 9, 2024
73a080e
Merge branch 'develop' of https://github.com/ls1intum/Artemis into fe…
Strohgelaender Jun 12, 2024
2b5bf6d
show no alert during exam mode
Strohgelaender Jun 12, 2024
6b396f5
Merge branch 'develop' into feature/programming/clone-in-intellij
Strohgelaender Jun 15, 2024
9fad368
Merge branch 'develop' into feature/programming/clone-in-intellij
Strohgelaender Jun 16, 2024
74e6d11
Merge branch 'develop' into feature/programming/clone-in-intellij
Strohgelaender Jun 22, 2024
7295575
Programming exercises: Add clone in VSCode button (#8797)
janthoXO Jun 23, 2024
1a830a7
Merge branch 'develop' into feature/programming/clone-in-intellij
Strohgelaender Jun 23, 2024
7cb1ecd
Merge branch 'develop' into feature/programming/clone-in-intellij
Strohgelaender Jun 23, 2024
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Injectable } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class ExternalCloningService {
/**
* Build source tree url.
* @param baseUrl - the base url of the version control system
* @param cloneUrl - url of the target.
*/
buildSourceTreeUrl(baseUrl: string, cloneUrl: string | undefined): string | undefined {
return cloneUrl ? `sourcetree://cloneRepo?type=stash&cloneUrl=${encodeURI(cloneUrl)}&baseWebUrl=${baseUrl}` : undefined;
}

/**
* Builds an url to clone a Repository in IntelliJ
* Structure: jetbrains://idea/checkout/git?idea.required.plugins.id=Git4Idea&checkout.repo=:RepoUrl
* @param cloneUrl the url of the repository to clone
*/
buildJetbrainsUrl(cloneUrl: string | undefined): string | undefined {
if (!cloneUrl) {
return undefined;
}
return `jetbrains://idea/checkout/git?idea.required.plugins.id=Git4Idea&checkout.repo=${encodeURIComponent(cloneUrl)}`;
Strohgelaender marked this conversation as resolved.
Show resolved Hide resolved
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Component, ContentChild, HostBinding, Input, OnChanges, OnInit, TemplateRef } from '@angular/core';
import { Router } from '@angular/router';
import { AlertService } from 'app/core/util/alert.service';
import { SourceTreeService } from 'app/exercises/programming/shared/service/sourceTree.service';
import { ExternalCloningService } from 'app/exercises/programming/shared/service/external-cloning.service';
import { FeatureToggle } from 'app/shared/feature-toggle/feature-toggle.service';
import { InitializationState } from 'app/entities/participation/participation.model';
import { Exercise, ExerciseType } from 'app/entities/exercise.model';
Expand All @@ -25,7 +25,7 @@ import { AssessmentType } from 'app/entities/assessment-type.model';
selector: 'jhi-exercise-details-student-actions',
templateUrl: './exercise-details-student-actions.component.html',
styleUrls: ['../course-overview.scss'],
providers: [SourceTreeService],
providers: [ExternalCloningService],
Strohgelaender marked this conversation as resolved.
Show resolved Hide resolved
})
export class ExerciseDetailsStudentActionsComponent implements OnInit, OnChanges {
readonly FeatureToggle = FeatureToggle;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,9 @@ <h5>{{ cloneHeadline | artemisTranslate }}</h5>
>
{{ wasCopied ? ('artemisApp.exerciseActions.copiedUrl' | artemisTranslate) : ('artemisApp.exerciseActions.copyUrl' | artemisTranslate) }}
</button>
<a class="btn btn-primary btn-sm" target="hidden-iframe" [href]="buildSourceTreeUrl() | safeUrl"
>{{ 'artemisApp.exerciseActions.cloneSourceTree.button' | artemisTranslate }}
</a>
<a class="btn btn-primary btn-sm" [href]="buildJetbrainsUrl() | safeUrl" jhiTranslate="artemisApp.exerciseActions.cloneIntelliJ.button"> </a>
<jhi-help-icon placement="right auto" text="artemisApp.exerciseActions.cloneIntelliJ.helpIcon" />
<a class="btn btn-primary btn-sm" target="hidden-iframe" [href]="buildSourceTreeUrl() | safeUrl" jhiTranslate="artemisApp.exerciseActions.cloneSourceTree.button"> </a>
<iframe name="hidden-iframe" style="visibility: hidden; position: absolute"></iframe>
<jhi-help-icon placement="right auto" text="artemisApp.exerciseActions.cloneSourceTree.helpIcon" />
</ng-template>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { Component, Input, OnChanges, OnInit } from '@angular/core';
import { ProgrammingExercise, ProgrammingLanguage } from 'app/entities/programming-exercise.model';
import { FeatureToggle } from 'app/shared/feature-toggle/feature-toggle.service';
import { SourceTreeService } from 'app/exercises/programming/shared/service/sourceTree.service';
import { ExternalCloningService } from 'app/exercises/programming/shared/service/external-cloning.service';
import { TranslateService } from '@ngx-translate/core';
import { AccountService } from 'app/core/auth/account.service';
import { User } from 'app/core/user/user.model';
import { ProfileService } from 'app/shared/layouts/profiles/profile.service';
import { LocalStorageService } from 'ngx-webstorage';
import { ProgrammingExerciseStudentParticipation } from 'app/entities/participation/programming-exercise-student-participation.model';
import { ParticipationService } from 'app/exercises/shared/participation/participation.service';
import { Exercise } from 'app/entities/exercise.model';
import { PROFILE_LOCALVC } from 'app/app.constants';
import { isPracticeMode } from 'app/entities/participation/student-participation.model';
import { faDownload, faExternalLink } from '@fortawesome/free-solid-svg-icons';
Expand All @@ -20,6 +20,7 @@ import { faDownload, faExternalLink } from '@fortawesome/free-solid-svg-icons';
})
export class CloneRepoButtonComponent implements OnInit, OnChanges {
readonly FeatureToggle = FeatureToggle;
readonly ProgrammingLanguage = ProgrammingLanguage;

@Input()
loading = false;
Expand All @@ -30,7 +31,7 @@ export class CloneRepoButtonComponent implements OnInit, OnChanges {
@Input()
participations?: ProgrammingExerciseStudentParticipation[];
@Input()
exercise?: Exercise;
exercise?: ProgrammingExercise;

useSsh = false;
sshKeysUrl?: string;
Expand All @@ -53,7 +54,7 @@ export class CloneRepoButtonComponent implements OnInit, OnChanges {

constructor(
private translateService: TranslateService,
private sourceTreeService: SourceTreeService,
private externalCloningService: ExternalCloningService,
private accountService: AccountService,
private profileService: ProfileService,
private localStorage: LocalStorageService,
Expand Down Expand Up @@ -189,8 +190,12 @@ export class CloneRepoButtonComponent implements OnInit, OnChanges {
* build the sourceTreeUrl from the repository uri
* @return sourceTreeUrl
*/
buildSourceTreeUrl() {
return this.sourceTreeService.buildSourceTreeUrl(this.versionControlUrl, this.getHttpOrSshRepositoryUri(false));
buildSourceTreeUrl(): string | undefined {
return this.externalCloningService.buildSourceTreeUrl(this.versionControlUrl, this.getHttpOrSshRepositoryUri(false));
}
Strohgelaender marked this conversation as resolved.
Show resolved Hide resolved

buildJetbrainsUrl(): string | undefined {
return this.externalCloningService.buildJetbrainsUrl(this.getHttpOrSshRepositoryUri(false));
Strohgelaender marked this conversation as resolved.
Show resolved Hide resolved
}

switchPracticeMode() {
Expand Down
4 changes: 4 additions & 0 deletions src/main/webapp/i18n/de/exercise-actions.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@
"button": "Im Git Client klonen",
"helpIcon": "Dies öffnet das Repository in Sourcetree oder Tower. Falls du diese nicht installiert hast, kannst du das Repository in deiner IDE oder dem Terminal klonen."
},
"cloneIntelliJ": {
"button": "In IntelliJ klonen",
"helpIcon": "Klone das Repository direkt in IntelliJ. Dieses Feature benötigt die Jetbrains Toolbox."
},
Strohgelaender marked this conversation as resolved.
Show resolved Hide resolved
"goToBuildPlan": "Zum Build Plan gehen",
"openTextEditor": "Texteditor öffnen",
"copyUrl": "URL kopieren",
Expand Down
4 changes: 4 additions & 0 deletions src/main/webapp/i18n/en/exercise-actions.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@
"button": "Clone in Git client",
"helpIcon": "This opens the repository in Sourcetree or Tower. If you do not have them, you can still clone the repository using your IDE or terminal."
},
"cloneIntelliJ": {
"button": "Clone in IntelliJ",
"helpIcon": "Clone this repository in IntelliJ IDEA. This feature requires the Jetbrains Toolbox."
},
"goToBuildPlan": "Go to build plan",
"openTextEditor": "Open text editor",
"copyUrl": "Copy URL",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { TestBed } from '@angular/core/testing';
import { ExternalCloningService } from 'app/exercises/programming/shared/service/external-cloning.service';

describe('ExternalCloningService', () => {
let service: ExternalCloningService;
const baseUrl = 'https://artemis.cit.tum.de';

beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(ExternalCloningService);
});

it('should build source tree url correctly', () => {
const cloneUrl = baseUrl + '/git/reo.git';
const expectedUrl = `sourcetree://cloneRepo?type=stash&cloneUrl=https://artemis.cit.tum.de/git/reo.git&baseWebUrl=https://artemis.cit.tum.de`;
expect(service.buildSourceTreeUrl(baseUrl, cloneUrl)).toEqual(expectedUrl);
Strohgelaender marked this conversation as resolved.
Show resolved Hide resolved
});

it('should return undefined when cloneUrl is undefined', () => {
expect(service.buildSourceTreeUrl(baseUrl, undefined)).toBeUndefined();
});

it('should build JetBrains url correctly', () => {
const cloneUrl = baseUrl + '/git/repo.git';
const expectedUrl = 'jetbrains://idea/checkout/git?idea.required.plugins.id=Git4Idea&checkout.repo=https%3A%2F%2Fartemis.cit.tum.de%2Fgit%2Frepo.git';
expect(service.buildJetbrainsUrl(cloneUrl)).toEqual(expectedUrl);
});

it('should return undefined when the argument is undefined', () => {
expect(service.buildJetbrainsUrl(undefined)).toBeUndefined();
});
});
Loading