mirror of
https://github.com/opf/openproject.git
synced 2026-06-13 19:20:00 +00:00
Use BorderBoxTable to render access token information
This is part of primerizing the access tokens view and makes everything look more consistent.
This commit is contained in:
@@ -67,7 +67,7 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
end
|
||||
flex.with_row do
|
||||
render(Primer::Alpha::Banner.new(scheme: :warning, icon: :alert)) do
|
||||
I18n.t(:warning, scope: i18n_scope)
|
||||
I18n.t("my.access_token.created_dialog.one_time_warning")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#
|
||||
# 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.
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# See COPYRIGHT and LICENSE files for more details.
|
||||
#++
|
||||
@@ -31,14 +31,14 @@
|
||||
module My
|
||||
module AccessToken
|
||||
module API
|
||||
class RowComponent < ::RowComponent
|
||||
class RowComponent < OpPrimer::BorderBoxRowComponent
|
||||
def api_token
|
||||
model
|
||||
end
|
||||
|
||||
def token_name
|
||||
if api_token.token_name.nil?
|
||||
t("my_account.access_tokens.api.static_token_name")
|
||||
if !api_token.respond_to?(:token_name) || api_token.token_name.nil?
|
||||
t(:static_token_name, scope: i18n_token_scope)
|
||||
else
|
||||
api_token.token_name
|
||||
end
|
||||
@@ -57,27 +57,33 @@ module My
|
||||
end
|
||||
|
||||
def delete_link
|
||||
link_to "",
|
||||
{
|
||||
action: delete_action,
|
||||
access_token_id: api_token.id
|
||||
},
|
||||
data: {
|
||||
turbo_method: :delete,
|
||||
turbo_confirm: t("my_account.access_tokens.simple_revoke_confirmation"),
|
||||
test_selector: "api-token-revoke"
|
||||
},
|
||||
class: "icon icon-delete"
|
||||
render(Primer::Beta::IconButton.new(
|
||||
icon: :trash,
|
||||
scheme: :danger,
|
||||
tag: :a,
|
||||
href: delete_path,
|
||||
"aria-label": t(:button_delete),
|
||||
test_selector: "api-token-revoke",
|
||||
data: {
|
||||
turbo_method: :delete,
|
||||
turbo_confirm: t("my_account.access_tokens.simple_revoke_confirmation")
|
||||
}
|
||||
))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def delete_action
|
||||
def delete_path
|
||||
case model
|
||||
when Token::API then :revoke_api_key
|
||||
when Token::ICalMeeting then :revoke_ical_meeting_token
|
||||
when Token::API then my_access_token_revoke_api_key_path(api_token.id)
|
||||
when Token::ICalMeeting then my_access_token_revoke_ical_meeting_token_path(api_token.id)
|
||||
when Token::RSS then revoke_rss_key_my_access_tokens_path(api_token.id)
|
||||
end
|
||||
end
|
||||
|
||||
def i18n_token_scope
|
||||
[:my_account, :access_tokens, api_token.class.model_name.i18n_key]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -31,25 +31,54 @@
|
||||
module My
|
||||
module AccessToken
|
||||
module API
|
||||
class TableComponent < ::TableComponent
|
||||
def initial_sort
|
||||
%i[id asc]
|
||||
end
|
||||
class TableComponent < OpPrimer::BorderBoxTableComponent
|
||||
columns :token_name, :created_at, :expires_on
|
||||
main_column :token_name
|
||||
mobile_labels :created_at, :expires_on
|
||||
|
||||
def sortable?
|
||||
false
|
||||
def initialize(title:, token_type:, **)
|
||||
super(**)
|
||||
|
||||
@title = title
|
||||
@token_type = token_type
|
||||
end
|
||||
|
||||
def headers
|
||||
[
|
||||
["token_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") }]
|
||||
[:token_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 columns
|
||||
headers.map(&:first)
|
||||
def mobile_title
|
||||
@title
|
||||
end
|
||||
|
||||
def row_class
|
||||
RowComponent
|
||||
end
|
||||
|
||||
def has_actions?
|
||||
true
|
||||
end
|
||||
|
||||
def blank_title
|
||||
I18n.t(:blank_title, scope: i18n_token_scope)
|
||||
end
|
||||
|
||||
def blank_description
|
||||
I18n.t(:blank_description, scope: i18n_token_scope)
|
||||
end
|
||||
|
||||
def blank_icon
|
||||
nil
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def i18n_token_scope
|
||||
[:my_account, :access_tokens, @token_type.model_name.i18n_key]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -38,25 +38,25 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
end %>
|
||||
|
||||
<% if token_available? %>
|
||||
<% if @tokens.any? %>
|
||||
<%= render My::AccessToken::API::TableComponent.new(rows: @tokens) %>
|
||||
<%= render My::AccessToken::API::TableComponent.new(rows: @tokens, title: t(:table_title, scope: i18n_scope), token_type:) %>
|
||||
<% if show_add_button? %>
|
||||
<%= render(
|
||||
Primer::Beta::Button.new(
|
||||
tag: :a,
|
||||
mt: 3,
|
||||
scheme: :secondary,
|
||||
test_selector: "#{token_type.model_name.element}-token-add",
|
||||
href: add_button_path,
|
||||
data: { turbo_stream: true, turbo_method: add_button_method },
|
||||
aria: { label: t(:add_button, scope: i18n_scope) },
|
||||
role: "button",
|
||||
title: t(:add_button, scope: i18n_scope)
|
||||
)
|
||||
) do |button|
|
||||
button.with_leading_visual_icon(icon: add_button_icon)
|
||||
t(:add_button, scope: i18n_scope)
|
||||
end %>
|
||||
<% end %>
|
||||
<%= render(
|
||||
Primer::Beta::Button.new(
|
||||
tag: :a,
|
||||
mt: 3,
|
||||
scheme: :secondary,
|
||||
test_selector: "#{token_type.model_name.element}-token-add",
|
||||
href: dialog_my_access_tokens_path(token_type: token_type.model_name.element),
|
||||
data: { turbo_stream: true },
|
||||
aria: { label: t(:add_button, scope: i18n_scope) },
|
||||
role: "button",
|
||||
title: t(:add_button, scope: i18n_scope)
|
||||
)
|
||||
) do |button|
|
||||
button.with_leading_visual_icon(icon: add_button_icon)
|
||||
t(:add_button, scope: i18n_scope)
|
||||
end %>
|
||||
<% else %>
|
||||
<div tabindex="0" class="-info op-toast">
|
||||
<div role="alert" aria-atomic="true" class="op-toast--content">
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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? %>
|
||||
<div class="generic-table--container">
|
||||
<div class="generic-table--results-container">
|
||||
<table class="generic-table" data-controller="table-highlighting">
|
||||
<%= 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")
|
||||
]
|
||||
} %>
|
||||
<tbody>
|
||||
<% ical_tokens_grouped_by_query.each do |query_id, tokens| %>
|
||||
<% tokens.sort_by(&:created_at).each do |token| %>
|
||||
<tr>
|
||||
<td class="-w-rel-20 -mw-abs-200" data-test-selector="ical-token-row-<%= token.id %>-name"><%= token.ical_token_query_assignment.name %></td>
|
||||
<td class="-w-rel-20 -mw-abs-200" data-test-selector="ical-token-row-<%= token.id %>-query-name">
|
||||
<%= link_to token.query.name,
|
||||
project_calendar_url(
|
||||
id: query_id,
|
||||
project_id: token.query.project_id
|
||||
) %>
|
||||
</td>
|
||||
<td class="-w-rel-20 -mw-abs-200" data-test-selector="ical-token-row-<%= token.id %>-project-name">
|
||||
<%= token.query.project.name %>
|
||||
</td>
|
||||
<td>
|
||||
<span title="<%= format_time(token.created_at) %>">
|
||||
<%= format_time(token.created_at.to_s) %>
|
||||
</span>
|
||||
</td>
|
||||
<td><%= t("my_account.access_tokens.indefinite_expiration") %></td>
|
||||
<td class="buttons">
|
||||
<%= 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" %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<% else %>
|
||||
<%= render Primer::Beta::Octicon.new(icon: :info, mr: 1) %>
|
||||
<i><%= t("my_account.access_tokens.ical.empty_text_hint") %></i>
|
||||
<% end %>
|
||||
<%= render(My::AccessToken::ICal::TableComponent.new(rows: ical_tokens_grouped_by_query.values.flatten)) %>
|
||||
<% else %>
|
||||
<div tabindex="0" class="-info op-toast">
|
||||
<div role="alert" aria-atomic="true" class="op-toast--content">
|
||||
|
||||
@@ -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? %>
|
||||
<div class="generic-table--container">
|
||||
<div class="generic-table--results-container">
|
||||
<table class="generic-table" data-controller="table-highlighting">
|
||||
<%= render partial: "token_table_header",
|
||||
locals: {
|
||||
column_headers: [
|
||||
t("attributes.name"),
|
||||
User.human_attribute_name(:created_at),
|
||||
t("my_account.access_tokens.headers.expiration")
|
||||
]
|
||||
} %>
|
||||
<tbody>
|
||||
<% granted_applications.each do |application, tokens| %>
|
||||
<% latest = tokens.max_by(&:created_at) %>
|
||||
<tr id="oauth-application-grant-<%= application.id %>">
|
||||
<td class="-w-rel-60" data-test-selector="oauth-token-row-<%= application.id %>-name">
|
||||
<%= t("oauth.application.named", name: application.name) %>
|
||||
|
||||
(<%= t("oauth.x_active_tokens", count: tokens.count) %>)
|
||||
</td>
|
||||
<td>
|
||||
<span><%= format_time(latest.created_at) %></span>
|
||||
</td>
|
||||
<td>
|
||||
<span><%= format_time(latest.created_at + latest.expires_in.seconds) %></span>
|
||||
</td>
|
||||
<td class="buttons">
|
||||
<%= 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" %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<% else %>
|
||||
<%= render Primer::Beta::Octicon.new(icon: :info, mr: 1) %>
|
||||
<i><%= t("my_account.access_tokens.oauth.empty_text_hint") %></i>
|
||||
<% end %>
|
||||
|
||||
<%= render(My::AccessToken::OAuthApplication::TableComponent.new(rows: granted_applications)) %>
|
||||
</div>
|
||||
|
||||
@@ -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.
|
||||
|
||||
++#%>
|
||||
<div class="attributes-group" id="rss-token-section">
|
||||
<%= render(Primer::Beta::Subhead.new) do |component|
|
||||
component.with_heading(size: :medium) do
|
||||
t("my_account.access_tokens.rss.title")
|
||||
end
|
||||
component.with_description do
|
||||
t("my_account.access_tokens.rss.text_hint")
|
||||
end
|
||||
end %>
|
||||
<% if Setting.feeds_enabled? %>
|
||||
<% if rss_token %>
|
||||
<div class="generic-table--container">
|
||||
<div class="generic-table--results-container">
|
||||
<table class="generic-table" data-controller="table-highlighting">
|
||||
<%= render partial: "token_table_header",
|
||||
locals: {
|
||||
column_headers: [
|
||||
t("attributes.name"),
|
||||
User.human_attribute_name(:created_at),
|
||||
t("my_account.access_tokens.headers.expiration")
|
||||
]
|
||||
} %>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="-w-rel-60" data-test-selector="rss-token-row-name"><%= t("my_account.access_tokens.rss.static_token_name") %></td>
|
||||
<td>
|
||||
<span title="<%= format_time(rss_token.created_at) %>">
|
||||
<%= format_time(rss_token.created_at.to_s) %>
|
||||
</span>
|
||||
</td>
|
||||
<td><%= t("my_account.access_tokens.indefinite_expiration") %></td>
|
||||
<td class="buttons">
|
||||
<%= link_to "",
|
||||
{ action: "revoke_rss_key" },
|
||||
data: {
|
||||
turbo_method: :delete,
|
||||
turbo_confirm: t("my_account.access_tokens.simple_revoke_confirmation"),
|
||||
test_selector: "rss-token-revoke"
|
||||
},
|
||||
class: "icon icon-delete" %>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<% else %>
|
||||
<%=
|
||||
render Primer::Beta::Link.new(href: generate_rss_key_my_access_tokens_path, data: { turbo_method: :post }, test_selector: "rss-token-add") do |link|
|
||||
link.with_leading_visual_icon(icon: :plus)
|
||||
t("my_account.access_tokens.rss.static_token_name")
|
||||
end
|
||||
%>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<div tabindex="0" class="-info op-toast">
|
||||
<div role="alert" aria-atomic="true" class="op-toast--content">
|
||||
<p>
|
||||
<span><%= t("my_account.access_tokens.rss.disabled_text") %></span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
@@ -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? %>
|
||||
<div class="generic-table--container">
|
||||
<div class="generic-table--results-container">
|
||||
<table class="generic-table" data-controller="table-highlighting">
|
||||
<%= render partial: "token_table_header",
|
||||
locals: {
|
||||
column_headers: [
|
||||
t("attributes.name"),
|
||||
User.human_attribute_name(:created_at),
|
||||
t("my_account.access_tokens.headers.expiration")
|
||||
]
|
||||
} %>
|
||||
<tbody>
|
||||
<% storage_tokens.each do |token| %>
|
||||
<tr id="storage-oauth-token-<%= token.id %>">
|
||||
<td class="-w-rel-60" data-test-selector="oauth-token-row-<%= token.oauth_client.integration.id %>-name">
|
||||
<%= token.oauth_client.integration.name %>
|
||||
</td>
|
||||
<td>
|
||||
<span><%= format_time(token.created_at) %></span>
|
||||
</td>
|
||||
<td>
|
||||
<span><%= format_time(token.updated_at + token.expires_in.seconds) %></span>
|
||||
</td>
|
||||
<td class="buttons">
|
||||
<%= 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" %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<% else %>
|
||||
<%= render Primer::Beta::Octicon.new(icon: :info, mr: 1) %>
|
||||
<i><%= t("my_account.access_tokens.storages.empty_text_hint") %></i>
|
||||
<% end %>
|
||||
|
||||
<%= render(My::AccessToken::Storages::TableComponent.new(rows: @storage_tokens)) %>
|
||||
</div>
|
||||
|
||||
@@ -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.
|
||||
|
||||
++#%>
|
||||
<colgroup>
|
||||
<% column_headers&.length&.times do %>
|
||||
<col>
|
||||
<% end %>
|
||||
<col>
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<% column_headers&.each do |column_header| %>
|
||||
<th>
|
||||
<div class="generic-table--sort-header-outer">
|
||||
<div class="generic-table--sort-header">
|
||||
<%= column_header %>
|
||||
</div>
|
||||
</div>
|
||||
</th>
|
||||
<% end %>
|
||||
<th>
|
||||
<div class="generic-table--sort-header-outer">
|
||||
<div class="generic-table--sort-header"></div>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -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 } %>
|
||||
<div class="generic-table--container">
|
||||
<div class="generic-table--results-container">
|
||||
|
||||
+26
-7
@@ -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."
|
||||
|
||||
@@ -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}")
|
||||
|
||||
@@ -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 <a href="%{path}">meetings section</a> 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."
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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/
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user