diff --git a/.github/actions/import-db/action.yml b/.github/actions/import-db/action.yml index 3b0252de4cd..2b74e9b5675 100644 --- a/.github/actions/import-db/action.yml +++ b/.github/actions/import-db/action.yml @@ -50,13 +50,6 @@ runs: cid=$(docker run -d --rm --name $DB5NAME -v $(pwd):/dq/scripts -v $DATADIR/dump:/dq/dump -p 127.0.0.1:$DB5PORT:5432 -e POSTGRES_DB=empty -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=dspace postgres /bin/bash -c "cd /dq/scripts && ./init.dspacedb5.sh") echo "cid=$cid" >> $GITHUB_OUTPUT sleep 60 - echo "=====" - # Generate the current timestamp for the log filename - timestamp=$(date +"%y%m%d%H%M") - log_file="${{ inputs.LOGDIR }}python.${timestamp}.log" - echo $log_file - docker logs $DB5NAME > "$log_file" || true - echo "=====" # copy assetstore echo Preparing assetstore if [[ -z ${{ inputs.ASSETSTORE }} ]]; then @@ -73,6 +66,15 @@ runs: rm __temp/resume/*.json || true # arguments of the script # required + echo "=====" + # Generate the current timestamp for the log filename + timestamp=$(date +"%y%m%d%H%M") + log_file_timestamp="${{ inputs.LOGDIR }}python.${timestamp}.log" + echo $log_file_timestamp + mkdir ../__logs + # Continuously append to the log file $log_file_timestamp. Start appending when the first file is added to the __logs folder. + inotifywait -m -e create --format "%w%f" ../__logs/ | while IFS= read -r file; do tail -F --retry "$file" >> "$log_file_timestamp" 2>/dev/null & done & + echo "=====" args=( --resume=false --config=backend.endpoint=$BEURL diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index c88f3422d76..46dc30fc251 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -2,6 +2,8 @@ name: Deploy DSpace on: + schedule: + - cron: '0 21 * * 6' # Runs every Saturday at 9 PM workflow_call: inputs: INSTANCE: @@ -37,6 +39,18 @@ on: type: boolean jobs: + import-every-week: + # Only run this job when triggered by the schedule event + if: github.event_name == 'schedule' + runs-on: ubuntu-latest + steps: + - name: Call the deploy action with predefined inputs + uses: ./.github/actions/deploy-dspace # Path to your custom action + with: + INSTANCE: '8' + IMPORT: true + ERASE_DB: true + deploy-5: if: inputs.INSTANCE == '*' || inputs.INSTANCE == '5' runs-on: dspace-dep-1 diff --git a/.github/workflows/trigger-builds.yml b/.github/workflows/trigger-builds.yml new file mode 100644 index 00000000000..1b5ce0b4da8 --- /dev/null +++ b/.github/workflows/trigger-builds.yml @@ -0,0 +1,29 @@ +name: Trigger Build for All Customer Branches + +on: + schedule: + - cron: "0 */4 * * *" # Runs every 4 hours + workflow_dispatch: # Allows manual triggering + +permissions: + actions: write # Grants permission to trigger workflows + contents: read # Access to repository contents + +jobs: + trigger-builds: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 # Ensures the repository is available + + - name: Get and Trigger Customer Branch Builds + run: | + # Authenticate with GitHub CLI using the token + echo ${{ secrets.GITHUB_TOKEN }} | gh auth login --with-token + + git fetch --prune origin # Ensure remote refs are fetched + BRANCHES=$(git ls-remote --heads origin | awk -F'/' '{print $3"/"$4}' | grep '^customer/') + for branch in $(echo "$BRANCHES" | sed -e 's/[\[\]"]//g' -e 's/,/\n/g'); do + echo "Triggering build for branch: $branch" + gh workflow run build.yml --ref $branch + done diff --git a/src/app/clarin-licenses/clarin-license-table/modal/define-license-form/define-license-form.component.ts b/src/app/clarin-licenses/clarin-license-table/modal/define-license-form/define-license-form.component.ts index 56538178e8c..d2c6a80b5da 100644 --- a/src/app/clarin-licenses/clarin-license-table/modal/define-license-form/define-license-form.component.ts +++ b/src/app/clarin-licenses/clarin-license-table/modal/define-license-form/define-license-form.component.ts @@ -169,11 +169,8 @@ export class DefineLicenseFormComponent implements OnInit { if (event.target.checked) { form.push(checkBoxValue); } else { - form.forEach((formValue, index) => { - if (formValue?.id === checkBoxValue.id) { - form.splice(index, 1); - } - }); + let index = form.findIndex(item => item.name === checkBoxValue.name); + form.splice(index, 1); } } diff --git a/src/app/item-page/clarin-files-section/clarin-files-section.component.ts b/src/app/item-page/clarin-files-section/clarin-files-section.component.ts index 00f875a93e7..f9092be3020 100644 --- a/src/app/item-page/clarin-files-section/clarin-files-section.component.ts +++ b/src/app/item-page/clarin-files-section/clarin-files-section.component.ts @@ -107,7 +107,7 @@ export class ClarinFilesSectionComponent implements OnInit { return file.name; }); - this.command = `curl --remote-name-all ` + this.halService.getRootHref() + `/core/items/${this.item.id}/allzip?handleId=${this.itemHandle}`; + this.command = `curl -o allzip.zip ` + this.halService.getRootHref() + `/core/items/${this.item.id}/allzip?handleId=${this.itemHandle}`; } loadDownloadZipConfigProperties() { diff --git a/src/app/item-page/clarin-ref-citation-modal/clarin-ref-citation-modal.component.ts b/src/app/item-page/clarin-ref-citation-modal/clarin-ref-citation-modal.component.ts index 2305496ba77..025a47eba84 100644 --- a/src/app/item-page/clarin-ref-citation-modal/clarin-ref-citation-modal.component.ts +++ b/src/app/item-page/clarin-ref-citation-modal/clarin-ref-citation-modal.component.ts @@ -11,13 +11,16 @@ import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; }) export class ClarinRefCitationModalComponent implements AfterViewInit { - constructor(public activeModal: NgbActiveModal) { - } + /** + * Reference to the textarea for selecting text. + */ + @ViewChild('copyCitationModal', { static: false }) citationContentRef!: ElementRef; /** - * The reference to make possible automatically select whole content. + * The citation context - data retrieved from OAI-PMH */ - @ViewChild('copyCitationModal', { static: true }) citationContentRef: ElementRef; + @Input() + citationText = ''; /** * The name of the showed Item @@ -31,19 +34,31 @@ export class ClarinRefCitationModalComponent implements AfterViewInit { @Input() citationType = ''; - /** - * The citation context - data retrieved from OAI-PMH - */ - @Input() - citationText = ''; + + private isViewInitialized = false; + + constructor(public activeModal: NgbActiveModal) {} // Inject modal service ngAfterViewInit(): void { - setTimeout(() => { + this.isViewInitialized = true; + if (this.citationText) { this.selectContent(); - }, 300); + } + } + + /** + * Selects the content of the citation text area. + */ + selectContent(): void { + setTimeout(() => { + this.citationContentRef?.nativeElement.select(); + }, 0); // Ensure DOM is updated before selection } - selectContent() { - this.citationContentRef?.nativeElement?.select(); + /** + * Allows external triggers for content selection. + */ + selectContentOnLoad(): void { + this.selectContent(); } } diff --git a/src/app/item-page/clarin-ref-citation/clarin-ref-citation.component.ts b/src/app/item-page/clarin-ref-citation/clarin-ref-citation.component.ts index c0cf5f05dac..a9cdbb1f0e5 100644 --- a/src/app/item-page/clarin-ref-citation/clarin-ref-citation.component.ts +++ b/src/app/item-page/clarin-ref-citation/clarin-ref-citation.component.ts @@ -230,20 +230,26 @@ export class ClarinRefCitationComponent implements OnInit { * Open the citation modal with the data retrieved from the OAI-PMH. * @param citationType */ - async openModal(citationType) { + async openModal(citationType: string) { const modal = this.modalService.open(ClarinRefCitationModalComponent, { size: 'xl', ariaLabelledBy: 'modal-basic-title' }); + + // Set initial properties modal.componentInstance.itemName = this.itemNameText; modal.componentInstance.citationType = citationType; + // Fetch the citation text from the API let citationText = ''; await this.getCitationText(citationType) .then(res => { - citationText = res.payload?.metadata; + citationText = res.payload?.metadata || ''; // Fallback to empty string if metadata is undefined + modal.componentInstance.citationText = citationText; // Set citationText after fetching }); - modal.componentInstance.citationText = citationText; + + // Ensure the modal content is selected after rendering + modal.componentInstance.selectContentOnLoad(); } /** diff --git a/src/app/item-page/versions/notice/item-versions-notice.component.html b/src/app/item-page/versions/notice/item-versions-notice.component.html index fb6fa347461..4e8aecab5c3 100644 --- a/src/app/item-page/versions/notice/item-versions-notice.component.html +++ b/src/app/item-page/versions/notice/item-versions-notice.component.html @@ -1,5 +1,5 @@ diff --git a/src/app/item-page/versions/notice/item-versions-notice.component.spec.ts b/src/app/item-page/versions/notice/item-versions-notice.component.spec.ts index fb29e54a217..2b3270442d8 100644 --- a/src/app/item-page/versions/notice/item-versions-notice.component.spec.ts +++ b/src/app/item-page/versions/notice/item-versions-notice.component.spec.ts @@ -57,7 +57,6 @@ describe('ItemVersionsNoticeComponent', () => { ); beforeEach(waitForAsync(() => { - TestBed.configureTestingModule({ declarations: [ItemVersionsNoticeComponent], imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([])], diff --git a/src/app/item-page/versions/notice/item-versions-notice.component.ts b/src/app/item-page/versions/notice/item-versions-notice.component.ts index 0e5e45806b7..37f81976132 100644 --- a/src/app/item-page/versions/notice/item-versions-notice.component.ts +++ b/src/app/item-page/versions/notice/item-versions-notice.component.ts @@ -1,6 +1,6 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Inject, Input, OnInit } from '@angular/core'; import { Item } from '../../../core/shared/item.model'; -import { Observable } from 'rxjs'; +import { Observable, of } from 'rxjs'; import { RemoteData } from '../../../core/data/remote-data'; import { VersionHistory } from '../../../core/shared/version-history.model'; import { Version } from '../../../core/shared/version.model'; @@ -13,7 +13,7 @@ import { import { map, startWith, switchMap } from 'rxjs/operators'; import { VersionHistoryDataService } from '../../../core/data/version-history-data.service'; import { AlertType } from '../../../shared/alert/alert-type'; -import { getItemPageRoute } from '../../item-page-routing-paths'; +import { DOCUMENT } from '@angular/common'; @Component({ selector: 'ds-item-versions-notice', @@ -50,17 +50,19 @@ export class ItemVersionsNoticeComponent implements OnInit { */ showLatestVersionNotice$: Observable; - /** - * Pagination options to fetch a single version on the first page (this is the latest version in the history) - */ - /** * The AlertType enumeration * @type {AlertType} */ public AlertTypeEnum = AlertType; - constructor(private versionHistoryService: VersionHistoryDataService) { + /** + * New observable for the full redirect URL (with namespace) + */ + destinationUrl$: Observable; + + constructor(private versionHistoryService: VersionHistoryDataService, + @Inject(DOCUMENT) private document: Document ) { } /** @@ -88,15 +90,27 @@ export class ItemVersionsNoticeComponent implements OnInit { startWith(false), ); } - } - /** - * Get the item page url - * @param item The item for which the url is requested - */ - getItemPage(item: Item): string { - if (hasValue(item)) { - return getItemPageRoute(item); - } + // Compute the destination URL from latestVersion$ with the namespace + this.destinationUrl$ = this.latestVersion$.pipe( + switchMap(latestVersion => latestVersion?.item || of(null)), + map(item => { + const itemId = item?.payload?.uuid; + + if (!itemId) { + console.error('No valid UUID found in payload'); + return this.document.location.pathname; // Fallback to the current path if extraction fails + } + + // Get the base URL dynamically - with the namespace. Remove the last part of the path (the item UUID). + const baseUrl = this.document.location.pathname.split('/').slice(0, -1).join('/'); + + // Construct the final URL dynamically + const finalUrl = `${baseUrl}/${itemId}`; + + return finalUrl; + }) + ); } + }