diff --git a/app/contracts/oauth_clients/create_contract.rb b/app/contracts/oauth_clients/create_contract.rb index 72646e3a0875..f367479e5b65 100644 --- a/app/contracts/oauth_clients/create_contract.rb +++ b/app/contracts/oauth_clients/create_contract.rb @@ -31,7 +31,7 @@ class CreateContract < ::ModelContract include ActiveModel::Validations attribute :client_id, writable: true - validates :client_id, presence: true, length: { maximum: 255 } + validates :client_id, presence: { message: I18n.t('oauth_client.errors.client_id_blank') }, length: { maximum: 255 } attribute :client_secret, writable: true validates :client_secret, presence: true, length: { maximum: 255 } diff --git a/app/services/oauth_clients/create_service.rb b/app/services/oauth_clients/create_service.rb index 35d75686e6c0..62ef02892ae3 100644 --- a/app/services/oauth_clients/create_service.rb +++ b/app/services/oauth_clients/create_service.rb @@ -36,10 +36,9 @@ module OAuthClients class CreateService < ::BaseServices::Create protected - def before_perform(params, _service_result) - OAuthClient.where(integration: params[:integration]).delete_all - - super + def after_validate(params, contract_call) + OAuthClient.where(integration: params[:integration]).delete_all if contract_call.success? + super(params, contract_call) end end end diff --git a/config/locales/en.yml b/config/locales/en.yml index 3929408c187f..a9b744ab2425 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -3513,6 +3513,7 @@ en: oauth_returned_json_error: "OAuth2 returned a JSON error" oauth_returned_http_error: "OAuth2 returned a network error" oauth_returned_standard_error: "OAuth2 returned an internal error" + client_id_blank: "ID can't be blank." wrong_token_type_returned: "OAuth2 returned a wrong type of token, expecting AccessToken::Bearer" oauth_issue_contact_admin: "OAuth2 reported an error. Please contact your system administrator." oauth_client_not_found: "OAuth2 client not found in 'callback' endpoint (redirect_uri)." diff --git a/docs/glossary/README.md b/docs/glossary/README.md index d2608d48e3e0..751c74e1d159 100644 --- a/docs/glossary/README.md +++ b/docs/glossary/README.md @@ -41,6 +41,10 @@ In OpenProject, the person who has administrative rights in an instance is calle Agile project management is an iterative and flexible approach to managing projects. It focuses on collaboration, adaptability, and self-organizing teams. OpenProject supports agile project management as well as [classic project management](#classic-project-management), and works best for [hybrid project management](#hybrid-project-management). +### Attribute help texts + +OpenProject offers Attribute help texts that provide additional information for attributes in work packages and projects, incl. custom fields. After setting them up in the Administration they are displayed when users click on the question mark symbol next to this specified attribute for projects and work packages. [Read more about Attribute help texts in OpenProject](https://www.openproject.org/docs/system-admin-guide/attribute-help-texts/). + ### Authentication In OpenProject, authentication is an important element to guarantee a data protected usage. To adapt these authentication settings, you need to have [admin](#admin) rights. Navigate to your username and select -> Administration -> Authentication. At OpenProject, we use [OAuth 2.0](#oauth) as this is the definitive industry standard for online authorization. @@ -56,15 +60,15 @@ In OpenProject, authentication is an important element to guarantee a data prote ### Backlogs -A backlog in OpenProject is defined as a [plugin](#plugin) that allows to use the backlogs feature in OpenProject. The backlog is a tool in scrum: a list that contains everything needed to achieve a specific outcome. In order to use backlogs in OpenProject, the backlogs module has to be activated in the [project settings](#project-settings) by a project admin. [Read how to work with backlogs in OpenProject](https://www.openproject.org/docs/user-guide/backlogs-scrum) +Backlogs is a [module](#module) in OpenProject that brings feautres that support the Scrum methodology in OpenProject, such as a product backlog and sprint backlogs, a task board, estimation of story points, a burndown chart and a Scrum wiki. In order to use backlogs in OpenProject, the backlogs module has to be activated in the [project settings](#project-settings) by a project admin. [Read how to work with backlogs in OpenProject](https://www.openproject.org/docs/user-guide/backlogs-scrum). -### Baseline (Comparison) +### Baseline comparison -Baseline is a feature in OpenProject released with version 13.0. It allows users to quickly track changes on [filtered](#filters) work packages table views. [Read more about technical challenges, design and next steps for Baseline in the OpenProject blog](https://www.openproject.org/blog/news-product-team-baseline/) +Baseline is a feature in OpenProject that allows users to quickly track changes on [filtered](#filters) work packages table views. Project managers can use baseline to get a quick overview of what has changed over time, making it easier to report on project progress and status. [Read more about OpenProject's Baseline comparison](https://www.openproject.org/docs/user-guide/work-packages/baseline-comparison/). ### BIM -BIM stands for Building Information Modeling. In OpenProject, we offer a special plan for users working in the construction industry. On top of the general project management features, OpenProject BIM enables construction teams to better plan, communicate and collaborate in their building projects. [Read the OpenProject BIM guide to get more information](https://www.openproject.org/docs/bim-guide/) +BIM stands for Building Information Modeling. In OpenProject, we offer a special OpenProject version for users working in the construction industry. On top of the general project management features, OpenProject BIM enables construction teams to better plan, communicate and collaborate in their building projects. [Read the OpenProject BIM guide to get more information](https://www.openproject.org/docs/bim-guide/). ### Board @@ -94,7 +98,7 @@ OpenProject can be installed either [on-premises](#on-premises) (available for b ### Community edition -Community edition is defined as the main and free-of-charge edition of OpenProject software. It is installed [on-premises](#on-premises) and therefore self-managed. Benefit from a wide range of features and data sovereignty in a free and open source project management software. The Community edition is actively maintained and is continuously being further developed. [Read more about OpenProject Community edition](https://www.openproject.org/community-edition/) +Community edition is defined as the main and free-of-charge edition of OpenProject software. It is installed [on-premises](#on-premises) and therefore self-managed. Benefit from a wide range of features and data sovereignty in a free and open source project management software. The Community edition is actively maintained and is continuously being further developed. [Read more about OpenProject Community edition](https://www.openproject.org/community-edition/). ### Custom action @@ -134,27 +138,27 @@ In addition to those overview dashboard options, you can create a [custom query] ### Date alerts -Date alerts in OpenProject are an [Enterprise add-on](#enterprise-add-on) and defined as a feature to generate automatic and customized [notifications](#notifications) regarding a work package's due date or start date. You can find the date alerts feature in your notification center, symbolized by a little bell on the right upper side of your instance. [Read more about the date alerts feature in our user guide](https://www.openproject.org/docs/user-guide/notifications/notification-settings/#date-alerts-enterprise-add-on) or in [this article on deadline management with OpenProject](https://www.openproject.org/blog/deadline-management-date-alert/) +Date alerts in OpenProject are an [Enterprise add-on](#enterprise-add-on) and defined as a feature to generate automatic and customized [notifications](#notifications) regarding a work package's due date or start date. You can find the date alerts feature in your notification center, symbolized by a little bell on the right upper side of your instance. [Read more about the date alerts feature in our user guide](https://www.openproject.org/docs/user-guide/notifications/notification-settings/#date-alerts-enterprise-add-on) or in [this article on deadline management with OpenProject](https://www.openproject.org/blog/deadline-management-date-alert/). ## E ### Enterprise add-on -In OpenProject, some features are defined as an Enterprise add-on. This means, they are not part of the [Community edition](#community-edition) and therefore not free of charge. Enterprise add-ons are available as [cloud](#cloud) or [on-premises](#on-premises) versions. You can choose from the following plans to get access to all Enterprise add-ons: Basic, Professional, Premium and Corporate. [Read more about OpenProject plans and pricing](https://www.openproject.org/pricing/) +In OpenProject, some features are defined as an Enterprise add-on. This means, they are not part of the [Community edition](#community-edition) and therefore not free of charge. Enterprise add-ons are available as [cloud](#cloud) or [on-premises](#on-premises) versions. You can choose from the following plans to get access to all Enterprise add-ons: Basic, Professional, Premium and Corporate. [Read more about OpenProject plans and pricing](https://www.openproject.org/pricing/). ### Enumerations -Enumerations in OpenProject is defined as a menu item in the admin settings that allows the configuration of Activities (for [time tracking](#time-and-costs)), [project status](#project-status) and work package priorities. [Read more about enumerations in OpenProject](https://www.openproject.org/docs/system-admin-guide/enumerations/) +Enumerations in OpenProject is defined as a menu item in the admin settings that allows the configuration of Activities (for [time tracking](#time-and-costs)), [project status](#project-status) and work package priorities. [Read more about enumerations in OpenProject](https://www.openproject.org/docs/system-admin-guide/enumerations/). ### Excel synchronization -Excel synchronization is an integration in OpenProject which allows you to easily import your issues from Excel to OpenProject or upload your work packages into an Excel spreadsheet. [See our video tutorials on how to work with the Excel synchronization integration](https://www.openproject.org/docs/system-admin-guide/integrations/excel-synchronization/) +Excel synchronization is an integration in OpenProject which allows you to easily import your issues from Excel to OpenProject or upload your work packages into an Excel spreadsheet. [See our video tutorials on how to work with the Excel synchronization integration](https://www.openproject.org/docs/system-admin-guide/integrations/excel-synchronization/). ## F ### File storage -File storages can be configured in the System Administration and then be selected in the [project settings](#project-settings). OpenProject offers a [Nextcloud integration](#nextcloud-integration) to support file storage. [More information on file storage with the Nextcloud integration](../user-guide/file-management/nextcloud-integration/) +File storages can be configured in the System Administration and then be selected in the [project settings](#project-settings). OpenProject offers a [Nextcloud integration](#nextcloud-integration) to support file storage. [More information on file storage with the Nextcloud integration](../user-guide/file-management/nextcloud-integration/). ### Filters @@ -162,17 +166,17 @@ Filters are essential for task and project management in OpenProject. You have s ### Forum -A forum in OpenProject is defined as a module used to display forums and forum messages. The module has to be activated in the [project settings](#project-settings) by a project admin and a forum has to be created in the forums tab in the project settings to be displayed in the side navigation. [Read more about forums in OpenProject](https://www.openproject.org/docs/user-guide/forums/) +A forum in OpenProject is defined as a module used to display forums and forum messages. The module has to be activated in the [project settings](#project-settings) by a project admin and a forum has to be created in the forums tab in the project settings to be displayed in the side navigation. [Read more about forums in OpenProject](https://www.openproject.org/docs/user-guide/forums/). ## G ### Gantt chart -The Gantt chart in OpenProject displays the work packages in a timeline. You can collaboratively create and manage your project plan. Have your project timelines available for all team [members](#member), and share up-to-date information with stakeholders. You can add start and finish dates and adapt it with drag and drop in the Gantt chart. Also, you can add dependencies, predecessor or follower within the Gantt chart. [Read more about how to activate and work with Gantt charts in OpenProject](https://www.openproject.org/docs/user-guide/gantt-chart/) +The Gantt chart in OpenProject displays the work packages in a timeline. You can collaboratively create and manage your project plan. Have your project timelines available for all team [members](#member), and share up-to-date information with stakeholders. You can add start and finish dates and adapt it with drag and drop in the Gantt chart. Also, you can add dependencies, predecessor or follower within the Gantt chart. [Read more about how to activate and work with Gantt charts in OpenProject](https://www.openproject.org/docs/user-guide/gantt-chart/). ### Global modules -In OpenProject, global modules are defined as a menu to access all [modules](#module) for *all* your projects. With global modules you can easily see all your project-overarching information at one place, e.g. for work packages, boards, calendars or meetings. To access the Global modules side menu, simply navigate to your [home page](#home-page) by clicking on the logo in the header, or use the grid icon in the top right corner. [Read more about global modules in OpenProject](https://www.openproject.org/docs/user-guide/home/global-modules/) +In OpenProject, global modules are defined as a menu to access all [modules](#module) for *all* your projects. With global modules you can easily see all your project-overarching information at one place, e.g. for work packages, boards, calendars or meetings. To access the Global modules side menu, simply navigate to your [home page](#home-page) by clicking on the logo in the header, or use the grid icon in the top right corner. [Read more about global modules in OpenProject](https://www.openproject.org/docs/user-guide/home/global-modules/). ### Group @@ -180,6 +184,10 @@ A Group in OpenProject is defined as a list of users which can be added as a mem ## H +### High contrast mode + +OpenProject offers a high contrast mode to make it easier for visually impaired people to use the software. This mode can be selected in the personal [account settings](https://www.openproject.org/docs/getting-started/my-account/#my-account) and will only affect the individual user's experience. + ### Home page In the OpenProject application, the home page is defined as a start page for your instance, where you get an overview about important information. From the home page you can access all global [modules](#module) of OpenProject. To get to the OpenProject application home page, click on the logo in the header of the application. [Read more about the OpenProject application home page](https://www.openproject.org/docs/user-guide/home/#application-home-page) @@ -192,11 +200,11 @@ Hybrid project management is an approach that combines elements of both classic ### Integration -In OpenProject, you can choose from several integrations, such as the [Excel synchronization](#excel-synchronization) or the [Nextcloud integration](#nextcloud-integration). In contrast to a plugin, an integration refers to cross features between two software products (e.g., OpenProject and Nextcloud), where both pieces of software have code that is responsible for interacting with the other software, through e.g., an API. Whereas a [plugin](#plugin) is installed on top of / as part of the OpenProject installation. You'll need to actively install it. [See all available plugins and integrations for OpenProject](https://www.openproject.org/docs/system-admin-guide/integrations/) +In OpenProject, you can choose from several integrations, such as the [Excel synchronization](#excel-synchronization) or the [Nextcloud integration](#nextcloud-integration). In contrast to a plugin, an integration refers to cross features between two software products (e.g., OpenProject and Nextcloud), where both pieces of software have code that is responsible for interacting with the other software, through e.g., an API. Whereas a [plugin](#plugin) is installed on top of / as part of the OpenProject installation. You'll need to actively install it. [See all available plugins and integrations for OpenProject](https://www.openproject.org/docs/system-admin-guide/integrations/). ### Meetings -In OpenProject Software, Meetings is defined as a [module](#module) that allows the organization of meetings. The module has to be activated in the [project settings](#project-settings) by a project admin in order to be displayed in the side navigation. [See our user guide for more information on Meeting management in OpenProject](https://www.openproject.org/docs/user-guide/meetings/) +In OpenProject Software, Meetings is defined as a [module](#module) that allows the organization of meetings. The module has to be activated in the [project settings](#project-settings) by a project admin in order to be displayed in the side navigation. Since the release of OpenProject 13.1, you can choose between the previously used classic meetings and the new dynamic meetings. [See our user guide for more information on Meeting management in OpenProject](https://www.openproject.org/docs/user-guide/meetings/). ### Member @@ -204,31 +212,35 @@ In OpenProject Software, a member is defined as a single person in a project. Pr ### Module -A module in OpenProject is defined as an independent unit of functionality that can be used to extend and improve the existing core functions. A project admin can activate and de-activate modules in the project settings. Some examples for modules in OpenProject are: Forums, Time and costs, Wiki or the Work packages module. [See our user guide for more information on all modules in OpenProject](https://www.openproject.org/docs/user-guide/#overview-of-modules-in-openproject) +A module in OpenProject is defined as an independent unit of functionality that can be used to extend and improve the existing core functions. A project admin can activate and de-activate modules in the project settings. Some examples for modules in OpenProject are: Forums, Time and costs, Wiki or the Work packages module. [See our user guide for more information on all modules in OpenProject](https://www.openproject.org/docs/user-guide/#overview-of-modules-in-openproject). ### My Page -The My Page in OpenProject is defined as your personal [dashboard](#dashboard) with important overarching project information, such as work package reports, news, spent time, or a calendar. It can be configured to your specific needs. [Read more about the My Page in OpenProject](https://www.openproject.org/docs/getting-started/my-page/) +The My Page in OpenProject is defined as your personal [dashboard](#dashboard) with important overarching project information, such as work package reports, news, spent time, or a calendar. It can be configured to your specific needs. [Read more about the My Page in OpenProject](https://www.openproject.org/docs/getting-started/my-page/). ## N ### News -In OpenProject, News is defined as a [module](#module) that allows the publication and use of news entries. On the news page, you can see the latest news in a project in reverse chronological order. News communicate general topics to all team members. They can be displayed on the [project overview](#project-overview). [Read more about how to work with the News module in OpenProject](https://www.openproject.org/docs/user-guide/news/) +In OpenProject, News is defined as a [module](#module) that allows the publication and use of news entries. On the news page, you can see the latest news in a project in reverse chronological order. News communicate general topics to all team members. They can be displayed on the [project overview](#project-overview). [Read more about how to work with the News module in OpenProject](https://www.openproject.org/docs/user-guide/news/). ### Nextcloud integration -OpenProject offers a Nextcloud integration which allows you to manage files in a secure and easy way, e.g. to link files or folders in Nextcloud or upload files to Nextcloud on work packages. You find the Nextcloud integration in the Files tab of your work package, if you have activated the Nextcloud integration for your instance. Get access to the OpenProject-Nextcloud integration by downloading and activating it in the built-in [Nextcloud app store](https://apps.nextcloud.com/) within your Nextcloud instance. [Read more about the Nextcloud integration of OpenProject](../user-guide/file-management/nextcloud-integration/) +OpenProject offers a Nextcloud integration which allows you to manage files in a secure and easy way, e.g. to link files or folders in Nextcloud or upload files to Nextcloud on work packages. You find the Nextcloud integration in the Files tab of your work package, if you have activated the Nextcloud integration for your instance. Get access to the OpenProject-Nextcloud integration by downloading and activating it in the built-in [Nextcloud app store](https://apps.nextcloud.com/) within your Nextcloud instance. [Read more about the Nextcloud integration of OpenProject](../user-guide/file-management/nextcloud-integration/). ### Notifications -In OpenProject, you get in-app notifications about important changes that are relevant to you – for example new comments that mention you, updates to status, [type](#work-package-types) or dates or new assignments. This feature is enabled by default and can be used as an addition or an alternative to email notifications. To view the notifications, click the bell icon at the top right of the header. The bell icon will be displayed with a red badge if there are new notifications for you. [Read more about notifications in OpenProject](https://www.openproject.org/docs/user-guide/notifications/) +In OpenProject, you get in-app notifications about important changes that are relevant to you – for example new comments that mention you, updates to status, [type](#work-package-types) or dates or new assignments. This feature is enabled by default and can be used as an addition or an alternative to email notifications. To view the notifications, click the bell icon at the top right of the header. The bell icon will be displayed with a red badge if there are new notifications for you. [Read more about notifications in OpenProject](https://www.openproject.org/docs/user-guide/notifications/). ## O ### OAuth -OAuth is an open authorization standard. It allows you to access certain information or resources on behalf of a user without accessing their username and password on each individual service. OpenProject acts as an OAuth provider, allowing you to optionally grant permissions to access your data to authorized third-party applications or services. [Read more about OAuth applications in OpenProject](https://www.openproject.org/docs/system-admin-guide/authentication/oauth-applications/) +OAuth is an open authorization standard. It allows you to access certain information or resources on behalf of a user without accessing their username and password on each individual service. OpenProject acts as an OAuth provider, allowing you to optionally grant permissions to access your data to authorized third-party applications or services. [Read more about OAuth applications in OpenProject](https://www.openproject.org/docs/system-admin-guide/authentication/oauth-applications/). + +### OneDrive/SharePoint integration + +OpenProject offers a OneDrive/SharePoint [integration](#integration) as an [Enterprise add-on](#enterprise-add-on). It must be setup by an administrator before members can use it. With this integration, users can link files and folders that are stored in OneDrive/SharePoint with work packages in OpenProject. They can also view, open and download files and folders linked to a work package. [Read more about OpenProject's OneDrive/SharePoint integration](https://www.openproject.org/docs/user-guide/file-management/one-drive-integration/). ### On-premises @@ -256,6 +268,10 @@ Your activated plugins are listed together with your [modules](#module) in your - [See all available plugins and integrations for OpenProject](https://www.openproject.org/docs/system-admin-guide/integrations/) - [Read how to create an OpenProject plugin](https://www.openproject.org/docs/development/create-openproject-plugin/) +### Primer design system + +OpenProject started adopting [Github's Primer Design System](https://primer.style/) in 2023. New features will be developed using Primer and existing features will will be gradually revised. Relevant reusable components from Primer as well as common patterns and compositions of these components will be documented in our [Lookbook](https://qa.openproject-edge.com/lookbook/pages/how_to_use). [Read more about OpenProject's decision to use Primer](https://www.openproject.org/blog/primer-design-system/). + ### Project In OpenProject, a [project](https://www.openproject.org/docs/user-guide/projects/) is defined as an individual or collaborative enterprise that is carefully planned to achieve a particular aim. Projects are the central organizational unit in OpenProject. Your projects can be available publicly or internally. OpenProject does not limit the number of projects, neither in the [Community edition](#community-edition) nor in the Enterprise cloud or in Enterprise [on-premises](#on-premises) edition. If you have more than one project in your instance, projects build a structure in OpenProject. You can have parent projects and sub-projects. For example, a project can represent @@ -266,7 +282,7 @@ In OpenProject, a [project](https://www.openproject.org/docs/user-guide/projects ### Project folder -Project folders are a feature in OpenProject's [Nextcloud integration](#nextcloud-integration) to collaborate in the most efficient way. [Read more about project folders in OpenProject](https://www.openproject.org/docs/user-guide/projects/project-settings/file-storages/#project-folders) +Project folders are a feature in OpenProject's [Nextcloud integration](#nextcloud-integration) to collaborate in the most efficient way. [Read more about project folders in OpenProject](https://www.openproject.org/docs/user-guide/projects/project-settings/file-storages/#project-folders). ### Project identifier @@ -278,15 +294,15 @@ The project navigation is the side navigation within a project. Entries in the p ### Project overview -In OpenProject, the project overview is defined as a single [dashboard](#dashboard) page where all important information of a selected project can be displayed. The idea is to provide a central repository of information for the whole project team. Project information is added to the dashboard as [widgets](#widget). Open the project overview by navigating to "Overview" in the project menu on the left. [Read more about the project overview in OpenProject](https://www.openproject.org/docs/user-guide/project-overview/#project-overview) +In OpenProject, the project overview is defined as a single [dashboard](#dashboard) page where all important information of a selected project can be displayed. The idea is to provide a central repository of information for the whole project team. Project information is added to the dashboard as [widgets](#widget). Open the project overview by navigating to "Overview" in the project menu on the left. [Read more about the project overview in OpenProject](https://www.openproject.org/docs/user-guide/project-overview/#project-overview). ### Project settings -Project settings means project-specific setting configuration. The project settings contain general settings (e.g. the name and [project identifier](#project-identifier)), configuration of [modules](#module), [work package categories](#work-package-categories) and [types](#work-package-types), [custom fields](#custom-field), [version](#versions) settings, [time tracking activities](#time-and-costs), required disk storage, [file storages](#file-storage) and [Backlogs](#backlogs) settings (if plugin is installed). [Read more about project settings in OpenProject](https://www.openproject.org/docs/user-guide/projects/#project-settings) +Project settings means project-specific setting configuration. The project settings contain general settings (e.g. the name and [project identifier](#project-identifier)), configuration of [modules](#module), [work package categories](#work-package-categories) and [types](#work-package-types), [custom fields](#custom-field), [version](#versions) settings, [time tracking activities](#time-and-costs), required disk storage, [file storages](#file-storage) and [Backlogs](#backlogs) settings (if plugin is installed). [Read more about project settings in OpenProject](https://www.openproject.org/docs/user-guide/projects/#project-settings). ### Project status -The project status in OpenProject is defined as an information for yourself and the team if the project is on track – to then being able to quickly act in case it is off track. [Read more about the project status in OpenProject](https://www.openproject.org/docs/user-guide/projects/project-status/) +The project status in OpenProject is defined as an information for yourself and the team if the project is on track – to then being able to quickly act in case it is off track. [Read more about the project status in OpenProject](https://www.openproject.org/docs/user-guide/projects/project-status/). ### Project template @@ -294,13 +310,13 @@ A project template in OpenProject is defined as a dummy project to copy and adju ### Public project -In OpenProject, projects can be private or public. Public means that the project is visible to any user, regardless of project [membership](#member). The visibility of a project can be changed in the project settings. [Read how to set a project to public in OpenProject](https://www.openproject.org/docs/user-guide/projects/#set-a-project-to-public) +In OpenProject, projects can be private or public. Public means that the project is visible to any user, regardless of project [membership](#member). The visibility of a project can be changed in the project settings. [Read how to set a project to public in OpenProject](https://www.openproject.org/docs/user-guide/projects/#set-a-project-to-public). ## R ## Repository -A repository is defined as a document or source code management system that allows users to manage files and folders via different version control systems (such as Subversion or Git). [Read more about Repository for source code control](https://www.openproject.org/docs/user-guide/repository/) +A repository is defined as a document or source code management system that allows users to manage files and folders via different version control systems (such as Subversion or Git). [Read more about Repository for source code control](https://www.openproject.org/docs/user-guide/repository/). ### Roadmap @@ -316,23 +332,27 @@ In product management, the RICE score indicates the level of prioritization of a ## S +### Share work packages + +OpenProject offers the possibility to share work packages with external groups or users that are not [members](#member) of the project. This feature is an [Enterprise add-on](#enterprise-add-on). Every user with whom a work package is shared must either already be a user of the instance or be newly created. The latter requires special rights. [Read more about OpenProject's feature to share work packages with project non-members](https://www.openproject.org/docs/user-guide/work-packages/share-work-packages/). + ### Story points -Story points is a term known in Scrum. They are defined as numbers assigned to a [work package](#work-package) used to estimate (relatively) the size of the work. In OpenProject, you can add story points to work packages. [Read how to work with story points in OpenProject](https://www.openproject.org/docs/user-guide/backlogs-scrum/work-with-backlogs/#working-with-story-points) +Story points is a term known in Scrum. They are defined as numbers assigned to a [work package](#work-package) used to estimate (relatively) the size of the work. In OpenProject, you can add story points to work packages. [Read how to work with story points in OpenProject](https://www.openproject.org/docs/user-guide/backlogs-scrum/work-with-backlogs/#working-with-story-points). ## T ### Team planner -The team planner in OpenProject is defined as a [module](#module) ([Enterprise add-on](#enterprise-add-on)) that helps you get a complete overview of what each [member](#member) of your team is working on in a weekly or bi-weekly view. You can use it to track the current progress of [work packages](#work-package) your team is working on, schedule new tasks, reschedule them or even reassign them to different members. [Read more about the OpenProject team planner](https://www.openproject.org/docs/user-guide/team-planner/) +The team planner in OpenProject is defined as a [module](#module) ([Enterprise add-on](#enterprise-add-on)) that helps you get a complete overview of what each [member](#member) of your team is working on in a weekly or bi-weekly view. You can use it to track the current progress of [work packages](#work-package) your team is working on, schedule new tasks, reschedule them or even reassign them to different members. [Read more about the OpenProject team planner](https://www.openproject.org/docs/user-guide/team-planner/). ### Time and costs -Time and costs in OpenProject is defined as a [module](#module) which allows users to log time on [work packages](#work-package), track costs and create time and cost reports. Once the time and costs module is activated by a project admin, time and unit cost can be logged via the action menu of a work package. Logged time and costs can be searched for, aggregated and reported using the Cost reports menu item. [Read more about the time and costs module in OpenProject](https://www.openproject.org/docs/user-guide/time-and-costs/) +Time and costs in OpenProject is defined as a [module](#module) which allows users to log time on [work packages](#work-package), track costs and create time and cost reports. Once the time and costs module is activated by a project admin, time and unit cost can be logged via the action menu of a work package. Logged time and costs can be searched for, aggregated and reported using the Cost reports menu item. [Read more about the time and costs module in OpenProject](https://www.openproject.org/docs/user-guide/time-and-costs/). ### Time tracking button -The time tracking button in OpenProject is a feature to track time spent on work packages in real time. [Read more about logging time via the time tracking button in OpenProject](https://www.openproject.org/docs/user-guide/time-and-costs/time-tracking/#log-time-via-the-time-tracking-button) +The time tracking button in OpenProject is a feature to track time spent on work packages in real time. [Read more about logging time via the time tracking button in OpenProject](https://www.openproject.org/docs/user-guide/time-and-costs/time-tracking/#log-time-via-the-time-tracking-button). ## U @@ -348,23 +368,23 @@ The OpenProject [user guide](https://www.openproject.org/docs/user-guide/) is an ### Versions -Versions in OpenProject are defined as an attribute for [work packages](#work-package) or in the [Backlogs](#backlogs) module. Versions will be displayed in the [Roadmap](#roadmap). In the [Enterprise edition](#enterprise-add-on), you can also create a version [board](#board) to get an overview of the progress of your versions. [Read more about how to manage versions in OpenProject](https://www.openproject.org/docs/user-guide/projects/project-settings/versions/) +Versions in OpenProject are defined as an attribute for [work packages](#work-package) or in the [Backlogs](#backlogs) module. Versions will be displayed in the [Roadmap](#roadmap). In the [Enterprise edition](#enterprise-add-on), you can also create a version [board](#board) to get an overview of the progress of your versions. [Read more about how to manage versions in OpenProject](https://www.openproject.org/docs/user-guide/projects/project-settings/versions/). ## W ### Widget -A widget in OpenProject is defined as a small and customizable element that provides relevant information at a glance. Use widgets on your [My Page](#my-page) dashboard or on the [project overview](#project-overview). [See all available project overview widgets](https://www.openproject.org/docs/user-guide/project-overview/#available-project-overview-widgets) and read [how to add a widget to the project overview](https://www.openproject.org/docs/user-guide/project-overview/#add-a-widget-to-the-project-overview) +A widget in OpenProject is defined as a small and customizable element that provides relevant information at a glance. Use widgets on your [My Page](#my-page) dashboard or on the [project overview](#project-overview). [See all available project overview widgets](https://www.openproject.org/docs/user-guide/project-overview/#available-project-overview-widgets) and read [how to add a widget to the project overview](https://www.openproject.org/docs/user-guide/project-overview/#add-a-widget-to-the-project-overview). ### Wiki -In OpenProject, a wiki is defined as a [module](#module) that allows to use wiki pages. In order to use the wiki module, it has to be activated in the [project settings](#project-settings) by a project admin. [Read more about wikis in OpenProject](https://www.openproject.org/docs/user-guide/wiki/) +In OpenProject, a wiki is defined as a [module](#module) that allows to use wiki pages. In order to use the wiki module, it has to be activated in the [project settings](#project-settings) by a project admin. [Read more about wikis in OpenProject](https://www.openproject.org/docs/user-guide/wiki/). ![A wiki module in OpenProject](glossary-openproject-wiki.png) ### Workflow -A workflow in OpenProject is defined as the allowed transitions between status for a [role](#role) and a type, i.e. which status changes can a certain role implement depending on the [work package type](#work-package-types). Workflows can be defined in the admin settings. For example, you might only want developers to be able to set the status "developed". [Read more about work package workflows in OpenProject](https://www.openproject.org/docs/system-admin-guide/manage-work-packages/work-package-workflows/#manage-work-package-workflows) +A workflow in OpenProject is defined as the allowed transitions between status for a [role](#role) and a type, i.e. which status changes can a certain role implement depending on the [work package type](#work-package-types). Workflows can be defined in the admin settings. For example, you might only want developers to be able to set the status "developed". [Read more about work package workflows in OpenProject](https://www.openproject.org/docs/system-admin-guide/manage-work-packages/work-package-workflows/#manage-work-package-workflows). ![glossary-openproject-sys-admin-edit-workflow](glossary-openproject-sys-admin-edit-workflow.png) @@ -380,7 +400,7 @@ In OpenProject, a [work package](https://www.openproject.org/docs/user-guide/wor ### Work package categories -Work package categories are a functionality used to automatically assign a [member](#member) to a work package by specifying a category. [Read more about work package categories in OpenProject](https://www.openproject.org/docs/user-guide/projects/project-settings/work-package-categories/#manage-work-package-categories) +Work package categories are a functionality used to automatically assign a [member](#member) to a work package by specifying a category. [Read more about work package categories in OpenProject](https://www.openproject.org/docs/user-guide/projects/project-settings/work-package-categories/#manage-work-package-categories). ### Work package ID @@ -388,14 +408,14 @@ Work package ID is defined as a unique ascending number assigned to a newly crea ### Work package table -The work package table in OpenProject is defined as the overview of all work packages in a project, together with their attributes in the columns. A synonym for work package table is the term "work package list". [Read how to configure a work package table](https://www.openproject.org/docs/user-guide/work-packages/work-package-table-configuration/) +The work package table in OpenProject is defined as the overview of all work packages in a project, together with their attributes in the columns. A synonym for work package table is the term "work package list". [Read how to configure a work package table](https://www.openproject.org/docs/user-guide/work-packages/work-package-table-configuration/). ![A work package table in OpenProject](glossary-openproject-work-package-table.png) ### Work package types -Work package types are the different items a work package can represent. Each work package is associated to exactly one type. Examples for most used work package types are a Task, a Milestone, a [Phase](#phase) or a Bug. The work package types can be customized in the system administration. [Read more about work package types in OpenProject](https://www.openproject.org/docs/user-guide/projects/project-settings/work-package-types/#work-package-types) +Work package types are the different items a work package can represent. Each work package is associated to exactly one type. Examples for most used work package types are a Task, a Milestone, a [Phase](#phase) or a Bug. The work package types can be customized in the system administration. [Read more about work package types in OpenProject](https://www.openproject.org/docs/user-guide/projects/project-settings/work-package-types/#work-package-types). ### Work package view -A list of work packages is considered a view. The containing work packages in any view can be displayed a number of different ways. Examples for most used work package views are the [table view](#work-package-table), the full screen view or the split screen view. You can also display work packages in a card view and use them in a [board](#board) to use agile methods. [Read more about work package views in OpenProject](https://www.openproject.org/docs/user-guide/work-packages/work-package-views/#work-packages-views) +A list of work packages is considered a view. The containing work packages in any view can be displayed a number of different ways. Examples for most used work package views are the [table view](#work-package-table), the full screen view or the split screen view. You can also display work packages in a card view and use them in a [board](#board) to use agile methods. [Read more about work package views in OpenProject](https://www.openproject.org/docs/user-guide/work-packages/work-package-views/#work-packages-views). diff --git a/frontend/src/app/features/backlogs/backlogs-page/styles/taskboard.sass b/frontend/src/app/features/backlogs/backlogs-page/styles/taskboard.sass index adb19d1acfe0..867d7265c8d9 100644 --- a/frontend/src/app/features/backlogs/backlogs-page/styles/taskboard.sass +++ b/frontend/src/app/features/backlogs/backlogs-page/styles/taskboard.sass @@ -220,8 +220,6 @@ &.ui-widget-content background: none border: none - .editor - color: var(--body-font-color) .ui-dialog-buttonpane.ui-widget-content background: none background-color: none @@ -230,17 +228,15 @@ .ui-dialog-titlebar-close overflow: hidden -.dark #task_editor label - color: #FFFFFF - -.light #task_editor label - color: #000000 - -.dark div - color: #FFFFFF +.dark + #task_editor label, .subject, .assigned_to_id, div + color: #FFFFFF + option + color: var(--body-font-color) -.light div - color: #000000 +.light + #task_editor label, .subject, .assigned_to_id, div + color: var(--body-font-color) /* item editor */ diff --git a/frontend/src/stimulus/controllers/dynamic/storages/oauth-client-form.controller.ts b/frontend/src/stimulus/controllers/dynamic/storages/oauth-client-form.controller.ts deleted file mode 100644 index 717aa0dc177c..000000000000 --- a/frontend/src/stimulus/controllers/dynamic/storages/oauth-client-form.controller.ts +++ /dev/null @@ -1,64 +0,0 @@ -/* - * -- copyright - * OpenProject is an open source project management software. - * Copyright (C) 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. - * ++ - */ - -import { Controller } from '@hotwired/stimulus'; - -export default class OAuthClientFormController extends Controller { - static targets = [ - 'clientId', - 'clientSecret', - 'submitButton', - ]; - - declare readonly hasClientIdTarget:boolean; - declare readonly hasClientSecretTarget:boolean; - declare readonly hasSubmitButtonTarget:boolean; - declare readonly clientIdTarget:HTMLInputElement; - declare readonly clientSecretTarget:HTMLInputElement; - declare readonly submitButtonTarget:HTMLInputElement; - - connect():void { - this.toggleSubmitButtonDisabled(); - } - - public toggleSubmitButtonDisabled():void { - const targetsConfigured = this.hasClientIdTarget && this.hasClientSecretTarget && this.hasSubmitButtonTarget; - - if (!targetsConfigured) { - return; - } - - if (this.clientIdTarget.value === '' || this.clientSecretTarget.value === '') { - this.submitButtonTarget.disabled = true; - } else { - this.submitButtonTarget.disabled = false; - } - } -} diff --git a/modules/storages/app/components/storages/admin/forms/automatically_managed_project_folders_form_component.rb b/modules/storages/app/components/storages/admin/forms/automatically_managed_project_folders_form_component.rb index bef5f6888f98..0a95ca480922 100644 --- a/modules/storages/app/components/storages/admin/forms/automatically_managed_project_folders_form_component.rb +++ b/modules/storages/app/components/storages/admin/forms/automatically_managed_project_folders_form_component.rb @@ -43,7 +43,7 @@ def form_url def submit_button_options { - label: I18n.t("storages.buttons.done_complete_setup"), + label: submit_button_label, data: { 'storages--automatically-managed-project-folders-form-target': 'submitButton' }.tap do |data_hash| # For create action, break from Turbo Frame and follow full page redirect data_hash[:turbo] = false if new_record? @@ -57,6 +57,14 @@ def cancel_button_options private + def submit_button_label + if storage.automatically_managed? + I18n.t("storages.buttons.done_complete_setup") + else + I18n.t("storages.buttons.complete_without_setup") + end + end + def application_password_display_options {}.tap do |options_hash| options_hash[:display] = :none unless storage.automatically_managed? diff --git a/modules/storages/app/components/storages/admin/forms/oauth_client_form_component.html.erb b/modules/storages/app/components/storages/admin/forms/oauth_client_form_component.html.erb index 351293b98d52..0ce74a15d846 100644 --- a/modules/storages/app/components/storages/admin/forms/oauth_client_form_component.html.erb +++ b/modules/storages/app/components/storages/admin/forms/oauth_client_form_component.html.erb @@ -3,11 +3,7 @@ primer_form_with( model: oauth_client, url: admin_settings_storage_oauth_client_path(storage), - method: form_method, - data: { - controller: 'storages--oauth-client-form', - application_target: 'dynamic' - } + method: form_method ) do |form| flex_layout do |oauth_client_row| oauth_client_row.with_row(mb: 3) do @@ -19,24 +15,7 @@ end oauth_client_row.with_row(mb: 3) do - render( - Storages::Admin::OAuthClientForm.new( - form, - storage:, - client_id_input_options: { - data: { - 'storages--oauth-client-form-target': 'clientId', - action: 'input->storages--oauth-client-form#toggleSubmitButtonDisabled' - } - }, - client_secret_input_options: { - data: { - 'storages--oauth-client-form-target': 'clientSecret', - action: 'input->storages--oauth-client-form#toggleSubmitButtonDisabled' - } - } - ) - ) + render(Storages::Admin::OAuthClientForm.new(form,storage:,)) end oauth_client_row.with_row do @@ -45,10 +24,6 @@ form, storage:, submit_button_options: { - disabled: submit_button_disabled?, - data: { - 'storages--oauth-client-form-target': 'submitButton', - }, test_selector: 'storage-oauth-client-submit-button' }, cancel_button_options: { diff --git a/modules/storages/app/components/storages/admin/forms/oauth_client_form_component.rb b/modules/storages/app/components/storages/admin/forms/oauth_client_form_component.rb index e38599d11519..202d58b91899 100644 --- a/modules/storages/app/components/storages/admin/forms/oauth_client_form_component.rb +++ b/modules/storages/app/components/storages/admin/forms/oauth_client_form_component.rb @@ -48,10 +48,6 @@ def cancel_button_path storage.persisted? ? edit_admin_settings_storage_path(storage) : admin_settings_storages_path end - def submit_button_disabled? - !oauth_client_configured? - end - def storage_provider_credentials_instructions I18n.t("storages.instructions.#{storage.short_provider_type}.oauth_configuration", application_link_text: send(:"#{storage.short_provider_type}_integration_link")).html_safe @@ -77,9 +73,5 @@ def first_time_configuration? def default_form_method first_time_configuration? ? :post : :patch end - - def oauth_client_configured? - oauth_client.present? && oauth_client.client_id.present? && oauth_client.client_secret.present? - end end end diff --git a/modules/storages/app/components/storages/admin/forms/redirect_uri_form_component.html.erb b/modules/storages/app/components/storages/admin/forms/redirect_uri_form_component.html.erb index cbeefce49e9a..6ae3d36d5305 100644 --- a/modules/storages/app/components/storages/admin/forms/redirect_uri_form_component.html.erb +++ b/modules/storages/app/components/storages/admin/forms/redirect_uri_form_component.html.erb @@ -3,11 +3,7 @@ primer_form_with( model: oauth_client, url: finish_setup_admin_settings_storage_oauth_client_path(storage), - method: :post, - data: { - controller: 'storages--oauth-client-form', - application_target: 'dynamic' - } + method: :post ) do |form| flex_layout do |oauth_client_row| oauth_client_row.with_row(mb: 3) do @@ -44,10 +40,7 @@ submit_button_options: { disabled: submit_button_disabled? || @options[:is_complete], label: I18n.t('storages.buttons.done_complete_setup'), - data: { - 'storages--oauth-client-form-target': 'submitButton', - turbo: false - }, + data: { turbo: false }, test_selector: 'storage-oauth-client-submit-button' }, cancel_button_options: { diff --git a/modules/storages/app/components/storages/admin/oauth_application_info_copy_component.rb b/modules/storages/app/components/storages/admin/oauth_application_info_copy_component.rb index 2bf6f2432b48..705e8fed8e18 100644 --- a/modules/storages/app/components/storages/admin/oauth_application_info_copy_component.rb +++ b/modules/storages/app/components/storages/admin/oauth_application_info_copy_component.rb @@ -61,7 +61,7 @@ def submit_button_options private def submit_button_path - options[:submit_button_path] || edit_admin_settings_storage_path(storage) + options[:submit_button_path] || show_oauth_application_admin_settings_storage_path(storage) end end end diff --git a/modules/storages/app/components/storages/admin/oauth_client_info_component.rb b/modules/storages/app/components/storages/admin/oauth_client_info_component.rb index 735f15a54d9d..1026081b3f31 100644 --- a/modules/storages/app/components/storages/admin/oauth_client_info_component.rb +++ b/modules/storages/app/components/storages/admin/oauth_client_info_component.rb @@ -58,6 +58,7 @@ def edit_icon_button_options def edit_icon_button_data_options {}.tap do |data_h| data_h[:confirm] = I18n.t("storages.confirm_replace_oauth_client") if oauth_client_configured? + data_h[:turbo_stream] = true end end diff --git a/modules/storages/app/components/storages/admin/storage_view_component.html.erb b/modules/storages/app/components/storages/admin/storage_view_component.html.erb index cbabc5e57de3..12c8930ca8c6 100644 --- a/modules/storages/app/components/storages/admin/storage_view_component.html.erb +++ b/modules/storages/app/components/storages/admin/storage_view_component.html.erb @@ -28,13 +28,7 @@ if storage.new_record? || openproject_oauth_application_section_closed? render(Storages::Admin::OAuthApplicationInfoComponent.new(oauth_application:, storage:)) else - render( - Storages::Admin::OAuthApplicationInfoCopyComponent.new( - oauth_application:, - storage:, - submit_button_path: show_oauth_application_admin_settings_storage_path(storage) - ) - ) + render(Storages::Admin::OAuthApplicationInfoCopyComponent.new(oauth_application:, storage:)) end end end diff --git a/modules/storages/app/controllers/storages/admin/oauth_clients_controller.rb b/modules/storages/app/controllers/storages/admin/oauth_clients_controller.rb index 7550f9c24d91..8e1f649b4640 100644 --- a/modules/storages/app/controllers/storages/admin/oauth_clients_controller.rb +++ b/modules/storages/app/controllers/storages/admin/oauth_clients_controller.rb @@ -37,7 +37,6 @@ class Storages::Admin::OAuthClientsController < ApplicationController before_action :require_admin before_action :find_storage - before_action :delete_current_oauth_client, only: %i[create update] # menu_item is defined in the Redmine::MenuManager::MenuController # module, included from ApplicationController. @@ -54,7 +53,6 @@ def new .result respond_to do |format| - format.html format.turbo_stream end end @@ -67,7 +65,7 @@ def create service_result.on_failure do respond_to do |format| - format.html { render :new } + format.turbo_stream { render :new } end end @@ -155,8 +153,4 @@ def oauth_client_params def find_storage @storage = ::Storages::Storage.find(params[:storage_id]) end - - def delete_current_oauth_client - ::OAuthClients::DeleteService.new(user: User.current, model: @storage.oauth_client).call if @storage.oauth_client - end end diff --git a/modules/storages/app/controllers/storages/admin/storages_controller.rb b/modules/storages/app/controllers/storages/admin/storages_controller.rb index f845ac9b540d..59a256e27092 100644 --- a/modules/storages/app/controllers/storages/admin/storages_controller.rb +++ b/modules/storages/app/controllers/storages/admin/storages_controller.rb @@ -188,8 +188,7 @@ def replace_oauth_application @oauth_application = service_result.result if service_result.success? - flash[:notice] = I18n.t('storages.notice_oauth_application_replaced') - render :show_oauth_application + render :replace_oauth_application else render :edit end diff --git a/modules/storages/app/views/storages/admin/oauth_clients/new.turbo_stream.erb b/modules/storages/app/views/storages/admin/oauth_clients/new.turbo_stream.erb index c0ff300b0b51..ecf0c5100019 100644 --- a/modules/storages/app/views/storages/admin/oauth_clients/new.turbo_stream.erb +++ b/modules/storages/app/views/storages/admin/oauth_clients/new.turbo_stream.erb @@ -27,23 +27,6 @@ See COPYRIGHT and LICENSE files for more details. ++#%> -<%= turbo_stream.update :storage_openproject_oauth_section do %> - <%= - render( - Storages::Admin::OAuthApplicationInfoComponent.new( - oauth_application: @oauth_application, - storage: @storage, - ) - ) - %> -<% end %> - -<%= turbo_stream.update :storage_redirect_uri_section do %> - <%= render(Storages::Admin::RedirectUriComponent.new(oauth_client: @oauth_client, storage: @storage)) %> -<% end %> - -<% if @storage.provider_type_nextcloud? && @storage.oauth_client.blank? %> - <%= turbo_stream.update :storage_oauth_client_section do %> - <%= render(Storages::Admin::Forms::OAuthClientFormComponent.new(oauth_client: @oauth_client, storage: @storage)) %> - <% end %> +<%= turbo_stream.update :storage_oauth_client_section do %> + <%= render(Storages::Admin::Forms::OAuthClientFormComponent.new(oauth_client: @oauth_client, storage: @storage)) %> <% end %> diff --git a/modules/storages/app/views/storages/admin/storages/create.turbo_stream.erb b/modules/storages/app/views/storages/admin/storages/create.turbo_stream.erb index 155a9b7fd5c2..72326bd65475 100644 --- a/modules/storages/app/views/storages/admin/storages/create.turbo_stream.erb +++ b/modules/storages/app/views/storages/admin/storages/create.turbo_stream.erb @@ -31,7 +31,6 @@ See COPYRIGHT and LICENSE files for more details. <%= render(Storages::Admin::GeneralInfoComponent.new(@storage)) %> <% end %> - <% if @storage.provider_type_nextcloud? %> <%= turbo_stream.update :storage_openproject_oauth_section do %> <%= @@ -41,8 +40,7 @@ See COPYRIGHT and LICENSE files for more details. storage: @storage, submit_button_options: { data: { turbo_stream: true } - }, - submit_button_path: new_admin_settings_storage_oauth_client_path(@storage) + } ) ) %> diff --git a/modules/storages/app/views/storages/admin/oauth_clients/new.html.erb b/modules/storages/app/views/storages/admin/storages/replace_oauth_application.turbo_stream.erb similarity index 71% rename from modules/storages/app/views/storages/admin/oauth_clients/new.html.erb rename to modules/storages/app/views/storages/admin/storages/replace_oauth_application.turbo_stream.erb index 13d13af21f81..fbaa8255d23b 100644 --- a/modules/storages/app/views/storages/admin/oauth_clients/new.html.erb +++ b/modules/storages/app/views/storages/admin/storages/replace_oauth_application.turbo_stream.erb @@ -27,10 +27,20 @@ See COPYRIGHT and LICENSE files for more details. ++#%> -<% html_title t(:label_administration), t("project_module_storages"), @storage.name, "#{t("storages.provider_types.#{@storage.short_provider_type}.name")} #{t("storages.label_oauth_client_details")}" %> - -<%= - render(OpTurbo::FrameComponent.new(id: :storage_oauth_client_section)) do - render(Storages::Admin::Forms::OAuthClientFormComponent.new(oauth_client: @oauth_client, storage: @storage)) - end -%> +<%= turbo_stream.update :storage_general_info_section do %> + <%= render(Storages::Admin::GeneralInfoComponent.new(@storage)) %> +<% end %> + +<%= turbo_stream.update :storage_openproject_oauth_section do %> + <%= + render( + Storages::Admin::OAuthApplicationInfoCopyComponent.new( + oauth_application: @oauth_application, + storage: @storage, + submit_button_options: { + data: { turbo_stream: true } + } + ) + ) + %> +<% end %> diff --git a/modules/storages/app/views/storages/admin/storages/show_oauth_application.turbo_stream.erb b/modules/storages/app/views/storages/admin/storages/show_oauth_application.turbo_stream.erb index 95531304160f..8c833bccd446 100644 --- a/modules/storages/app/views/storages/admin/storages/show_oauth_application.turbo_stream.erb +++ b/modules/storages/app/views/storages/admin/storages/show_oauth_application.turbo_stream.erb @@ -27,21 +27,12 @@ See COPYRIGHT and LICENSE files for more details. ++#%> -<%= turbo_stream.update :storage_general_info_section do %> - <%= render(Storages::Admin::GeneralInfoComponent.new(@storage)) %> +<%= turbo_stream.update :storage_openproject_oauth_section do %> + <%= render(Storages::Admin::OAuthApplicationInfoComponent.new(oauth_application: @oauth_application, storage: @storage)) %> <% end %> -<%= turbo_stream.update :storage_openproject_oauth_section do %> - <%= - render( - Storages::Admin::OAuthApplicationInfoCopyComponent.new( - oauth_application: @oauth_application, - storage: @storage, - submit_button_options: { - data: { turbo_stream: true } - }, - submit_button_path: new_admin_settings_storage_oauth_client_path(@storage) - ) - ) - %> +<% if @storage.oauth_client.blank? %> + <%= turbo_stream.update :storage_oauth_client_section do %> + <%= render(Storages::Admin::Forms::OAuthClientFormComponent.new(oauth_client: @storage.build_oauth_client, storage: @storage)) %> + <% end %> <% end %> diff --git a/modules/storages/spec/components/storages/admin/oauth_client_info_component_spec.rb b/modules/storages/spec/components/storages/admin/oauth_client_info_component_spec.rb index 82806801892e..fc6a09cf3588 100644 --- a/modules/storages/spec/components/storages/admin/oauth_client_info_component_spec.rb +++ b/modules/storages/spec/components/storages/admin/oauth_client_info_component_spec.rb @@ -36,10 +36,9 @@ storage = build_stubbed(:nextcloud_storage, oauth_client: build_stubbed(:oauth_client)) component = described_class.new(storage:, oauth_client: storage.oauth_client) - expect(component.edit_icon_button_options).to include( icon: :sync, - data: { confirm: "Are you sure? All users will have to authorize again against the storage." } + data: { confirm: "Are you sure? All users will have to authorize again against the storage.", turbo_stream: true } ) end end @@ -50,7 +49,7 @@ component = described_class.new(storage:, oauth_client: nil) edit_icon_button_data_options = component.edit_icon_button_options - expect(edit_icon_button_data_options).to include(icon: :pencil) + expect(edit_icon_button_data_options).to include(icon: :pencil, data: { turbo_stream: true }) expect(edit_icon_button_data_options[:data]).not_to include(:confirm) end end diff --git a/modules/storages/spec/features/admin_storages_spec.rb b/modules/storages/spec/features/admin_storages_spec.rb index b9ecaa9cfe94..adee62c46867 100644 --- a/modules/storages/spec/features/admin_storages_spec.rb +++ b/modules/storages/spec/features/admin_storages_spec.rb @@ -134,13 +134,13 @@ expect(page).to have_test_selector('storage-new-page-header--title', text: 'New Nextcloud storage') expect(page).to have_test_selector('storage-new-page-header--description', text: "Read our documentation on setting up a Nextcloud file storage " \ - "integration for more information.") + "integration for more information.") # General information expect(page).to have_test_selector('storage-provider-configuration-instructions', text: "Please make sure you have administration privileges in your " \ - "Nextcloud instance and the application “Integration OpenProject” " \ - "is installed before doing the setup.") + "Nextcloud instance and the application “Integration OpenProject” " \ + "is installed before doing the setup.") # OAuth application expect(page).to have_test_selector('storage-openproject-oauth-label', text: 'OpenProject OAuth') @@ -181,8 +181,8 @@ within_test_selector('storage-openproject-oauth-application-form') do warning_section = find_test_selector('storage-openproject_oauth_application_warning') expect(warning_section).to have_text('The client secret value will not be accessible again after you close ' \ - 'this window. Please copy these values into the Nextcloud ' \ - 'OpenProject Integration settings.') + 'this window. Please copy these values into the Nextcloud ' \ + 'OpenProject Integration settings.') expect(warning_section).to have_link('Nextcloud OpenProject Integration settings', href: "https://example.com/settings/admin/openproject") @@ -201,15 +201,17 @@ expect(page).to have_test_selector('storage-provider-credentials-instructions', text: 'Copy these values from Nextcloud Administration / OpenProject.') - # With null values, submit button should be disabled + # With null values, form should render inline errors expect(page).to have_css('#oauth_client_client_id', value: '') expect(page).to have_css('#oauth_client_client_secret', value: '') - expect(find_test_selector('storage-oauth-client-submit-button')).to be_disabled + click_button 'Save and continue' + + expect(page).to have_text("Client ID can't be blank.") + expect(page).to have_text("Client secret can't be blank.") # Happy path - Submit valid values fill_in 'oauth_client_client_id', with: '1234567890' fill_in 'oauth_client_client_secret', with: '0987654321' - expect(find_test_selector('storage-oauth-client-submit-button')).not_to be_disabled click_button 'Save and continue' end @@ -231,7 +233,7 @@ # Test the error path for an invalid storage password. # Mock a valid response (=401) for example.com, so the password validation should fail mock_nextcloud_application_credentials_validation('https://example.com', password: "1234567890", - response_code: 401) + response_code: 401) automatically_managed_switch = page.find('[name="storages_nextcloud_storage[automatically_managed]"]') expect(automatically_managed_switch).to be_checked fill_in 'storages_nextcloud_storage_password', with: "1234567890" @@ -251,10 +253,10 @@ expect(page).to have_current_path(admin_settings_storages_path) expect(page).to have_test_selector( - "primer-banner-message-component", - text: "Storage connected successfully! Remember to activate the module and the specific " \ - "storage in the project settings of each desired project to use it." - ) + "primer-banner-message-component", + text: "Storage connected successfully! Remember to activate the module and the specific " \ + "storage in the project settings of each desired project to use it." + ) end end end @@ -288,21 +290,21 @@ expect(page).to have_test_selector('storage-new-page-header--title', text: 'New OneDrive/SharePoint storage') expect(page).to have_test_selector('storage-new-page-header--description', text: "Read our documentation on setting up a OneDrive/SharePoint " \ - "file storage integration for more information.") + "file storage integration for more information.") # General information expect(page).to have_test_selector('storage-provider-configuration-instructions', text: "Please make sure you have administration privileges in the " \ - "Azure portal or contact your Microsoft administrator before " \ - "doing the setup. In the portal, you also need to register an " \ - "Azure application or use an existing one for authentication.") + "Azure portal or contact your Microsoft administrator before " \ + "doing the setup. In the portal, you also need to register an " \ + "Azure application or use an existing one for authentication.") # OAuth client wait_for(page).to have_test_selector('storage-oauth-client-label', text: 'Azure OAuth') expect(page).not_to have_test_selector('label-storage_oauth_client_configured-status') expect(page).to have_test_selector('storage-oauth-client-id-description', text: "Allow OpenProject to access Azure data using OAuth " \ - "to connect OneDrive/Sharepoint.") + "to connect OneDrive/Sharepoint.") expect(page).to have_test_selector('storage-redirect-uri-description', text: "Complete the setup with the correct URI redirection.") end @@ -328,12 +330,15 @@ within_test_selector('storage-oauth-client-form') do expect(page).to have_test_selector('storage-provider-credentials-instructions', text: 'Copy these values from the desired application in the ' \ - 'Azure portal.') + 'Azure portal.') - # With null values, submit button should be disabled + # With null values, upon submit validation errors are show expect(page).to have_css('#oauth_client_client_id', value: '') expect(page).to have_css('#oauth_client_client_secret', value: '') - expect(find_test_selector('storage-oauth-client-submit-button')).to be_disabled + click_button 'Save and continue' + + expect(page).to have_text("Client ID can't be blank.") + expect(page).to have_text("Client secret can't be blank.") # Happy path - Submit valid values fill_in 'oauth_client_client_id', with: '1234567890' @@ -348,10 +353,10 @@ expect(page).to have_current_path(admin_settings_storages_path) wait_for(page).to have_test_selector( - "primer-banner-message-component", - text: "Storage connected successfully! Remember to activate the module and the specific " \ - "storage in the project settings of each desired project to use it." - ) + "primer-banner-message-component", + text: "Storage connected successfully! Remember to activate the module and the specific " \ + "storage in the project settings of each desired project to use it." + ) end end end @@ -478,8 +483,8 @@ within_test_selector('storage-openproject-oauth-application-form') do warning_section = find_test_selector('storage-openproject_oauth_application_warning') expect(warning_section).to have_text('The client secret value will not be accessible again after you close ' \ - 'this window. Please copy these values into the Nextcloud ' \ - 'OpenProject Integration settings.') + 'this window. Please copy these values into the Nextcloud ' \ + 'OpenProject Integration settings.') expect(warning_section).to have_link('Nextcloud OpenProject Integration settings', href: "#{storage.host}/settings/admin/openproject") @@ -498,10 +503,13 @@ end within_test_selector('storage-oauth-client-form') do - # With null values, submit button should be disabled + # With null values, form should render inline errors expect(page).to have_css('#oauth_client_client_id', value: '') expect(page).to have_css('#oauth_client_client_secret', value: '') - expect(find_test_selector('storage-oauth-client-submit-button')).to be_disabled + click_button 'Save and continue' + + expect(page).to have_text("Client ID can't be blank.") + expect(page).to have_text("Client secret can't be blank.") # Happy path - Submit valid values fill_in 'oauth_client_client_id', with: '1234567890' @@ -512,6 +520,7 @@ expect(page).to have_test_selector('label-storage_oauth_client_configured-status', text: 'Completed') expect(page).to have_test_selector('storage-oauth-client-id-description', text: "OAuth Client ID: 1234567890") + expect(OAuthClient.where(integration: storage).count).to eq(1) end aggregate_failures 'Automatically managed project folders' do @@ -530,7 +539,7 @@ # Test the error path for an invalid storage password. # Mock a valid response (=401) for example.com, so the password validation should fail mock_nextcloud_application_credentials_validation(storage.host, password: "1234567890", - response_code: 401) + response_code: 401) automatically_managed_switch = page.find('[name="storages_nextcloud_storage[automatically_managed]"]') expect(automatically_managed_switch).to be_checked fill_in 'storages_nextcloud_storage_password', with: "1234567890" @@ -609,15 +618,17 @@ end within_test_selector('storage-oauth-client-form') do - # With null values, submit button should be disabled + # With null values, form should render inline errors expect(page).to have_css('#oauth_client_client_id', value: '') expect(page).to have_css('#oauth_client_client_secret', value: '') - expect(find_test_selector('storage-oauth-client-submit-button')).to be_disabled + click_button 'Save and continue' + + expect(page).to have_text("Client ID can't be blank.") + expect(page).to have_text("Client secret can't be blank.") # Happy path - Submit valid values fill_in 'oauth_client_client_id', with: '1234567890' fill_in 'oauth_client_client_secret', with: '0987654321' - expect(find_test_selector('storage-oauth-client-submit-button')).not_to be_disabled click_button 'Save and continue' end diff --git a/modules/storages/spec/requests/api/v3/storages/storages_api_spec.rb b/modules/storages/spec/requests/api/v3/storages/storages_api_spec.rb index 649840dcfb4e..ce4875475017 100644 --- a/modules/storages/spec/requests/api/v3/storages/storages_api_spec.rb +++ b/modules/storages/spec/requests/api/v3/storages/storages_api_spec.rb @@ -492,7 +492,7 @@ end it_behaves_like 'constraint violation' do - let(:message) { 'Client can\'t be blank.' } + let(:message) { 'Client ID can\'t be blank.' } end end end diff --git a/spec/contracts/oauth_clients/create_contract_spec.rb b/spec/contracts/oauth_clients/create_contract_spec.rb index 80e81a1e724d..a64a257ecdcc 100644 --- a/spec/contracts/oauth_clients/create_contract_spec.rb +++ b/spec/contracts/oauth_clients/create_contract_spec.rb @@ -72,6 +72,16 @@ end end + context 'with blank client ID' do + let(:client_id) { '' } + + it 'is invalid, includes `ID` in error message' do + expect(contract).not_to be_valid + + expect(contract.errors[:client_id]).to eq(["ID can't be blank."]) + end + end + context 'with integration (polymorphic attribute) linked' do let(:integration) { create(:nextcloud_storage) }