diff --git a/app/components/my/access_token/api_tokens_section_component.rb b/app/components/my/access_token/api_tokens_section_component.rb
index 23cb48834f0..8c2625a9415 100644
--- a/app/components/my/access_token/api_tokens_section_component.rb
+++ b/app/components/my/access_token/api_tokens_section_component.rb
@@ -58,16 +58,37 @@ module My
case token_type.to_s
when "Token::API" then Setting.rest_api_enabled?
when "Token::ICalMeeting" then Setting.ical_enabled?
+ when "Token::RSS" then Setting.feeds_enabled?
else raise ArgumentError, "Unknown token type: #{token_type}"
end
end
+ def show_add_button?
+ return @tokens.empty? if token_type.to_s == "Token::RSS"
+
+ true
+ end
+
def add_button_icon
case token_type.to_s
- when "Token::ICalMeeting" then :rss
+ when "Token::RSS", "Token::ICalMeeting" then :rss
else :plus
end
end
+
+ def add_button_method
+ case token_type.to_s
+ when "Token::RSS" then :post
+ else :get
+ end
+ end
+
+ def add_button_path
+ case token_type.to_s
+ when "Token::RSS" then generate_rss_key_my_access_tokens_path
+ else dialog_my_access_tokens_path(token_type: token_type.model_name.element)
+ end
+ end
end
end
end
diff --git a/app/components/my/access_token/ical/row_component.rb b/app/components/my/access_token/ical/row_component.rb
new file mode 100644
index 00000000000..107db936073
--- /dev/null
+++ b/app/components/my/access_token/ical/row_component.rb
@@ -0,0 +1,89 @@
+# frozen_string_literal: true
+
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) 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 My
+ module AccessToken
+ module ICal
+ class RowComponent < OpPrimer::BorderBoxRowComponent
+ def api_token
+ model
+ end
+
+ def name
+ render(Primer::Beta::Text.new(test_selector: "ical-token-#{api_token.id}-name")) do
+ api_token.ical_token_query_assignment.name
+ end
+ end
+
+ def calendar
+ render(
+ Primer::Beta::Link.new(
+ href: project_calendar_path(id: api_token.query.id, project_id: api_token.query.project_id),
+ test_selector: "ical-token-#{api_token.id}-query-name"
+ )
+ ) { api_token.query.name }
+ end
+
+ def project
+ render(Primer::Beta::Text.new(test_selector: "ical-token-#{api_token.id}-project-name")) do
+ api_token.query.project.name
+ end
+ end
+
+ def created_at
+ helpers.format_time(api_token.created_at)
+ end
+
+ def expires_on
+ I18n.t("my_account.access_tokens.indefinite_expiration")
+ end
+
+ def button_links
+ [delete_link].compact
+ end
+
+ def delete_link
+ render(Primer::Beta::IconButton.new(
+ icon: :trash,
+ scheme: :danger,
+ tag: :a,
+ href: my_access_token_revoke_ical_token_path(access_token_id: api_token.id),
+ "aria-label": t(:button_delete),
+ test_selector: "ical-token-#{api_token.id}-revoke",
+ data: {
+ turbo_method: :delete,
+ turbo_confirm: t("my_account.access_tokens.simple_revoke_confirmation")
+ }
+ ))
+ end
+ end
+ end
+ end
+end
diff --git a/app/components/my/access_token/ical/table_component.rb b/app/components/my/access_token/ical/table_component.rb
new file mode 100644
index 00000000000..8d9f3f174a6
--- /dev/null
+++ b/app/components/my/access_token/ical/table_component.rb
@@ -0,0 +1,75 @@
+# frozen_string_literal: true
+
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) 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 My
+ module AccessToken
+ module ICal
+ class TableComponent < OpPrimer::BorderBoxTableComponent
+ columns :name, :calendar, :project, :created_at, :expires_on
+ main_column :name
+ mobile_labels :created_at, :expires_on
+
+ def headers
+ [
+ [:name, { caption: I18n.t("attributes.name") }],
+ [:calendar, { caption: Token::ICal.human_attribute_name(:calendar) }],
+ [:project, { caption: WorkPackage.human_attribute_name(:project) }],
+ [:created_at, { caption: User.human_attribute_name(:created_at) }],
+ [:expires_on, { caption: I18n.t("my_account.access_tokens.headers.expiration") }]
+ ]
+ end
+
+ def mobile_title
+ I18n.t("my_account.access_tokens.ical.table_title")
+ end
+
+ def row_class
+ RowComponent
+ end
+
+ def has_actions?
+ true
+ end
+
+ def blank_title
+ I18n.t("my_account.access_tokens.ical.blank_title")
+ end
+
+ def blank_description
+ I18n.t("my_account.access_tokens.ical.blank_description")
+ end
+
+ def blank_icon
+ nil
+ end
+ end
+ end
+ end
+end
diff --git a/app/components/my/access_token/oauth_application/row_component.rb b/app/components/my/access_token/oauth_application/row_component.rb
new file mode 100644
index 00000000000..44a9a1e6d6f
--- /dev/null
+++ b/app/components/my/access_token/oauth_application/row_component.rb
@@ -0,0 +1,88 @@
+# frozen_string_literal: true
+
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) 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 My
+ module AccessToken
+ module OAuthApplication
+ class RowComponent < OpPrimer::BorderBoxRowComponent
+ def oauth_application
+ model.first
+ end
+
+ def oauth_application_tokens
+ model.last
+ end
+
+ def name
+ render(Primer::Beta::Text.new(test_selector: "oauth-application-#{oauth_application.id}-name")) do
+ oauth_application.name
+ end
+ end
+
+ def active_tokens
+ render(Primer::Beta::Text.new(test_selector: "oauth-application-#{oauth_application.id}-active-tokens")) do
+ oauth_application_tokens.count { |t| !t.expired? && !t.revoked? }.to_s
+ end
+ end
+
+ def last_used_at
+ return "—" if oauth_application_tokens.empty?
+
+ helpers.format_time(oauth_application_tokens.max_by(&:created_at).created_at)
+ end
+
+ def button_links
+ [delete_link].compact
+ end
+
+ def delete_link
+ render(Primer::Beta::IconButton.new(
+ icon: :trash,
+ scheme: :danger,
+ tag: :a,
+ href: revoke_my_oauth_application_path(application_id: oauth_application.id),
+ "aria-label": t(:button_delete),
+ test_selector: "oauth-token-row-#{oauth_application.id}-revoke",
+ data: {
+ turbo_method: :post,
+ turbo_confirm: t(
+ "oauth.revoke_my_application_confirmation",
+ token_count: t(
+ "oauth.x_active_tokens",
+ count: oauth_application_tokens.count
+ )
+ )
+ }
+ ))
+ end
+ end
+ end
+ end
+end
diff --git a/app/components/my/access_token/oauth_application/table_component.rb b/app/components/my/access_token/oauth_application/table_component.rb
new file mode 100644
index 00000000000..05c019bc12f
--- /dev/null
+++ b/app/components/my/access_token/oauth_application/table_component.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) 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 My
+ module AccessToken
+ module OAuthApplication
+ class TableComponent < OpPrimer::BorderBoxTableComponent
+ columns :name, :active_tokens, :last_used_at
+ main_column :name
+ mobile_labels :active_tokens, :last_used_at
+
+ def headers
+ [
+ [:name, { caption: I18n.t("attributes.name") }],
+ [:active_tokens, { caption: I18n.t("my_account.access_tokens.oauth.active_tokens") }],
+ [:last_used_at, { caption: I18n.t("my_account.access_tokens.oauth.last_used_at") }]
+ ]
+ end
+
+ def mobile_title
+ I18n.t("my_account.access_tokens.oauth.table_title")
+ end
+
+ def row_class
+ RowComponent
+ end
+
+ def has_actions?
+ true
+ end
+
+ def blank_title
+ I18n.t("my_account.access_tokens.oauth.blank_title")
+ end
+
+ def blank_description
+ I18n.t("my_account.access_tokens.oauth.blank_description")
+ end
+
+ def blank_icon
+ nil
+ end
+ end
+ end
+ end
+end
diff --git a/app/components/my/access_token/storages/row_component.rb b/app/components/my/access_token/storages/row_component.rb
new file mode 100644
index 00000000000..102e7162b30
--- /dev/null
+++ b/app/components/my/access_token/storages/row_component.rb
@@ -0,0 +1,75 @@
+# frozen_string_literal: true
+
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) 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 My
+ module AccessToken
+ module Storages
+ class RowComponent < OpPrimer::BorderBoxRowComponent
+ def client_token
+ model
+ end
+
+ def name
+ client_token.oauth_client.integration.name
+ end
+
+ def created_at
+ helpers.format_time(client_token.created_at)
+ end
+
+ def expires_on
+ helpers.format_time(client_token.updated_at + client_token.expires_in.seconds)
+ end
+
+ def button_links
+ [delete_link].compact
+ end
+
+ def delete_link
+ render(Primer::Beta::IconButton.new(
+ icon: :trash,
+ scheme: :danger,
+ tag: :a,
+ href: my_access_token_revoke_storage_token_path(client_token),
+ "aria-label": t(:button_delete),
+ test_selector: "storages-token-row-#{client_token.id}-revoke",
+ data: {
+ turbo_method: :delete,
+ turbo_confirm: t(
+ "my_account.access_tokens.storages.revoke_token",
+ storage: client_token.oauth_client.integration.name
+ )
+ }
+ ))
+ end
+ end
+ end
+ end
+end
diff --git a/app/components/my/access_token/storages/table_component.rb b/app/components/my/access_token/storages/table_component.rb
new file mode 100644
index 00000000000..51b039564dc
--- /dev/null
+++ b/app/components/my/access_token/storages/table_component.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) 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 My
+ module AccessToken
+ module Storages
+ class TableComponent < OpPrimer::BorderBoxTableComponent
+ columns :name, :created_at, :expires_on
+ main_column :name
+ mobile_labels :created_at, :expires_on
+
+ def headers
+ [
+ [:name, { caption: I18n.t("attributes.name") }],
+ [:created_at, { caption: User.human_attribute_name(:created_at) }],
+ [:expires_on, { caption: I18n.t("my_account.access_tokens.headers.expiration") }]
+ ]
+ end
+
+ def mobile_title
+ I18n.t("my_account.access_tokens.storages.table_title")
+ end
+
+ def row_class
+ RowComponent
+ end
+
+ def has_actions?
+ true
+ end
+
+ def blank_title
+ I18n.t("my_account.access_tokens.storages.blank_title")
+ end
+
+ def blank_description
+ I18n.t("my_account.access_tokens.storages.blank_description")
+ end
+
+ def blank_icon
+ nil
+ end
+ end
+ end
+ end
+end
diff --git a/app/controllers/my/access_tokens_controller.rb b/app/controllers/my/access_tokens_controller.rb
index a1216c467f5..eb02c44ca2e 100644
--- a/app/controllers/my/access_tokens_controller.rb
+++ b/app/controllers/my/access_tokens_controller.rb
@@ -80,15 +80,14 @@ module My
def generate_rss_key # rubocop:disable Metrics/AbcSize
token = Token::RSS.create!(user: current_user)
- flash[:info] = [
- t("my.access_token.notice_reset_token", type: "RSS").html_safe,
- helpers.content_tag(:strong, helpers.content_tag(:code, token.plain_value)),
- t("my.access_token.token_value_warning")
- ]
+
+ update_via_turbo_stream(
+ component: My::AccessToken::APITokensSectionComponent.new(tokens: [token], token_type: Token::RSS)
+ )
+ respond_with_dialog(My::AccessToken::AccessTokenCreatedDialogComponent.new(token:))
rescue StandardError => e
Rails.logger.error "Failed to reset user ##{current_user.id} RSS key: #{e}"
flash[:error] = t("my.access_token.failed_to_reset_token", error: e.message)
- ensure
redirect_to action: :index, status: :see_other
end
diff --git a/app/views/my/access_tokens/_icalendar_tokens_section.html.erb b/app/views/my/access_tokens/_icalendar_tokens_section.html.erb
index 78642362331..4542186c5f6 100644
--- a/app/views/my/access_tokens/_icalendar_tokens_section.html.erb
+++ b/app/views/my/access_tokens/_icalendar_tokens_section.html.erb
@@ -41,62 +41,7 @@ See COPYRIGHT and LICENSE files for more details.
end
end %>
<% if Setting.ical_enabled? %>
- <% if ical_tokens_grouped_by_query.any? %>
-
-
-
- <%= render partial: "token_table_header",
- locals: {
- column_headers: [
- t("attributes.name"),
- Token::ICal.human_attribute_name(:calendar),
- WorkPackage.human_attribute_name(:project),
- User.human_attribute_name(:created_at),
- t("my_account.access_tokens.headers.expiration")
- ]
- } %>
-
- <% ical_tokens_grouped_by_query.each do |query_id, tokens| %>
- <% tokens.sort_by(&:created_at).each do |token| %>
-
- | <%= token.ical_token_query_assignment.name %> |
-
- <%= link_to token.query.name,
- project_calendar_url(
- id: query_id,
- project_id: token.query.project_id
- ) %>
- |
-
- <%= token.query.project.name %>
- |
-
-
- <%= format_time(token.created_at.to_s) %>
-
- |
- <%= t("my_account.access_tokens.indefinite_expiration") %> |
-
- <%= link_to "",
- { action: "revoke_ical_token", access_token_id: token.id },
- data: {
- turbo_method: :delete,
- turbo_confirm: t("my_account.access_tokens.simple_revoke_confirmation"),
- test_selector: "ical-token-row-#{token.id}-revoke"
- },
- class: "icon icon-delete" %>
- |
-
- <% end %>
- <% end %>
-
-
-
-
- <% else %>
- <%= render Primer::Beta::Octicon.new(icon: :info, mr: 1) %>
-
<%= t("my_account.access_tokens.ical.empty_text_hint") %>
- <% end %>
+ <%= render(My::AccessToken::ICal::TableComponent.new(rows: ical_tokens_grouped_by_query.values.flatten)) %>
<% else %>
diff --git a/app/views/my/access_tokens/_oauth_tokens_section.html.erb b/app/views/my/access_tokens/_oauth_tokens_section.html.erb
index c8544b4b5a5..3a55c148b58 100644
--- a/app/views/my/access_tokens/_oauth_tokens_section.html.erb
+++ b/app/views/my/access_tokens/_oauth_tokens_section.html.erb
@@ -35,57 +35,6 @@ See COPYRIGHT and LICENSE files for more details.
t("my_account.access_tokens.oauth.text_hint")
end
end %>
- <% if granted_applications.any? %>
-
-
-
- <%= render partial: "token_table_header",
- locals: {
- column_headers: [
- t("attributes.name"),
- User.human_attribute_name(:created_at),
- t("my_account.access_tokens.headers.expiration")
- ]
- } %>
-
- <% granted_applications.each do |application, tokens| %>
- <% latest = tokens.max_by(&:created_at) %>
-
- |
- <%= t("oauth.application.named", name: application.name) %>
-
- (<%= t("oauth.x_active_tokens", count: tokens.count) %>)
- |
-
- <%= format_time(latest.created_at) %>
- |
-
- <%= format_time(latest.created_at + latest.expires_in.seconds) %>
- |
-
- <%= link_to "",
- revoke_my_oauth_application_path(application_id: application.id),
- data: {
- turbo_method: :post,
- turbo_confirm: t(
- "oauth.revoke_my_application_confirmation",
- token_count: t(
- "oauth.x_active_tokens",
- count: tokens.count
- )
- ),
- test_selector: "oauth-token-row-#{application.id}-revoke"
- },
- class: "icon icon-delete" %>
- |
-
- <% end %>
-
-
-
-
- <% else %>
- <%= render Primer::Beta::Octicon.new(icon: :info, mr: 1) %>
-
<%= t("my_account.access_tokens.oauth.empty_text_hint") %>
- <% end %>
+
+ <%= render(My::AccessToken::OAuthApplication::TableComponent.new(rows: granted_applications)) %>
diff --git a/app/views/my/access_tokens/_rss_tokens_section.html.erb b/app/views/my/access_tokens/_rss_tokens_section.html.erb
deleted file mode 100644
index 3dec74bd218..00000000000
--- a/app/views/my/access_tokens/_rss_tokens_section.html.erb
+++ /dev/null
@@ -1,92 +0,0 @@
-<%#-- copyright
-OpenProject is an open source project management software.
-Copyright (C) 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.
-
-++#%>
-
diff --git a/app/views/my/access_tokens/_storage_tokens_section.html.erb b/app/views/my/access_tokens/_storage_tokens_section.html.erb
index ed1e630b72f..0ffb6390199 100644
--- a/app/views/my/access_tokens/_storage_tokens_section.html.erb
+++ b/app/views/my/access_tokens/_storage_tokens_section.html.erb
@@ -35,48 +35,6 @@ See COPYRIGHT and LICENSE files for more details.
t("my_account.access_tokens.storages.text_hint")
end
end %>
- <% if @storage_tokens.any? %>
-
-
-
- <%= render partial: "token_table_header",
- locals: {
- column_headers: [
- t("attributes.name"),
- User.human_attribute_name(:created_at),
- t("my_account.access_tokens.headers.expiration")
- ]
- } %>
-
- <% storage_tokens.each do |token| %>
-
- |
- <%= token.oauth_client.integration.name %>
- |
-
- <%= format_time(token.created_at) %>
- |
-
- <%= format_time(token.updated_at + token.expires_in.seconds) %>
- |
-
- <%= link_to "",
- my_access_token_revoke_storage_token_path(token),
- data: {
- turbo_method: :delete,
- turbo_confirm: t("my_account.access_tokens.storages.revoke_token", storage: token.oauth_client.integration.name),
- test_selector: "storages-token-row-#{token.id}-revoke"
- },
- class: "icon icon-delete" %>
- |
-
- <% end %>
-
-
-
-
- <% else %>
- <%= render Primer::Beta::Octicon.new(icon: :info, mr: 1) %>
-
<%= t("my_account.access_tokens.storages.empty_text_hint") %>
- <% end %>
+
+ <%= render(My::AccessToken::Storages::TableComponent.new(rows: @storage_tokens)) %>
diff --git a/app/views/my/access_tokens/_token_table_header.html.erb b/app/views/my/access_tokens/_token_table_header.html.erb
deleted file mode 100644
index 42978397726..00000000000
--- a/app/views/my/access_tokens/_token_table_header.html.erb
+++ /dev/null
@@ -1,52 +0,0 @@
-<%#-- copyright
-OpenProject is an open source project management software.
-Copyright (C) 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.
-
-++#%>
-
- <% column_headers&.length&.times do %>
-
- <% end %>
-
-
-
-
- <% column_headers&.each do |column_header| %>
- |
-
- |
- <% end %>
-
-
- |
-
-
diff --git a/app/views/my/access_tokens/index.html.erb b/app/views/my/access_tokens/index.html.erb
index 479fb6c3f7f..6145a34e1a5 100644
--- a/app/views/my/access_tokens/index.html.erb
+++ b/app/views/my/access_tokens/index.html.erb
@@ -43,7 +43,7 @@ See COPYRIGHT and LICENSE files for more details.
<%= render partial: "icalendar_tokens_section", locals: { ical_tokens_grouped_by_query: @ical_tokens_grouped_by_query } %>
<%= render ::My::AccessToken::APITokensSectionComponent.new(tokens: @user.ical_meeting_tokens, token_type: Token::ICalMeeting) %>
<%= render partial: "oauth_tokens_section", locals: { granted_applications: granted_applications } %>
-<%= render partial: "rss_tokens_section", locals: { rss_token: @user.rss_token } %>
+<%= render ::My::AccessToken::APITokensSectionComponent.new(tokens: Array(@user.rss_token), token_type: Token::RSS) %>
<%= render partial: "storage_tokens_section", locals: { storage_tokens: @storage_tokens } %>
diff --git a/config/locales/en.yml b/config/locales/en.yml
index d7320747fad..cfd8adbe30a 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -764,9 +764,11 @@ en:
create_button: "Create"
name_label: "Token name"
created_dialog:
+ one_time_warning: "This is the only time you will see this token. Make sure to copy it now."
token/api:
title: "The API token has been generated"
- warning: "This is the only time you will see this token. Make sure to copy it now."
+ token/rss:
+ title: "The RSS token has been generated"
failed_to_reset_token: "Failed to reset access token: %{error}"
failed_to_create_token: "Failed to create access token: %{error}"
failed_to_revoke_token: "Failed to revoke access token: %{error}"
@@ -2022,6 +2024,9 @@ en:
token/api:
one: Access token
other: Access tokens
+ token/rss:
+ one: "RSS token"
+ other: "RSS tokens"
type:
one: "Type"
other: "Types"
@@ -3068,30 +3073,44 @@ en:
indefinite_expiration: "Never"
simple_revoke_confirmation: "Are you sure you want to revoke this token?"
token/api:
+ blank_description: "There is no API token yet. You can create one using the button below."
+ blank_title: "No API token"
title: "API"
+ table_title: "API tokens"
text_hint: "API tokens allow third-party applications to communicate with this OpenProject instance via REST APIs."
+ static_token_name: "API token"
disabled_text: "API tokens are not enabled by the administrator. Please contact your administrator to use this feature."
add_button: "API Token"
- api:
- static_token_name: "API token"
ical:
+ blank_description: "To add an iCalendar token, subscribe to a new or existing calendar from within the Calendar module of a project. You must have the necessary permissions."
+ blank_title: "No iCalendar token"
title: "iCalendar"
+ table_title: "iCalendar tokens"
text_hint_link: "iCalendar tokens allow users to [subscribe to OpenProject calendars](docs_url) and view up-to-date work package information from external clients."
disabled_text: "iCalendar subscriptions are not enabled by the administrator. Please contact your administrator to use this feature."
- empty_text_hint: "To add an iCalendar token, subscribe to a new or existing calendar from within the Calendar module of a project. You must have the necessary permissions."
oauth:
+ active_tokens: "Active tokens"
+ blank_description: "There is no third-party application access configured and active for you. Please contact your administrator to activate this feature."
+ blank_title: "No OAuth token"
+ last_used_at: "Last used at"
title: "OAuth"
+ table_title: "OAuth tokens"
text_hint: "OAuth tokens allow third-party applications to connect with this OpenProject instance."
- empty_text_hint: "There is no third-party application access configured and active for you. Please contact your administrator to activate this feature."
- rss:
+ token/rss:
+ add_button: "RSS Token"
+ blank_description: "There is no RSS token yet. You can create one using the button below."
+ blank_title: "No RSS token"
title: "RSS"
+ table_title: "RSS tokens"
text_hint: "RSS tokens allow users to keep up with the latest changes in this OpenProject instance via an external RSS reader."
static_token_name: "RSS token"
disabled_text: "RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature."
storages:
+ blank_description: "There is no storage access linked to your account."
+ blank_title: "No file storage tokens"
title: "File storages"
+ table_title: "File storage tokens"
text_hint: "File Storage tokens connect this OpenProject instance with an external File Storage."
- empty_text_hint: "There is no storage access linked to your account."
revoke_token: "Do you really want to remove this token? You will need to login again on %{storage}"
removed: "File Storage token successfully removed"
failed: "An error occurred and the token couldn't be removed. Please try again later."
diff --git a/modules/bim/spec/features/bcf/api_authorization_spec.rb b/modules/bim/spec/features/bcf/api_authorization_spec.rb
index 3ed09f9e548..9669287fb26 100644
--- a/modules/bim/spec/features/bcf/api_authorization_spec.rb
+++ b/modules/bim/spec/features/bcf/api_authorization_spec.rb
@@ -121,8 +121,7 @@ RSpec.describe "authorization for BCF api",
visit my_account_path
click_on "Access token"
- expect(page).to have_css("#oauth-application-grant-#{app.id}", text: app.name)
- expect(page).to have_css("td", text: app.name)
+ expect(page).to have_test_selector("oauth-application-#{app.id}-name", text: app.name)
# While being logged in, the api can be accessed with the session
visit("/api/bcf/2.1/projects/#{project.id}")
diff --git a/modules/meeting/config/locales/en.yml b/modules/meeting/config/locales/en.yml
index 4f9c7d9fe0c..9657ed00e08 100644
--- a/modules/meeting/config/locales/en.yml
+++ b/modules/meeting/config/locales/en.yml
@@ -683,10 +683,12 @@ en:
my_account:
access_tokens:
token/ical_meeting:
+ blank_description: "You can create one using the button below."
+ blank_title: "No iCalendar meeting token"
title: "iCalendar for meetings"
+ table_title: "iCalendar meeting tokens"
text_hint: "iCalendar meeting tokens allow users to subscribe to all their meetings and view up-to-date meeting information in external clients."
disabled_text: "iCalendar meeting subscriptions are not enabled by the administrator. Please contact your administrator to use this feature."
- empty_text_hint: 'To add an iCalendar meeting token, go to the
meetings section and subscribe from there.'
add_button: "Subscribe to calendar"
my:
@@ -702,7 +704,6 @@ en:
token/ical_meeting:
title: "An iCal meeting subscription token has been generated"
body: "Treat the following URL as you would a password. Anyone who has access to it can view all your meetings."
- warning: "This is the only time you will see this token. Make sure to copy it now."
revocation:
token/ical_meeting:
notice_success: "The iCalendar meeting subscription has been revoked successfully."
diff --git a/spec/controllers/my/access_tokens_controller_spec.rb b/spec/controllers/my/access_tokens_controller_spec.rb
index 32339fcaf27..ce3b60c02b5 100644
--- a/spec/controllers/my/access_tokens_controller_spec.rb
+++ b/spec/controllers/my/access_tokens_controller_spec.rb
@@ -41,12 +41,13 @@ RSpec.describe My::AccessTokensController do
it "creates a key" do
expect(user.rss_token).to be_nil
- post :generate_rss_key
+ post :generate_rss_key, format: :turbo_stream
expect(user.reload.rss_token).to be_present
- expect(flash[:info]).to be_present
- expect(flash[:error]).not_to be_present
- expect(response).to redirect_to action: :index
+ expect(flash[:error]).to be_blank
+
+ expect(response).to be_successful
+ expect(response.body).to include(user.rss_token.value)
end
context "with existing key" do
@@ -55,15 +56,15 @@ RSpec.describe My::AccessTokensController do
it "replaces the key" do
expect(user.rss_token).to eq(key)
- post :generate_rss_key
+ post :generate_rss_key, format: :turbo_stream
new_token = user.reload.rss_token
expect(new_token).not_to eq(key)
expect(new_token.value).not_to eq(key.value)
expect(new_token.value).to eq(user.rss_key)
- expect(flash[:info]).to be_present
expect(flash[:error]).not_to be_present
- expect(response).to redirect_to action: :index
+ expect(response).to be_successful
+ expect(response.body).to include(new_token.value)
end
end
end
diff --git a/spec/features/oauth/authorization_code_flow_spec.rb b/spec/features/oauth/authorization_code_flow_spec.rb
index 3e7823fab89..d0c19fe9bbe 100644
--- a/spec/features/oauth/authorization_code_flow_spec.rb
+++ b/spec/features/oauth/authorization_code_flow_spec.rb
@@ -91,20 +91,16 @@ RSpec.describe "OAuth authorization code flow", :js, :selenium do
visit my_account_path
click_on "Access token"
- expect(page).to have_css("#oauth-application-grant-#{app.id}", text: app.name)
- expect(page).to have_css("td", text: app.name)
+ expect(page).to have_test_selector("oauth-application-#{app.id}-name", text: app.name)
# Revoke the application
- within("#oauth-application-grant-#{app.id}") do
- SeleniumHubWaiter.wait
- find_test_selector("oauth-token-row-#{app.id}-revoke").click
- end
+ find_test_selector("oauth-token-row-#{app.id}-revoke").click
page.driver.browser.switch_to.alert.accept
# Should be back on access_token path
expect_flash(message: "Revocation of application Cool API app! successful.")
- expect(page).to have_no_css("[id^=oauth-application-grant]")
+ expect(page).to have_no_test_selector("oauth-application-#{app.id}-name")
expect(page).to have_current_path /\/my\/access_token/
diff --git a/spec/features/oauth/pkce_spec.rb b/spec/features/oauth/pkce_spec.rb
index f9c31ef21cd..1a7d59c6915 100644
--- a/spec/features/oauth/pkce_spec.rb
+++ b/spec/features/oauth/pkce_spec.rb
@@ -108,7 +108,6 @@ RSpec.describe "OAuth authorization code flow with PKCE", :js do
visit my_account_path
click_on "Access token"
- expect(page).to have_css("#oauth-application-grant-#{app.id}", text: app.name)
- expect(page).to have_css("td", text: app.name)
+ expect(page).to have_test_selector("oauth-application-#{app.id}-name", text: app.name)
end
end
diff --git a/spec/features/users/my/access_tokens_spec.rb b/spec/features/users/my/access_tokens_spec.rb
index e712360cce8..237bf0c5131 100644
--- a/spec/features/users/my/access_tokens_spec.rb
+++ b/spec/features/users/my/access_tokens_spec.rb
@@ -117,9 +117,9 @@ RSpec.describe "my access tokens", :js do
it "shows notice about disabled token" do
visit my_access_tokens_path
- within "#rss-token-section" do
+ within "#rss-token-component" do
expect(page).to have_content("RSS tokens are not enabled by the administrator.")
- expect(page).not_to have_test_selector("rss-token-add", text: "RSS token")
+ expect(page).not_to have_test_selector("rss-token-add", text: "RSS Token")
end
end
end
@@ -130,25 +130,25 @@ RSpec.describe "my access tokens", :js do
expect(page).to have_no_content("RSS tokens are not enabled by the administrator.")
- within "#rss-token-section" do
- expect(page).to have_test_selector("rss-token-add", text: "RSS token")
+ within "#rss-token-component" do
+ expect(page).to have_test_selector("rss-token-add", text: "RSS Token")
find_test_selector("rss-token-add").click
end
- expect(page).to have_content "A new RSS token has been generated. Your access token is"
+ expect(page).to have_content "The RSS token has been generated"
User.current.reload
visit my_access_tokens_path
# only one RSS token can be created
- within "#rss-token-section" do
- expect(page).not_to have_test_selector("rss-token-add", text: "RSS token")
+ within "#rss-token-component" do
+ expect(page).not_to have_test_selector("rss-token-add", text: "RSS Token")
end
# revoke RSS token
- within "#rss-token-section" do
+ within "#rss-token-component" do
accept_confirm do
- find_test_selector("rss-token-revoke").click
+ find_test_selector("api-token-revoke").click
end
end
@@ -158,8 +158,8 @@ RSpec.describe "my access tokens", :js do
visit my_access_tokens_path
# RSS token can be created again
- within "#rss-token-section" do
- expect(page).to have_test_selector("rss-token-add", text: "RSS token")
+ within "#rss-token-component" do
+ expect(page).to have_test_selector("rss-token-add", text: "RSS Token")
end
end
end
@@ -209,9 +209,9 @@ RSpec.describe "my access tokens", :js do
token_name = ical_token.ical_token_query_assignment.name
query = ical_token.ical_token_query_assignment.query
- expect(page).to have_test_selector("ical-token-row-#{ical_token.id}-name", text: token_name)
- expect(page).to have_test_selector("ical-token-row-#{ical_token.id}-query-name", text: query.name)
- expect(page).to have_test_selector("ical-token-row-#{ical_token.id}-project-name",
+ expect(page).to have_test_selector("ical-token-#{ical_token.id}-name", text: token_name)
+ expect(page).to have_test_selector("ical-token-#{ical_token.id}-query-name", text: query.name)
+ expect(page).to have_test_selector("ical-token-#{ical_token.id}-project-name",
text: query.project.name)
end
end
@@ -222,7 +222,7 @@ RSpec.describe "my access tokens", :js do
within "#icalendar-token-section" do
accept_confirm do
- find_test_selector("ical-token-row-#{ical_token_for_query.id}-revoke").click
+ find_test_selector("ical-token-#{ical_token_for_query.id}-revoke").click
end
end
@@ -232,7 +232,7 @@ RSpec.describe "my access tokens", :js do
visit my_access_tokens_path
within "#icalendar-token-section" do
- expect(page).not_to have_test_selector("ical-token-row-#{ical_token_for_query.id}-revoke")
+ expect(page).not_to have_test_selector("ical-token-#{ical_token_for_query.id}-revoke")
end
end
end
@@ -347,9 +347,9 @@ RSpec.describe "my access tokens", :js do
visit my_access_tokens_path
[app, second_app].each do |app|
- within "#oauth-token-section" do
- expect(page).to have_test_selector("oauth-token-row-#{app.id}-name", text: app.name)
- expect(page).to have_test_selector("oauth-token-row-#{app.id}-name", text: "(one active token)")
+ within "#oauth-application-token-section" do
+ expect(page).to have_test_selector("oauth-application-#{app.id}-name", text: app.name)
+ expect(page).to have_test_selector("oauth-application-#{app.id}-active-tokens", text: "1")
end
end
end
@@ -393,9 +393,9 @@ RSpec.describe "my access tokens", :js do
visit my_access_tokens_path
[app, second_app].each do |app|
- within "#oauth-token-section" do
- expect(page).to have_test_selector("oauth-token-row-#{app.id}-name", text: app.name)
- expect(page).to have_test_selector("oauth-token-row-#{app.id}-name", text: "(2 active token)")
+ within "#oauth-application-token-section" do
+ expect(page).to have_test_selector("oauth-application-#{app.id}-name", text: app.name)
+ expect(page).to have_test_selector("oauth-application-#{app.id}-active-tokens", text: "2")
end
end
end