Skip to content

Commit

Permalink
Merge branch 'main' into datahub-ign-2905
Browse files Browse the repository at this point in the history
  • Loading branch information
cdebarros committed Sep 30, 2024
2 parents b85e6b1 + fdf8f64 commit 74fab3f
Show file tree
Hide file tree
Showing 147 changed files with 8,607 additions and 1,537 deletions.
30 changes: 30 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,33 @@ jobs:
comment_tag: github-links
pr_number: ${{ github.event.issue.number }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

apps:
needs: checks
if: github.event_name != 'issue_comment'
name: Deploy Apps to GitHub Pages
runs-on: ubuntu-latest
env:
BRANCH_NAME: main

steps:
- name: Checkout
uses: actions/checkout@v2

- name: Use Node.js ${{ env.NODE_VERSION }}
uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'

- name: Install
run: npm ci

- name: Build metadata-converter
run: npx nx build metadata-converter --prod

- name: Deploy to directory ${{ env.BRANCH_NAME }}
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
npx gh-pages --dist dist/apps/ --dest ${{env.BRANCH_NAME}} --remove "${{env.BRANCH_NAME}}/**" --no-history --repo "https://${GITHUB_ACTOR}:${{secrets.GITHUB_TOKEN}}@github.com/${GITHUB_REPOSITORY}.git"
109 changes: 9 additions & 100 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,15 @@

# GeoNetwork UI

GeoNetwork UI is a suite of Applications made to provide a modern facade to your GeoNetwork 4 catalog.
GeoNetwork-UI provides several applications to improve the user experience of your GeoNetwork catalog.

It also provides Web Components to embed various parts of your data catalog in third party websites.

## Documentation
End users are [invited](https://discourse.osgeo.org/t/about-the-geonetwork-ui-category/59280) to join us on the GeoNetwork-UI forum on OsGeo Discourse: https://discourse.osgeo.org/c/geonetwork/ui

To check out docs, visit [geonetwork-ui website](https://geonetwork.github.io/geonetwork-ui/main/docs/)
Developers should check out the [GitHub discussions](https://github.com/geonetwork/geonetwork-ui/discussions).

## Requirements

- GeoNetwork version 4.2.2
- ElasticSearch version 7.11+

:warning: A bug currently in GeoNetwork 4.2.2 prevents the organizations of showing up correctly in the Datahub application.

As a temporary workaround, the following change is necessary in GeoNetwork data directory:

```diff
diff --git a/web/src/main/webResources/WEB-INF/data/config/index/records.json b/web/src/main/webResources/WEB-INF/data/config/index/records.json
index 1d7e499af7..78e682e3db 100644
--- a/web/src/main/webResources/WEB-INF/data/config/index/records.json
+++ b/web/src/main/webResources/WEB-INF/data/config/index/records.json
@@ -1317,7 +1317,7 @@
"mapping": {
"type": "nested",
"properties": {
- "org": {
+ "organisation": {
"type": "keyword"
},
"role": {
```
To learn more about the project, visit the [GeoNetwork-UI website](https://geonetwork.github.io/geonetwork-ui/main/docs/)

## Getting started

Expand Down Expand Up @@ -99,67 +76,6 @@ npx nx build (app_name)

The build artifacts will be stored in the `dist/` directory. Note: this always produces a production build.

### A word on authentication

GeoNetwork-UI applications rely on the GeoNetwork authentication mechanism. This means that if the user is authenticated in GeoNetwork, they will have access to authenticated features in the corresponding GeoNetwork-UI apps.

There are a few caveats, depending on the deployment scenario:

#### 1. GeoNetwork and GeoNetwork-UI are deployed on the same host, e.g. https://my.host/geonetwork and https://my.host/datahub

In this scenario, requests from the GeoNetwork-UI app to GeoNetwork are _not_ [cross-origin requests](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#what_requests_use_cors), so CORS rules do not apply.

GeoNetwork has an XSRF protection by default, which _will_ make authenticated requests fail unless the following is done:

- either make sure that the XSRF cookies sent by GeoNetwork have a `path` value of `/`; this is typically done like so in GeoNetwork:

```diff
--- a/web/src/main/webapp/WEB-INF/config-security/config-security-core.xml
+++ b/web/src/main/webapp/WEB-INF/config-security/config-security-core.xml
@@ -361,6 +361,7 @@
<bean class="org.fao.geonet.security.web.csrf.CookieCsrfTokenRepository"
id="csrfTokenRepository">
<property name="cookieHttpOnly" value="false"/>
+ <property name="cookiePath" value="/"/>
</bean>
```

Also make sure that the GeoNetwork API URL used by the application is _not_ an absolute URL; a relative URL should be enough in that scenario:

```diff
--- a/conf/default.toml
+++ b/conf/default.toml
@@ -5,7 +5,7 @@
[global]
-geonetwork4_api_url = "https://my.host/geonetwork/srv/api"
+geonetwork4_api_url = "/geonetwork/srv/api"
```

- or disable the XSRF protection selectively for non-critical endpoints of GeoNetwork, e.g. https://my.host/geonetwork/srv/api/userSelections for marking records as favorites; this is typically done like so in GeoNetwork:

```diff
--- a/web/src/main/webapp/WEB-INF/config-security/config-security-core.xml
+++ b/web/src/main/webapp/WEB-INF/config-security/config-security-core.xml
@@ -374,6 +374,9 @@
<value>/[a-zA-Z0-9_\-]+/[a-z]{2,3}/csw!?.*</value>
<value>/[a-zA-Z0-9_\-]+/api/search/.*</value>
<value>/[a-zA-Z0-9_\-]+/api/site</value>
+ <value>/[a-zA-Z0-9_\-]+/api/userselections.*</value>
</set>
</constructor-arg>
</bean>
```

:warning: Please do this responsibly as this could have security implications! :warning:

#### 2. GeoNetwork and GeoNetwork-UI are _not_ deployed on the same host, e.g. https://my.host/geonetwork and https://another.org/datahub

In this scenario, even if CORS settings are correctly set up on GeoNetwork side, most authenticated request will probably fail because by default they are not sent with the [`withCredentials: true`](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials) option.

As such, **authenticated requests are not yet supported in GeoNetwork-UI in the case of a cross-origin deployment**; non-authenticated requests (e.g. public search) should still work provided CORS settings were correctly set up on the GeoNetwork side (see [CORS resonse headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#the_http_response_headers)).

Lastly, even if authenticated requests were cleared regarding CORS rules, it would still be needed to disable the XSRF mechanism for the endpoints that GeoNetwork-UI relies on; XSRF protections works by making the client read the content of an HTTP cookie, and that is forbidden in a cross-origin context

### Tests

#### Unit tests
Expand All @@ -183,31 +99,24 @@ npx nx test --test-match=/data/dev/gn/ui/libs/common/src/lib/services/bootstrap.

#### End-to-end-tests

You can test the datahub app by page :

- home page
- search page
- organisations page
- dataset pages

##### To run the tests with the interface :
##### To run the tests with the interface:

Start docker from 'support-services', and then in the 'geonetwork-ui' folder :
Start docker from 'support-services', and then in the project root folder :

```shell script
npx nx e2e appname --watch
npx nx e2e (app_name) --watch
```

Then select the file(s) you want to test in the interface.

##### To run the tests without interface :

Start docker from 'support-services', and then in the 'geonetwork-ui' folder :
Start docker from 'support-services', and then in the project root folder :

--> ALl tests :

```shell script
npx nx e2e appname
npx nx e2e (app_name)
```

## Project structure
Expand Down
27 changes: 22 additions & 5 deletions apps/metadata-converter/src/app/app.component.html
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<div class="grid grid-cols-3 grid-rows-auto auto-rows-min h-full">
<header class="col-span-3 flex flex-row items-center">
<header class="col-span-3 flex flex-row items-start">
<div>
<h1 class="text-[50px] font-bold font-title my-6 mx-10">
Simple metadata editor<br />
<div class="whitespace-nowrap">Simple metadata editor</div>
<gn-ui-status
#status
class="font-main font-normal text-gray-900 text-[16px]"
Expand All @@ -11,20 +11,37 @@ <h1 class="text-[50px] font-bold font-title my-6 mx-10">
></gn-ui-status>
</h1>
</div>
<div class="grow p-3">
<div class="grow shrink-0 p-3 min-w-[50%]">
<input
type="file"
accept=".xml, application/xml"
accept=".xml, .rdf, application/xml, application/rdf+xml, .jsonld, application/ld+json, application/json, .ttl, text/turtle, .n3, text/n3, .nt, application/n-triples"
class="mb-3 border border-gray-500 rounded-sm w-full bg-white p-2"
(change)="onFileSelect($event)"
placeholder="Upload a record from your computer"
/>
<input
type="text"
class="border border-gray-500 rounded-sm w-full bg-white p-2"
class="mb-3 border border-gray-500 rounded-sm w-full bg-white p-2"
(change)="onFileUrlInput($event.target.value)"
placeholder="...or enter a URL pointing to a metadata record"
/>
<div class="w-full">
<label
*ngFor="let format of formats | keyvalue"
class="mr-4"
[ngClass]="{ 'font-bold': format.key === originalFormat }"
>
<input
type="radio"
name="format"
[checked]="format.key === currentFormat"
[value]="format.key"
(change)="onFormatChange($event.target.value)"
/>
{{ format.key }}
<span *ngIf="format.key === originalFormat">(native)</span>
</label>
</div>
</div>
</header>
<gn-ui-record-form
Expand Down
14 changes: 14 additions & 0 deletions apps/metadata-converter/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { CatalogRecord } from '@geonetwork-ui/common/domain/model/record'
import { StatusComponent } from './components/status/status.component'
import { RecordOutputXmlComponent } from './components/record-output-xml/record-output-xml.component'
import { RecordFormComponent } from './components/record-form/record-form.component'
import { FORMATS, getFormatName } from './md-formats'

@Component({
selector: 'gn-ui-root',
Expand All @@ -14,11 +15,21 @@ export class AppComponent {
@ViewChild('output') outputComponent: RecordOutputXmlComponent
@ViewChild('form') formComponent: RecordFormComponent

formats = FORMATS

get originalFormat(): string {
const converter = this.statusComponent?.currentConverter
return getFormatName(converter)
}

currentFormat: string

onRecordChange(record: CatalogRecord) {
this.statusComponent.recordNative = record
}
onRecordOutputReceived(output: string) {
this.outputComponent.recordXml = output
this.currentFormat = this.originalFormat
}
onRecordNativeReceived(record: CatalogRecord) {
this.formComponent.record = record
Expand Down Expand Up @@ -52,4 +63,7 @@ export class AppComponent {
})
.catch((e) => this.statusComponent.errorLoadingFile(e.message))
}
onFormatChange(format: string) {
this.statusComponent.changeFormat(format)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,29 +29,39 @@
></gn-ui-record-field-simple>
</p>
<p>
<gn-ui-record-field-group label="Owner Organisation">
<gn-ui-record-field-simple
label="Name"
[(fieldValue)]="record.ownerOrganization.name"
(confirm)="emitChangedRecord()"
></gn-ui-record-field-simple>
<gn-ui-record-field-simple
label="Description"
[(fieldValue)]="record.ownerOrganization.description"
(confirm)="emitChangedRecord()"
></gn-ui-record-field-simple>
<gn-ui-record-field-simple
label="Website"
[(fieldValue)]="record.ownerOrganization.website"
(confirm)="emitChangedRecord()"
[type]="'url'"
></gn-ui-record-field-simple>
<gn-ui-record-field-simple
label="Logo URL"
[(fieldValue)]="record.ownerOrganization.logoUrl"
(confirm)="emitChangedRecord()"
[type]="'url'"
></gn-ui-record-field-simple>
<gn-ui-record-field-group label="Owner Organization">
<gn-ui-button
*ngIf="!record.ownerOrganization"
[type]="'outline'"
(buttonClick)="addOwnerOrg()"
extraClass="m-2 py-[0.5em] px-[1em]"
>
<span class="opacity-70">Add a owner organization to this record</span>
</gn-ui-button>
<ng-container *ngIf="record.ownerOrganization">
<gn-ui-record-field-simple
label="Name"
[(fieldValue)]="record.ownerOrganization.name"
(confirm)="emitChangedRecord()"
></gn-ui-record-field-simple>
<gn-ui-record-field-simple
label="Description"
[(fieldValue)]="record.ownerOrganization.description"
(confirm)="emitChangedRecord()"
></gn-ui-record-field-simple>
<gn-ui-record-field-simple
label="Website"
[(fieldValue)]="record.ownerOrganization.website"
(confirm)="emitChangedRecord()"
[type]="'url'"
></gn-ui-record-field-simple>
<gn-ui-record-field-simple
label="Logo URL"
[(fieldValue)]="record.ownerOrganization.logoUrl"
(confirm)="emitChangedRecord()"
[type]="'url'"
></gn-ui-record-field-simple>
</ng-container>
</gn-ui-record-field-group>
</p>
<p>
Expand Down Expand Up @@ -119,7 +129,7 @@
(fieldValueChange)="set($event, 'position')"
(confirm)="emitChangedRecord()"
></gn-ui-record-field-simple>
<gn-ui-record-field-group label="Organisation">
<gn-ui-record-field-group label="Organisation" *ngIf="get().organization">
<gn-ui-record-field-simple
label="Name"
[fieldValue]="get().organization.name"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,14 @@ export class RecordFormComponent implements AfterViewInit {
}
this.recordChanged.emit(this.record)
}

addOwnerOrg() {
this.record = {
...this.record,
ownerOrganization: {
name: 'My Organization',
},
}
this.recordChanged.emit(this.record)
}
}
Loading

0 comments on commit 74fab3f

Please sign in to comment.