diff --git a/docs/customize/look-and-feel/img/communities-dashboard.png b/docs/customize/look-and-feel/img/communities-dashboard.png new file mode 100644 index 00000000..7e04b1c7 Binary files /dev/null and b/docs/customize/look-and-feel/img/communities-dashboard.png differ diff --git a/docs/customize/look-and-feel/img/settings-menu.png b/docs/customize/look-and-feel/img/settings-menu.png new file mode 100644 index 00000000..76a4950c Binary files /dev/null and b/docs/customize/look-and-feel/img/settings-menu.png differ diff --git a/docs/customize/look-and-feel/img/user-dashboard.png b/docs/customize/look-and-feel/img/user-dashboard.png new file mode 100644 index 00000000..68a2902a Binary files /dev/null and b/docs/customize/look-and-feel/img/user-dashboard.png differ diff --git a/docs/customize/look-and-feel/menus.md b/docs/customize/look-and-feel/menus.md new file mode 100644 index 00000000..75bf7569 --- /dev/null +++ b/docs/customize/look-and-feel/menus.md @@ -0,0 +1,150 @@ +# Change Menus/Tabs + +For navigation, InvenioRDM often uses menus or tabs. For example, one such menu is the user-dropdown menu: + +![User-settings menu with its 5 default entries](./img/settings-menu.png) + +And one such set of tabs is at the top of the user-dashboard: + +![User's personal dashboard with three tabs labeled (left-to-right) "Uploads", "Communities", and "Requests"](./img/user-dashboard.png) + +In the codebase, menus and sets of tabs like those are referred as *menus*, so we do so here for the remainder of this how-to as well. +Their options (e.g., "Uploads", "Profile") are referred as *submenus*. + +These *menus* can be configured in the following ways: + +- which submenus to show +- what title-text to show in those submenus +- which endpoint to request when clicking them +- how to dynamically show/hide them (e.g. depending on currently logged-in user) + +## Configurable Properties + +Properties of existing submenus can be overridden, +properties of newly added submenus can be freely chosen. +All submenus share the following properties: + +| Name | Example values | Notes | +|----------------|--------------------------------------|-----------------------------------------------------| +| `endpoint` | `"invenio_app_rdm_users.uploads"` | flask-endpoint requested when clicking this submenu | +| `order` | `1`, `2`, `10`, `-1`, `1.5` | lower ordered submenus are shown further left | +| `text` | `_("Uploads")`, `_("Communities")` | title-text shown on submenus | +| `visible_when` | `flask_menu.menu.CONDITION_TRUE` | when to show this submenu
- must be of type `callable[[], bool]`
- `CONDITION_TRUE` is just a named `lambda: True`
- use `CONDITION_FALSE` to hide a submenu | +| ... | ... | in addtion to the above properties, all arguments for `flask_menu.menu:MenuNode.register` are configurable | + +For example, consider the user-dashboard pictured above. +Its *Uploads* submenu has the following default configuration: +```python +{ + "endpoint": "invenio_app_rdm_users.uploads", + "order": 1, # small number as to show this submenu left-most + "text": _("Uploads"), + "visible_when": flask_menu.menu.CONDITION_TRUE, # always show this submenu + ..., # advanced configuration properties omitted for brevity +} +``` + +## Add new submenus to existing menu + +Existing menus can be extended by custom-configured submenus. +To add a new submenu to an existing menu: + +1. Get the name of the to-be-added-to menu from the [list of menus](#list-of-menus) + For example, the user-dashboard is named `"dashboard"` (most names of menus/submenus are straightforward). +2. Write a function that registers the new submenu + ```python + # ext.py # ext.py is commonly used, you may use another file though + + from flask_menu import current_menu + from invenio_i18n import lazy_gettext as _ # for translations + + def finalize_app(): + # get the user-dashboard menu via its name "dashboard": + user_dashboard_menu = current_menu.submenu("dashboard") + + # register a new submenu to the user-dashboard: + user_dashboard_menu.register( + "name-of-submenu", + endpoint="my_blueprint.my_endpoint", + order=4, # the three already existing submenus have `order` 1 through 3 + text=_("Title of new Submenu"), # could also use an untranslated bare string + # note: could add other properties here, otherwise their default is used + # note: `visible_when` defaults to always visible + ) + + ... # could .register more submenus here (or do so in another package) + ``` +3. Register your function with the `invenio_base.finalize_app` entrypoint. + This will make your function be called at the app-finalization build-step. + For example, when using setuptools' `setup.cfg` with your python package, add: + ```ini + # setup.cfg + + [options.entry_points] + invenio_base.finalize_app = + my_package_name = my_package_name.ext:finalize_app + ``` + +!!! info "For entrypoints to take effect" + For entrypoint changes to be picked up, you will need to reinstall the python package. + *This is necessary even if the package is installed editably!* + + After entrypoint changes are picked up, + you will further need to restart the server for changes to take effect: + ```shell + + invenio-cli run + ``` + +## Modify existing submenus + +The defaults of submenus' properties are selectively overridable via config-variables. +To modify an existing submenu's properties: + +1. Find the name of the corresponding override-variable in the [list of menus](#list-of-menus) + For example, the override-variable for the user-dashboard is named `USER_DASHBOARD_MENU_OVERRIDES`. +2. Find the name of the to-be-overridden submenu in that same [list of menus](#list-of-menus) + For example, the user-dashboard has a submenu for communities. + This submenu is named (obviously enough) `"communities"`. +3. Add to your `invenio.cfg`: + ```python + # invenio.cfg + + from flask_menu.menu import CONDITION_FALSE + + USER_DASHBOARD_MENU_OVERRIDES = { + "communities": { + "visible_when": CONDITION_FALSE, + # other properties will be left unchanged + } + # other submenus will be left unchanged + } + ``` + +The above example hides the *Communities* submenu from the user-dashboard menu +by overriding its `visible_when` property. +For other overridable properties see [configurable properties](#configurable-properties). + +!!! info "Have you tried to turn it off and on again?" + You will need to restart the server for changes to take effect: + ```shell + + invenio-cli run + ``` + +!!! warning "On overriding user-added submenus: DON'T" + Currently, (sub)menu-overrides are adopted at app-finalization. + Users adding their own submenus (as described above) is also done at app-finalization. + Hence attempting to override submenus added by (other) users depends on + loading order of app-finalization entrypoints and might break anytime. + +## List of menus + +The following list of menus is non-exhaustive. +Not all menus are overridable, but all menus can be extended by additional submenus. + +| Image | Description | Name of menu and its submenus | Name of override-variable | +|-------|-------------|-------------------------------|---------------------------| +| ![User's personal dashboard with three tabs labeled (left-to-right) "Uploads", "Communities", and "Requests"](./img/user-dashboard.png) | Dashboard on user's personal page | `"dashboard"`
├─`"uploads"`
├─`"communities"`
└─`"requests"` | `USER_DASHBOARD_MENU_OVERRIDES` | +| ![Communities dashboard with its 6 default tabs](./img/communities-dashboard.png) | Dashboard on communities page | `"communities"`
├─`"home"`
├─`"search"`
├─`"requests"`
├─`"submit"`
├─`"members"`
├─`"settings"`
├─`"curation_policy"`
└─`"about"` | No associated override-variable | +| ![User-settings menu with its 5 default entries](./img/settings-menu.png) | User settings menu | `"settings"`
├─`"profile"`
├─`"change_password"`
├─`"security"`
├─`"notifications"`
├─`"oauthclient"`
└─`"applications"` | No associated override-variable | diff --git a/docs/customize/metadata/custom_fields/records.md b/docs/customize/metadata/custom_fields/records.md index a912750d..921ce9c2 100644 --- a/docs/customize/metadata/custom_fields/records.md +++ b/docs/customize/metadata/custom_fields/records.md @@ -283,7 +283,7 @@ You should add the `my_template.html` file in the `my-site/templates` folder in - `field_value`: the value of the field, as it is stored in the record after the UI serialization i.e. what is returned from the `ui_field` method when you [define your custom field](../../../develop/howtos/custom_fields.md). - `field_cfg`: the UI configuration for that specific field as it is defined in the `RDM_CUSTOM_FIELDS_UI` config. -See the example in the [How-to](../../../develop/howtos/custom_fields.md#define-the-template-for-the-record-landing-page). +See the example in the [How-to](../../../develop/howtos/custom_fields.md). ### Search diff --git a/docs/customize/vocabularies/names.md b/docs/customize/vocabularies/names.md index 79e72fa2..2dd4647a 100644 --- a/docs/customize/vocabularies/names.md +++ b/docs/customize/vocabularies/names.md @@ -17,7 +17,7 @@ A _Name_ record contains: are present they will overwrite `name`. - A list of `identifiers`, composed by their identifier value and scheme. The scheme can potentially be autocompleted if it is known by the _idutils_ - library (e.g. ORCiD). + library (e.g. ORCID). - A list of `affiliations`, which can be represented by its `name` or, if it belongs to the _Affiliations_ vocabulary, by its `id`. @@ -96,7 +96,7 @@ invenio vocabularies update \ ### Creating a `names.yaml` file The Names vocabulary has been implemented with the -[ORCiD public dataset](https://orcid.figshare.com/articles/dataset/ORCID_Public_Data_File_2021/16750535?file=31020067) +[ORCID public dataset](https://support.orcid.org/hc/en-us/articles/360006897394-How-do-I-get-the-public-data-file) as a possible source to import entries from. This means that the functionality to **read** entries from this format is already available. For example, you can use the `vocabularies convert` command to convert this dataset into a YAML @@ -112,7 +112,7 @@ invenio vocabularies convert \ Alternatively, you can simply import it directly: !!! warning "Long and blocking operation" - Note that the import process is done synchronously and the ORCiD dataset is + Note that the import process is done synchronously and the ORCID dataset is very large. Therefore, this operation can take a long time. ```bash diff --git a/docs/develop/howtos/custom_code.md b/docs/develop/howtos/custom_code.md index 5c333eff..9cdd99ba 100644 --- a/docs/develop/howtos/custom_code.md +++ b/docs/develop/howtos/custom_code.md @@ -24,7 +24,7 @@ To generate the site folder, you will need to select option `1 - yes` (this is a ```python hl_lines="3" [packages] ... -my-site = {editable="True", path="./site"} +my-site = {editable=true, path="./site"} ``` This means that the site folder will be installed as a package with the name `my-site`, and it is editable. This package now works as any other package installed in your instance (`invenio-app-rdm`, `invenio-communities`, etc.), allowing you to customize your instance and create new views and features without adding a separate package manually. diff --git a/docs/develop/howtos/restrict_access.md b/docs/develop/howtos/restrict_access.md new file mode 100644 index 00000000..05d91412 --- /dev/null +++ b/docs/develop/howtos/restrict_access.md @@ -0,0 +1,52 @@ +# How to restrict access to pages + +Sometimes it can be desirable to live by the motto "better safe than sorry", especially regarding potentially sensitive features like the administration panel (enabled in InvenioRDM v12). + +This guide briefly describes how to narrow down access to subsets of the system. + + +## Restricting access for IP ranges via `nginx` + +While most features in InvenioRDM are guarded by configurable permission policies, this isn't necessarily always the case. +For these exceptions, as well as extra precautions generally, it can be beneficial to restrict access on an `nginx` level. + +!!! info "Current exceptions" + At the time of writing, one of these exceptions is the administration panel which has a hard-coded check for the `administration-access` action. + +An access restriction based on the client's IP address can be put into place via the `nginx` configuration, e.g. by adding nested `location` directives in the existing configuration: + +```nginx +location / { + uwsgi_pass ui_server; + include uwsgi_params; + # ... your configuration for the UI paths ... + + # restrict access to the administration panel UI to your network only + location /administration/ { + # action directives like `uwsgi_pass` aren't inherited like other configs + uwsgi_pass ui_server; + + # allow your networks (replace with your IP ranges) + allow 128.130.0.0/15; + allow 192.35.240.0/22; + allow 2001:629::/32; + # etc. + + # also allow localhost and private networks (e.g. for local access through Docker) + allow 127.0.0.1/8; + allow ::1/128; + allow 10.0.0.0/8; + allow 172.16.0.0/12; + allow 192.168.0.0/16; + allow fd00::/8; + + # disallow anybody else + deny all; + } +} +``` + +!!! info "The `uwsgi_pass` directive doesn't get inherited" + Note that the `uwsgi_pass` directive is part of a [class of directives that do not get inherited in nested locations](https://forum.nginx.org/read.php?2,243488,243488) and thus has to be specified explicitly again. + +Restricting access to API endpoints follows a similar schema, but in the `location /api` block and with `uwsgi_pass api_server` instead. diff --git a/docs/develop/index.md b/docs/develop/index.md index 62eca14c..29e4149b 100644 --- a/docs/develop/index.md +++ b/docs/develop/index.md @@ -53,6 +53,7 @@ Step-by-step guides on how to perform certain tasks: - [Fix a vulnerability](howtos/security-fix.md) - [Test emails locally](howtos/dev_email.md) - [Migrate legacy routes](howtos/route_migration.md) +- [Restrict access to pages](howtos/restrict_access.md) ## Architecture diff --git a/docs/reference/cli.md b/docs/reference/cli.md index 253016d8..d6dd4526 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -480,3 +480,7 @@ invenio rdm rebuild-all-indices -o users,communities,records,requests,request_ev if you don't specify services, The following services will be reindexed: `users, groups, domains, communities, members, records, record-media-files, affiliations, awards, funders, names, subjects, vocabularies, requests, request_events, oaipmh-server` + +Note that the users, groups, and members use bulk indexing and rely on celery running. They will not be reindexed if celery is not running. + +This command does not impact usage statistics indexes. You need to manually restore statistics indexes [from a backup](../develop/howtos/backup_search_indices.md). diff --git a/docs/reference/metadata.md b/docs/reference/metadata.md index b4383be3..d07554f4 100644 --- a/docs/reference/metadata.md +++ b/docs/reference/metadata.md @@ -117,7 +117,7 @@ External PIDs are persistent identifiers managed via [Invenio-PIDStore](https:// with external registration services. Persistent identifiers are globally unique in the system, thus you cannot have two records -with the same system-managed persistent identifier (see also [Metadata > Identifiers](#identifiers-0-n)). +with the same system-managed persistent identifier (see also [Metadata > Identifiers](#alternate-identifiers-0-n)). You can add a DOI that is not managed by InvenioRDM by using the provider `external`. You are not able to add `external` DOIs that have a prefix that is configured as part of a different PID provider. @@ -1091,7 +1091,7 @@ Example: IIIF links are only returned for files who are compatible with IIIF. Those formats are defined by the `IIIF_FORMATS` configuration variable. By default _gif_, _jp2_, _jpeg_, _jpg_, _png_, _tif_, and _tiff_. - + ### Default preview (0-1) The default preview field names the filename of the file which should by default diff --git a/docs/reference/rest_api_communities.md b/docs/reference/rest_api_communities.md index 1228c33b..11f64390 100644 --- a/docs/reference/rest_api_communities.md +++ b/docs/reference/rest_api_communities.md @@ -779,7 +779,7 @@ Content-Type: application/json |----------------|--------|----------|-----------------------------------------------------------------------------------------------| | `accept` | string | header | - `application/json` (default)
- `application/vnd.inveniordm.v1+json` | | `community_id` | string | path | ID of the community. | -| `start_date` | string | body | Required, datetime in iso format. Community will be featured from this point in time onwards. | +| `start_date` | string | body | Required, ISO 8601 DateTime format in UTC (YYYY-MM-DDTHH:MM:SS.ssssssZ). Community will be featured from this point in time onwards. | **Request** diff --git a/docs/reference/rest_api_drafts_records.md b/docs/reference/rest_api_drafts_records.md index 4e737079..86c924dc 100644 --- a/docs/reference/rest_api_drafts_records.md +++ b/docs/reference/rest_api_drafts_records.md @@ -29,6 +29,8 @@ Used for interacting with unpublished or edited draft records. | `default_preview`   | string | body | Filename of file to be previewed by default. | | `order` | array | body | Array of filename strings in display order. | +A file must be uploaded to the draft before it can be used as the default +preview. See "[Start a draft file upload](#start-draft-file-uploads)" below. **Request** @@ -285,7 +287,7 @@ Content-Type: application/json | Name | Type | Location | Description | | ---------- | ------ | -------- | ------------------------------------------------------------ | | `id` | string | path | Identifier of the record, e.g. `4d0ns-ntd89` | -| `access` | object | body | [Access options](metadata.md#access-information) for the record. | +| `access` | object | body | [Access options](metadata.md#access) for the record. | | `files` | object | body | [Files options](#files-options) for the record. | | `metadata` | object | body | [Metadata](metadata.md#metadata) of the record. | | `custom_fields` | object | body | [Custom fields](../customize/metadata/custom_fields/records.md#declaring-custom-fields) metadata for the record. (v10 and newer) | @@ -1591,7 +1593,7 @@ Access links are URLs that can be shared with others to give them access and per | -------- | ------ | -------- | ------------------------------------------------------------ | | `id` | string | path | Identifier of the record, e.g. `cbc2k-q9x58` | | `accept` | string | header | - `application/json` (default)
- `application/vnd.inveniordm.v1+json` | -| `expires_at` | string | body | Date time string. When the link expires. | +| `expires_at` | string | body | `ISO 8601 Date`Format (YYYY-MM-DD) When the link expires. | | `permission` | string | body | Required. Action that can be undertaken with the link (``view``, ``preview`` or ``edit``). | @@ -1602,7 +1604,8 @@ POST /api/records/{id}/access/links HTTP/1.1 Content-Type: application/json { - "permission": "view" + "permission": "view", + "expires_at": "2024-11-06" } ``` @@ -1613,11 +1616,12 @@ HTTP/1.1 201 CREATED Content-Type: application/json { - "permission": "view", - "created_at": "2021-03-25T21:06:29.563235", - "token": "eyJhbGciOiJIUzUxMiJ9.eyJpZCI6IjNkMzMyMGVhLTA3NTUtNGQ5My1hNzZlLWUyZjJmYzY1NWQyYSIsImRhdGEiOnt9LCJyYW5kb20iOiI2NzZhYTk3OTczMzgwMjkyNTJiM2MwZDBjNjliMTVkYSJ9.dBqk7YzIZ7kwG4oijNgH1VU-cjQmBiQlMQKMoB2y-YjVWmgnZetFAESsqRP6VpGTtaKdftrtob1PVZJF4YGpfg", - "id": "3d3320ea-0755-4d93-a76e-e2f2fc655d2a", - "expires_at": null + "id": "07fb00f3-928c-4ce9-8d2e-8e9c4dca3092", + "created_at": "2024-06-12T13:07:09.951029+00:00", + "expires_at": "2024-11-06", + "permission": "view", + "description": "", + "token": "eyJhbGciOiJIUzUxMiIsImlhdCI6MTcxODE5NzYyOSwiZXhwIjoxNzMwODUxMTk5fQ.eyJpZCI6IjA3ZmIwMGYzLTkyOGMtNGNlOS04ZDJlLThlOWM0ZGNhMzA5MiIsImRhdGEiOnt9LCJyYW5kb20iOiI1NzVjNzEwY2QwNWI3YWFhMTM2MzY3ZmMzZWFkYzA0MSJ9.GPfPBvrbvEu-JMddFXjb5MZKNWRnzAK53oTVOSgfdZOcMIoRfszO39GEglko74dohZiUcJ11jWXj0fwfdq1WnQ" } ``` @@ -1647,11 +1651,12 @@ HTTP/1.1 200 OK Content-Type: application/json { - "permission": "view", - "created_at": "2021-03-25T21:06:29.563235", - "token": "eyJhbGciOiJIUzUxMiJ9.eyJpZCI6IjNkMzMyMGVhLTA3NTUtNGQ5My1hNzZlLWUyZjJmYzY1NWQyYSIsImRhdGEiOnt9LCJyYW5kb20iOiI2NzZhYTk3OTczMzgwMjkyNTJiM2MwZDBjNjliMTVkYSJ9.dBqk7YzIZ7kwG4oijNgH1VU-cjQmBiQlMQKMoB2y-YjVWmgnZetFAESsqRP6VpGTtaKdftrtob1PVZJF4YGpfg", - "id": "3d3320ea-0755-4d93-a76e-e2f2fc655d2a", - "expires_at": null + "id": "61c2d20f-4c88-440d-9978-dd16a69bf97e", + "created_at": "2024-06-12T13:23:11.271139+00:00", + "expires_at": "2024-11-06", + "permission": "view", + "description": "", + "token": "eyJhbGciOiJIUzUxMiIsImlhdCI6MTcxODE5ODU5MSwiZXhwIjoxNzMwODUxMTk5fQ.eyJpZCI6IjYxYzJkMjBmLTRjODgtNDQwZC05OTc4LWRkMTZhNjliZjk3ZSIsImRhdGEiOnt9LCJyYW5kb20iOiI2MWYwZTg4YjgzY2E2ZDhkMjJiMTY0MGFjNmIzMmEwZiJ9.AFEmgQ8_gtEj7dvlZ2MHD9qneKy0UEC1HMByo8J5xVGMYG8PXwuRsyUgeq_k_ZeHybO5W4_Do_P4NVGXsrjHyg" } ``` @@ -1663,10 +1668,10 @@ Content-Type: application/json | Name | Type | Location | Description | | -------- | ------ | -------- | ------------------------------------------------------------ | -| `id` | string | path | Identifier of the record, e.g. `cbc2k-q9x58` | -| `link-id` | string | path | Identifier of the link, e.g. `3d3320ea-0755-4d93-a76e-e2f2fc655d2a` | +| `id` | string | path | Identifier of the record, e.g. `cbc2k-q9x58` | +| `link-id` | string | path | Identifier of the link, e.g. `3d3320ea-0755-4d93-a76e-e2f2fc655d2a` | | `accept` | string | header | - `application/json` (default)
- `application/vnd.inveniordm.v1+json` | -| `expires_at` | string | body | Date time string. When the link expires. | +| `expires_at` | string | body | `ISO 8601 Date`Format (YYYY-MM-DD) When the link expires. | | `permission` | string | body | Required. Action that can be undertaken with the link. | **Request** @@ -1677,7 +1682,7 @@ Content-Type: application/json { "permission": "edit", - "expires_at": "2121-03-25T21:06:29.563235" + "expires_at": "2024-11-06" } ``` @@ -1688,11 +1693,12 @@ HTTP/1.1 200 OK Content-Type: application/json { - "permission": "edit", - "created_at": "2021-03-25T21:06:29.563235", - "token": "eyJhbGciOiJIUzUxMiJ9.eyJpZCI6IjNkMzMyMGVhLTA3NTUtNGQ5My1hNzZlLWUyZjJmYzY1NWQyYSIsImRhdGEiOnt9LCJyYW5kb20iOiI2NzZhYTk3OTczMzgwMjkyNTJiM2MwZDBjNjliMTVkYSJ9.dBqk7YzIZ7kwG4oijNgH1VU-cjQmBiQlMQKMoB2y-YjVWmgnZetFAESsqRP6VpGTtaKdftrtob1PVZJF4YGpfg", - "id": "3d3320ea-0755-4d93-a76e-e2f2fc655d2a", - "expires_at": "2121-03-25T21:06:29.563235" + "id": "df672812-6b23-411a-b40a-9bb22787f0a2", + "created_at": "2024-06-12T12:48:43.724970+00:00", + "expires_at": "2024-11-06", + "permission": "edit", + "description": "", + "token": "eyJhbGciOiJIUzUxMiJ9.eyJpZCI6ImRmNjcyODEyLTZiMjMtNDExYS1iNDBhLTliYjIyNzg3ZjBhMiIsImRhdGEiOnt9LCJyYW5kb20iOiIwZWE3ZWQ5YTBiZTE3N2ZjMjE4YjNjYzY3M2RiOTI5OSJ9.kqJ_gTvgjEc_-1Jxv-XHqSCUmOpcQDdBzx-T5BP7ybvQItK91wGxmVT_gfHxyHHDQ_7e8_LH1A5TotAZCA8q_w" } ``` @@ -1747,16 +1753,17 @@ Content-Type: application/json { "hits": { - "hits": [ - { - "permission": "view", - "id": "140f69c9-a8a5-41d4-8ae2-3dfbfe0e2796", - "created_at": "2021-03-25T21:48:03.289198", - "expires_at": null, - "token": "eyJhbGciOiJIUzUxMiJ9.eyJpZCI6IjE0MGY2OWM5LWE4YTUtNDFkNC04YWUyLTNkZmJmZTBlMjc5NiIsImRhdGEiOnt9LCJyYW5kb20iOiI2NzE3MmY4MTNkYzhkNGJjZDAwOWFlOTlhOWM3NjU1MSJ9.1O9MwTmt_nfvsCm4qvlkUH0Rpe5bK3hT422A879DJSblOCONsNxPe_feNHrgTV3s6ZA6t6vLziXjhAwgKjHhIQ" - } - ], - "total": 1 + "hits": [ + { + "id": "61c2d20f-4c88-440d-9978-dd16a69bf97e", + "created_at": "2024-06-12T13:23:11.271139+00:00", + "expires_at": "2024-11-06", + "permission": "edit", + "description": "", + "token": "eyJhbGciOiJIUzUxMiIsImlhdCI6MTcxODE5ODU5MSwiZXhwIjoxNzMwODUxMTk5fQ.eyJpZCI6IjYxYzJkMjBmLTRjODgtNDQwZC05OTc4LWRkMTZhNjliZjk3ZSIsImRhdGEiOnt9LCJyYW5kb20iOiI2MWYwZTg4YjgzY2E2ZDhkMjJiMTY0MGFjNmIzMmEwZiJ9.AFEmgQ8_gtEj7dvlZ2MHD9qneKy0UEC1HMByo8J5xVGMYG8PXwuRsyUgeq_k_ZeHybO5W4_Do_P4NVGXsrjHyg" + } + ], + "total": 1 } } ``` diff --git a/docs/reference/rest_api_requests.md b/docs/reference/rest_api_requests.md index a0c1aa30..4de5f8ad 100644 --- a/docs/reference/rest_api_requests.md +++ b/docs/reference/rest_api_requests.md @@ -80,16 +80,13 @@ PUT /api/requests/{request_id} HTTP/1.1 Content-Type: application/json { - "created_by": {"user": "1"}, "expires_at": null, "id": "{request_id}", "number": "{request_number}", - "receiver": {"user": "2"}, "revision_id": 1, "status": "draft", "title": "A new title", - "topic": {"record": "abcd-1234"}, - "type": "default-request", + "type": "default-request" } ``` diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css index c358c25f..a679ff8d 100644 --- a/docs/stylesheets/extra.css +++ b/docs/stylesheets/extra.css @@ -1,249 +1,156 @@ -.md-typeset a, -.md-nav__link--active { - color: #000000; - opacity: 0.54; -} - -.md-typeset a:hover, -.md-nav__link:hover { - color: #000000; -} - -.md-nav__title { - color: unset; -} - -.md-typeset table code { - word-break: normal; -} - -[data-md-color-primary="white"] .md-header { - background: linear-gradient( - 90deg, - rgba(3, 119, 205, 1), - rgba(3, 119, 205, 1) 52.52%, - rgba(251, 130, 115, 0.69)) - no-repeat; - color: #ffffff; -} -[data-md-color-primary="white"] .md-tabs { - background: linear-gradient( +/* + Copyright (C) 2018-2024 CERN. + Copyright (C) 2018-2024 Northwestern University. + Copyright (C) 2022-2024 KTH Royal Institute of Technology. + + Invenio is free software; you can redistribute it and/or modify it + under the terms of the MIT License; see LICENSE file for more details. +*/ +/* Color variables +https://github.com/squidfunk/mkdocs-material/blob/master/src/templates/assets/stylesheets/main/_colors.scss +*/ +:root { + --color-invenio: #0377cd; + --color-black: #000000; + --color-white: #ffffff; + --color-gray: #707070; + --color-dark-blue: #003258; + --color-light-coral: rgba(251, 130, 115, 0.69); +} + +[data-md-color-scheme='default'] { + --md-primary-fg-color: var(--color-gray); + --md-primary-fg-color--light: var(--color-invenio); + --md-primary-fg-color--dark: var(--color-dark-blue); + --gradient-header: linear-gradient( 90deg, - rgba(3, 119, 205, 1), - rgba(3, 119, 205, 1) 52.52%, - rgba(251, 130, 115, 0.69)) - no-repeat; - color: #ffffff; -} - -.md-search form.md-search__form { - background-color: rgba(255,255,255, 0.1); - color: #fff; - transition: 0.3 ease-in; -} - -.md-search form.md-search__form:hover { - background-color: rgba(255,255,255, 0.5); - color: rgba(0,0,0,0.54); -} - -.md-search form.md-search__form:hover .md-search__input::placeholder { - color: rgba(0,0,0,0.54); -} - -.md-search form.md-search__form:hover .md-search__icon.md-icon svg { - fill: rgba(0,0,0,0.54); -} - -[data-md-toggle=search]:checked~.md-header .md-search__inner .md-search__icon.md-icon svg { - fill: rgba(0,0,0,0.54); -} - -.md-search form.md-search__form .md-search__input::placeholder { - color: #fff; -} - -.md-search form.md-search__form .md-search__icon.md-icon svg { - fill: #fff; - transition: 0.3s; -} - -.md-header[data-md-state="shadow"] { - box-shadow: none; - border-bottom: 0.05rem solid #ffffff; -} - -.md-header__topic:first-child { - font-weight: unset; -} - -.md-source__facts { - display: none; -} - -.md-logo img { - width: 131px !important; - height: 28px !important; -} - -.md-footer { - border-top: 0.05rem solid #ffffff; - background-color: #0377cd; -} - -.md-footer__link { - padding-top: 0.8rem; - padding-bottom: 0; - color: #ffffff; - opacity: 0.75; - margin-bottom: 0; - margin-top: 0; -} - -.md-footer__direction { - color: #ffffff; - opacity: 0.75; - font-size: .64rem; - left: 0; - position: absolute; - right: 0; - padding: 0 1rem; - margin-top: -1rem; -} - -.md-footer__title { - line-height: 2.4rem; - position: relative; - margin-bottom: 0; -} - -.md-footer .made-with { - color: var(--md-footer-fg-color--lighter); - font-size: .64rem; -} - -.md-footer__link:hover, -.md-footer__direction:hover { - opacity: 1; -} + var(--color-invenio), + var(--color-invenio) 52.52%, + var(--color-light-coral) + ) no-repeat; + + .md-nav__link--active { + color: var(--color-black); + opacity: 0.54; + } -.md-footer-meta { - background-color: rgb(1, 98, 170); -} + .md-typeset a:hover, + .md-nav__link:hover { + color: var(--color-black); + } -html .md-footer-meta .md-footer-meta__inner a { - color: #ffffff; -} + .md-nav__title { + color: unset; + } -.md-footer-meta__inner { - padding: 0 0.2rem; -} + .md-tabs__link { + color: inherit; + } -.md-footer-copyright { - padding: 0.7rem 0; - color: var(--md-footer-fg-color--lighter); - font-size: .64rem; - width: auto; - margin: auto .6rem; -} + .md-header, + .md-tabs { + background: var(--gradient-header); + color: var(--color-white); + } -.md-footer-copyright__highlight { - color: var(--md-footer-fg-color--light); -} + .md-search form.md-search__form { + background-color: rgba(255, 255, 255, 0.1); + color: var(--color-white); + transition: 0.3 ease-in; + } -.md-footer-social { - padding: 0.4rem 0; - margin: 0 .4rem; -} + .md-search form.md-search__form:hover { + color: rgba(0, 0, 0, 0.54); + } -.md-footer-social__link svg { - fill: currentColor; - max-height: .8rem; - vertical-align: -25%; -} + .md-header[data-md-state='shadow'] { + box-shadow: none; + border-bottom: 0.05rem solid var(--color-white); + } -.md-search-result__link[data-md-state="active"] { - background-color: #ffffff; -} + .md-footer { + background-color: var(--color-invenio); + } -.md-typeset h1[id] .headerlink:focus, -.md-typeset h1[id]:hover .headerlink:hover, -.md-typeset h1[id]:target .headerlink, -.md-typeset h2[id] .headerlink:focus, -.md-typeset h2[id]:hover .headerlink:hover, -.md-typeset h2[id]:target .headerlink, -.md-typeset h3[id] .headerlink:focus, -.md-typeset h3[id]:hover .headerlink:hover, -.md-typeset h3[id]:target .headerlink, -.md-typeset h4[id] .headerlink:focus, -.md-typeset h4[id]:hover .headerlink:hover, -.md-typeset h4[id]:target .headerlink, -.md-typeset h5[id] .headerlink:focus, -.md-typeset h5[id]:hover .headerlink:hover, -.md-typeset h5[id]:target .headerlink, -.md-typeset h6[id] .headerlink:focus, -.md-typeset h6[id]:hover .headerlink:hover, -.md-typeset h6[id]:target .headerlink { - color: #0377cd; -} + .rdm-hero-bg { + background: linear-gradient( + 12deg, + rgba(3, 119, 205, 1), + rgba(3, 119, 205, 1) 52.52%, + rgba(251, 130, 115, 0.69) + ); + } -.md-typeset .md-button--primary { - color: #0377cd; -} + .rdm-hero .rdm-hero-subtitle { + color: rgba(255, 255, 255, 0.6); + } -[data-md-color-primary="white"] .md-typeset .md-button.md-button--primary { - background-color: var(--md-primary-fg-color); - border-color: var(--md-primary-fg-color); - color: #0377cd; + body[data-md-color-scheme] .md-main { + background-color: var(--color-white); + } } -[data-md-color-primary="white"] .md-typeset .md-button { - color: var(--md-primary-fg-color); +/* Dark mode */ +/* --------- */ +[data-md-color-scheme='slate'] { + .rdm-hero-bg { + background: linear-gradient( + 12deg, + hsla(205, 99%, 30%, 0.432), + rgba(1, 88, 150, 0.438) 52.52%, + rgba(179, 93, 81, 0.329) + ); + } } -::placeholder { - color: #ffffff; -} +/* Common styles */ +/* ------------- */ -.rdm-toc { - display: flex; - justify-content: center; -} +.rdm-hero { + height: 557px; + text-align: center; + margin-bottom: -2px; -.rdm-toc-item.level1 { - display: inline-flex; -} + > img { + margin-top: 75px; + height: 80px; + } -.rdm-toc-item.level1 > div > a { - font-weight: bolder; -} + > a.md-button:hover { + background-color: var(--color-white); + border-color: var(--color-white); + color: var(--color-invenio); + } -.rdm-toc-item.level1 > div { - width: 200px; + .rdm-hero-subtitle { + font-size: 30px; + font-weight: 300; + font-family: Oswald, 'Open Sans', sans-serif; + margin: 15px 0px 20px 0px; + } } -.rdm-toc-item.level2 { - font-size: small; +.rdm-hero > a.md-button.md-button--primary:hover { + background-color: transparent; + border-color: var(--color-white); + color: var(--color-white); } .rdm-hero-bg { display: block; width: 100%; height: 673px; - background: linear-gradient( - 12deg, - rgba(3, 119, 205, 1), - rgba(3, 119, 205, 1) 52.52%, - rgba(251, 130, 115, 0.69)); position: absolute; top: 0; z-index: -1; } .rdm-hero-bg .wave-svg { + fill: var(--md-default-bg-color); width: 100%; position: absolute; bottom: 0; + transform: translateY(1px); } @media screen and (min-width: 1920px) { @@ -264,74 +171,53 @@ html .md-footer-meta .md-footer-meta__inner a { } } -@media screen and (min-width: 3500px) { - .rdm-hero-bg { - height: 900px; - } -} - -@media screen and (min-width: 4000px) { +@media screen and (min-width: 4500px) { .rdm-hero-bg { - height: 950px; - } -} + height: 1000px; -@media screen and (min-width: 4500px) { - .rdm-hero-bg .wave-svg { - display: none; + .wave-svg { + display: none; + } } } -.rdm-hero { - height: 557px; - text-align: center; - margin-bottom: -2px; -} - -.rdm-hero > img { - margin-top: 75px; - height: 80px; +.md-typeset table code { + word-break: normal; } -.rdm-hero .rdm-hero-subtitle { - font-size: 30px; - font-weight: 300; - font-family: Oswald, "Open Sans", sans-serif; - color: rgba(255, 255, 255, 0.6); - margin: 15px 0px 20px 0px; +.md-typeset .md-button.md-button--primary { + background-color: var(--color-white); + border-color: var(--color-white); + color: var(--color-invenio); + opacity: 100%; } -.rdm-hero > a { - opacity: 1; +.md-typeset .md-button--primary { + color: var(--color-white); + opacity: 100%; } -.rdm-hero > a.md-button.md-button--primary:hover { - background-color: transparent; - border-color: white; - color: white; +.md-typeset .md-button { + color: var(--color-white); } -.rdm-hero > a.md-button:hover { - background-color: white; - border-color: white; - color: #0377cd; +.rdm-toc { + display: flex; + justify-content: center; } -.frontpage .md-main > .md-main__inner, .frontpage .md-main .md-content__inner { - margin-top: 0; - padding-top: 0; +.rdm-toc-item.level1 { + display: inline-flex; } -.frontpage .md-header { - background: transparent; - position: static; +.rdm-toc-item.level1 > div > a { + font-weight: bolder; } -.frontpage .md-tabs { - border-bottom: none; - background: transparent; +.rdm-toc-item.level1 > div { + width: 200px; } -.frontpage .md-main { - background-color: white; +.rdm-toc-item.level2 { + font-size: small; } diff --git a/mkdocs.yml b/mkdocs.yml index 26c77687..c8e4262b 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -10,11 +10,28 @@ copyright: "Copyright © 2019-2024 CERN, Northwestern University and contrib # Configuration theme: - name: "material" - custom_dir: "theme/" + name: material + custom_dir: theme palette: - primary: "white" - accent: "white" + # Palette toggle for light mode + - media: "(prefers-color-scheme: light)" + scheme: default + primary: custom + accent: custom + extra_css: + - stylesheets/extra.css + toggle: + icon: material/brightness-7 + name: Switch to dark mode + # Palette toggle for dark mode + - media: "(prefers-color-scheme: dark)" + scheme: slate + primary: black + accent: orange + toggle: + accent: orange + icon: material/brightness-4 + name: Switch to light mode font: false logo: "images/logo-rdm.png" favicon: "images/favicon.svg" @@ -55,6 +72,7 @@ nav: - Change templates: "customize/look-and-feel/templates.md" - Change theme: "customize/look-and-feel/theme.md" - Change font: "customize/look-and-feel/font.md" + - Change menus: "customize/look-and-feel/menus.md" - Authentication: "customize/authentication.md" - Sending emails: "customize/emails.md" - Search: @@ -121,6 +139,7 @@ nav: - Test emails locally: develop/howtos/dev_email.md - Migrate legacy routes: develop/howtos/route_migration.md - Back up search indices: develop/howtos/backup_search_indices.md + - Restrict access to pages: develop/howtos/restrict_access.md - Architecture: - Introduction: develop/architecture/index.md - Infrastructure: develop/architecture/infrastructure.md @@ -224,8 +243,8 @@ nav: # Customization extra: social: - - icon: "fontawesome/brands/github" - link: "https://github.com/inveniosoftware" + - icon: fontawesome/brands/github + link: https://github.com/inveniosoftware extra_css: - stylesheets/extra.css diff --git a/theme/features_base.html b/theme/features_base.html index e31b0e6b..f9ba4549 100644 --- a/theme/features_base.html +++ b/theme/features_base.html @@ -1,4 +1,14 @@ -{% extends "frontpage_base.html" %} + +{% extends "main.html" %} {% block styles %} {{ super() }} @@ -6,10 +16,25 @@ {% endblock %} -{% block hero %} +{% block tabs %} +{{ super() }} + +
- +
diff --git a/theme/features_sub.html b/theme/features_sub.html index 326905a1..8526e377 100644 --- a/theme/features_sub.html +++ b/theme/features_sub.html @@ -1,4 +1,14 @@ -{% extends "base.html" %} + +{% extends "main.html" %} {% block styles %} {{ super() }} diff --git a/theme/frontpage.html b/theme/frontpage.html index 9f811a1b..320dae51 100644 --- a/theme/frontpage.html +++ b/theme/frontpage.html @@ -1,4 +1,14 @@ -{% extends "frontpage_base.html" %} + +{% extends "main.html" %} {% macro render(nav_item, path, level) %} {% if nav_item.url %} @@ -10,52 +20,81 @@ {% endif %} {% if nav_item.children and level <= 1 %}
  • -
    - - {{ nav_item.title }} - -
      - {% for nav_item in nav_item.children %} - {{ render(nav_item, path ~ "_" ~ loop.index, level + 1) }} - {% endfor %} -
    -
    +
    + {{ nav_item.title }} +
      + {% for nav_item in nav_item.children %} + {{ render(nav_item, path ~ "_" ~ loop.index, level + 1) }} + {% endfor %} +
    +
  • {% else %}
  • - - {{ nav_item.title }} - + {{ nav_item.title }}
  • {% endif %} {% endmacro %} -{% block hero %} -
    - - - -
    -
    + +{% block tabs %} +{{ super() }} + + + +
    +
    + + + +
    +
    -
    The turn-key research data management repository
    +
    + The turn-key research data management repository +
    Quick start Demo site -
    +
    + {% endblock %} + {% block content %} -{{super()}} - -
      - {% for nav_item in nav %} - {%- if nav_item.title != "Home" %} - {%- set path = "__nav_" ~ loop.index %} - {%- set level = 1 %} - {{render(nav_item, path, level)}} - {%- endif %} - {% endfor %} -
    + +{% if "material/tags" in config.plugins and tags %} + {% include "partials/tags.html" %} +{% endif %} +{% include "partials/actions.html" %} +{{ page.content }} +{% include "partials/source-file.html" %} +
      + {% for nav_item in nav %} + {%- if nav_item.title != "Home" %} + {%- set path = "__nav_" ~ loop.index %} + {%- set level = 1 %} + {{ render(nav_item, path, level) }} + {%- endif %} + {% endfor %} +
    {% endblock %} diff --git a/theme/frontpage_base.html b/theme/frontpage_base.html deleted file mode 100644 index 1cdd5bd0..00000000 --- a/theme/frontpage_base.html +++ /dev/null @@ -1,217 +0,0 @@ -{#- - This file was originally automatically generated, but we edited it to customize the frontpage. --#} -{% import "partials/language.html" as lang with context %} - - - - {% block site_meta %} - - - {% if page and page.meta and page.meta.description %} - - {% elif config.site_description %} - - {% endif %} - {% if page and page.meta and page.meta.keywords %} - - {% elif config.site_keywords %} - - {% endif %} - {% if page and page.meta and page.meta.author %} - - {% elif config.site_author %} - - {% endif %} - {% if page.canonical_url %} - - {% endif %} - - - {% endblock %} - {% block htmltitle %} - {% if page and page.meta and page.meta.title %} - {{ page.meta.title }} - {{ config.site_name }} - {% elif page and page.title and not page.is_homepage %} - {{ page.title | striptags }} - {{ config.site_name }} - {% else %} - {{ config.site_name }} - {% endif %} - {% endblock %} - {% block styles %} - - - - {% endblock %} - {% block libs %}{% endblock %} - {% block fonts %} - {% if config.theme.font != false %} - {% set font = config.theme.font %} - - - - {% endif %} - {% endblock %} - {% if config.extra.manifest %} - - {% endif %} - {% for path in config["extra_css"] %} - - {% endfor %} - {% block analytics %} - {% include "partials/integrations/analytics.html" %} - {% endblock %} - {% block extrahead %}{% endblock %} - - {% set direction = config.theme.direction or lang.t('direction') %} - {% if config.theme.palette %} - {% set palette = config.theme.palette %} - {% if not palette is mapping %} - {% set palette = palette | first %} - {% endif %} - {% set scheme = palette.scheme | replace(" ", "-") | lower %} - {% set primary = palette.primary | replace(" ", "-") | lower %} - {% set accent = palette.accent | replace(" ", "-") | lower %} - {# CHANGE START - added class frontpage #} - - {% else %} - - {% endif %} - {# CHANGE END #} - {% set features = config.theme.features or [] %} - {% include "partials/javascripts/base.html" %} - {% if not config.theme.palette is mapping %} - {% include "partials/javascripts/palette.html" %} - {% endif %} - - - -
    - {% if page.toc | first is defined %} - {% set skip = page.toc | first %} - - {{ lang.t('skip.link.title') }} - - {% endif %} -
    -
    - {% if self.announce() %} - - {% endif %} -
    - {% block header %} - {% include "partials/header.html" %} - {% endblock %} -
    - {# CHANGE START #} - {% block tabs %} - {% if "navigation.tabs" in features %} - {% include "partials/tabs.html" %} - {% endif %} - {% endblock %} - {% block hero %}{% endblock %} - {# CHANGE END #} -
    -
    - {% block site_nav %} - {% if nav %} - {% if page and page.meta and page.meta.hide %} - {% set hidden = "hidden" if "navigation" in page.meta.hide %} - {% endif %} - - {% endif %} - {% if page.toc and not "toc.integrate" in features %} - {% if page and page.meta and page.meta.hide %} - {% set hidden = "hidden" if "toc" in page.meta.hide %} - {% endif %} - - {% endif %} - {% endblock %} -
    -
    - {% block content %} - {# CHANGE START - remove page edit, title#} - {# CHANGE END #} - {{ page.content }} - {% if page and page.meta %} - {% if page.meta.git_revision_date_localized or - page.meta.revision_date - %} - {% include "partials/source-file.html" %} - {% endif %} - {% endif %} - {% endblock %} -
    -
    -
    - {% if "navigation.top" in features %} - - {% include ".icons/material/arrow-up.svg" %} - {{ lang.t('top.title') }} - - {% endif %} -
    - {% block footer %} - {% include "partials/footer.html" %} - {% endblock %} -
    -
    -
    -
    - {% block config %} - {%- set app = { - "base": base_url, - "features": features, - "translations": {}, - "search": "assets/javascripts/workers/search.53c85856.min.js" | url, - "version": config.extra.version or None - } -%} - {%- set translations = app.translations -%} - {%- for key in [ - "clipboard.copy", - "clipboard.copied", - "search.config.lang", - "search.config.pipeline", - "search.config.separator", - "search.placeholder", - "search.result.placeholder", - "search.result.none", - "search.result.one", - "search.result.other", - "search.result.more.one", - "search.result.more.other", - "search.result.term.missing", - "select.version.title" - ] -%} - {%- set _ = translations.update({ key: lang.t(key) }) -%} - {%- endfor -%} - - {% endblock %} - {% block scripts %} - {% for path in config["extra_javascript"] %} - - {% endfor %} - {% endblock %} - - diff --git a/theme/partials/footer.html b/theme/partials/footer.html deleted file mode 100644 index 2314baba..00000000 --- a/theme/partials/footer.html +++ /dev/null @@ -1,58 +0,0 @@ -{% import "partials/language.html" as lang with context %} - diff --git a/theme/partials/social.html b/theme/partials/social.html deleted file mode 100644 index 2e2a16b9..00000000 --- a/theme/partials/social.html +++ /dev/null @@ -1,22 +0,0 @@ -{% if config.extra.social %} - -{% endif %}