diff --git a/app/contracts/projects/archive_contract.rb b/app/contracts/projects/archive_contract.rb index ca7bb32a1a8..04a2ffd6d87 100644 --- a/app/contracts/projects/archive_contract.rb +++ b/app/contracts/projects/archive_contract.rb @@ -60,9 +60,9 @@ module Projects # prevent adding another error if there is already one present return if errors.present? - subprojects = model.descendants - return if subprojects.empty? - return if user.allowed_to?(:archive_project, subprojects) + active_subprojects = model.active_subprojects + return if active_subprojects.empty? + return if user.allowed_to?(:archive_project, active_subprojects) errors.add :base, :archive_permission_missing_on_subprojects end diff --git a/app/models/project.rb b/app/models/project.rb index 8f20bb877c4..8cf4ad4c7e3 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -342,6 +342,11 @@ class Project < ApplicationRecord parents | descendants # Set union end + # Returns an array of active subprojects. + def active_subprojects + project.descendants.where(active: true) + end + class << self # builds up a project hierarchy helper structure for use with #project_tree_from_hierarchy # diff --git a/app/services/projects/archive_service.rb b/app/services/projects/archive_service.rb index a1facb5320d..02c2e08fae6 100644 --- a/app/services/projects/archive_service.rb +++ b/app/services/projects/archive_service.rb @@ -39,8 +39,8 @@ module Projects private def persist(service_call) - archive_project(model) and model.children.each do |child| - archive_project(child) + archive_project(model) and model.active_subprojects.each do |subproject| + archive_project(subproject) end service_call diff --git a/app/services/users/register_user_service.rb b/app/services/users/register_user_service.rb index 58ed3fc8bfe..e20297a1f77 100644 --- a/app/services/users/register_user_service.rb +++ b/app/services/users/register_user_service.rb @@ -104,7 +104,7 @@ module Users # Try to register a user with an existsing omniauth connection # bypassing regular account registration restrictions def register_omniauth_user - return if user.identity_url.blank? + return if skip_omniauth_user? user.activate @@ -113,6 +113,10 @@ module Users end end + def skip_omniauth_user? + user.identity_url.blank? + end + def register_by_email_activation return unless Setting::SelfRegistration.by_email? diff --git a/bin/compose b/bin/compose index a1269bc9eb1..76861cedbb4 100755 --- a/bin/compose +++ b/bin/compose @@ -57,12 +57,24 @@ elif [[ "$1" = "setup" ]]; then elif [[ "$1" = "reset" ]]; then $DOCKER_COMPOSE -f $COMPOSE_FILE down && docker volume rm `docker volume ls -q | grep ${PWD##*/}_` elif [[ "$1" = "rspec" ]]; then - if ! docker ps | grep ${PWD##*/}_backend-test_1 > /dev/null; then + function get-container-name() { + name=`$DOCKER_COMPOSE ps backend-test | tail -n1 | cut -d ' ' -f1` + + if [ "$name" = 'NAME' ]; then + return 1; + else + echo "$name" + fi + } + + if ! get-container-name > /dev/null; then echo "Test backend not running yet. Starting it..." $DOCKER_COMPOSE -f $COMPOSE_FILE up -d backend-test - while ! docker logs --since 1m ${PWD##*/}_backend-test_1 | grep "Ready for tests" > /dev/null; do + container=`get-container-name` + + while ! docker logs --since 1m $container 2>&1 | grep "Ready for tests" &> /dev/null; do sleep 1 printf "." done diff --git a/db/migrate/20220428071221_restore_defaults_on_empty_settings.rb b/db/migrate/20220428071221_restore_defaults_on_empty_settings.rb index 22c7a33e761..1a26a8d94f2 100644 --- a/db/migrate/20220428071221_restore_defaults_on_empty_settings.rb +++ b/db/migrate/20220428071221_restore_defaults_on_empty_settings.rb @@ -12,7 +12,7 @@ class RestoreDefaultsOnEmptySettings < ActiveRecord::Migration[6.1] next if definition.value == '' - setting.update_column(:value, definition.value) + setting.update_attribute(:value, definition.value) end end diff --git a/docker-compose.yml b/docker-compose.yml index 537dd7891fa..1fed0d964e0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -44,7 +44,7 @@ x-op-backend: &backend OPENPROJECT_RAILS__CACHE__STORE: file_store OPENPROJECT_RAILS__RELATIVE__URL__ROOT: "${OPENPROJECT_RAILS__RELATIVE__URL__ROOT:-}" DATABASE_URL: postgresql://${DB_USERNAME:-postgres}:${DB_PASSWORD:-postgres}@${DB_HOST:-db}:${DB_PORT:-5432}/${DB_DATABASE:-openproject} - OPENPROJECT_EDITION: $OPENPROJECT_EDITION + OPENPROJECT_EDITION: ${OPENPROJECT_EDITION:-standard} volumes: - ".:/home/dev/openproject" - "opdata:/var/openproject/assets" diff --git a/docs/installation-and-operations/installation/docker/README.md b/docs/installation-and-operations/installation/docker/README.md index 5de4c392c44..135231fd6d3 100644 --- a/docs/installation-and-operations/installation/docker/README.md +++ b/docs/installation-and-operations/installation/docker/README.md @@ -198,11 +198,16 @@ If you _really_ want to disable HSTS headers and request upgrades, you will need For more advanced configuration, please have a look at the [Advanced configuration](../../configuration) section. -### Apache Reverse Proxy Setup +### Reverse Proxy Setup -The containers above are not meant as public facing endpoints. Always use an existing proxying web server or load balancer to provide access to OpenProject +The containers above are not meant as public facing endpoints. +Always use an existing proxying web server or load balancer to provide access to OpenProject. There are two ways to run OpenProject. We'll cover each configuration in a separate of the following sections. +Moreover we're going to give basic configurations for both the [Apache](https://httpd.apache.org/) +and [nginx](https://nginx.org/en/) web servers. + +**Apache** For both configurations the following Apache mods are required: @@ -214,14 +219,21 @@ For both configurations the following Apache mods are required: In each case you will create a file `/usr/local/apache2/conf/sites/openproject.conf` with the contents as described in the respective sections. -Both configuration examples are based on the following assumptions: +**Nginx** + +The nginx configuration will go into `/etc/nginx/conf.d/openproject.conf`. + +**Assumptions** + +All examples are based on the following assumptions: * the site is accessed via https * certificate and key are located under `/etc/ssl/crt/server.{crt, key}` * the OpenProject docker container's port 80 is mapped to the docker host's port 8080 -*Important:* Once OpenProject is running make sure to also set the host name and protocol -accordingly under Administration -> System Settings. +*Important:* Once OpenProject is running make sure to also set the host name accordingly under Administration -> System Settings or set it directly during startup by setting `OPENPROJECT_HOST__NAME`. + +> **NOTE:** There is [another example](../packaged/#external-ssltls-termination) for external SSL/TLS termination for **packaged** installations #### 1) Virtual host root @@ -229,10 +241,9 @@ The default scenario is to have OpenProject serve the whole virtual host. This requires no further configuration for the docker container beyond what is described above. -Assuming the desired *server name* is `openproject.example.com` the configuration -will look like this: +Let's assume we want OpenProject to be accessed under https://openproject.example.com. -> **NOTE:** There is [another example](../packaged/#external-ssltls-termination) for external SSL/TLS termination for **packaged** installations +The **apache** configuration for this looks as follows. ``` @@ -265,6 +276,37 @@ will look like this: ``` +The **nginx** counterpart can be seen below. + +``` +server { + listen 80; + + server_name openproject.example.com; + + return 301 https://$host$request_uri; +} + +server { + listen 443 ssl; + server_name openproject.example.com; + + ssl_certificate /etc/ssl/crt/server.crt; + ssl_certificate_key /etc/ssl/crt/server.key; + + proxy_redirect off; + + location / { + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto https; + + proxy_pass http://127.0.0.1:8080; + } +} +``` + #### 2) Location (subdirectory) Let's assume you want OpenProject to run on your host with the *server name* `example.com` @@ -277,7 +319,7 @@ need to configure OpenProject accordingly by adding the following options to the -e OPENPROJECT_RAILS__RELATIVE__URL__ROOT=/openproject ``` -The apache configuration for this configuration then looks like this: +The **apache** configuration for this configuration then looks like this: ``` @@ -310,6 +352,39 @@ The apache configuration for this configuration then looks like this: ``` +The equivalent **nginx** configuration looks as follows. + +``` +server { + listen 80; + + server_name example.com; + + location /openproject { + return 301 https://$host$request_uri; + } +} + +server { + listen 443 ssl; + server_name example.com; + + ssl_certificate /etc/ssl/crt/server.crt; + ssl_certificate_key /etc/ssl/crt/server.key; + + proxy_redirect off; + + location /openproject { + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto https; + + proxy_pass http://127.0.0.1:8080/openproject; + } +} +``` + ### OpenProject plugins The docker image itself does not support plugins. But you can create your own docker image to include plugins. diff --git a/docs/installation-and-operations/installation/helm-chart/README.md b/docs/installation-and-operations/installation/helm-chart/README.md new file mode 100644 index 00000000000..2a6864f82a4 --- /dev/null +++ b/docs/installation-and-operations/installation/helm-chart/README.md @@ -0,0 +1,84 @@ +--- +sidebar_navigation: + title: Helm Chart + priority: 280 +--- + +# Helm Chart + +## Basic commands + +```bash +helm repo add openproject https://charts.openproject.org +helm upgrade --install my-openproject openproject/openproject +``` + +## Introduction + +This chart bootstraps an OpenProject instance, optionally with a PostgreSQL database and Memcached. + +## Prerequisites +- Kubernetes 1.16+ +- Helm 3.0.0+ +- PV provisioner support in the underlying infrastructure + +## Installing the Chart + +You can install the chart with the release name `my-openproject` in its own namespace like this: + +```bash +helm upgrade --create-namespace --namespace openproject --install my-openproject openproject/openproject +``` + +The namespace is optional, but using it does make it easier to manage the resources +created for OpenProject. + +## Updating the configuration + +The OpenProject configuration can be changed through environment variables. +You can use `helm upgrade` to set individual values. + +For instance: + +``` +helm upgrade --reuse-values --namespace openproject my-openproject --set environment.OPENPROJECT_IMPRESSUM__LINK=https://www.openproject.org/legal/imprint/ --set environment.OPENPROJECT_APP__TITLE='My OpenProject' +``` + +Find out more about the [configuration](../../configuration/environment/) section. + +## Uninstalling the Chart + +To uninstall the release with the name my-openproject do the following: + +```bash +helm uninstall --namespace openproject my-openproject +``` + +> **Note**: This will not remove the persistent volumes created while installing. +> The easiest way to ensure all PVCs are deleted as well is to delete the openproject namespace +> (`kubectl delete namespace openproject`). If you installed OpenProject into the default +> namespace, you can delete the volumes manually one by one. + +## Troubleshooting + +### Web deployment stuck in `CrashLoopBackoff` + +Describing the pod may yield an error like the following: + +``` +65s) kubelet Error: failed to start container "openproject": Error response from daemon: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: error setting cgroup config for procHooks process: failed to write "400000": write /sys/fs/cgroup/cpu,cpuacct/kubepods/burstable/pod990fa25e-dbf0-4fb7-9b31-9d7106473813/openproject/cpu.cfs_quota_us: invalid argument: unknown +``` + +This can happen when using **minikube**. By default, it initialises the cluster with 2 CPUs only. + +Either increase the cluster's resources to have at least 4 CPUs or install the OpenProject helm chart with a reduced CPU limit by adding the following option to the install command: + +``` +--set resources.limits.cpu=2 +``` + +### Root access in OpenShift + +The OpenProject container performs tasks as root during setup. +In [OpenShift](https://www.redhat.com/en/technologies/cloud-computing/openshift) this is not allowed. You will have to [add](https://examples.openshift.pub/deploy/scc-anyuid/) the `anyuid` SCC (Security Context Constraint) +to OpenProject's service account. diff --git a/docs/installation-and-operations/installation/kubernetes/README.md b/docs/installation-and-operations/installation/kubernetes/README.md index 60fbcae8c8a..734fa6db1d7 100644 --- a/docs/installation-and-operations/installation/kubernetes/README.md +++ b/docs/installation-and-operations/installation/kubernetes/README.md @@ -1,5 +1,7 @@ --- -sidebar_navigation: false +sidebar_navigation: + title: Kubernetes + priority: 290 --- # Kubernetes @@ -8,7 +10,3 @@ Kubernetes is a container orchestration tool. As such it can use the OpenProject docker container in the same manner as shown in the [docker section](../docker/#one-container-per-process-recommended). In the [openproject-deploy](https://github.com/opf/openproject-deploy/blob/stable/12/kubernetes/README.md) repository we provide further information and an exemplary set of YAML files defining a complete OpenProject setup on Kubernetes. - -## Helm - -You can find instructions for the official Helm charts under https://charts.openproject.org. diff --git a/docs/installation-and-operations/misc/custom-openid-connect-providers/README.md b/docs/installation-and-operations/misc/custom-openid-connect-providers/README.md index 84b602bf860..97db8a325ad 100644 --- a/docs/installation-and-operations/misc/custom-openid-connect-providers/README.md +++ b/docs/installation-and-operations/misc/custom-openid-connect-providers/README.md @@ -145,6 +145,23 @@ OpenProject OIDC integration supports [back-channel logouts](https://openid.net/ On the identity provider side, you need to set `https:///auth//backchannel-logout`. `` is the identifier of the OIDC configuration as provided above. + + +#### Respecting self-registration + +You can configure OpenProject to restrict which users can register on the system with the [authentication self-registration setting](../authentication-settings) + + By default, users returning from a SAML idP will be automatically created. If you'd like for the SAML integration to respect the configured self-registration option, please use setting `limit_self_registration`: + +```ruby +options = { + # ... other options + limit_self_registration: true +} +``` + + + ### Claims You can also request [claims](https://openid.net/specs/openid-connect-core-1_0-final.html#Claims) for both the id_token and userinfo endpoint. diff --git a/docs/release-notes/12-4-0/README.md b/docs/release-notes/12-4-0/README.md index 2e4e0ee7178..c4f0b5cb067 100644 --- a/docs/release-notes/12-4-0/README.md +++ b/docs/release-notes/12-4-0/README.md @@ -66,7 +66,7 @@ It is now possible to choose between a full 1-week view, a 2-week view or only t ## OpenProject Helm Charts -Starting with OpenProject 12.4 offical [OpenProject Helm charts](../../installation-and-operations/installation/kubernetes/#helm) are availble. +Starting with OpenProject 12.4 offical [OpenProject Helm charts](../../installation-and-operations/installation/helm-chart) are available. ![openproject helm charts](openproject-helm-charts.jpg) diff --git a/docs/system-admin-guide/authentication/authentication-settings/README.md b/docs/system-admin-guide/authentication/authentication-settings/README.md index 0792bb3629b..21efc1da25d 100644 --- a/docs/system-admin-guide/authentication/authentication-settings/README.md +++ b/docs/system-admin-guide/authentication/authentication-settings/README.md @@ -23,6 +23,8 @@ You can adapt the following under the authentication settings: c) **Automatic account activation** means that a newly registered user will automatically be active. + **Note:** By default, self-registration is only applied to internal users (logging in with username and password). If you have an identity provider such as LDAP, SAML or OpenID Connect, use the respective settings in their configuration to control which users are applicable for automatic user creation. + 3. Define if the **email address should be used as login** name. 4. Define after how many days the **activation email sent to new users will expire**. Afterwards, you will have the possibility to [re-send the activation email](../../users-permissions/users/#resend-user-invitation-via-email) via the user settings. diff --git a/docs/system-admin-guide/authentication/openid-providers/README.md b/docs/system-admin-guide/authentication/openid-providers/README.md index 90e2bd5925a..813b4ab0ded 100644 --- a/docs/system-admin-guide/authentication/openid-providers/README.md +++ b/docs/system-admin-guide/authentication/openid-providers/README.md @@ -29,10 +29,9 @@ You can configure the following options. 2. Optionally enter a **display name**. 3. Enter the **Identifier**. 4. Enter the **Secret**. -5. Press the blue **create** button. - - - +5. Optionally, if you want to honor the system-wide self-registration setting, enable "Limit self registration". +When checked, users will be created according to the [self-registration setting](../authentication-settings). +6. Press the blue **create** button. ## Google Workspace diff --git a/docs/system-admin-guide/authentication/saml/README.md b/docs/system-admin-guide/authentication/saml/README.md index afb9e048166..88357e30184 100644 --- a/docs/system-admin-guide/authentication/saml/README.md +++ b/docs/system-admin-guide/authentication/saml/README.md @@ -233,13 +233,13 @@ Be sure to choose the correct indentation and base key. The items below the `sam In this section, we detail some of the required and optional configuration options for SAML. -**Mandatory: Response signature verification** +#### 2.1 Mandatory: Response signature verification SAML responses by identity providers are required to be signed. You can configure this by either specifying the response's certificate fingerprint in `idp_cert_fingerprint` , or by passing the entire PEM-encoded certificate string in `idp_cert` (beware of newlines and formatting the cert, [c.f. the idP certificate options in omniauth-saml](https://github.com/omniauth/omniauth-saml#options)) -**Mandatory: Attribute mapping** +#### 2.2 Mandatory: Attribute mapping Use the key `attribute_statements` to provide mappings for attributes returned by the SAML identity provider's response to OpenProject internal attributes. @@ -291,7 +291,9 @@ default: last_name: ['sn'] ``` -**Optional: Setting the attribute format** + + +#### 2.3 Optional: Setting the attribute format By default, the attributes above will be requested with the format `urn:oasis:names:tc:SAML:2.0:attrname-format:basic`. That means the response should contain attribute names 'mail', etc. as configured above. @@ -327,7 +329,9 @@ default: last_name: ['urn:oid:2.5.4.4'] ``` -**Optional: Request signature and Assertion Encryption** + + +#### 2.4 Optional: Request signature and Assertion Encryption Your identity provider may optionally encrypt the assertion response, however note that with the required use of TLS transport security, in many cases this is not necessary. You may wish to use Assertion Encryption if TLS is terminated before the OpenProject application server (e.g., on the load balancer level). @@ -368,10 +372,26 @@ default: digest_method: 'http://www.w3.org/2001/04/xmlenc#sha256' ``` - With request signing enabled, the certificate will be added to the identity provider to validate the signature of the service provider's request. + +#### 2.5. Optional: Restrict who can automatically self-register + +You can configure OpenProject to restrict which users can register on the system with the [authentication self-registration setting](../authentication-settings) + + By default, users returning from a SAML idP will be automatically created. If you'd like for the SAML integration to respect the configured self-registration option, please use this setting: + +```yml +default: + # <-- other configuration --> + mysaml1: + # <-- other configuration --> + limit_self_registration: true +``` + + + ### 3: Restarting the server Once the configuration is completed, restart your OpenProject server with `service openproject restart`. If you configured SAML through settings, this step can be ignored. diff --git a/docs/user-guide/nextcloud-integration/README.md b/docs/user-guide/nextcloud-integration/README.md index bb7eb426ef7..13a9c10caf1 100644 --- a/docs/user-guide/nextcloud-integration/README.md +++ b/docs/user-guide/nextcloud-integration/README.md @@ -233,4 +233,4 @@ In rare occasions, it is possible for the integration to not be able to fetch al If OpenProject notifications are not properly displayed in Nextcloud, navigate to *Nextcloud settings → Basic settings → Background jobs* and ensure that _Cron_ is selected. -## ![NC_notifications_not_displayed](Cron_job_settings.png) \ No newline at end of file +![NC_notifications_not_displayed](Cron_job_settings.png) diff --git a/frontend/src/app/features/calendar/op-calendar.service.ts b/frontend/src/app/features/calendar/op-calendar.service.ts index 8b9ae2ad1c5..845373979cb 100644 --- a/frontend/src/app/features/calendar/op-calendar.service.ts +++ b/frontend/src/app/features/calendar/op-calendar.service.ts @@ -34,11 +34,14 @@ export class OpCalendarService extends UntilDestroyedMixin { } applyNonWorkingDay({ date }:{ date?:Date }, nonWorkingDays:IDay[]):string[] { - const formatted = moment(date).format('YYYY-MM-DD'); - if (date && (this.weekdayService.isNonWorkingDay(date) || nonWorkingDays.find((el) => el.date === formatted))) { - return ['fc-non-working-day']; + if (date) { + // we need to find the UTC date for each date while highlighting non-wrking days on full-calendar + const utcDate = new Date(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds(), date.getUTCMilliseconds()); + const formatted = moment(utcDate).format('YYYY-MM-DD'); + if (this.weekdayService.isNonWorkingDay(utcDate) || nonWorkingDays.find((el) => el.date === formatted)) { + return ['fc-non-working-day']; + } } - return []; } } diff --git a/frontend/src/app/features/work-packages/components/filters/filter-date-times-value/filter-date-times-value.component.ts b/frontend/src/app/features/work-packages/components/filters/filter-date-times-value/filter-date-times-value.component.ts index eaf50eaec36..16c99443b01 100644 --- a/frontend/src/app/features/work-packages/components/filters/filter-date-times-value/filter-date-times-value.component.ts +++ b/frontend/src/app/features/work-packages/components/filters/filter-date-times-value/filter-date-times-value.component.ts @@ -113,8 +113,8 @@ export class FilterDateTimesValueComponent extends AbstractDateTimeValueControll const parsed = this .timezoneService .parseISODatetime(date) - .utc() - .startOf('day'); + .startOf('day') + .utc(); this.begin = this.timezoneService.formattedISODateTime(parsed); } @@ -131,8 +131,8 @@ export class FilterDateTimesValueComponent extends AbstractDateTimeValueControll const parsed = this .timezoneService .parseISODatetime(date) - .utc() - .endOf('day'); + .endOf('day') + .utc(); this.end = this.timezoneService.formattedISODateTime(parsed); } diff --git a/frontend/src/app/features/work-packages/components/wp-relations/wp-relation-row/wp-relation-row.template.html b/frontend/src/app/features/work-packages/components/wp-relations/wp-relation-row/wp-relation-row.template.html index 29e3990b57c..7cd263a8f7f 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations/wp-relation-row/wp-relation-row.template.html +++ b/frontend/src/app/features/work-packages/components/wp-relations/wp-relation-row/wp-relation-row.template.html @@ -1,56 +1,74 @@
-
-
+
+
-
- +
-
- - -
+ #{{relatedWorkPackage.id}} -
- - - -
+ -
+ + + + +
-
+
+
- -
- - - + +
diff --git a/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-group/wp-relations-group.template.html b/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-group/wp-relations-group.template.html index 529f39ab774..530a8e9d0ac 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-group/wp-relations-group.template.html +++ b/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-group/wp-relations-group.template.html @@ -22,7 +22,7 @@
{ + private matchingItems(elements:IProjectAutocompleteItem[], matching:string):Observable { let filtered:IProjectAutocompleteItem[]; if (matching === '' || !matching) { @@ -186,6 +186,25 @@ export class ProjectAutocompleterComponent implements ControlValueAccessor { return of(filtered); } + private disableSelectedItems( + projects:IProjectAutocompleteItem[], + value:IProjectAutocompleterData|IProjectAutocompleterData[]|null, + ) { + if (!this.multiple) { + return projects; + } + + const normalizedValue = (value || []); + const arrayedValue = (Array.isArray(normalizedValue) ? normalizedValue : [normalizedValue]).map((p) => p.href || p.id); + return projects.map((project) => { + const isSelected = !!arrayedValue.find((selected) => selected === this.projectTracker(project)); + return { + ...project, + disabled: isSelected || project.disabled, + }; + }); + } + public getAvailableProjects(searchTerm:string):Observable { if (this.dataLoaded === true) { return this.matchingItems(this.projects, searchTerm).pipe( @@ -194,6 +213,11 @@ export class ProjectAutocompleterComponent implements ControlValueAccessor { map((projects) => buildTree(projects)), map((projects) => recursiveSort(projects)), map((projectTreeItems) => flattenProjectTree(projectTreeItems)), + switchMap( + (projects) => merge(of([]), this.valueChange).pipe( + map(() => this.disableSelectedItems(projects, this.value)), + ), + ), ); } return getPaginatedResults( @@ -234,10 +258,19 @@ export class ProjectAutocompleterComponent implements ControlValueAccessor { children: [], }))), map(this.mapResultsFn), - map((projects) => { this.dataLoaded = true; this.projects = projects; return projects.sort((a, b) => a.ancestors.length - b.ancestors.length); }), + map((projects) => { + this.dataLoaded = true; + this.projects = projects; + return projects.sort((a, b) => a.ancestors.length - b.ancestors.length); + }), map((projects) => buildTree(projects)), map((projects) => recursiveSort(projects)), map((projectTreeItems) => flattenProjectTree(projectTreeItems)), + switchMap( + (projects) => merge(of([]), this.valueChange).pipe( + map(() => this.disableSelectedItems(projects, this.value)), + ), + ), ); } diff --git a/frontend/src/app/shared/components/datepicker/modal-single-date-picker/modal-single-date-picker.component.ts b/frontend/src/app/shared/components/datepicker/modal-single-date-picker/modal-single-date-picker.component.ts index 28f68c11eee..9190960e948 100644 --- a/frontend/src/app/shared/components/datepicker/modal-single-date-picker/modal-single-date-picker.component.ts +++ b/frontend/src/app/shared/components/datepicker/modal-single-date-picker/modal-single-date-picker.component.ts @@ -270,8 +270,11 @@ export class OpModalSingleDatePickerComponent implements ControlValueAccessor, O } writeWorkingValue(value:string):void { + const date = new Date(value); + // since new Date() returns a date in our local timezone, we need to find the UTC date + const utcDate = new Date(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds(), date.getUTCMilliseconds()); this.workingValue = value; - this.workingDate = new Date(value); + this.workingDate = utcDate; } writeValue(value:string):void { diff --git a/frontend/src/global_styles/content/_datepicker.sass b/frontend/src/global_styles/content/_datepicker.sass index 244f983ff81..122883df6fc 100644 --- a/frontend/src/global_styles/content/_datepicker.sass +++ b/frontend/src/global_styles/content/_datepicker.sass @@ -140,6 +140,9 @@ $datepicker--selected-border-radius: 5px border-radius: 0 box-shadow: none !important + &:hover + border-color: $spot-color-basic-gray-3 + &.flatpickr-non-working-day @include non-working-day @@ -152,8 +155,8 @@ $datepicker--selected-border-radius: 5px border-radius: 0 &:hover - color: $spot-color-basic-gray-1 !important - border-color: #e6e6e6 !important + color: $spot-color-basic-gray-1 + border-color: $spot-color-basic-gray-3 &.selected:not(.startRange, .endRange) border-radius: $datepicker--selected-border-radius @@ -165,6 +168,10 @@ $datepicker--selected-border-radius: 5px border-color: var(--primary-color) color: $spot-color-basic-white + &:hover + background: var(--primary-color-dark) + border-color: var(--primary-color-dark) + &.startRange border-radius: $datepicker--selected-border-radius 0 0 $datepicker--selected-border-radius @@ -180,6 +187,10 @@ $datepicker--selected-border-radius: 5px border-color: var(--primary-color--minor3) border-radius: 0 + &:hover + color: var(--primary-color-dark) + border-color: var(--primary-color-dark) + &.flatpickr-non-working-day background: $spot-color-basic-gray-6 border-color: $spot-color-basic-gray-6 diff --git a/frontend/src/global_styles/content/work_packages/tabs/_relations.sass b/frontend/src/global_styles/content/work_packages/tabs/_relations.sass index 20e140c50f1..02dbc3c0025 100644 --- a/frontend/src/global_styles/content/work_packages/tabs/_relations.sass +++ b/frontend/src/global_styles/content/work_packages/tabs/_relations.sass @@ -28,12 +28,16 @@ .detail-panel-description-content .relation - clear: both //resolve the problem if inside are elements with float + //resolve the problem if inside are elements with float + clear: both + h3 cursor: pointer + a text-decoration: none color: inherit + i font-size: 0.8rem @@ -43,31 +47,48 @@ .hierarchy-item margin-bottom: 2px +.relation-container + margin-bottom: $spot-spacing-1 + .relation-row line-height: 2em - .attribute-header - font-size: 0.8em - text-transform: uppercase - font-weight: bold - .description-section - border: 1px dotted lightblue - padding: 4px + .inline-edit--container @include text-shortener // Similar to inner span's line-height line-height: 1.6em + .inline-edit--display-field vertical-align: middle - .controls-container - text-align: right + + &--grid + display: grid + align-items: center + grid-template: "id info subject status actions" / 10% 12.5% 45% 22.5% 10% + margin-bottom: $spot-spacing-0_5 + + @media #{$spot-mq-mobile} + grid-template: "id info status actions" "subject subject subject subject" / 25% 30% 30% 15% + + + &-actions + grid-area: actions + display: flex + justify-content: end + + &-id + @include text-shortener() + grid-area: id + font-size: 0.875rem + + &-subject + @include text-shortener() + grid-area: subject + font-size: 0.875rem .wp-relations-hierarchy-section margin-top: 35px -.wp-relations-hierarchy-subject - @include text-shortener - display: block - .wp-relations-controls-section text-align: right flex-shrink: 1 @@ -85,6 +106,7 @@ .wp-relations-create-button margin: 0.25rem 0 line-height: 1.5 + .-create-button-full-width margin-top: 1.5em width: 100% diff --git a/lib/tasks/email.rake b/lib/tasks/email.rake index 7b31fafe42d..8958aa4e3f3 100644 --- a/lib/tasks/email.rake +++ b/lib/tasks/email.rake @@ -145,8 +145,8 @@ namespace :redmine do username: ENV.fetch('username', nil), password: ENV.fetch('password', nil), folder: ENV.fetch('folder', nil), - move_on_success: ActiveRecord::Type::Boolean.new.cast(ENV.fetch('move_on_success', nil)), - move_on_failure: ActiveRecord::Type::Boolean.new.cast(ENV.fetch('move_on_failure', nil)) + move_on_success: ENV.fetch('move_on_success', nil), + move_on_failure: ENV.fetch('move_on_failure', nil) } Redmine::IMAP.check(imap_options, options_from_env) diff --git a/modules/auth_plugins/lib/open_project/auth_plugins/engine.rb b/modules/auth_plugins/lib/open_project/auth_plugins/engine.rb index 0af40ed036d..c13fc9b40bb 100644 --- a/modules/auth_plugins/lib/open_project/auth_plugins/engine.rb +++ b/modules/auth_plugins/lib/open_project/auth_plugins/engine.rb @@ -38,6 +38,8 @@ module OpenProject::AuthPlugins author_url: 'https://www.openproject.org', bundled: true + patch_with_namespace :Users, :RegisterUserService + config.to_prepare do OpenProject::AuthPlugins::Hooks end diff --git a/modules/auth_plugins/lib/open_project/auth_plugins/patches.rb b/modules/auth_plugins/lib/open_project/auth_plugins/patches.rb new file mode 100644 index 00000000000..a96108b2162 --- /dev/null +++ b/modules/auth_plugins/lib/open_project/auth_plugins/patches.rb @@ -0,0 +1,30 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-2023 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module OpenProject::AuthPlugins::Patches +end diff --git a/modules/auth_plugins/lib/open_project/auth_plugins/patches/register_user_service_patch.rb b/modules/auth_plugins/lib/open_project/auth_plugins/patches/register_user_service_patch.rb new file mode 100644 index 00000000000..f39ca3332c6 --- /dev/null +++ b/modules/auth_plugins/lib/open_project/auth_plugins/patches/register_user_service_patch.rb @@ -0,0 +1,45 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-2023 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module OpenProject::AuthPlugins::Patches::RegisterUserServicePatch + def self.included(base) # :nodoc: + base.prepend InstanceMethods + end + + module InstanceMethods + def skip_omniauth_user? + super || limit_self_registration?(user) + end + + def limit_self_registration?(user) + provider = user.authentication_provider.downcase + + OpenProject::Plugins::AuthPlugin.limit_self_registration? provider: + end + end +end diff --git a/modules/auth_plugins/lib/open_project/plugins/auth_plugin.rb b/modules/auth_plugins/lib/open_project/plugins/auth_plugin.rb index 521f7125b91..3a62138b07c 100644 --- a/modules/auth_plugins/lib/open_project/plugins/auth_plugin.rb +++ b/modules/auth_plugins/lib/open_project/plugins/auth_plugin.rb @@ -92,6 +92,15 @@ module OpenProject::Plugins [camelization, name].compact.first.underscore.to_sym end + ## + # Indicates whether or not self registration should be limited for the provider + # with the given name. + # + # @param provider [String] Name of the provider + def self.limit_self_registration?(provider:) + Hash(find_provider_by_name(provider))[:limit_self_registration] + end + def self.warn_unavailable(name) RequestStore.fetch("warn_unavailable_auth_#{name}") do Rails.logger.warn { "OmniAuth SSO strategy #{name} is only available for Enterprise Editions." } diff --git a/modules/openid_connect/app/controllers/openid_connect/providers_controller.rb b/modules/openid_connect/app/controllers/openid_connect/providers_controller.rb index 58de9fddf58..24f0eac2676 100644 --- a/modules/openid_connect/app/controllers/openid_connect/providers_controller.rb +++ b/modules/openid_connect/app/controllers/openid_connect/providers_controller.rb @@ -62,11 +62,11 @@ module OpenIDConnect end def create_params - params.require(:openid_connect_provider).permit(:name, :display_name, :identifier, :secret) + params.require(:openid_connect_provider).permit(:name, :display_name, :identifier, :secret, :limit_self_registration) end def update_params - params.require(:openid_connect_provider).permit(:display_name, :identifier, :secret) + params.require(:openid_connect_provider).permit(:display_name, :identifier, :secret, :limit_self_registration) end def find_provider diff --git a/modules/openid_connect/app/models/openid_connect/provider.rb b/modules/openid_connect/app/models/openid_connect/provider.rb index 56ae712dce7..35edacbef0f 100644 --- a/modules/openid_connect/app/models/openid_connect/provider.rb +++ b/modules/openid_connect/app/models/openid_connect/provider.rb @@ -21,14 +21,27 @@ module OpenIDConnect delegate :scope, to: :omniauth_provider, allow_nil: true delegate :to_h, to: :omniauth_provider, allow_nil: false + ## + # Controls whether or not self registration shall be limited for this provider. + # + # See also: + # - OpenProject::Plugins::AuthPlugin.limit_self_registration? + # - OpenProject::AuthPlugins::Patches::RegisterUserServicePatch + attr_reader :limit_self_registration + def initialize(omniauth_provider) @omniauth_provider = omniauth_provider @errors = ActiveModel::Errors.new(self) @display_name = omniauth_provider.to_h[:display_name] + @limit_self_registration = initial_value_for_limit_self_registration end def self.initialize_with(params) - new(NewProvider.new(params)) + do_limit = params[:limit_self_registration] + + new(NewProvider.new(params.except(:limit_self_registration))).tap do |p| + p.limit_self_registration = String(do_limit).to_bool unless do_limit.nil? + end end def new_record? @@ -39,6 +52,24 @@ module OpenIDConnect omniauth_provider.is_a?(OmniAuth::OpenIDConnect::Provider) end + def limit_self_registration? + @limit_self_registration + end + + def limit_self_registration=(value) + @limit_self_registration = value + end + + def to_h + return {} if omniauth_provider.nil? + + omniauth_provider.to_h.merge(limit_self_registration: limit_self_registration?) + end + + def limit_self_registration_default + name == "google" # limit by default only for Google since anyone can sign in + end + def id return nil unless persisted? @@ -46,33 +77,64 @@ module OpenIDConnect end def valid? - @errors.add(:name, :invalid) unless ALLOWED_TYPES.include?(name) + @errors.add(:name, :invalid) unless type_allowed?(name) @errors.add(:identifier, :blank) if identifier.blank? @errors.add(:secret, :blank) if secret.blank? @errors.none? end + ## + # Checks if the provider with the given name is of an allowed type. + # + # Types can be followed by a period and arbitrary names to add several + # providers of the same type. E.g. 'azure', 'azure.dep1', 'azure.dep2'. + def type_allowed?(name) + ALLOWED_TYPES.any? { |allowed| name =~ /\A#{allowed}(\..+)?\Z/ } + end + def save return false unless valid? - config = Setting.plugin_openproject_openid_connect || Hash.new - config["providers"] ||= Hash.new - config["providers"][name] = omniauth_provider.to_h.stringify_keys - Setting.plugin_openproject_openid_connect = config + Setting.plugin_openproject_openid_connect = setting_with_provider + true end def destroy - config = Setting.plugin_openproject_openid_connect - config["providers"] ||= {} - config["providers"].delete(name) - Setting.plugin_openproject_openid_connect = config + Setting.plugin_openproject_openid_connect = setting_without_provider + true end + def setting_with_provider + setting.deep_merge "providers" => { name => to_h.stringify_keys } + end + + def setting_without_provider + setting.tap do |s| + s["providers"].delete name + end + end + + def setting + Hash(Setting.plugin_openproject_openid_connect).tap do |h| + h["providers"] ||= Hash.new + end + end + # https://api.rubyonrails.org/classes/ActiveModel/Errors.html def read_attribute_for_validation(attr) send(attr) end + + private + + def initial_value_for_limit_self_registration + if omniauth_provider.configuration&.has_key? :limit_self_registration + omniauth_provider.configuration[:limit_self_registration] + else + limit_self_registration_default + end + end end end diff --git a/modules/openid_connect/app/views/openid_connect/providers/_form.html.erb b/modules/openid_connect/app/views/openid_connect/providers/_form.html.erb index 0d63cb07370..86d0a2804c8 100644 --- a/modules/openid_connect/app/views/openid_connect/providers/_form.html.erb +++ b/modules/openid_connect/app/views/openid_connect/providers/_form.html.erb @@ -22,4 +22,11 @@
<%= f.text_field :secret, required: true, container_class: '-middle' %>
+ +
+ <%= f.check_box :limit_self_registration, required: false, container_class: '-middle' %> +
+ <%= I18n.t('openid_connect.setting_instructions.limit_self_registration') %> +
+
diff --git a/modules/openid_connect/config/locales/crowdin/af.yml b/modules/openid_connect/config/locales/crowdin/af.yml index 7bc538e6702..637591b07a7 100644 --- a/modules/openid_connect/config/locales/crowdin/af.yml +++ b/modules/openid_connect/config/locales/crowdin/af.yml @@ -9,6 +9,7 @@ af: identifier: Identifiseerder secret: Secret scope: Scope + limit_self_registration: Limit self registration openid_connect: menu_title: OpenID providers providers: @@ -17,3 +18,6 @@ af: no_results_table: No providers have been defined yet. plural: OpenID providers singular: OpenID provider + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/ar.yml b/modules/openid_connect/config/locales/crowdin/ar.yml index 88a1d6a2772..e846d4e9fce 100644 --- a/modules/openid_connect/config/locales/crowdin/ar.yml +++ b/modules/openid_connect/config/locales/crowdin/ar.yml @@ -9,6 +9,7 @@ ar: identifier: المعرّف secret: Secret scope: Scope + limit_self_registration: Limit self registration openid_connect: menu_title: OpenID providers providers: @@ -17,3 +18,6 @@ ar: no_results_table: No providers have been defined yet. plural: OpenID providers singular: OpenID provider + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/az.yml b/modules/openid_connect/config/locales/crowdin/az.yml index 9dddd52373c..396667b6871 100644 --- a/modules/openid_connect/config/locales/crowdin/az.yml +++ b/modules/openid_connect/config/locales/crowdin/az.yml @@ -9,6 +9,7 @@ az: identifier: Identifier secret: Secret scope: Scope + limit_self_registration: Limit self registration openid_connect: menu_title: OpenID providers providers: @@ -17,3 +18,6 @@ az: no_results_table: No providers have been defined yet. plural: OpenID providers singular: OpenID provider + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/be.yml b/modules/openid_connect/config/locales/crowdin/be.yml index 0db96faa743..ad7f371646c 100644 --- a/modules/openid_connect/config/locales/crowdin/be.yml +++ b/modules/openid_connect/config/locales/crowdin/be.yml @@ -9,6 +9,7 @@ be: identifier: Identifier secret: Secret scope: Scope + limit_self_registration: Limit self registration openid_connect: menu_title: OpenID providers providers: @@ -17,3 +18,6 @@ be: no_results_table: No providers have been defined yet. plural: OpenID providers singular: OpenID provider + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/bg.yml b/modules/openid_connect/config/locales/crowdin/bg.yml index 20d3f67c929..3b408126315 100644 --- a/modules/openid_connect/config/locales/crowdin/bg.yml +++ b/modules/openid_connect/config/locales/crowdin/bg.yml @@ -9,6 +9,7 @@ bg: identifier: Идентификатор secret: Secret scope: Scope + limit_self_registration: Limit self registration openid_connect: menu_title: OpenID providers providers: @@ -17,3 +18,6 @@ bg: no_results_table: No providers have been defined yet. plural: OpenID providers singular: OpenID provider + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/ca.yml b/modules/openid_connect/config/locales/crowdin/ca.yml index 67be2c28926..f1f746fcba8 100644 --- a/modules/openid_connect/config/locales/crowdin/ca.yml +++ b/modules/openid_connect/config/locales/crowdin/ca.yml @@ -9,6 +9,7 @@ ca: identifier: Identificador secret: Secret scope: Abast + limit_self_registration: Limit self registration openid_connect: menu_title: Proveïdor d’OpenID providers: @@ -17,3 +18,6 @@ ca: no_results_table: Encara no s'han definit cap proveïdor. plural: Proveïdors d’OpenID singular: Proveïdor d’OpenID + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/ckb-IR.yml b/modules/openid_connect/config/locales/crowdin/ckb-IR.yml index 56c7025fbdd..f4536208c28 100644 --- a/modules/openid_connect/config/locales/crowdin/ckb-IR.yml +++ b/modules/openid_connect/config/locales/crowdin/ckb-IR.yml @@ -9,6 +9,7 @@ ckb-IR: identifier: Identifier secret: Secret scope: Scope + limit_self_registration: Limit self registration openid_connect: menu_title: OpenID providers providers: @@ -17,3 +18,6 @@ ckb-IR: no_results_table: No providers have been defined yet. plural: OpenID providers singular: OpenID provider + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/cs.yml b/modules/openid_connect/config/locales/crowdin/cs.yml index 78d528f5e23..96c4f08910b 100644 --- a/modules/openid_connect/config/locales/crowdin/cs.yml +++ b/modules/openid_connect/config/locales/crowdin/cs.yml @@ -9,6 +9,7 @@ cs: identifier: Identifikátor secret: Secret scope: Rozsah + limit_self_registration: Limit self registration openid_connect: menu_title: Poskytovatelé OpenID providers: @@ -17,3 +18,6 @@ cs: no_results_table: Zatím nebyli definováni žádní poskytovatelé. plural: Poskytovatelé OpenID singular: Poskytovatel OpenID + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/da.yml b/modules/openid_connect/config/locales/crowdin/da.yml index 9854c4ca8dd..8c63695a619 100644 --- a/modules/openid_connect/config/locales/crowdin/da.yml +++ b/modules/openid_connect/config/locales/crowdin/da.yml @@ -9,6 +9,7 @@ da: identifier: ID secret: Secret scope: Scope + limit_self_registration: Limit self registration openid_connect: menu_title: OpenID providers providers: @@ -17,3 +18,6 @@ da: no_results_table: No providers have been defined yet. plural: OpenID providers singular: OpenID provider + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/de.yml b/modules/openid_connect/config/locales/crowdin/de.yml index 21831b8d9a4..752b8fda033 100644 --- a/modules/openid_connect/config/locales/crowdin/de.yml +++ b/modules/openid_connect/config/locales/crowdin/de.yml @@ -9,6 +9,7 @@ de: identifier: Kennung secret: Secret scope: Geltungsbereich + limit_self_registration: Limit self registration openid_connect: menu_title: OpenID-Anbieter providers: @@ -17,3 +18,6 @@ de: no_results_table: Noch keine Anbieter definiert. plural: OpenID-Anbieter singular: OpenID-Anbieter + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/el.yml b/modules/openid_connect/config/locales/crowdin/el.yml index c3200c27eac..172c8eccf6f 100644 --- a/modules/openid_connect/config/locales/crowdin/el.yml +++ b/modules/openid_connect/config/locales/crowdin/el.yml @@ -9,6 +9,7 @@ el: identifier: Αναγνωριστικό secret: Μυστικό scope: Φυσικό Αντικείμενο + limit_self_registration: Limit self registration openid_connect: menu_title: Πάροχοι OpenID providers: @@ -17,3 +18,6 @@ el: no_results_table: Δεν έχουν οριστεί πάροχοι ακόμη. plural: Πάροχοι OpenID singular: Πάροχος OpenID + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/eo.yml b/modules/openid_connect/config/locales/crowdin/eo.yml index 1ac47b814f8..63a1d56c0e9 100644 --- a/modules/openid_connect/config/locales/crowdin/eo.yml +++ b/modules/openid_connect/config/locales/crowdin/eo.yml @@ -9,6 +9,7 @@ eo: identifier: Identigilo secret: Secret scope: Scope + limit_self_registration: Limit self registration openid_connect: menu_title: OpenID providers providers: @@ -17,3 +18,6 @@ eo: no_results_table: No providers have been defined yet. plural: OpenID providers singular: OpenID provider + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/es.yml b/modules/openid_connect/config/locales/crowdin/es.yml index 41c23e3e8ea..c6268cf55f0 100644 --- a/modules/openid_connect/config/locales/crowdin/es.yml +++ b/modules/openid_connect/config/locales/crowdin/es.yml @@ -9,6 +9,7 @@ es: identifier: Identificador secret: Secreto scope: Ámbito + limit_self_registration: Limit self registration openid_connect: menu_title: Proveedores de OpenID providers: @@ -17,3 +18,6 @@ es: no_results_table: Aún no se han definido proveedores. plural: Proveedores de OpenID singular: Proveedor de OpenID + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/et.yml b/modules/openid_connect/config/locales/crowdin/et.yml index aafac45047f..b39caff17a8 100644 --- a/modules/openid_connect/config/locales/crowdin/et.yml +++ b/modules/openid_connect/config/locales/crowdin/et.yml @@ -9,6 +9,7 @@ et: identifier: Identifikaator secret: Secret scope: Scope + limit_self_registration: Limit self registration openid_connect: menu_title: OpenID providers providers: @@ -17,3 +18,6 @@ et: no_results_table: No providers have been defined yet. plural: OpenID providers singular: OpenID provider + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/eu.yml b/modules/openid_connect/config/locales/crowdin/eu.yml index 37a5f1edaaf..bc7ebe9a947 100644 --- a/modules/openid_connect/config/locales/crowdin/eu.yml +++ b/modules/openid_connect/config/locales/crowdin/eu.yml @@ -9,6 +9,7 @@ eu: identifier: Identifier secret: Secret scope: Scope + limit_self_registration: Limit self registration openid_connect: menu_title: OpenID providers providers: @@ -17,3 +18,6 @@ eu: no_results_table: No providers have been defined yet. plural: OpenID providers singular: OpenID provider + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/fa.yml b/modules/openid_connect/config/locales/crowdin/fa.yml index 87902982371..fd81ac9026e 100644 --- a/modules/openid_connect/config/locales/crowdin/fa.yml +++ b/modules/openid_connect/config/locales/crowdin/fa.yml @@ -9,6 +9,7 @@ fa: identifier: شناسه secret: Secret scope: Scope + limit_self_registration: Limit self registration openid_connect: menu_title: OpenID providers providers: @@ -17,3 +18,6 @@ fa: no_results_table: No providers have been defined yet. plural: OpenID providers singular: OpenID provider + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/fi.yml b/modules/openid_connect/config/locales/crowdin/fi.yml index d636d5d9045..312e9b99afa 100644 --- a/modules/openid_connect/config/locales/crowdin/fi.yml +++ b/modules/openid_connect/config/locales/crowdin/fi.yml @@ -9,6 +9,7 @@ fi: identifier: Tunniste secret: Secret scope: Scope + limit_self_registration: Limit self registration openid_connect: menu_title: OpenID providers providers: @@ -17,3 +18,6 @@ fi: no_results_table: No providers have been defined yet. plural: OpenID providers singular: OpenID provider + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/fil.yml b/modules/openid_connect/config/locales/crowdin/fil.yml index 79f7a78df28..834aba9d2d1 100644 --- a/modules/openid_connect/config/locales/crowdin/fil.yml +++ b/modules/openid_connect/config/locales/crowdin/fil.yml @@ -9,6 +9,7 @@ fil: identifier: Ang pagkakakilanlan secret: Secret scope: Scope + limit_self_registration: Limit self registration openid_connect: menu_title: OpenID providers providers: @@ -17,3 +18,6 @@ fil: no_results_table: No providers have been defined yet. plural: OpenID providers singular: OpenID provider + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/fr.yml b/modules/openid_connect/config/locales/crowdin/fr.yml index 08c6127e440..458e2d7838e 100644 --- a/modules/openid_connect/config/locales/crowdin/fr.yml +++ b/modules/openid_connect/config/locales/crowdin/fr.yml @@ -9,6 +9,7 @@ fr: identifier: Identifiant secret: Secret scope: Portée + limit_self_registration: Limit self registration openid_connect: menu_title: Fournisseurs OpenID providers: @@ -17,3 +18,6 @@ fr: no_results_table: Aucun fournisseur n'a encore été défini. plural: Fournisseurs OpenID singular: Fournisseur OpenID + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/he.yml b/modules/openid_connect/config/locales/crowdin/he.yml index eac162ca2e1..b6df9584639 100644 --- a/modules/openid_connect/config/locales/crowdin/he.yml +++ b/modules/openid_connect/config/locales/crowdin/he.yml @@ -9,6 +9,7 @@ he: identifier: מזהה secret: Secret scope: Scope + limit_self_registration: Limit self registration openid_connect: menu_title: OpenID providers providers: @@ -17,3 +18,6 @@ he: no_results_table: No providers have been defined yet. plural: OpenID providers singular: OpenID provider + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/hi.yml b/modules/openid_connect/config/locales/crowdin/hi.yml index 7779839bcfd..8755cfd4983 100644 --- a/modules/openid_connect/config/locales/crowdin/hi.yml +++ b/modules/openid_connect/config/locales/crowdin/hi.yml @@ -9,6 +9,7 @@ hi: identifier: पहचानकर्ता secret: Secret scope: Scope + limit_self_registration: Limit self registration openid_connect: menu_title: OpenID providers providers: @@ -17,3 +18,6 @@ hi: no_results_table: No providers have been defined yet. plural: OpenID providers singular: OpenID provider + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/hr.yml b/modules/openid_connect/config/locales/crowdin/hr.yml index f716112476f..fd12ca665e2 100644 --- a/modules/openid_connect/config/locales/crowdin/hr.yml +++ b/modules/openid_connect/config/locales/crowdin/hr.yml @@ -9,6 +9,7 @@ hr: identifier: Identifikator secret: Secret scope: Scope + limit_self_registration: Limit self registration openid_connect: menu_title: OpenID providers providers: @@ -17,3 +18,6 @@ hr: no_results_table: No providers have been defined yet. plural: OpenID providers singular: OpenID provider + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/hu.yml b/modules/openid_connect/config/locales/crowdin/hu.yml index b497946164d..0a2d9acfaed 100644 --- a/modules/openid_connect/config/locales/crowdin/hu.yml +++ b/modules/openid_connect/config/locales/crowdin/hu.yml @@ -9,6 +9,7 @@ hu: identifier: Azonosító secret: Titok scope: Hatókör + limit_self_registration: Limit self registration openid_connect: menu_title: OpenID szolgáltató providers: @@ -17,3 +18,6 @@ hu: no_results_table: Nincs szolgáltató definiálva plural: OpenID szolgáltatók singular: OpenID szolgáltató + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/id.yml b/modules/openid_connect/config/locales/crowdin/id.yml index bb1d0416d54..370b31a1cec 100644 --- a/modules/openid_connect/config/locales/crowdin/id.yml +++ b/modules/openid_connect/config/locales/crowdin/id.yml @@ -9,6 +9,7 @@ id: identifier: Pengenal secret: Secret scope: Scope + limit_self_registration: Limit self registration openid_connect: menu_title: OpenID providers providers: @@ -17,3 +18,6 @@ id: no_results_table: Belum ada penyedia yang ditentukan. plural: penyedia OpenID singular: penyedia OpenID + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/it.yml b/modules/openid_connect/config/locales/crowdin/it.yml index 95452b04865..ded3645e39f 100644 --- a/modules/openid_connect/config/locales/crowdin/it.yml +++ b/modules/openid_connect/config/locales/crowdin/it.yml @@ -9,6 +9,7 @@ it: identifier: Identificativo secret: Parola chiave scope: Ambito + limit_self_registration: Limit self registration openid_connect: menu_title: OpenID provider providers: @@ -17,3 +18,6 @@ it: no_results_table: Non è stato definito alcun provider. plural: OpenID provider singular: Provider OpenID + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/ja.yml b/modules/openid_connect/config/locales/crowdin/ja.yml index 4a6d5539361..1d34dd6662e 100644 --- a/modules/openid_connect/config/locales/crowdin/ja.yml +++ b/modules/openid_connect/config/locales/crowdin/ja.yml @@ -9,6 +9,7 @@ ja: identifier: 識別子 secret: シークレット scope: スコープ + limit_self_registration: Limit self registration openid_connect: menu_title: OpenID プロバイダー providers: @@ -17,3 +18,6 @@ ja: no_results_table: プロバイダーが定義されていません。 plural: OpenID プロバイダー singular: OpenID プロバイダー + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/ko.yml b/modules/openid_connect/config/locales/crowdin/ko.yml index 00754dac526..8cf979cf4e0 100644 --- a/modules/openid_connect/config/locales/crowdin/ko.yml +++ b/modules/openid_connect/config/locales/crowdin/ko.yml @@ -9,6 +9,7 @@ ko: identifier: 식별자 secret: 비밀번호 scope: 범위 + limit_self_registration: Limit self registration openid_connect: menu_title: OpenID 공급자 providers: @@ -17,3 +18,6 @@ ko: no_results_table: 아직 정의된 공급자가 없습니다. plural: OpenID 공급자 singular: OpenID 공급자 + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/lt.yml b/modules/openid_connect/config/locales/crowdin/lt.yml index 4d76107a071..0d8333b6be7 100644 --- a/modules/openid_connect/config/locales/crowdin/lt.yml +++ b/modules/openid_connect/config/locales/crowdin/lt.yml @@ -9,6 +9,7 @@ lt: identifier: Identifikatorius secret: Paslaptis scope: Apimtis + limit_self_registration: Limit self registration openid_connect: menu_title: OpenID tiekėjai providers: @@ -17,3 +18,6 @@ lt: no_results_table: Dar neapibrėžtas joks tiekėjas. plural: OpenID tiekėjai singular: OpenID tiekėjas + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/lv.yml b/modules/openid_connect/config/locales/crowdin/lv.yml index 47d10fa6647..f6dc88e2c68 100644 --- a/modules/openid_connect/config/locales/crowdin/lv.yml +++ b/modules/openid_connect/config/locales/crowdin/lv.yml @@ -9,6 +9,7 @@ lv: identifier: Identifikators secret: Secret scope: Scope + limit_self_registration: Limit self registration openid_connect: menu_title: OpenID providers providers: @@ -17,3 +18,6 @@ lv: no_results_table: No providers have been defined yet. plural: OpenID providers singular: OpenID provider + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/mn.yml b/modules/openid_connect/config/locales/crowdin/mn.yml index 0149ce8fa11..b6ca33e63f0 100644 --- a/modules/openid_connect/config/locales/crowdin/mn.yml +++ b/modules/openid_connect/config/locales/crowdin/mn.yml @@ -9,6 +9,7 @@ mn: identifier: Identifier secret: Secret scope: Scope + limit_self_registration: Limit self registration openid_connect: menu_title: OpenID providers providers: @@ -17,3 +18,6 @@ mn: no_results_table: No providers have been defined yet. plural: OpenID providers singular: OpenID provider + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/ne.yml b/modules/openid_connect/config/locales/crowdin/ne.yml index 11d3e8e859f..2832ab11334 100644 --- a/modules/openid_connect/config/locales/crowdin/ne.yml +++ b/modules/openid_connect/config/locales/crowdin/ne.yml @@ -9,6 +9,7 @@ ne: identifier: परिचायक secret: Secret scope: Scope + limit_self_registration: Limit self registration openid_connect: menu_title: OpenID providers providers: @@ -17,3 +18,6 @@ ne: no_results_table: No providers have been defined yet. plural: OpenID providers singular: OpenID provider + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/nl.yml b/modules/openid_connect/config/locales/crowdin/nl.yml index a7b3788c4b6..a3fa96d1b05 100644 --- a/modules/openid_connect/config/locales/crowdin/nl.yml +++ b/modules/openid_connect/config/locales/crowdin/nl.yml @@ -9,6 +9,7 @@ nl: identifier: Identifier secret: Geheim scope: Scope + limit_self_registration: Limit self registration openid_connect: menu_title: OpenID aanbieders providers: @@ -17,3 +18,6 @@ nl: no_results_table: Er zijn nog geen aanbieders gedefinieerd. plural: OpenID aanbieders singular: OpenID aanbieders + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/no.yml b/modules/openid_connect/config/locales/crowdin/no.yml index 6225c0befef..517ce7bb231 100644 --- a/modules/openid_connect/config/locales/crowdin/no.yml +++ b/modules/openid_connect/config/locales/crowdin/no.yml @@ -9,6 +9,7 @@ identifier: Identifikator secret: Hemmelig scope: Omfang + limit_self_registration: Limit self registration openid_connect: menu_title: OpenID leverandører providers: @@ -17,3 +18,6 @@ no_results_table: Ingen leverandører har blitt definert ennå. plural: OpenID leverandører singular: OpenID-leverandør + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/pl.yml b/modules/openid_connect/config/locales/crowdin/pl.yml index 09d2aad9ded..5af79e6d8ee 100644 --- a/modules/openid_connect/config/locales/crowdin/pl.yml +++ b/modules/openid_connect/config/locales/crowdin/pl.yml @@ -9,6 +9,7 @@ pl: identifier: Identyfikator secret: Tajny klucz scope: Zakres + limit_self_registration: Limit self registration openid_connect: menu_title: Dostawcy OpenID providers: @@ -17,3 +18,6 @@ pl: no_results_table: Jeszcze nie określono żadnych dostawców. plural: Dostawcy OpenID singular: Dostawca OpenID + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/pt.yml b/modules/openid_connect/config/locales/crowdin/pt.yml index c19a2a7dbd4..dd78cd08227 100644 --- a/modules/openid_connect/config/locales/crowdin/pt.yml +++ b/modules/openid_connect/config/locales/crowdin/pt.yml @@ -9,6 +9,7 @@ pt: identifier: Identificador secret: Chave scope: Escopo + limit_self_registration: Limit self registration openid_connect: menu_title: Provedores OpenID providers: @@ -17,3 +18,6 @@ pt: no_results_table: Nenhum provedor foi definido. plural: Provedores OpenID singular: Provedor OpenID + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/ro.yml b/modules/openid_connect/config/locales/crowdin/ro.yml index 20d4490d831..6169ba3b811 100644 --- a/modules/openid_connect/config/locales/crowdin/ro.yml +++ b/modules/openid_connect/config/locales/crowdin/ro.yml @@ -9,6 +9,7 @@ ro: identifier: Identificator secret: Secret scope: Scop + limit_self_registration: Limit self registration openid_connect: menu_title: Furnizori OpenID providers: @@ -17,3 +18,6 @@ ro: no_results_table: Încă nu au fost definiți furnizori. plural: Furnizori OpenID singular: Furnizor de autentificare + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/ru.yml b/modules/openid_connect/config/locales/crowdin/ru.yml index 814010106cf..b1cdbfb9185 100644 --- a/modules/openid_connect/config/locales/crowdin/ru.yml +++ b/modules/openid_connect/config/locales/crowdin/ru.yml @@ -9,6 +9,7 @@ ru: identifier: Идентификатор secret: Секретный ключ scope: Область + limit_self_registration: Limit self registration openid_connect: menu_title: Провайдеры OpenID providers: @@ -17,3 +18,6 @@ ru: no_results_table: Ни один провайдер еще не был определен. plural: Провайдеры OpenID singular: Провайдер OpenID + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/rw.yml b/modules/openid_connect/config/locales/crowdin/rw.yml index a4846ff6b5b..85139778768 100644 --- a/modules/openid_connect/config/locales/crowdin/rw.yml +++ b/modules/openid_connect/config/locales/crowdin/rw.yml @@ -9,6 +9,7 @@ rw: identifier: Identifier secret: Secret scope: Scope + limit_self_registration: Limit self registration openid_connect: menu_title: OpenID providers providers: @@ -17,3 +18,6 @@ rw: no_results_table: No providers have been defined yet. plural: OpenID providers singular: OpenID provider + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/si.yml b/modules/openid_connect/config/locales/crowdin/si.yml index adb71791420..c2ded41f040 100644 --- a/modules/openid_connect/config/locales/crowdin/si.yml +++ b/modules/openid_connect/config/locales/crowdin/si.yml @@ -9,6 +9,7 @@ si: identifier: හඳුනාගැනීමේ secret: Secret scope: Scope + limit_self_registration: Limit self registration openid_connect: menu_title: OpenID providers providers: @@ -17,3 +18,6 @@ si: no_results_table: No providers have been defined yet. plural: OpenID providers singular: OpenID provider + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/sk.yml b/modules/openid_connect/config/locales/crowdin/sk.yml index 678bf72f978..b4c015b4cdd 100644 --- a/modules/openid_connect/config/locales/crowdin/sk.yml +++ b/modules/openid_connect/config/locales/crowdin/sk.yml @@ -9,6 +9,7 @@ sk: identifier: Identifikátor secret: Secret scope: Scope + limit_self_registration: Limit self registration openid_connect: menu_title: OpenID providers providers: @@ -17,3 +18,6 @@ sk: no_results_table: No providers have been defined yet. plural: OpenID providers singular: OpenID provider + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/sl.yml b/modules/openid_connect/config/locales/crowdin/sl.yml index 4eced25a0f5..67cab62b334 100644 --- a/modules/openid_connect/config/locales/crowdin/sl.yml +++ b/modules/openid_connect/config/locales/crowdin/sl.yml @@ -9,6 +9,7 @@ sl: identifier: Identifikator secret: Skrivnost scope: Področje + limit_self_registration: Limit self registration openid_connect: menu_title: OpenID ponudniki providers: @@ -17,3 +18,6 @@ sl: no_results_table: Zaenkrat še ni opredeljenih ponudnikov. plural: OpenID ponudniki singular: OpenID ponudnik + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/sr.yml b/modules/openid_connect/config/locales/crowdin/sr.yml index 399113a6cc3..2c42541ea1b 100644 --- a/modules/openid_connect/config/locales/crowdin/sr.yml +++ b/modules/openid_connect/config/locales/crowdin/sr.yml @@ -9,6 +9,7 @@ sr: identifier: Identifier secret: Secret scope: Scope + limit_self_registration: Limit self registration openid_connect: menu_title: OpenID providers providers: @@ -17,3 +18,6 @@ sr: no_results_table: No providers have been defined yet. plural: OpenID providers singular: OpenID provider + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/sv.yml b/modules/openid_connect/config/locales/crowdin/sv.yml index 2e2965784e2..ae42c08ccb5 100644 --- a/modules/openid_connect/config/locales/crowdin/sv.yml +++ b/modules/openid_connect/config/locales/crowdin/sv.yml @@ -9,6 +9,7 @@ sv: identifier: Identifierare secret: Hemlighet scope: Omfattning + limit_self_registration: Limit self registration openid_connect: menu_title: OpenID leverantörer providers: @@ -17,3 +18,6 @@ sv: no_results_table: Inga leverantörer har definierats ännu. plural: OpenID leverantörer singular: OpenID leverantör + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/th.yml b/modules/openid_connect/config/locales/crowdin/th.yml index e6c4b8e07e4..1e4098d65fe 100644 --- a/modules/openid_connect/config/locales/crowdin/th.yml +++ b/modules/openid_connect/config/locales/crowdin/th.yml @@ -9,6 +9,7 @@ th: identifier: รหัส secret: Secret scope: Scope + limit_self_registration: Limit self registration openid_connect: menu_title: OpenID providers providers: @@ -17,3 +18,6 @@ th: no_results_table: No providers have been defined yet. plural: OpenID providers singular: OpenID provider + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/tr.yml b/modules/openid_connect/config/locales/crowdin/tr.yml index ef24cd35d80..c1410ee3266 100644 --- a/modules/openid_connect/config/locales/crowdin/tr.yml +++ b/modules/openid_connect/config/locales/crowdin/tr.yml @@ -9,6 +9,7 @@ tr: identifier: Tanımlayıcı secret: Gizli scope: kapsam + limit_self_registration: Limit self registration openid_connect: menu_title: OpenID sağlayıcıları providers: @@ -17,3 +18,6 @@ tr: no_results_table: Henüz bir sağlayıcı tanımlanmadı. plural: OpenID sağlayıcıları singular: OpenID sağlayıcı + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/uk.yml b/modules/openid_connect/config/locales/crowdin/uk.yml index e30557d43e6..f768bbd5719 100644 --- a/modules/openid_connect/config/locales/crowdin/uk.yml +++ b/modules/openid_connect/config/locales/crowdin/uk.yml @@ -9,6 +9,7 @@ uk: identifier: Ідентифікатор secret: Таємний код scope: Область використання + limit_self_registration: Limit self registration openid_connect: menu_title: Постачальники OpenID providers: @@ -17,3 +18,6 @@ uk: no_results_table: Ще не додано жодного постачальника. plural: Постачальники OpenID singular: OpenID постачальник + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/vi.yml b/modules/openid_connect/config/locales/crowdin/vi.yml index d6e388336a2..419d88ca720 100644 --- a/modules/openid_connect/config/locales/crowdin/vi.yml +++ b/modules/openid_connect/config/locales/crowdin/vi.yml @@ -9,6 +9,7 @@ vi: identifier: Định danh secret: Secret scope: Scope + limit_self_registration: Limit self registration openid_connect: menu_title: OpenID providers providers: @@ -17,3 +18,6 @@ vi: no_results_table: No providers have been defined yet. plural: OpenID providers singular: OpenID provider + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/crowdin/zh-TW.yml b/modules/openid_connect/config/locales/crowdin/zh-TW.yml index fe15c031e86..7ac602aa05a 100644 --- a/modules/openid_connect/config/locales/crowdin/zh-TW.yml +++ b/modules/openid_connect/config/locales/crowdin/zh-TW.yml @@ -9,6 +9,7 @@ zh-TW: identifier: 識別碼 secret: 金鑰 scope: 範圍 + limit_self_registration: Limit self registration openid_connect: menu_title: OpenID 提供者 providers: @@ -17,3 +18,6 @@ zh-TW: no_results_table: 尚未定義任何提供者。 plural: OpenID 提供者 singular: OpenID 提供者 + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/config/locales/de.yml b/modules/openid_connect/config/locales/de.yml index f6256321b22..b7a30641707 100644 --- a/modules/openid_connect/config/locales/de.yml +++ b/modules/openid_connect/config/locales/de.yml @@ -10,6 +10,7 @@ de: identifier: Identifier secret: Secret scope: Scope + limit_self_registration: Selbstregistrierung einschränken openid_connect: menu_title: OpenID-Provider providers: @@ -20,3 +21,7 @@ de: singular: OpenID-Provider upsale: description: Use existing OpenID credentials with OpenProject for easier access and interoperability with a range of other providers. + setting_instructions: + limit_self_registration: > + Wenn diese Option aktiv ist, können sich neue Nutzer mit diesem OpenID-Provider nur registrieren, + wenn die Selbstregistrierungs-Einstellung es erlaubt. diff --git a/modules/openid_connect/config/locales/en.yml b/modules/openid_connect/config/locales/en.yml index c353ed49079..c056fee2d26 100644 --- a/modules/openid_connect/config/locales/en.yml +++ b/modules/openid_connect/config/locales/en.yml @@ -10,6 +10,7 @@ en: identifier: Identifier secret: Secret scope: Scope + limit_self_registration: Limit self registration openid_connect: menu_title: OpenID providers providers: @@ -18,3 +19,6 @@ en: no_results_table: No providers have been defined yet. plural: OpenID providers singular: OpenID provider + setting_instructions: + limit_self_registration: > + If enabled users can only register using this provider if the self registration setting allows for it. diff --git a/modules/openid_connect/spec/controllers/providers_controller_spec.rb b/modules/openid_connect/spec/controllers/providers_controller_spec.rb index 35c1e675bf6..26cd3e12e14 100644 --- a/modules/openid_connect/spec/controllers/providers_controller_spec.rb +++ b/modules/openid_connect/spec/controllers/providers_controller_spec.rb @@ -105,11 +105,40 @@ describe OpenIDConnect::ProvidersController do end describe '#create' do - it 'is successful if valid params' do - post :create, params: { openid_connect_provider: valid_params } - expect(flash[:notice]).to eq(I18n.t(:notice_successful_create)) - expect(Setting.plugin_openproject_openid_connect["providers"]).to have_key("azure") - expect(response).to be_redirect + context 'with valid params' do + let(:params) { { openid_connect_provider: valid_params } } + + before do + post :create, params: + end + + it 'is successful' do + expect(flash[:notice]).to eq(I18n.t(:notice_successful_create)) + expect(Setting.plugin_openproject_openid_connect["providers"]).to have_key("azure") + expect(response).to be_redirect + end + + context 'with limit_self_registration checked' do + let(:params) do + { openid_connect_provider: valid_params.merge(limit_self_registration: 1) } + end + + it 'sets the setting' do + expect(OpenProject::Plugins::AuthPlugin) + .to be_limit_self_registration provider: valid_params[:name] + end + end + + context 'with limit_self_registration unchecked' do + let(:params) do + { openid_connect_provider: valid_params.merge(limit_self_registration: 0) } + end + + it 'does not set the setting' do + expect(OpenProject::Plugins::AuthPlugin) + .not_to be_limit_self_registration provider: valid_params[:name] + end + end end it 'renders an error if invalid params' do @@ -130,6 +159,39 @@ describe OpenIDConnect::ProvidersController do expect(assigns[:provider]).to be_present expect(response).to render_template 'edit' end + + context( + 'with limit_self_registration set', + with_settings: { + plugin_openproject_openid_connect: { + "providers" => { + "azure" => { + "identifier" => "IDENTIFIER", + "secret" => "SECRET", + "limit_self_registration" => true + } + } + } + } + ) do + before do + get :edit, params: { id: 'azure' } + end + + it 'shows limit_self_registration as checked' do + expect(assigns[:provider]).to be_limit_self_registration + end + end + + context 'with limit_self_registration not set' do + before do + get :edit, params: { id: 'azure' } + end + + it 'shows limit_self_registration as unchecked' do + expect(assigns[:provider]).not_to be_limit_self_registration + end + end end context 'when not found' do @@ -142,11 +204,13 @@ describe OpenIDConnect::ProvidersController do end describe '#update' do - context 'when found', with_settings: { - plugin_openproject_openid_connect: { - "providers" => { "azure" => { "identifier" => "IDENTIFIER", "secret" => "SECRET" } } - } - } do + context 'when found' do + before do + Setting.plugin_openproject_openid_connect = { + "providers" => { "azure" => { "identifier" => "IDENTIFIER", "secret" => "SECRET" } } + } + end + it 'successfully updates the provider configuration' do put :update, params: { id: "azure", openid_connect_provider: valid_params.merge(secret: "NEWSECRET") } expect(response).to be_redirect @@ -158,11 +222,13 @@ describe OpenIDConnect::ProvidersController do end describe '#destroy' do - context 'when found', with_settings: { - plugin_openproject_openid_connect: { - "providers" => { "azure" => { "identifier" => "IDENTIFIER", "secret" => "SECRET" } } - } - } do + context 'when found' do + before do + Setting.plugin_openproject_openid_connect = { + "providers" => { "azure" => { "identifier" => "IDENTIFIER", "secret" => "SECRET" } } + } + end + it 'removes the provider' do delete :destroy, params: { id: "azure" } expect(response).to be_redirect diff --git a/modules/openid_connect/spec/models/provider_spec.rb b/modules/openid_connect/spec/models/provider_spec.rb new file mode 100644 index 00000000000..7118fe1867c --- /dev/null +++ b/modules/openid_connect/spec/models/provider_spec.rb @@ -0,0 +1,96 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-2023 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +require 'spec_helper' + +describe OpenIDConnect::Provider do + let(:provider) do + described_class.initialize_with name: "azure", identifier: "id", secret: "secret" + end + + def auth_plugin + OpenProject::Plugins::AuthPlugin + end + + describe 'limit_self_registration' do + before do + # required so that the auth plugin sees any providers (ee feature) + allow(EnterpriseToken).to receive(:show_banners?).and_return false + end + + context 'with no limited providers' do + it "shows the provider as unlimited" do + expect(auth_plugin).not_to be_limit_self_registration provider: provider.name + end + + context 'when set to true' do + before do + provider.limit_self_registration = true + end + + it "saving the provider makes it limited" do + provider.save + + expect(auth_plugin).to be_limit_self_registration provider: provider.name + end + end + + context 'when set to false' do + before do + provider.limit_self_registration = false + end + + it "saving the provider does nothing" do + provider.save + + expect(auth_plugin).not_to be_limit_self_registration provider: provider.name + end + end + end + + context( + 'with a limited provider', + with_settings: { + plugin_openproject_openid_connect: { + "providers" => { + "azure" => { + "name" => "azure", + "identifier" => "id", + "secret" => "secret", + "limit_self_registration" => true + } + } + } + } + ) do + it "shows the provider as limited" do + expect(auth_plugin).to be_limit_self_registration provider: provider.name + end + end + end +end diff --git a/spec/contracts/projects/archive_contract_spec.rb b/spec/contracts/projects/archive_contract_spec.rb index cf61ad67c33..b91f035978a 100644 --- a/spec/contracts/projects/archive_contract_spec.rb +++ b/spec/contracts/projects/archive_contract_spec.rb @@ -72,6 +72,24 @@ describe Projects::ArchiveContract do include_examples 'contract is valid' end + + context 'when some of subprojects are archived but not all' do + before do + subproject1.update_column(:active, false) + create(:member, user: current_user, project: subproject2, roles: [archivist_role]) + end + + include_examples 'contract is valid' + end + + context 'when all of subprojects are archived' do + before do + subproject1.update_column(:active, false) + subproject2.update_column(:active, false) + end + + include_examples 'contract is valid' + end end include_examples 'contract is valid for active admins and invalid for regular users' diff --git a/spec/features/admin/working_days_spec.rb b/spec/features/admin/working_days_spec.rb index 03b1a0c0de6..035de2e154c 100644 --- a/spec/features/admin/working_days_spec.rb +++ b/spec/features/admin/working_days_spec.rb @@ -189,6 +189,10 @@ describe 'Working Days', js: true do it 'can add non-working days' do click_on 'Non-working day' + # Check if a date is correctly highlighted after selecting it in different time zones + datepicker.select_day 5 + datepicker.expect_day '5' + # It can cancel and reopen page.within('[data-qa-selector="op-datepicker-modal"]') do click_on 'Cancel' diff --git a/spec/features/work_packages/copy_spec.rb b/spec/features/work_packages/copy_spec.rb index f63b00f6d66..30df41460ee 100644 --- a/spec/features/work_packages/copy_spec.rb +++ b/spec/features/work_packages/copy_spec.rb @@ -128,7 +128,7 @@ RSpec.describe 'Work package copy', js: true, selenium: true do work_package_page.visit_tab! :relations expect_angular_frontend_initialized expect(page).to have_selector('.relation-group--header', text: 'RELATED TO', wait: 20) - expect(page).to have_selector('.wp-relations--subject-field', text: original_work_package.subject) + expect(page).to have_selector("[data-qa-selector='op-relation--row-subject']", text: original_work_package.subject) end describe 'when source work package has an attachment' do @@ -181,6 +181,6 @@ RSpec.describe 'Work package copy', js: true, selenium: true do work_package_page.visit_tab!('relations') expect_angular_frontend_initialized expect(page).to have_selector('.relation-group--header', text: 'RELATED TO', wait: 20) - expect(page).to have_selector('.wp-relations--subject-field', text: original_work_package.subject) + expect(page).to have_selector("[data-qa-selector='op-relation--row-subject']", text: original_work_package.subject) end end diff --git a/spec/features/work_packages/details/relations/hierarchy_spec.rb b/spec/features/work_packages/details/relations/hierarchy_spec.rb index f071d4dbdaf..a8301ef0532 100644 --- a/spec/features/work_packages/details/relations/hierarchy_spec.rb +++ b/spec/features/work_packages/details/relations/hierarchy_spec.rb @@ -69,14 +69,14 @@ shared_examples 'work package relations tab', js: true, selenium: true do ## # Add child #1 - relations.openChildrenAutocompleter + relations.open_children_autocompleter relations.add_existing_child(child) relations.expect_child(child) ## # Add child #2 - relations.openChildrenAutocompleter + relations.open_children_autocompleter relations.add_existing_child(child2) relations.expect_child(child2) @@ -240,7 +240,7 @@ shared_examples 'work package relations tab', js: true, selenium: true do ## # Add child - relations.openChildrenAutocompleter + relations.open_children_autocompleter relations.add_existing_child(child) wp_page.expect_and_dismiss_toaster(message: 'Successful update.') diff --git a/spec/features/work_packages/details/relations/relations_spec.rb b/spec/features/work_packages/details/relations/relations_spec.rb index 588a98da1d0..3ab48316224 100644 --- a/spec/features/work_packages/details/relations/relations_spec.rb +++ b/spec/features/work_packages/details/relations/relations_spec.rb @@ -249,7 +249,7 @@ describe 'Work package relations tab', js: true, selenium: true do # Wait for the relations table to be present sleep 2 - expect(page).to have_selector('.wp-relations--subject-field') + expect(page).to have_selector("[data-qa-selector='op-relation--row-subject']") scroll_to_element find('.detail-panel--relations') diff --git a/spec/migrations/restore_defaults_on_empty_settings_spec.rb b/spec/migrations/restore_defaults_on_empty_settings_spec.rb new file mode 100644 index 00000000000..fd57674e1e9 --- /dev/null +++ b/spec/migrations/restore_defaults_on_empty_settings_spec.rb @@ -0,0 +1,85 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-2023 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +require 'spec_helper' +require Rails.root.join("db/migrate/20220428071221_restore_defaults_on_empty_settings.rb") + +describe RestoreDefaultsOnEmptySettings, type: :model do + # Silencing migration logs, since we are not interested in that during testing + subject { ActiveRecord::Migration.suppress_messages { described_class.new.up } } + + shared_examples_for "a successful migration of an empty setting" do + let(:setting_name) { raise "define me!" } + let(:old_value) { nil } + let(:expected_value) { raise "define me!" } + + before do + Setting.create name: setting_name, value: "" + end + + it 'migrates the value to the expected value' do + expect { subject } + .to change { Setting.find_by(name: setting_name).value } + .from(old_value) + .to(expected_value) + end + + it 'does not raise a type error' do + expect { subject }.not_to raise_error(TypeError) + end + end + + context "with an empty setting which must be an array" do + it_behaves_like "a successful migration of an empty setting" do + let(:setting_name) { "apiv3_cors_origins" } + let(:expected_value) { [] } + end + end + + context "with an empty setting which must be a hash" do + it_behaves_like "a successful migration of an empty setting" do + let(:setting_name) { "ldap_tls_options" } + let(:expected_value) { {} } + end + end + + context "with an empty setting which must be a string" do + it_behaves_like "a successful migration of an empty setting" do + let(:setting_name) { "default_language" } + let(:old_value) { "" } + let(:expected_value) { "en" } + end + end + + context "with an empty setting which must be a boolean" do + it_behaves_like "a successful migration of an empty setting" do + let(:setting_name) { "smtp_enable_starttls_auto" } + let(:expected_value) { false } + end + end +end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index a36e293fb71..7481767c398 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -358,6 +358,27 @@ describe Project do end end + describe '#active_subprojects' do + subject { root_project.active_subprojects } + + shared_let(:root_project) { create(:project) } + shared_let(:parent_project) { create(:project, parent: root_project) } + shared_let(:child_project1) { create(:project, parent: parent_project) } + + context 'with an archived subproject' do + before do + child_project1.active = false + child_project1.save + end + + it { is_expected.to eq [parent_project] } + end + + context 'with all active subprojects' do + it { is_expected.to eq [parent_project, child_project1] } + end + end + describe '#rolled_up_types' do let!(:parent) do create(:project, types: [parent_type]).tap do |p| diff --git a/spec/services/projects/archive_service_spec.rb b/spec/services/projects/archive_service_spec.rb new file mode 100644 index 00000000000..73c44ce8bab --- /dev/null +++ b/spec/services/projects/archive_service_spec.rb @@ -0,0 +1,127 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-2023 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +require 'spec_helper' + +describe Projects::ArchiveService do + let(:project) { create(:project) } + let(:subproject1) { create(:project) } + let(:subproject2) { create(:project) } + let(:subproject3) { create(:project) } + let(:user) { create(:admin) } + let(:instance) { described_class.new(user:, model: project) } + + context 'with project without any subprojects' do + it 'archives the project' do + expect(project.reload).not_to be_archived + + expect(instance.call).to be_truthy + expect(project.reload).to be_archived + end + end + + context 'with project having subprojects' do + before do + project.update(children: [subproject1, subproject2, subproject3]) + project.reload + end + + shared_examples 'when archiving a project' do + it 'archives the project' do + # Baseline verification. + expect(project.reload).not_to be_archived + + # Action. + expect(instance.call).to be_truthy + + # Endline verification. + expect(project.reload).to be_archived + end + + it 'archives all the subprojects' do + # Baseline verification. + expect(subproject1.reload).not_to be_archived + expect(subproject2.reload).not_to be_archived + expect(subproject3.reload).not_to be_archived + + # Action. + expect(instance.call).to be_truthy + + # Endline verification. + expect(subproject1.reload).to be_archived + expect(subproject2.reload).to be_archived + expect(subproject3.reload).to be_archived + end + end + + include_examples 'when archiving a project' + + context 'with deep nesting' do + before do + project.update(children: [subproject1]) + subproject1.update(children: [subproject2]) + subproject2.update(children: [subproject3]) + project.reload + subproject1.reload + end + + include_examples 'when archiving a project' + end + end + + context 'with project having an archived subproject' do + let(:subproject1) { create(:project, active: false) } + + before do + project.update(children: [subproject1, subproject2, subproject3]) + project.reload + end + + context 'while archiving the project' do + it 'does not change timestamp of the already archived subproject' do + expect(subproject1.reload).to be_archived + before_timestamp = subproject1.updated_at + + expect(instance.call).to be_truthy + + after_timestamp = subproject1.reload.updated_at + expect(before_timestamp).to eq(after_timestamp) + end + + it 'changes timestamp of the active subproject' do + expect(subproject2.reload).not_to be_archived + before_timestamp = subproject2.updated_at + + expect(instance.call).to be_truthy + + after_timestamp = subproject2.reload.updated_at + expect(before_timestamp).not_to eq(after_timestamp) + end + end + end +end diff --git a/spec/services/users/register_user_service_spec.rb b/spec/services/users/register_user_service_spec.rb index 3f55dcef370..a5b0cbdc592 100644 --- a/spec/services/users/register_user_service_spec.rb +++ b/spec/services/users/register_user_service_spec.rb @@ -76,6 +76,48 @@ describe Users::RegisterUserService do end end + describe '#register_omniauth_user' do + let(:user) { User.new(status: Principal.statuses[:registered], identity_url: 'azure:1234') } + let(:instance) { described_class.new(user) } + let(:call) { instance.call } + + before do + allow(user).to receive(:activate) + allow(user).to receive(:save).and_return true + + # required so that the azure provider is visible (ee feature) + allow(EnterpriseToken).to receive(:show_banners?).and_return false + + with_all_registration_options do |_type| + call + end + end + + it 'tries to activate that user regardless of settings' do + expect(call).to be_success + expect(call.result).to eq user + expect(call.message).to eq I18n.t(:notice_account_registered_and_logged_in) + end + + context( + 'with limit_self_registration enabled and self_registration disabled', + with_settings: { + self_registration: 0, + plugin_openproject_openid_connect: { + providers: { + azure: { identifier: "foo", secret: "bar", limit_self_registration: true } + } + } + } + ) do + it 'fails to activate due to disabled self registration' do + expect(call).not_to be_success + expect(call.result).to eq user + expect(call.message).to eq I18n.t('account.error_self_registration_disabled') + end + end + end + describe '#ensure_registration_allowed!' do it 'returns an error for disabled' do allow(Setting).to receive(:self_registration).and_return(0) diff --git a/spec/support/components/work_packages/relations.rb b/spec/support/components/work_packages/relations.rb index 23359c83cf2..f9881b4df9b 100644 --- a/spec/support/components/work_packages/relations.rb +++ b/spec/support/components/work_packages/relations.rb @@ -48,7 +48,7 @@ module Components def click_relation(relatable) SeleniumHubWaiter.wait - page.find(".relation-row-#{relatable.id} .wp-relations--subject-field").click + page.find(".relation-row-#{relatable.id} [data-qa-selector='op-relation--row-id']").click end def edit_relation_type(relatable, to_type:) @@ -63,7 +63,7 @@ module Components def hover_action(relatable, action) retry_block do # Focus type edit to expose buttons - span = page.find(".relation-row-#{relatable.id} .relation-row--type", wait: 20) + span = page.find(".relation-row-#{relatable.id} [data-qa-selector='op-relation--row-type']", wait: 20) scroll_to_element(span) page.driver.browser.action.move_to(span.native).perform @@ -96,7 +96,7 @@ module Components container = find('.wp-relations-create--form', wait: 10) # Labels to expect - relation_label = I18n.t('js.relation_labels.' + type) + relation_label = I18n.t("js.relation_labels.#{type}") select relation_label, from: 'relation-type--select' @@ -111,9 +111,9 @@ module Components text: relation_label.upcase, wait: 10) - expect(page).to have_selector('.relation-row--type', text: to.type.name.upcase) + expect(page).to have_selector("[data-qa-selector='op-relation--row-type']", text: to.type.name.upcase) - expect(page).to have_selector('.wp-relations--subject-field', text: to.subject) + expect(page).to have_selector("[data-qa-selector='op-relation--row-subject']", text: to.subject) ## Test if relation exist work_package.reload @@ -123,15 +123,15 @@ module Components end def expect_relation(relatable) - expect(relations_group).to have_selector('.wp-relations--subject-field', text: relatable.subject) + expect(relations_group).to have_selector("[data-qa-selector='op-relation--row-subject']", text: relatable.subject) end def expect_relation_by_text(text) - expect(relations_group).to have_selector('.wp-relations--subject-field', text:) + expect(relations_group).to have_selector("[data-qa-selector='op-relation--row-subject']", text:) end def expect_no_relation(relatable) - expect(page).not_to have_selector('.wp-relations--subject-field', text: relatable.subject) + expect(page).not_to have_selector("[data-qa-selector='op-relation--row-subject']", text: relatable.subject) end def add_parent(query, work_package) @@ -172,7 +172,7 @@ module Components subject.update subject_text end - def openChildrenAutocompleter + def open_children_autocompleter retry_block do next if page.has_selector?('.wp-relations--children .ng-input input')