mirror of
https://github.com/opf/openproject.git
synced 2026-06-14 03:30:14 +00:00
Merge branch 'dev' into implementation/61533-extend-data-model-to-allow-logging-time-on-other-resources
This commit is contained in:
@@ -138,9 +138,6 @@ gem "rack-protection", "~> 3.2.0"
|
||||
# https://github.com/kickstarter/rack-attack
|
||||
gem "rack-attack", "~> 6.7.0"
|
||||
|
||||
# CSP headers
|
||||
gem "secure_headers", "~> 7.1.0"
|
||||
|
||||
# Browser detection for incompatibility checks
|
||||
gem "browser", "~> 6.2.0"
|
||||
|
||||
@@ -217,7 +214,6 @@ gem "mini_magick", "~> 5.2.0", require: false
|
||||
gem "validate_url"
|
||||
|
||||
# Storages support code
|
||||
gem "dry-auto_inject"
|
||||
gem "dry-container"
|
||||
gem "dry-monads"
|
||||
gem "dry-validation"
|
||||
|
||||
@@ -481,9 +481,6 @@ GEM
|
||||
dotenv (= 3.1.8)
|
||||
railties (>= 6.1)
|
||||
drb (2.2.3)
|
||||
dry-auto_inject (1.1.0)
|
||||
dry-core (~> 1.1)
|
||||
zeitwerk (~> 2.6)
|
||||
dry-configurable (1.3.0)
|
||||
dry-core (~> 1.1)
|
||||
zeitwerk (~> 2.6)
|
||||
@@ -1167,7 +1164,6 @@ GEM
|
||||
nokogiri (>= 1.16.8)
|
||||
scimitar (2.11.0)
|
||||
rails (>= 7.0)
|
||||
secure_headers (7.1.0)
|
||||
securerandom (0.4.1)
|
||||
selenium-devtools (0.138.0)
|
||||
selenium-webdriver (~> 4.2)
|
||||
@@ -1383,7 +1379,6 @@ DEPENDENCIES
|
||||
disposable (~> 0.6.2)
|
||||
doorkeeper (~> 5.8.0)
|
||||
dotenv-rails
|
||||
dry-auto_inject
|
||||
dry-container
|
||||
dry-monads
|
||||
dry-validation
|
||||
@@ -1516,7 +1511,6 @@ DEPENDENCIES
|
||||
rubytree (~> 2.1.0)
|
||||
sanitize (~> 7.0.0)
|
||||
scimitar (~> 2.11)
|
||||
secure_headers (~> 7.1.0)
|
||||
selenium-devtools
|
||||
selenium-webdriver (~> 4.20)
|
||||
semantic (~> 1.6.1)
|
||||
@@ -1658,7 +1652,6 @@ CHECKSUMS
|
||||
dotenv (3.1.8) sha256=9e1176060ced581f8e6ce4384e91361817763a76e3c625c8bddc18b35bd392c3
|
||||
dotenv-rails (3.1.8) sha256=46c9d1226a8b58a83b5f61325aa8cffd25cea1c0fafdfbbbee1e5dfea77980c4
|
||||
drb (2.2.3) sha256=0b00d6fdb50995fe4a45dea13663493c841112e4068656854646f418fda13373
|
||||
dry-auto_inject (1.1.0) sha256=f9276cb5d15a3ef138e1f1149e289e287f636de57ef4a6decd233542eb708f78
|
||||
dry-configurable (1.3.0) sha256=882d862858567fc1210d2549d4c090f34370fc1bb7c5c1933de3fe792e18afa8
|
||||
dry-container (0.11.0) sha256=23be9381644d47343f3bf13b082b4255994ada0bfd88e0737eaaadc99d035229
|
||||
dry-core (1.1.0) sha256=0903821a9707649a7da545a2cd88e20f3a663ab1c5288abd7f914fa7751ab195
|
||||
@@ -1949,7 +1942,6 @@ CHECKSUMS
|
||||
safety_net_attestation (0.4.0) sha256=96be2d74e7ed26453a51894913449bea0e072f44490021545ac2d1c38b0718ce
|
||||
sanitize (7.0.0) sha256=269d1b9d7326e69307723af5643ec032ff86ad616e72a3b36d301ac75a273984
|
||||
scimitar (2.11.0) sha256=77cf779a843be7d572046acdcf0a1829bd3b1c33db993fa83faf7f1863d8c625
|
||||
secure_headers (7.1.0) sha256=6b1f9d5f9507af2948f4636452c41c09371927836396c2185438ffdf0a731124
|
||||
securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1
|
||||
selenium-devtools (0.138.0) sha256=596c08e114342dd89513bc3d18bbb2ae39e532864dbc25c09a48fb9922fc5b7b
|
||||
selenium-webdriver (4.34.0) sha256=ec7bb718cbe66fe2b247d8ca5e6ba26caed0976d76579d7cb2fadd8dae8b271e
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
mobile_label: I18n.t(:label_setting_plural),
|
||||
href: my_notifications_path,
|
||||
size: :medium,
|
||||
target: "_blank",
|
||||
aria: { label: I18n.t("js.notifications.settings.title") }
|
||||
) do |button|
|
||||
button.with_leading_visual_icon(icon: :gear)
|
||||
|
||||
@@ -67,8 +67,9 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
component.with_row(scheme: :default) { render_blank_slate }
|
||||
else
|
||||
rows.each do |row|
|
||||
component.with_row(scheme: :default) do
|
||||
render(row_class.new(row:, table: self))
|
||||
row_instance = row_class.new(row: row, table: self)
|
||||
component.with_row(scheme: row_instance.scheme) do
|
||||
render(row_instance)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -68,6 +68,10 @@ class RowComponent < ApplicationComponent
|
||||
nil
|
||||
end
|
||||
|
||||
def scheme
|
||||
:default
|
||||
end
|
||||
|
||||
def checkmark(condition)
|
||||
if condition
|
||||
helpers.op_icon "icon icon-checkmark"
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
# 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 WorkPackageTypes
|
||||
module Types
|
||||
class RowComponent < ::RowComponent
|
||||
include ApplicationHelper
|
||||
include TypesHelper
|
||||
|
||||
def column_css_classes
|
||||
super.merge(
|
||||
name: "timelines-pet-name",
|
||||
color: "timelines-pet-color",
|
||||
default: "timelines-pet-is_default",
|
||||
milestone: "timelines-pet-is_milestone",
|
||||
sort: "timelines-pet-reorder"
|
||||
)
|
||||
end
|
||||
|
||||
def name
|
||||
link_to model.name, edit_type_settings_path(type_id: model.id)
|
||||
end
|
||||
|
||||
def workflow_warning
|
||||
return unless model.workflows.empty?
|
||||
|
||||
safe_join([
|
||||
op_icon("icon3 icon-warning"),
|
||||
t(:text_type_no_workflow),
|
||||
" (",
|
||||
link_to(t(:button_edit), edit_workflows_path(type: model)),
|
||||
")"
|
||||
])
|
||||
end
|
||||
|
||||
def color
|
||||
icon_for_type model
|
||||
end
|
||||
|
||||
def default
|
||||
checked_image model.is_default
|
||||
end
|
||||
|
||||
def milestone
|
||||
checked_image model.is_milestone
|
||||
end
|
||||
|
||||
def sort
|
||||
helpers.reorder_links("type", { action: "move", id: model })
|
||||
end
|
||||
|
||||
def button_links
|
||||
[delete_link]
|
||||
end
|
||||
|
||||
def delete_link
|
||||
return if model.is_standard?
|
||||
|
||||
link_to(
|
||||
"",
|
||||
model,
|
||||
method: :delete,
|
||||
data: { confirm: t(:text_are_you_sure) },
|
||||
class: "icon icon-delete",
|
||||
title: t(:button_delete)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
+25
-20
@@ -28,28 +28,33 @@
|
||||
# See COPYRIGHT and LICENSE files for more details.
|
||||
#++
|
||||
|
||||
module Storages
|
||||
module Peripherals
|
||||
module StorageInteraction
|
||||
module Inputs
|
||||
class SetPermissionsContract < Dry::Validation::Contract
|
||||
params do
|
||||
required(:file_id).filled(:string)
|
||||
required(:user_permissions).array(:hash) do
|
||||
optional(:user_id).filled(:string)
|
||||
optional(:group_id).filled(:string)
|
||||
required(:permissions)
|
||||
.array(:symbol, included_in?: OpenProject::Storages::Engine.external_file_permissions)
|
||||
end
|
||||
end
|
||||
module WorkPackageTypes
|
||||
module Types
|
||||
class TableComponent < ::TableComponent
|
||||
columns :name, :color, :workflow_warning, :default, :milestone, :sort
|
||||
|
||||
rule(:user_permissions).each do
|
||||
both = value.key?(:user_id) && value.key?(:group_id)
|
||||
none = !value.key?(:user_id) && !value.key?(:group_id)
|
||||
def headers
|
||||
[
|
||||
[:name, { caption: Type.human_attribute_name(:name) }],
|
||||
[:color, { caption: Type.human_attribute_name(:color) }],
|
||||
[:workflow_warning, { caption: "Workflow" }],
|
||||
[:default, { caption: I18n.t(:label_active_in_new_projects) }],
|
||||
[:milestone, { caption: Type.human_attribute_name(:is_milestone) }],
|
||||
[:sort, { caption: I18n.t(:button_sort) }]
|
||||
]
|
||||
end
|
||||
|
||||
key.failure("must have either user_id or group_id") if both || none
|
||||
end
|
||||
end
|
||||
def header_options(name)
|
||||
headers_hash = headers.to_h
|
||||
headers_hash[name.to_sym] || { caption: name.to_s }
|
||||
end
|
||||
|
||||
def mobile_title
|
||||
I18n.t(:label_type_plural)
|
||||
end
|
||||
|
||||
def sortable?
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -36,6 +36,8 @@ module WorkPackageTypes
|
||||
attribute :is_in_roadmap
|
||||
attribute :is_milestone
|
||||
attribute :name
|
||||
attribute :project_ids
|
||||
attribute :attribute_groups
|
||||
|
||||
validates :name, presence: true, length: { maximum: 255 }
|
||||
validates :is_default, :is_milestone, :is_in_roadmap, inclusion: { in: [true, false] }
|
||||
|
||||
@@ -56,6 +56,7 @@ class ApplicationController < ActionController::Base
|
||||
include OpenProjectErrorHelper
|
||||
include Security::DefaultUrlOptions
|
||||
include OpModalFlashable
|
||||
include DynamicContentSecurityPolicy
|
||||
|
||||
layout "base"
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
# 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 DynamicContentSecurityPolicy
|
||||
##
|
||||
# Dynamically append sources to CSP directives
|
||||
# This replaces the secure_headers named append functionality
|
||||
def append_content_security_policy_directives(directives)
|
||||
policy = current_content_security_policy
|
||||
directives.each do |directive, source_values|
|
||||
current_value = policy.send(directive) || policy.directives["default-src"]
|
||||
new_values =
|
||||
if current_value == %w('none') # rubocop:disable Lint/PercentStringArray
|
||||
source_values.compact.uniq
|
||||
else
|
||||
(current_value + source_values).compact.uniq
|
||||
end
|
||||
|
||||
policy.send(directive, *new_values)
|
||||
request.content_security_policy = policy
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -118,6 +118,10 @@ module OpTurbo
|
||||
.render_in(view_context)
|
||||
end
|
||||
|
||||
def reload_page_via_turbo_stream
|
||||
turbo_streams << OpTurbo::StreamComponent.new(action: :reloadPage, target: nil).render_in(view_context)
|
||||
end
|
||||
|
||||
def turbo_streams
|
||||
@turbo_streams ||= []
|
||||
end
|
||||
|
||||
@@ -82,8 +82,7 @@ class OAuthClientsController < ApplicationController
|
||||
storage = oauth_client.integration
|
||||
# check if the origin is the same
|
||||
destination_url = destination_url(params.fetch(:destination_url, ""))
|
||||
auth_state = ::Storages::Peripherals::StorageInteraction::Authentication
|
||||
.authorization_state(storage:, user: User.current)
|
||||
auth_state = ::Storages::Adapters::Authentication.authorization_state(storage:, user: User.current)
|
||||
|
||||
if auth_state == :connected
|
||||
redirect_to(destination_url)
|
||||
|
||||
@@ -30,6 +30,6 @@ class Projects::Settings::VersionsController < Projects::SettingsController
|
||||
menu_item :settings_versions
|
||||
|
||||
def show
|
||||
@versions = @project.shared_versions.order_by_semver_name
|
||||
@versions = @project.shared_versions.order(:name)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -255,8 +255,7 @@ class RepositoriesController < ApplicationController
|
||||
end
|
||||
|
||||
def stats
|
||||
# allow object_src self to be able to load dynamic stats SVGs from ./graph
|
||||
override_content_security_policy_directives object_src: %w('self')
|
||||
append_content_security_policy_directives object_src: %w('self') # rubocop:disable Lint/PercentStringArray
|
||||
|
||||
@show_commits_per_author = current_user.allowed_in_project?(:view_commit_author_statistics, @project)
|
||||
end
|
||||
|
||||
@@ -49,7 +49,7 @@ class SearchController < ApplicationController
|
||||
|
||||
provision_gon
|
||||
|
||||
render layout: layout_non_or_no_menu
|
||||
render "index", locals: { menu_name: project_or_global_menu }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -1,170 +0,0 @@
|
||||
# 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.
|
||||
#++
|
||||
|
||||
class TypesController < ApplicationController
|
||||
include PaginationHelper
|
||||
|
||||
layout "admin"
|
||||
|
||||
before_action :require_admin
|
||||
before_action :find_type, only: %i[update move destroy]
|
||||
|
||||
def index
|
||||
@types = ::Type.page(page_param).per_page(per_page_param)
|
||||
end
|
||||
|
||||
def type
|
||||
@type
|
||||
end
|
||||
|
||||
def new
|
||||
@type = Type.new(params[:type])
|
||||
load_projects_and_types
|
||||
end
|
||||
|
||||
def edit
|
||||
if params[:tab].blank?
|
||||
redirect_to tab: :settings
|
||||
else
|
||||
type = ::Type.includes(:projects, :custom_fields).find(params[:id])
|
||||
render_edit_tab(type)
|
||||
end
|
||||
end
|
||||
|
||||
def create
|
||||
additional_params = {}
|
||||
value = params.dig(:type, :copy_workflow_from)
|
||||
additional_params[:copy_workflow_from] = value if value.present?
|
||||
|
||||
service_call = WorkPackageTypes::CreateService
|
||||
.new(user: current_user)
|
||||
.call(permitted_type_params.merge(additional_params))
|
||||
|
||||
@type = service_call.result
|
||||
if service_call.success?
|
||||
redirect_to edit_type_settings_path(@type), notice: t(:notice_successful_create), status: :see_other
|
||||
else
|
||||
render action: :new, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
UpdateTypeService
|
||||
.new(@type, current_user)
|
||||
.call(permitted_type_params) do |call|
|
||||
call.on_success { redirect_to_type_tab_path(@type, t(:notice_successful_update)) }
|
||||
call.on_failure { render_edit_tab(@type, status: :unprocessable_entity) }
|
||||
end
|
||||
end
|
||||
|
||||
def move
|
||||
if @type.update(permitted_params.type_move)
|
||||
flash[:notice] = I18n.t(:notice_successful_update)
|
||||
redirect_to types_path
|
||||
else
|
||||
flash.now[:error] = I18n.t(:error_type_could_not_be_saved)
|
||||
render action: :edit, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
# types cannot be deleted when they have work packages
|
||||
# or they are standard types
|
||||
# put that into the model and do a `if @type.destroy`
|
||||
if @type.work_packages.empty? && !@type.is_standard?
|
||||
@type.destroy
|
||||
flash[:notice] = I18n.t(:notice_successful_delete)
|
||||
else
|
||||
flash[:error] = destroy_error_message
|
||||
end
|
||||
redirect_to action: "index"
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def find_type
|
||||
@type = ::Type.find(params[:id])
|
||||
end
|
||||
|
||||
def permitted_type_params
|
||||
# having to call #to_unsafe_h as a query hash the attribute_groups
|
||||
# parameters would otherwise still be an ActiveSupport::Parameter
|
||||
permitted_params.type.to_unsafe_h
|
||||
end
|
||||
|
||||
def load_projects_and_types
|
||||
@types = ::Type.order(Arel.sql("position"))
|
||||
@projects = Project.all
|
||||
end
|
||||
|
||||
def redirect_to_type_tab_path(type, notice)
|
||||
tab = params["tab"] || "settings"
|
||||
redirect_to(edit_tab_type_path(type, tab:), notice:, status: :see_other)
|
||||
end
|
||||
|
||||
def render_edit_tab(type, status: :ok)
|
||||
@tab = params[:tab]
|
||||
@projects = Project.all
|
||||
@type = type
|
||||
|
||||
render action: :edit, status:
|
||||
end
|
||||
|
||||
def destroy_error_message
|
||||
if @type.is_standard?
|
||||
t(:error_can_not_delete_standard_type)
|
||||
else
|
||||
error_message = [
|
||||
ApplicationController.helpers.sanitize(
|
||||
t(:"error_can_not_delete_type.explanation", url: belonging_wps_url(@type.id)),
|
||||
attributes: %w(href target)
|
||||
)
|
||||
]
|
||||
|
||||
if archived_projects.any?
|
||||
error_message << ApplicationController.helpers.sanitize(
|
||||
t(:error_can_not_delete_in_use_archived_work_packages,
|
||||
archived_projects_urls: helpers.archived_projects_urls_for(archived_projects)),
|
||||
attributes: %w(href target)
|
||||
)
|
||||
end
|
||||
|
||||
error_message
|
||||
end
|
||||
end
|
||||
|
||||
def belonging_wps_url(type_id)
|
||||
work_packages_path query_props: { f: [{ n: "type", o: "=", v: [type_id] }] }.to_json
|
||||
end
|
||||
|
||||
def archived_projects
|
||||
@archived_projects ||= @type.projects.archived
|
||||
end
|
||||
end
|
||||
@@ -168,7 +168,7 @@ class VersionsController < ApplicationController
|
||||
versions = versions.or(@project.rolled_up_versions.includes(:custom_values))
|
||||
end
|
||||
|
||||
versions = versions.visible.order_by_semver_name.except(:distinct).uniq
|
||||
versions = versions.visible.order(:name).except(:distinct).uniq
|
||||
versions.reject! { |version| version.closed? || version.completed? } unless completed
|
||||
versions
|
||||
end
|
||||
|
||||
@@ -31,10 +31,18 @@
|
||||
module WorkPackageTypes
|
||||
class PdfExportTemplateController < ApplicationController
|
||||
include OpTurbo::ComponentStream
|
||||
layout "admin"
|
||||
|
||||
before_action :require_admin
|
||||
before_action :find_type, only: %i[toggle drop enable_all disable_all]
|
||||
before_action :find_type, only: %i[edit toggle drop enable_all disable_all]
|
||||
before_action :find_template, only: %i[toggle drop]
|
||||
|
||||
current_menu_item do
|
||||
:types
|
||||
end
|
||||
|
||||
def edit; end
|
||||
|
||||
def enable_all
|
||||
return render_404_turbo_stream if @type.nil?
|
||||
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
# 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 WorkPackageTypes
|
||||
class TypesController < ApplicationController
|
||||
include PaginationHelper
|
||||
|
||||
layout "admin"
|
||||
|
||||
before_action :require_admin
|
||||
before_action :find_type, only: %i[move destroy]
|
||||
|
||||
current_menu_item do
|
||||
:types
|
||||
end
|
||||
|
||||
def index
|
||||
@types = ::Type
|
||||
.includes(:workflows, :projects, :custom_fields, :color)
|
||||
.page(page_param)
|
||||
.per_page(per_page_param)
|
||||
end
|
||||
|
||||
def type
|
||||
@type
|
||||
end
|
||||
|
||||
def new
|
||||
@type = Type.new(params[:type])
|
||||
load_projects_and_types
|
||||
end
|
||||
|
||||
def create
|
||||
additional_params = {}
|
||||
value = params.dig(:type, :copy_workflow_from)
|
||||
additional_params[:copy_workflow_from] = value if value.present?
|
||||
|
||||
service_call = WorkPackageTypes::CreateService
|
||||
.new(user: current_user)
|
||||
.call(permitted_type_params.merge(additional_params))
|
||||
|
||||
@type = service_call.result
|
||||
if service_call.success?
|
||||
redirect_to edit_type_settings_path(@type), notice: t(:notice_successful_create), status: :see_other
|
||||
else
|
||||
render action: :new, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def move
|
||||
if @type.update(permitted_params.type_move)
|
||||
flash[:notice] = I18n.t(:notice_successful_update)
|
||||
else
|
||||
flash.now[:error] = I18n.t(:error_type_could_not_be_saved)
|
||||
end
|
||||
redirect_to types_path
|
||||
end
|
||||
|
||||
def destroy
|
||||
# types cannot be deleted when they have work packages
|
||||
# or they are standard types
|
||||
# put that into the model and do a `if @type.destroy`
|
||||
if @type.work_packages.empty? && !@type.is_standard?
|
||||
@type.destroy
|
||||
flash[:notice] = I18n.t(:notice_successful_delete)
|
||||
else
|
||||
flash[:error] = destroy_error_message
|
||||
end
|
||||
redirect_to action: "index"
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def find_type
|
||||
@type = ::Type.find(params[:id])
|
||||
end
|
||||
|
||||
def permitted_type_params
|
||||
# having to call #to_unsafe_h as a query hash the attribute_groups
|
||||
# parameters would otherwise still be an ActiveSupport::Parameter
|
||||
permitted_params.type.to_unsafe_h
|
||||
end
|
||||
|
||||
def load_projects_and_types
|
||||
@types = ::Type.order(Arel.sql("position"))
|
||||
@projects = Project.all
|
||||
end
|
||||
|
||||
def destroy_error_message
|
||||
if @type.is_standard?
|
||||
t(:error_can_not_delete_standard_type)
|
||||
else
|
||||
error_message = [
|
||||
ApplicationController.helpers.sanitize(
|
||||
t(:"error_can_not_delete_type.explanation", url: belonging_wps_url(@type.id)),
|
||||
attributes: %w(href target)
|
||||
)
|
||||
]
|
||||
|
||||
if archived_projects.any?
|
||||
error_message << ApplicationController.helpers.sanitize(
|
||||
t(:error_can_not_delete_in_use_archived_work_packages,
|
||||
archived_projects_urls: helpers.archived_projects_urls_for(archived_projects)),
|
||||
attributes: %w(href target)
|
||||
)
|
||||
end
|
||||
|
||||
error_message
|
||||
end
|
||||
end
|
||||
|
||||
def belonging_wps_url(type_id)
|
||||
work_packages_path query_props: { f: [{ n: "type", o: "=", v: [type_id] }] }.to_json
|
||||
end
|
||||
|
||||
def archived_projects
|
||||
@archived_projects ||= @type.projects.archived
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -219,6 +219,12 @@ module ApplicationHelper
|
||||
end
|
||||
end
|
||||
|
||||
# Backward compatibility helper for secure_headers gem migration
|
||||
# Rails built-in CSP equivalent of nonced_javascript_tag
|
||||
def nonced_javascript_tag(**, &)
|
||||
javascript_tag(nonce: true, **, &)
|
||||
end
|
||||
|
||||
def to_path_param(path)
|
||||
path.to_s
|
||||
end
|
||||
|
||||
@@ -66,7 +66,7 @@ module FrontendAssetHelper
|
||||
end
|
||||
|
||||
def nonced_javascript_include_tag(path, **)
|
||||
javascript_include_tag(path, nonce: content_security_policy_script_nonce, **)
|
||||
javascript_include_tag(path, nonce: content_security_policy_nonce, **)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -29,10 +29,8 @@
|
||||
module SecureHeadersHelper
|
||||
##
|
||||
# Output a rails +csp_meta_tag+ compatible tag
|
||||
# while we're still using the +secure_headers+ gem.
|
||||
# using Rails built-in CSP functionality.
|
||||
def secure_header_csp_meta_tag
|
||||
tag :meta,
|
||||
name: "csp-nonce",
|
||||
content: content_security_policy_script_nonce
|
||||
csp_meta_tag
|
||||
end
|
||||
end
|
||||
|
||||
@@ -55,7 +55,7 @@ module ::TypesHelper
|
||||
},
|
||||
{
|
||||
name: "export_configuration",
|
||||
path: edit_tab_type_path(id: @type.id, tab: :export_configuration),
|
||||
path: edit_type_pdf_export_template_index_path(type_id: @type.id),
|
||||
label: I18n.t("types.edit.export_configuration.tab"),
|
||||
view_component: WorkPackageTypes::ExportConfigurationComponent
|
||||
}
|
||||
|
||||
@@ -68,10 +68,10 @@ module Projects::Versions
|
||||
def assignable_versions(only_open: true)
|
||||
if only_open
|
||||
@assignable_versions ||=
|
||||
shared_versions.references(:project).with_status_open.order_by_semver_name.to_a
|
||||
shared_versions.references(:project).with_status_open.order(:name).to_a
|
||||
else
|
||||
@assignable_versions_including_non_open ||= # rubocop:disable Naming/MemoizedInstanceVariableName
|
||||
shared_versions.references(:project).order_by_semver_name.to_a
|
||||
shared_versions.references(:project).order(:name).to_a
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) the OpenProject GmbH
|
||||
@@ -30,16 +32,6 @@ class Queries::Versions::Orders::DefaultOrder < Queries::Orders::Base
|
||||
self.model = Version
|
||||
|
||||
def self.key
|
||||
/\A(id|name|semver_name)\z/
|
||||
end
|
||||
|
||||
def initialize(attribute)
|
||||
if attribute == :semver_name
|
||||
OpenProject::Deprecation.warn("Sorting by semver_name is deprecated, name should be used instead")
|
||||
|
||||
super(:name)
|
||||
else
|
||||
super
|
||||
end
|
||||
/\A(id|name)\z/
|
||||
end
|
||||
end
|
||||
|
||||
@@ -35,4 +35,7 @@ class RemoteIdentity < ApplicationRecord
|
||||
|
||||
validates :user, uniqueness: { scope: %i[auth_source integration] }
|
||||
validates :origin_user_id, :user, :auth_source, :integration, presence: true
|
||||
|
||||
# FIXME: This needs a better name - 2025.03.18 @mereghost
|
||||
scope :of_user_and_client, ->(user, client, integration) { find_by(user:, auth_source: client, integration:) }
|
||||
end
|
||||
|
||||
@@ -317,6 +317,10 @@ class User < Principal
|
||||
authentication_provider&.display_name
|
||||
end
|
||||
|
||||
def provided_by_oidc?
|
||||
authentication_provider.is_a?(OpenIDConnect::Provider)
|
||||
end
|
||||
|
||||
##
|
||||
# Allows the API and other sources to determine locking actions
|
||||
# on represented collections of children of Principals.
|
||||
|
||||
@@ -46,8 +46,7 @@ class Version < ApplicationRecord
|
||||
validates :status, inclusion: { in: VERSION_STATUSES }
|
||||
validate :validate_start_date_before_effective_date
|
||||
|
||||
scopes :order_by_semver_name,
|
||||
:rolled_up,
|
||||
scopes :rolled_up,
|
||||
:shared_with
|
||||
|
||||
# Returns versions that are either:
|
||||
|
||||
@@ -1,192 +0,0 @@
|
||||
# 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.
|
||||
#++
|
||||
|
||||
class BaseTypeService
|
||||
include Shared::BlockService
|
||||
include Contracted
|
||||
|
||||
attr_accessor :contract_class, :type, :user
|
||||
|
||||
def initialize(type, user)
|
||||
self.type = type
|
||||
self.user = user
|
||||
self.contract_class = ::Types::BaseContract
|
||||
end
|
||||
|
||||
def call(params, options, &)
|
||||
result = update(params, options)
|
||||
|
||||
block_with_result(result, &)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def update(params, options)
|
||||
success = false
|
||||
errors = type.errors
|
||||
|
||||
Type.transaction do
|
||||
success, errors = set_params_and_validate(params)
|
||||
if success
|
||||
after_type_save(params, options)
|
||||
else
|
||||
raise(ActiveRecord::Rollback)
|
||||
end
|
||||
end
|
||||
|
||||
ServiceResult.new(success:,
|
||||
errors:,
|
||||
result: type)
|
||||
rescue StandardError => e
|
||||
ServiceResult.failure.tap do |result|
|
||||
result.errors.add(:base, e.message)
|
||||
end
|
||||
end
|
||||
|
||||
def set_params_and_validate(params)
|
||||
# Only set attribute groups when it exists
|
||||
# (Regression #28400)
|
||||
set_attribute_groups(params) if params[:attribute_groups]
|
||||
|
||||
# This should go before `set_scalar_params` call to get the
|
||||
# project_ids, custom_field_ids diffs from the type and the params.
|
||||
# For determining the active custom fields for the type, it is necessary
|
||||
# to know whether the type is a milestone or not.
|
||||
set_milestone_param(params) unless params[:is_milestone].nil?
|
||||
|
||||
set_active_custom_fields
|
||||
|
||||
set_active_custom_fields_for_project_ids(params[:project_ids]) if params[:project_ids].present?
|
||||
|
||||
set_scalar_params(params)
|
||||
|
||||
validate_and_save(type, user)
|
||||
end
|
||||
|
||||
def set_milestone_param(params)
|
||||
type.is_milestone = params[:is_milestone]
|
||||
end
|
||||
|
||||
def set_scalar_params(params)
|
||||
type.attributes = params.except(:attribute_groups, :subject_configuration, :subject_pattern)
|
||||
end
|
||||
|
||||
def set_attribute_groups(params)
|
||||
if params[:attribute_groups].empty?
|
||||
type.reset_attribute_groups
|
||||
else
|
||||
type.attribute_groups = parse_attribute_groups_params(params)
|
||||
end
|
||||
end
|
||||
|
||||
def parse_attribute_groups_params(params)
|
||||
return if params[:attribute_groups].nil?
|
||||
|
||||
transform_attribute_groups(params[:attribute_groups])
|
||||
end
|
||||
|
||||
def after_type_save(_params, _options)
|
||||
# noop to be overwritten by subclasses
|
||||
end
|
||||
|
||||
def transform_attribute_groups(groups)
|
||||
groups.map do |group|
|
||||
if group["type"] == "query"
|
||||
transform_query_group(group)
|
||||
else
|
||||
transform_attribute_group(group)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def transform_attribute_group(group)
|
||||
name =
|
||||
if group["key"]
|
||||
group["key"].to_sym
|
||||
else
|
||||
group["name"]
|
||||
end
|
||||
|
||||
[
|
||||
name,
|
||||
group["attributes"].pluck("key")
|
||||
]
|
||||
end
|
||||
|
||||
def transform_query_group(group)
|
||||
name = group["name"]
|
||||
props = JSON.parse group["query"]
|
||||
|
||||
query = Query.new_default(name: "Embedded table: #{name}")
|
||||
|
||||
query.extend(OpenProject::ChangedBySystem)
|
||||
query.change_by_system do
|
||||
query.user = User.system
|
||||
end
|
||||
|
||||
::API::V3::UpdateQueryFromV3ParamsService
|
||||
.new(query, user)
|
||||
.call(props.with_indifferent_access)
|
||||
|
||||
query.show_hierarchies = false
|
||||
|
||||
[
|
||||
name,
|
||||
[query]
|
||||
]
|
||||
end
|
||||
|
||||
##
|
||||
# Syncs attribute group settings for custom fields with enabled custom fields
|
||||
# for this type. If a custom field is not in a group, it is removed from the
|
||||
# custom_field_ids list.
|
||||
def set_active_custom_fields
|
||||
type.custom_field_ids = type
|
||||
.attribute_groups
|
||||
.flat_map(&:members)
|
||||
.select { CustomField.custom_field_attribute? it }
|
||||
.map { it.gsub(/^custom_field_/, "").to_i }
|
||||
.uniq
|
||||
end
|
||||
|
||||
def set_active_custom_fields_for_project_ids(project_ids)
|
||||
new_project_ids_to_activate_cfs = project_ids.reject(&:empty?).map(&:to_i) - type.project_ids
|
||||
|
||||
values = Project
|
||||
.where(id: new_project_ids_to_activate_cfs)
|
||||
.to_a
|
||||
.product(type.custom_field_ids)
|
||||
.map { |p, cf_ids| { project_id: p.id, custom_field_id: cf_ids } }
|
||||
|
||||
return if values.empty?
|
||||
|
||||
CustomFieldsProject.insert_all(values)
|
||||
end
|
||||
end
|
||||
@@ -47,10 +47,11 @@ module RemoteIdentities
|
||||
|
||||
def call
|
||||
if @model.new_record? || @force_update
|
||||
user_id = @integration.extract_origin_user_id(@token)
|
||||
return user_id if user_id.failure?
|
||||
origin_result = @integration.extract_origin_user_id(@token)
|
||||
|
||||
@model.origin_user_id = user_id.result
|
||||
user_id = origin_result.value_or { return ServiceResult.failure(errors: it) }
|
||||
|
||||
@model.origin_user_id = user_id
|
||||
return success unless @model.changed?
|
||||
return failure unless @model.save
|
||||
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
# 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.
|
||||
#++
|
||||
|
||||
class UpdateTypeService < BaseTypeService
|
||||
def call(params)
|
||||
# forbid renaming if it is a standard type
|
||||
if params[:type] && type.is_standard?
|
||||
params[:type].delete :name
|
||||
end
|
||||
|
||||
super(params, {})
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_params_and_validate(params)
|
||||
# Set patterns includes a data validation before assigning the value to the attribute.
|
||||
# A validation failure should return a service call failure.
|
||||
patterns = params[:patterns]
|
||||
if patterns.present?
|
||||
validate_enterprise_action(patterns)
|
||||
set_patterns(patterns)
|
||||
return [false, type.errors] if type.errors.any?
|
||||
end
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
def validate_enterprise_action(patterns)
|
||||
change_from_manual_to_generated = !type.patterns.subject&.enabled? && patterns.dig(:subject, :enabled)
|
||||
action = :work_package_subject_generation
|
||||
|
||||
if change_from_manual_to_generated && !EnterpriseToken.allows_to?(action)
|
||||
type.errors.add(:patterns, :error_enterprise_only, action: action.to_s.titleize)
|
||||
end
|
||||
end
|
||||
|
||||
def set_patterns(patterns)
|
||||
WorkPackageTypes::Patterns::Collection
|
||||
.build(patterns:)
|
||||
.either(
|
||||
->(collection) { type.patterns = collection },
|
||||
->(result) do
|
||||
result.errors(full: true).messages.each do |message|
|
||||
type.errors.add(:patterns, message.text)
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
||||
@@ -85,7 +85,7 @@ module WorkPackageTypes
|
||||
def check_projects(params)
|
||||
return unless params.key?(:project_ids)
|
||||
|
||||
invalid_project_ids = params[:project_ids].reject { |id| Project.exists?(id) }
|
||||
invalid_project_ids = params[:project_ids].reject { |id| id.blank? || Project.exists?(id) }
|
||||
unless invalid_project_ids.empty?
|
||||
@param_validations.update({ project_ids: "Projects with ids #{invalid_project_ids.join(', ')} do not exist." })
|
||||
end
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<%= render "common/favicons" %>
|
||||
|
||||
<%# Allow gon output when necessary %>
|
||||
<%= include_gon(nonce: content_security_policy_script_nonce) %>
|
||||
<%= include_gon(nonce: content_security_policy_nonce) %>
|
||||
|
||||
<%# Include CLI assets (development) or prod build assets %>
|
||||
<%= include_frontend_assets %>
|
||||
|
||||
@@ -96,7 +96,7 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
</td>
|
||||
<td>
|
||||
<% if current_user.admin? %>
|
||||
<% type_links = custom_field.types.map { |t| link_to(t.name, edit_tab_type_path(id: t.id, tab: "form_configuration")) } %>
|
||||
<% type_links = custom_field.types.map { |t| link_to(t.name, edit_type_form_configuration_path(t)) } %>
|
||||
<%= safe_join type_links, ", " %>
|
||||
<% else %>
|
||||
<%= custom_field.types.map(&:name).join(", ") %>
|
||||
|
||||
@@ -1,170 +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.
|
||||
|
||||
++#%>
|
||||
|
||||
<% html_title t(:label_administration), t("label_type_plural") %>
|
||||
|
||||
<%=
|
||||
render Primer::OpenProject::PageHeader.new do |header|
|
||||
header.with_title { t(:label_type_plural) }
|
||||
header.with_breadcrumbs(
|
||||
[{ href: admin_index_path, text: t("label_administration") },
|
||||
{ href: admin_settings_work_packages_general_path, text: t(:label_work_package_plural) },
|
||||
t(:label_type_plural)]
|
||||
)
|
||||
end
|
||||
%>
|
||||
<%=
|
||||
render(Primer::OpenProject::SubHeader.new) do |subheader|
|
||||
subheader.with_action_button(
|
||||
scheme: :primary,
|
||||
leading_icon: :plus,
|
||||
label: t(:label_type_new),
|
||||
test_selector: "op-admin-types--button-new",
|
||||
tag: :a,
|
||||
href: new_type_path
|
||||
) do
|
||||
t("activerecord.attributes.work_package.type")
|
||||
end
|
||||
end
|
||||
%>
|
||||
|
||||
<% if @types.any? %>
|
||||
<div class="generic-table--container">
|
||||
<div class="generic-table--results-container">
|
||||
<table class="generic-table" data-controller="table-highlighting">
|
||||
<colgroup>
|
||||
<col>
|
||||
<col data-highlight="false">
|
||||
<col>
|
||||
<col>
|
||||
<col>
|
||||
<col>
|
||||
<col>
|
||||
<col data-highlight="false">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<div class="generic-table--sort-header-outer">
|
||||
<div class="generic-table--sort-header">
|
||||
<span>
|
||||
<%= Type.human_attribute_name(:name) %>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</th>
|
||||
<th><div class="generic-table--empty-header"></div></th>
|
||||
<!-- Missing workflow warning -->
|
||||
<th>
|
||||
<div class="generic-table--sort-header-outer">
|
||||
<div class="generic-table--sort-header">
|
||||
<span>
|
||||
<%= Type.human_attribute_name(:color) %>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</th>
|
||||
<th>
|
||||
<div class="generic-table--sort-header-outer">
|
||||
<div class="generic-table--sort-header">
|
||||
<span>
|
||||
<%= t(:label_active_in_new_projects) %>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</th>
|
||||
<th>
|
||||
<div class="generic-table--sort-header-outer">
|
||||
<div class="generic-table--sort-header">
|
||||
<span>
|
||||
<%= Type.human_attribute_name(:is_milestone) %>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</th>
|
||||
<th>
|
||||
<div class="generic-table--sort-header-outer">
|
||||
<div class="generic-table--sort-header">
|
||||
<span>
|
||||
<%= t(:button_sort) %>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</th>
|
||||
<th><div class="generic-table--empty-header"></div></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% for type in @types %>
|
||||
<tr>
|
||||
<td class="timelines-pet-name">
|
||||
<%= link_to type.name, edit_type_settings_path(type_id: type.id) %>
|
||||
</td>
|
||||
<td>
|
||||
<% if type.workflows.empty? %>
|
||||
<span class="icon-context icon-warning">
|
||||
<%= t(:text_type_no_workflow) %>
|
||||
(<%= link_to t(:button_edit), { controller: "/workflows",
|
||||
action: "edit",
|
||||
type_id: type } %>)
|
||||
</span>
|
||||
<% end %>
|
||||
</td>
|
||||
<td class="timelines-pet-color">
|
||||
<%= icon_for_type type %>
|
||||
</td>
|
||||
<td class="timelines-pet-is_default">
|
||||
<%= checked_image(type.is_default) %>
|
||||
</td>
|
||||
<td class="timelines-pet-is_milestone">
|
||||
<%= checked_image(type.is_milestone) %>
|
||||
</td>
|
||||
<td class="timelines-pet-reorder">
|
||||
<%= reorder_links("type", { action: "move", id: type }) %>
|
||||
</td>
|
||||
<td class="buttons">
|
||||
<% if !type.is_standard? %>
|
||||
<%= link_to "", type,
|
||||
method: :delete,
|
||||
data: { confirm: t(:text_are_you_sure) },
|
||||
class: "icon icon-delete",
|
||||
title: t(:button_delete) %>
|
||||
<% end %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<%= pagination_links_full @types %>
|
||||
<% else %>
|
||||
<%= no_results_box(action_url: new_type_path, display_action: true) %>
|
||||
<% end %>
|
||||
+2
-21
@@ -29,24 +29,5 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
|
||||
<% html_title t(:label_administration), "#{t(:label_edit)} #{t(:label_work_package_types)} #{h @type.name}" %>
|
||||
|
||||
<% tabs = types_tabs %>
|
||||
|
||||
<%= render ::Types::EditPageHeaderComponent.new(type: @type, tabs:) %>
|
||||
|
||||
<%=
|
||||
selected_tab = selected_tab(tabs)
|
||||
form_data = {
|
||||
subject_configuration_form_data: params[:types_forms_subject_configuration_form_model]
|
||||
}
|
||||
|
||||
if selected_tab.key?(:view_component)
|
||||
render selected_tab[:view_component].new(@type, **form_data)
|
||||
else
|
||||
form_for @type,
|
||||
url: update_tab_type_path(id: @type.id, tab: @tab),
|
||||
builder: TabularFormBuilder,
|
||||
lang: current_language do |f|
|
||||
render partial: "common/tabs", locals: { f:, tabs:, selected_tab:, with_tab_nav: false }
|
||||
end
|
||||
end
|
||||
%>
|
||||
<%= render ::Types::EditPageHeaderComponent.new(type: @type, tabs: types_tabs) %>
|
||||
<%= render WorkPackageTypes::ExportConfigurationComponent.new(@type) %>
|
||||
@@ -0,0 +1,64 @@
|
||||
<%#-- 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.
|
||||
|
||||
++#%>
|
||||
|
||||
<% html_title t(:label_administration), t("label_type_plural") %>
|
||||
|
||||
<%=
|
||||
render Primer::OpenProject::PageHeader.new do |header|
|
||||
header.with_title { t(:label_type_plural) }
|
||||
header.with_breadcrumbs(
|
||||
[{ href: admin_index_path, text: t("label_administration") },
|
||||
{ href: admin_settings_work_packages_general_path, text: t(:label_work_package_plural) },
|
||||
t(:label_type_plural)]
|
||||
)
|
||||
end
|
||||
%>
|
||||
<%=
|
||||
render(Primer::OpenProject::SubHeader.new) do |subheader|
|
||||
subheader.with_action_button(
|
||||
scheme: :primary,
|
||||
leading_icon: :plus,
|
||||
label: t(:label_type_new),
|
||||
test_selector: "op-admin-types--button-new",
|
||||
tag: :a,
|
||||
href: new_type_path
|
||||
) do
|
||||
t("activerecord.attributes.work_package.type")
|
||||
end
|
||||
end
|
||||
%>
|
||||
|
||||
<% if @types.any? %>
|
||||
<%= render WorkPackageTypes::Types::TableComponent.new(rows: @types) %>
|
||||
<% else %>
|
||||
<%= no_results_box(
|
||||
action_url: new_type_path,
|
||||
display_action: true,
|
||||
custom_action_text: t("types.index.no_results_content_text")) %>
|
||||
<% end %>
|
||||
@@ -29,7 +29,16 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
|
||||
<% html_title t(:label_bulk_edit_selected_work_packages) %>
|
||||
|
||||
<h2><%= t(:label_bulk_edit_selected_work_packages) %></h2>
|
||||
<%=
|
||||
render Primer::OpenProject::PageHeader.new do |header|
|
||||
header.with_title { t(:label_bulk_edit_selected_work_packages) }
|
||||
header.with_breadcrumbs(
|
||||
[*([href: project_overview_path(@project.id), text: @project.name] if @project),
|
||||
{ href: (@project ? project_work_packages_path(@project) : work_packages_path), text: t(:label_work_package_plural) },
|
||||
t(:label_bulk_edit_selected_work_packages)]
|
||||
)
|
||||
end
|
||||
%>
|
||||
<ul>
|
||||
<% @work_packages.each do |wp| %>
|
||||
<li>
|
||||
@@ -132,7 +141,7 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
<%= styled_select_tag(
|
||||
"work_package[version_id]",
|
||||
content_tag("option", t(:label_none), value: "none") +
|
||||
version_options_for_select(@project.shared_versions.with_status_open.order_by_semver_name),
|
||||
version_options_for_select(@project.shared_versions.with_status_open.order(:name)),
|
||||
include_blank: t(:label_no_change_option)
|
||||
) %>
|
||||
</div>
|
||||
|
||||
@@ -29,7 +29,20 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
|
||||
<% html_title @copy ? t(:button_duplicate) : t(:button_move), t("activerecord.models.work_package") %>
|
||||
|
||||
<h2><%= @copy ? t(:button_duplicate) : t(:button_move) %></h2>
|
||||
<% single_wp = @work_packages.size == 1 ? @work_packages.first : nil %>
|
||||
|
||||
<%=
|
||||
render Primer::OpenProject::PageHeader.new do |header|
|
||||
header.with_title { @copy ? t(:button_duplicate) : t(:button_move) }
|
||||
header.with_breadcrumbs(
|
||||
[*([href: project_overview_path(@project.id), text: @project.name] if @project),
|
||||
{ href: project_work_packages_path(@project), text: t(:label_work_package_plural) },
|
||||
*(single_wp ? [{ href: work_package_path(single_wp), text: single_wp.subject }] : []),
|
||||
@copy ? t(:button_duplicate) : t(:button_move)]
|
||||
)
|
||||
end
|
||||
%>
|
||||
|
||||
<ul>
|
||||
<% @work_packages.each do |work_package| -%>
|
||||
<li>
|
||||
@@ -162,8 +175,8 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
<%= styled_label_tag :budget_id, Budget.model_name.human %>
|
||||
<%= styled_select_tag(
|
||||
"budget_id", (@target_project == @project ? content_tag("option", t(:label_no_change_option), value: "") : "") +
|
||||
content_tag("option", t(:label_none), value: "none") +
|
||||
options_from_collection_for_select(@target_project.budgets, :id, :subject)
|
||||
content_tag("option", t(:label_none), value: "none") +
|
||||
options_from_collection_for_select(@target_project.budgets, :id, :subject)
|
||||
) %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -125,9 +125,6 @@ module OpenProject
|
||||
# http://stackoverflow.com/questions/4590229
|
||||
config.middleware.use Rack::TempfileReaper
|
||||
|
||||
# Move secure_headers middleware to after the ShowExceptions
|
||||
config.middleware.move_after ActionDispatch::ShowExceptions, SecureHeaders::Middleware
|
||||
|
||||
# Add lookbook preview paths when enabled
|
||||
if OpenProject::Configuration.lookbook_enabled?
|
||||
config.paths.add Primer::ViewComponents::Engine.root.join("app/components").to_s, eager_load: true
|
||||
|
||||
@@ -1,25 +1,114 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Be sure to restart your server when you modify this file.
|
||||
|
||||
# Define an application-wide content security policy.
|
||||
# See the Securing Rails Applications Guide for more information:
|
||||
# https://guides.rubyonrails.org/security.html#content-security-policy-header
|
||||
|
||||
# Rails.application.configure do
|
||||
# config.content_security_policy do |policy|
|
||||
# policy.default_src :self, :https
|
||||
# policy.font_src :self, :https, :data
|
||||
# policy.img_src :self, :https, :data
|
||||
# policy.object_src :none
|
||||
# policy.script_src :self, :https
|
||||
# policy.style_src :self, :https
|
||||
# # Specify URI for violation reports
|
||||
# # policy.report_uri "/csp-violation-report-endpoint"
|
||||
# end
|
||||
#
|
||||
# # Generate session nonces for permitted importmap, inline scripts, and inline styles.
|
||||
# config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s }
|
||||
# config.content_security_policy_nonce_directives = %w(script-src style-src)
|
||||
#
|
||||
# # Report violations without enforcing the policy.
|
||||
# # config.content_security_policy_report_only = true
|
||||
# end
|
||||
Rails.application.config.after_initialize do
|
||||
Rails.application.configure do
|
||||
config.content_security_policy do |policy|
|
||||
# Valid for assets
|
||||
assets_src = ["'self'"]
|
||||
asset_host = OpenProject::Configuration.rails_asset_host
|
||||
assets_src << asset_host if asset_host.present?
|
||||
|
||||
# Valid for iframes
|
||||
frame_src = %w['self' https://player.vimeo.com https://www.youtube.com] # rubocop:disable Lint/PercentStringArray
|
||||
frame_src << OpenProject::Configuration[:security_badge_url]
|
||||
|
||||
# Default src
|
||||
default_src = %w('self') # rubocop:disable Lint/PercentStringArray
|
||||
|
||||
# Attachment uploaders
|
||||
default_src += OpenProject::Configuration.remote_storage_hosts
|
||||
|
||||
# Chargebee self-service
|
||||
chargebee_src = ["https://*.chargebee.com"]
|
||||
|
||||
assets_src += chargebee_src
|
||||
frame_src += chargebee_src
|
||||
default_src += chargebee_src
|
||||
|
||||
# Allow requests to CLI in dev mode
|
||||
connect_src = default_src + [OpenProject::Configuration.enterprise_trial_creation_host]
|
||||
|
||||
# Rules for media (e.g. video sources)
|
||||
media_src = default_src
|
||||
media_src << asset_host if asset_host.present?
|
||||
|
||||
if OpenProject::Configuration.appsignal_frontend_key
|
||||
connect_src += ["https://appsignal-endpoint.net"]
|
||||
end
|
||||
|
||||
# Allow connections to S3 for BIM
|
||||
if OpenProject::Configuration.fog_directory.present?
|
||||
connect_src += [
|
||||
"#{OpenProject::Configuration.fog_directory}.s3-#{OpenProject::Configuration.fog_credentials[:region]}.amazonaws.com"
|
||||
]
|
||||
end
|
||||
|
||||
# Add proxy configuration for Angular CLI to csp
|
||||
if FrontendAssetHelper.assets_proxied?
|
||||
proxied = ["ws://#{Setting.host_name}", "http://#{Setting.host_name}",
|
||||
FrontendAssetHelper.cli_proxy.sub("http", "ws"), FrontendAssetHelper.cli_proxy]
|
||||
connect_src += proxied
|
||||
assets_src += proxied
|
||||
media_src += proxied
|
||||
end
|
||||
|
||||
# Allow to extend the script-src in specific situations
|
||||
script_src = assets_src + %w(js.chargebee.com)
|
||||
|
||||
# Allow unsafe-eval for rack-mini-profiler
|
||||
if Rails.env.development? && ENV.fetch("OPENPROJECT_RACK_PROFILER_ENABLED", false)
|
||||
script_src += %w('unsafe-eval') # rubocop:disable Lint/PercentStringArray
|
||||
end
|
||||
|
||||
# Allow ANDI bookmarklet to run in development mode
|
||||
# https://www.ssa.gov/accessibility/andi/help/install.html
|
||||
if Rails.env.development?
|
||||
script_src += ["https://www.ssa.gov"]
|
||||
assets_src += ["https://www.ssa.gov"]
|
||||
end
|
||||
|
||||
form_action = default_src
|
||||
|
||||
# Allow test s3 bucket for direct uploads in tests
|
||||
if Rails.env.test?
|
||||
connect_src += ["test-bucket.s3.amazonaws.com"]
|
||||
form_action += ["test-bucket.s3.amazonaws.com"]
|
||||
end
|
||||
|
||||
# Configure CSP directives
|
||||
policy.default_src(*default_src)
|
||||
policy.base_uri("'self'")
|
||||
policy.font_src(*assets_src, "data:", "'self'")
|
||||
policy.form_action(*form_action)
|
||||
policy.frame_src(*frame_src, "'self'")
|
||||
policy.frame_ancestors("'self'")
|
||||
policy.img_src("*", "data:", "blob:")
|
||||
policy.script_src(*script_src)
|
||||
policy.script_src_attr("'none'")
|
||||
policy.style_src(*assets_src, "'unsafe-inline'")
|
||||
policy.object_src(OpenProject::Configuration[:security_badge_url])
|
||||
policy.connect_src(*connect_src)
|
||||
policy.media_src(*media_src)
|
||||
end
|
||||
|
||||
# Generate session nonces for permitted importmap, inline scripts, and inline styles.
|
||||
# This handles Turbo integration natively
|
||||
config.content_security_policy_nonce_generator = lambda do |request|
|
||||
# Use Turbo nonce if available (for Turbo navigation)
|
||||
if request.env["HTTP_TURBO_REFERRER"].present? && request.env["HTTP_X_TURBO_NONCE"].present?
|
||||
request.env["HTTP_X_TURBO_NONCE"]
|
||||
else
|
||||
# Generate a new nonce based on session
|
||||
SecureRandom.base64(16)
|
||||
end
|
||||
end
|
||||
|
||||
config.content_security_policy_nonce_directives = %w(script-src)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -22,49 +22,4 @@ Rails.application.configure do
|
||||
# Show notes first, all other panels next
|
||||
config.lookbook.preview_inspector.drawer_panels = [:notes, "*"]
|
||||
config.lookbook.ui_theme = "blue"
|
||||
|
||||
SecureHeaders::Configuration.named_append(:lookbook) do
|
||||
proxied =
|
||||
if FrontendAssetHelper.assets_proxied?
|
||||
["ws://#{Setting.host_name}", "http://#{Setting.host_name}",
|
||||
FrontendAssetHelper.cli_proxy.sub("http", "ws"), FrontendAssetHelper.cli_proxy]
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
||||
{
|
||||
script_src: proxied + %w('unsafe-eval' 'unsafe-inline' 'self'), # rubocop:disable Lint/PercentStringArray
|
||||
script_src_elem: proxied + %w('unsafe-eval' 'unsafe-inline' 'self'), # rubocop:disable Lint/PercentStringArray
|
||||
style_src: proxied + %w('self' 'unsafe-inline'), # rubocop:disable Lint/PercentStringArray
|
||||
style_src_attr: proxied + %w('self' 'unsafe-inline') # rubocop:disable Lint/PercentStringArray
|
||||
}
|
||||
end
|
||||
|
||||
# rubocop:disable Lint/ConstantDefinitionInBlock
|
||||
module LookbookCspExtender
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
before_action do
|
||||
use_content_security_policy_named_append :lookbook
|
||||
end
|
||||
end
|
||||
end
|
||||
# rubocop:enable Lint/ConstantDefinitionInBlock
|
||||
|
||||
Rails.application.reloader.to_prepare do
|
||||
Lookbook.add_input_type(:octicon, "lookbook/previews/inputs/octicon")
|
||||
|
||||
[
|
||||
Lookbook::ApplicationController,
|
||||
Lookbook::PreviewController,
|
||||
Lookbook::PreviewsController,
|
||||
Lookbook::PageController,
|
||||
Lookbook::PagesController,
|
||||
Lookbook::InspectorController,
|
||||
Lookbook::EmbedsController
|
||||
].each do |controller|
|
||||
controller.include LookbookCspExtender
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -329,7 +329,7 @@ Redmine::MenuManager.map :admin_menu do |menu|
|
||||
parent: :admin_work_packages
|
||||
|
||||
menu.push :types,
|
||||
{ controller: "/types" },
|
||||
{ controller: "/work_package_types/types" },
|
||||
if: ->(_) { User.current.admin? },
|
||||
caption: :label_type_plural,
|
||||
parent: :admin_work_packages
|
||||
|
||||
@@ -1,141 +0,0 @@
|
||||
# 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.
|
||||
#++
|
||||
|
||||
# rubocop:disable Lint/PercentStringArray
|
||||
Rails.application.config.after_initialize do
|
||||
SecureHeaders::Configuration.default do |config|
|
||||
config.cookies = {
|
||||
secure: true,
|
||||
httponly: true
|
||||
}
|
||||
|
||||
# Let Rails ActionDispatch::SSL middleware handle the Strict-Transport-Security header
|
||||
config.hsts = SecureHeaders::OPT_OUT
|
||||
|
||||
config.x_frame_options = "SAMEORIGIN"
|
||||
config.x_content_type_options = "nosniff"
|
||||
config.x_xss_protection = "1; mode=block"
|
||||
config.x_permitted_cross_domain_policies = "none"
|
||||
config.referrer_policy = "origin-when-cross-origin"
|
||||
|
||||
# Valid for assets
|
||||
assets_src = ["'self'"]
|
||||
asset_host = OpenProject::Configuration.rails_asset_host
|
||||
assets_src << asset_host if asset_host.present?
|
||||
|
||||
# Valid for iframes
|
||||
frame_src = %w['self' https://player.vimeo.com https://www.youtube.com]
|
||||
frame_src << OpenProject::Configuration[:security_badge_url]
|
||||
|
||||
# Default src
|
||||
default_src = %w('self')
|
||||
|
||||
# Attachment uploaders
|
||||
default_src += OpenProject::Configuration.remote_storage_hosts
|
||||
|
||||
# Chargebee self-service
|
||||
frame_src += [
|
||||
"https://js.chargebee.com/",
|
||||
"#{OpenProject::Configuration.enterprise_chargebee_site}.chargebee.com"
|
||||
]
|
||||
|
||||
default_src << "#{OpenProject::Configuration.enterprise_chargebee_site}.chargebee.com"
|
||||
|
||||
# Allow requests to CLI in dev mode
|
||||
connect_src = default_src + [OpenProject::Configuration.enterprise_trial_creation_host]
|
||||
|
||||
# Rules for media (e.g. video sources)
|
||||
media_src = default_src
|
||||
media_src << asset_host if asset_host.present?
|
||||
|
||||
if OpenProject::Configuration.appsignal_frontend_key
|
||||
connect_src += ["https://appsignal-endpoint.net"]
|
||||
end
|
||||
|
||||
# Add proxy configuration for Angular CLI to csp
|
||||
if FrontendAssetHelper.assets_proxied?
|
||||
proxied = ["ws://#{Setting.host_name}", "http://#{Setting.host_name}",
|
||||
FrontendAssetHelper.cli_proxy.sub("http", "ws"), FrontendAssetHelper.cli_proxy]
|
||||
connect_src += proxied
|
||||
assets_src += proxied
|
||||
media_src += proxied
|
||||
end
|
||||
|
||||
# Allow to extend the script-src in specific situations
|
||||
script_src = assets_src + %w(js.chargebee.com)
|
||||
|
||||
# Allow unsafe-eval for rack-mini-profiler
|
||||
if Rails.env.development? && ENV.fetch("OPENPROJECT_RACK_PROFILER_ENABLED", false)
|
||||
script_src += %w('unsafe-eval')
|
||||
end
|
||||
|
||||
# Allow ANDI bookmarklet to run in development mode
|
||||
# https://www.ssa.gov/accessibility/andi/help/install.html
|
||||
if Rails.env.development?
|
||||
script_src += ["https://www.ssa.gov"]
|
||||
assets_src += ["https://www.ssa.gov"]
|
||||
end
|
||||
|
||||
config.csp = {
|
||||
preserve_schemes: true,
|
||||
# Don't append unsafe-inline in CSP as a fallback
|
||||
disable_nonce_backwards_compatibility: true,
|
||||
|
||||
# Fallback when no value is defined
|
||||
default_src:,
|
||||
# Allowed uri in <base> tag
|
||||
base_uri: %w('self'),
|
||||
|
||||
# Allow fonts from self, asset host, or DATA uri
|
||||
font_src: assets_src + %w(data: 'self'),
|
||||
# Form targets can only be self
|
||||
form_action: default_src,
|
||||
# Allow iframe from vimeo (welcome video)
|
||||
frame_src: frame_src + %w('self'),
|
||||
frame_ancestors: %w('self'),
|
||||
# Allow images from anywhere including data urls and blobs (used in resizing)
|
||||
img_src: %w(* data: blob:),
|
||||
# Allow scripts from self
|
||||
script_src:,
|
||||
script_src_attr: %w('none'),
|
||||
# Allow unsafe-inline styles
|
||||
style_src: assets_src + %w('unsafe-inline'),
|
||||
# Allow object-src from Release API
|
||||
object_src: [OpenProject::Configuration[:security_badge_url]],
|
||||
|
||||
# Connect sources for CLI in dev mode
|
||||
connect_src:,
|
||||
|
||||
# Allow videos from self and from the asset proxy in dev mode.
|
||||
media_src:
|
||||
}
|
||||
end
|
||||
end
|
||||
# rubocop:enable Lint/PercentStringArray
|
||||
+126
-126
@@ -112,57 +112,57 @@ es:
|
||||
link: "webhook"
|
||||
scim_clients:
|
||||
authentication_methods:
|
||||
sso: "JWT from identity provider"
|
||||
oauth2_client: "OAuth 2.0 client credentials"
|
||||
oauth2_token: "Static access token"
|
||||
sso: "JWT del proveedor de identidad"
|
||||
oauth2_client: "Credenciales de cliente OAuth 2.0"
|
||||
oauth2_token: "Token de acceso estático"
|
||||
created_client_credentials_dialog_component:
|
||||
title: "Client credentials created"
|
||||
heading: "Client credentials have been generated"
|
||||
one_time_hint: "This is the only time you will see the client secret. Make sure to copy it now."
|
||||
title: "Credenciales del cliente creadas"
|
||||
heading: "Se han generado las credenciales del cliente"
|
||||
one_time_hint: "Esta es la única vez que verá el secreto del cliente. Asegúrese de copiarlo ahora."
|
||||
created_token_dialog_component:
|
||||
title: "Token created"
|
||||
heading: "An token has been generated"
|
||||
title: "Token creado"
|
||||
heading: "Se ha generado un token"
|
||||
label_token: "Token"
|
||||
one_time_hint: "This is the only time you will see this token. Make sure to copy it now."
|
||||
one_time_hint: "Esta es la única vez que verá este token. Asegúrese de copiarlo ahora."
|
||||
delete_scim_client_dialog_component:
|
||||
title: "Delete SCIM client"
|
||||
heading: "Are you sure you want to delete this SCIM client?"
|
||||
description: "Users managed by this SCIM client can no longer be updated by it."
|
||||
title: "Eliminar cliente SCIM"
|
||||
heading: "¿Seguro que desea eliminar este cliente SCIM?"
|
||||
description: "Los usuarios gestionados por este cliente SCIM ya no pueden ser actualizados por él."
|
||||
edit:
|
||||
label_delete_scim_client: "Delete SCIM client"
|
||||
label_delete_scim_client: "Eliminar cliente SCIM"
|
||||
form:
|
||||
auth_provider_description: "This is the service that users added by the SCIM provider will use to authenticate in OpenProject."
|
||||
authentication_method_description_html: "This is how the SCIM client authenticates at OpenProject. Please ensure that OAuth tokens include the <code>scim_v2</code> scope."
|
||||
description_html: 'Please refer to our <a href="%{docs_url}">documentation on configuring SCIM clients</a> for more information on these configuration options.'
|
||||
jwt_sub_description_html: 'For example, for Keycloak, this is the UUID of the service account associated with the SCIM client. Consult <a href="%{docs_url}">our documentation</a> to learn how to find the subject claim for your use case.'
|
||||
name_description: "Choose a name that will help other admins better understand why this client was configured."
|
||||
auth_provider_description: "Este es el servicio que los usuarios añadidos por el proveedor SCIM utilizarán para autenticarse en OpenProject."
|
||||
authentication_method_description_html: "Así es como el cliente SCIM se autentica en OpenProject. Asegúrese de que los tokens OAuth incluyen el ámbito <code>scim_v2</code>."
|
||||
description_html: 'Consulte nuestra <a href="%{docs_url}">documentación sobre la configuración de clientes SCIM</a> para obtener más información sobre estas opciones de configuración.'
|
||||
jwt_sub_description_html: 'Por ejemplo, para Keycloak, se trata del UUID de la cuenta de servicio asociada al cliente SCIM. Consulte <a href="%{docs_url}">nuestra documentación</a> para saber cómo encontrar la reclamación del asunto para su caso de uso.'
|
||||
name_description: "Elija un nombre que ayude a otros administradores a entender mejor por qué se configuró este cliente."
|
||||
index:
|
||||
description: "SCIM clients configured here are able to interact with OpenProject SCIM server API to provision, update, and deprovision user accounts and groups."
|
||||
label_create_button: "Add SCIM client"
|
||||
description: "Los clientes SCIM configurados aquí pueden interactuar con la API del servidor SCIM de OpenProject para aprovisionar, actualizar y desaprovisionar cuentas y grupos de usuarios."
|
||||
label_create_button: "Añadir cliente SCIM"
|
||||
new:
|
||||
title: "New SCIM client"
|
||||
title: "Nuevo cliente SCIM"
|
||||
revoke_static_token_dialog_component:
|
||||
confirm_button: "Revoke"
|
||||
title: "Revoke static token"
|
||||
heading: "Are you sure you want to revoke this token?"
|
||||
description: "The SCIM client that uses this token will no longer be able to access OpenProject's SCIM server API."
|
||||
confirm_button: "Revocar"
|
||||
title: "Revocar token estático"
|
||||
heading: "¿Seguro que desea revocar este token?"
|
||||
description: "El cliente SCIM que utilice este token ya no podrá acceder a la API del servidor SCIM de OpenProject."
|
||||
table_component:
|
||||
blank_slate:
|
||||
title: "No SCIM clients configured yet"
|
||||
description: "Add clients to see them here"
|
||||
user_count: "Users"
|
||||
title: "Aún no se ha configurado ningún cliente SCIM"
|
||||
description: "Añada clientes para verlos aquí"
|
||||
user_count: "Usuarios"
|
||||
token_list_component:
|
||||
description: "The tokens you generate here can be passed by a SCIM client to access the OpenProject SCIM API."
|
||||
description: "Los tokens que genere aquí pueden ser pasados por un cliente SCIM para acceder a la API SCIM de OpenProject."
|
||||
heading: "Tokens"
|
||||
label_add_token: "Token"
|
||||
label_aria_add_token: "Add token"
|
||||
label_aria_add_token: "Añadir token"
|
||||
token_table_component:
|
||||
blank_slate:
|
||||
title: "No tokens have been created yet"
|
||||
description: "You can create one now"
|
||||
expired: "Expired on %{date}"
|
||||
revoked: "Revoked on %{date}"
|
||||
title: "Access token table"
|
||||
title: "Aún no se ha creado ningún token"
|
||||
description: "Puede crear uno ahora"
|
||||
expired: "Caducó el %{date}"
|
||||
revoked: "Revocado el %{date}"
|
||||
title: "Tabla de tokens de acceso"
|
||||
authentication:
|
||||
login_and_registration: "Inicio de sesión y registro"
|
||||
announcements:
|
||||
@@ -327,7 +327,7 @@ es:
|
||||
admin_only: "Marque esta casilla para que este atributo solo sea visible para los administradores. Los usuarios sin derechos de administrador no podrán verlo ni editarlo."
|
||||
is_filter: >
|
||||
Permitir que el campo personalizado se utilice como filtro en las vistas de paquetes de trabajo. Tenga en cuenta que solo 'Para todos los proyectos' seleccionados, el campo personalizado se mostrará en vistas globales.
|
||||
formula: "Add numeric values or type / to search for an attribute or a mathematical operator."
|
||||
formula: "Añada valores numéricos o escriba / para buscar un atributo o un operador matemático."
|
||||
tab:
|
||||
no_results_title_text: Actualmente no hay campos personalizados.
|
||||
no_results_content_text: Crear un nuevo campo personalizado
|
||||
@@ -341,7 +341,7 @@ es:
|
||||
not_found: "no encontrado."
|
||||
rules:
|
||||
copy_workflow_from:
|
||||
workflow_missing: "has no own workflow."
|
||||
workflow_missing: "no tiene flujo de trabajo propio."
|
||||
item:
|
||||
root_item: "no puede ser un elemento raíz."
|
||||
not_persisted: "debe ser un elemento ya existente."
|
||||
@@ -352,8 +352,8 @@ es:
|
||||
parent:
|
||||
not_descendant: "debe ser descendiente de la raíz de la jerarquía."
|
||||
rules:
|
||||
copy_workflow_from: "Type for workflow copy"
|
||||
enabled: "Enabled"
|
||||
copy_workflow_from: "Tipo para la copia del flujo de trabajo"
|
||||
enabled: "Habilitado"
|
||||
depth: "Profundidad"
|
||||
item: "Elemento"
|
||||
label: "Etiqueta"
|
||||
@@ -627,11 +627,11 @@ es:
|
||||
priorities:
|
||||
edit:
|
||||
priority_color_text: |
|
||||
Click to assign or change the color of this priority.
|
||||
It can be used for highlighting work packages in the table.
|
||||
Haga clic para asignar o cambiar el color de esta prioridad.
|
||||
Puede usarse para resaltar paquetes de trabajo en la tabla.
|
||||
admin:
|
||||
default:
|
||||
caption: Making this priority default will override the previous default priority.
|
||||
caption: Si establece esta prioridad como predeterminada, anulará la prioridad predeterminada anterior.
|
||||
reactions:
|
||||
action_title: "Reaccionar"
|
||||
add_reaction: "Añadir reacción"
|
||||
@@ -685,9 +685,9 @@ es:
|
||||
tab: "Configuración del formulario"
|
||||
projects:
|
||||
tab: Proyectos
|
||||
enable_all: Enable for all projects
|
||||
select_projects: Select projects
|
||||
select_projects_description: Select the projects in which you would like to use this type.
|
||||
enable_all: Habilitar en todos los proyectos
|
||||
select_projects: Seleccionar proyectos
|
||||
select_projects_description: Seleccione los proyectos en los que desea utilizar este tipo.
|
||||
settings:
|
||||
tab: "Ajustes"
|
||||
type_color_text: El color seleccionado distingue distintos tipos en los diagramas de Gantt o en las tablas de paquetes de trabajo. Por lo tanto, se recomienda usar un color intenso.
|
||||
@@ -886,7 +886,7 @@ es:
|
||||
required_description: "Marca este paquete de trabajo como requisito del relacionado"
|
||||
label_parent_singular: "principal"
|
||||
label_parent_plural: "principal"
|
||||
label_other_relations: "Other relations"
|
||||
label_other_relations: "Otras relaciones"
|
||||
ghost_relation_title: "Paquete de trabajo relacionado"
|
||||
ghost_relation_description: "No tiene los permisos necesarios para ver esto."
|
||||
label_invitation: Invitación
|
||||
@@ -941,7 +941,7 @@ es:
|
||||
attribute_name: "Atributo"
|
||||
help_text: "Texto de ayuda"
|
||||
auth_provider:
|
||||
scim_clients: "SCIM clients"
|
||||
scim_clients: "Clientes SCIM"
|
||||
capability:
|
||||
context: "Contexto"
|
||||
changeset:
|
||||
@@ -955,7 +955,7 @@ es:
|
||||
default_value: "Valor predeterminado"
|
||||
editable: "Editable"
|
||||
field_format: "Formato"
|
||||
formula: "Formula"
|
||||
formula: "Fórmula"
|
||||
is_filter: "Usado como filtro"
|
||||
is_for_all: "Para todos los proyectos"
|
||||
is_required: "Obligatorio"
|
||||
@@ -984,12 +984,12 @@ es:
|
||||
enterprise_token:
|
||||
starts_at: "Válido desde"
|
||||
subscriber: "Suscriptor"
|
||||
subscription: "Subscription"
|
||||
subscription: "Suscripción"
|
||||
plan: "Plan"
|
||||
encoded_token: "Token de soporte de Enterprise"
|
||||
active_user_count_restriction: "Active users"
|
||||
active_user_count_restriction: "Usuarios activos"
|
||||
enterprise_trial:
|
||||
company: "Company"
|
||||
company: "Empresa"
|
||||
grids/grid:
|
||||
page: "Página"
|
||||
row_count: "Número de filas"
|
||||
@@ -1107,9 +1107,9 @@ es:
|
||||
role:
|
||||
permissions: "Permisos"
|
||||
scim_client:
|
||||
auth_provider: "Authentication provider"
|
||||
authentication_method: "Authentication method"
|
||||
jwt_sub: "Subject claim"
|
||||
auth_provider: "Proveedor de autenticación"
|
||||
authentication_method: "Método de autenticación"
|
||||
jwt_sub: "Reclamación de asunto"
|
||||
status:
|
||||
is_closed: "Paquete de trabajo cerrado"
|
||||
is_readonly: "Paquete de trabajo de solo lectura"
|
||||
@@ -1126,7 +1126,7 @@ es:
|
||||
ongoing: "En curso"
|
||||
type:
|
||||
description: "Texto predeterminado para descripción"
|
||||
attribute_groups: "Form configuration"
|
||||
attribute_groups: "Configuración del formulario"
|
||||
is_in_roadmap: "Mostrado en la hoja de ruta de forma predeterminada"
|
||||
is_default: "Activado para nuevos proyectos de forma predeterminada"
|
||||
is_milestone: "Es un hito"
|
||||
@@ -1153,24 +1153,24 @@ es:
|
||||
password_confirmation: "Confirmación"
|
||||
consented_at: "Consentido en"
|
||||
group:
|
||||
identity_url: "Identity URL"
|
||||
identity_url: "URL de identidad"
|
||||
user_preference:
|
||||
header_look_and_feel: "Look and feel"
|
||||
header_alerts: "Alerts"
|
||||
button_update_look_and_feel: "Update look and feel"
|
||||
button_update_alerts: "Update alerts"
|
||||
comments_sorting: "Display work package activity sorted by"
|
||||
disable_keyboard_shortcuts: "Disable keyboard shortcuts"
|
||||
header_look_and_feel: "Apariencia"
|
||||
header_alerts: "Alertas"
|
||||
button_update_look_and_feel: "Actualizar apariencia"
|
||||
button_update_alerts: "Actualizar alertas"
|
||||
comments_sorting: "Mostrar la actividad del paquete de trabajo ordenada por"
|
||||
disable_keyboard_shortcuts: "Deshabilitar atajos de teclado"
|
||||
disable_keyboard_shortcuts_caption_html: |-
|
||||
You can choose to disable default <a href="%{href}">keyboard shortcuts</a> if you use a screen reader or want to avoid accidentally triggering an action with a shortcut.
|
||||
Puede optar por deshabilitar los <a href="%{href}">atajos de teclado</a> predeterminados si utiliza un lector de pantalla o desea evitar activar accidentalmente una acción con un atajo.
|
||||
dismissed_enterprise_banners: "Banners empresariales ocultos"
|
||||
impaired: "Modo de accesibilidad"
|
||||
auto_hide_popups: "Automatically hide success banners"
|
||||
auto_hide_popups_caption: "When enabled, the green success banners will automatically disappear after 5 seconds."
|
||||
auto_hide_popups: "Ocultar automáticamente los banners de éxito"
|
||||
auto_hide_popups_caption: "Si se habilitan, los banners verdes de éxito desaparecerán automáticamente al cabo de 5 segundos."
|
||||
warn_on_leaving_unsaved: "Avisarme cuando salga de un paquete de trabajo con cambios sin guardar"
|
||||
theme: "Colour mode"
|
||||
theme: "Modo de color"
|
||||
time_zone: "Zona horaria"
|
||||
mode_guideline: "Some modes will overwrite custom theme colours for accessibility and legibility. Please select Light mode for full custom theme support."
|
||||
mode_guideline: "Algunos modos sobrescribirán los colores de los temas personalizados por motivos de accesibilidad y legibilidad. Seleccione el modo Claro para obtener una compatibilidad total con los temas personalizados."
|
||||
daily_reminders: "Recordatorios diarios"
|
||||
workdays: "Días laborables"
|
||||
version:
|
||||
@@ -1239,7 +1239,7 @@ es:
|
||||
error_unauthorized: "no se puede acceder."
|
||||
error_readonly: "se intentó escribir pero no se puede escribir."
|
||||
error_conflict: "La información ha sido actualizada por al menos otro usuario mientras tanto."
|
||||
error_not_found: "not found."
|
||||
error_not_found: "no encontrado."
|
||||
email: "no es una dirección de correo válida."
|
||||
empty: "No puede estar vacío."
|
||||
enterprise_plan_required: "requiere al menos el plan %{plan_name}."
|
||||
@@ -1257,7 +1257,7 @@ es:
|
||||
inclusion: "no está establecido a uno de los valores permitidos."
|
||||
inclusion_nested: "no está establecido en uno de los valores permitidos en la ruta '%{path}'."
|
||||
invalid: "no es válido."
|
||||
invalid_characters: "contains invalid characters."
|
||||
invalid_characters: "contiene caracteres no válidos."
|
||||
invalid_url: "no es una URL válida."
|
||||
invalid_url_scheme: "no es un protocolo admitido (permitidos: %{allowed_schemes})."
|
||||
less_than_or_equal_to: "debe ser menor o igual a %{count}."
|
||||
@@ -1300,7 +1300,7 @@ es:
|
||||
attributes:
|
||||
content_type:
|
||||
blank: "El tipo de contenido del archivo no puede estar en blanco."
|
||||
not_allowlisted: "The file was rejected by an automatic filter. '%{value}' is not allowed for upload."
|
||||
not_allowlisted: "Un filtro automático ha rechazado el archivo. No se permite «%{value}» en la carga."
|
||||
format: "%{message}"
|
||||
capability:
|
||||
context:
|
||||
@@ -1334,13 +1334,13 @@ es:
|
||||
scopes:
|
||||
not_match_configured: "no coincide con los ámbitos disponibles."
|
||||
enterprise_trial:
|
||||
already_used: "was already used to create a trial."
|
||||
failed_to_create: "Trial could not be created (%{status})"
|
||||
general_consent: "Please accept the terms and conditions."
|
||||
already_used: "ya se utilizó para crear una prueba."
|
||||
failed_to_create: "No se ha podido crear la prueba (%{status})"
|
||||
general_consent: "Acepte los términos y condiciones."
|
||||
enterprise_token:
|
||||
only_one_trial: "Only one trial token can be active. Please delete the previous trial token before adding another."
|
||||
only_one_trial: "Solo puede haber un token de prueba activo. Elimine el token de prueba anterior antes de añadir otro."
|
||||
unreadable: "no se puede leer. ¿Seguro que es un token de soporte?"
|
||||
already_added: "This token has already been added."
|
||||
already_added: "Este token ya se ha añadido."
|
||||
grids/grid:
|
||||
overlaps: "superpuesto."
|
||||
outside: "está fuera de la red."
|
||||
@@ -1635,8 +1635,8 @@ es:
|
||||
one: "Rol"
|
||||
other: "Roles"
|
||||
scim_client:
|
||||
one: "SCIM client"
|
||||
other: "SCIM clients"
|
||||
one: "Cliente SCIM"
|
||||
other: "Clientes SCIM"
|
||||
status: "Estado del paquete de trabajo"
|
||||
token/api:
|
||||
one: Token de acceso
|
||||
@@ -1733,13 +1733,13 @@ es:
|
||||
derived_start_date: "Fecha de comienzo deseada"
|
||||
direction: "Dirección"
|
||||
display_sums: "Mostrar sumas"
|
||||
domain: "Domain"
|
||||
domain: "Dominio"
|
||||
due_date: "Fecha de finalización"
|
||||
estimated_hours: "Trabajo"
|
||||
estimated_time: "Trabajo"
|
||||
email: "Correo electrónico"
|
||||
entity_type: "Entidad"
|
||||
expires_at: "Expires on"
|
||||
expires_at: "Caduca el"
|
||||
firstname: "Nombre"
|
||||
filter: "Filtro"
|
||||
group: "Grupo"
|
||||
@@ -1788,7 +1788,7 @@ es:
|
||||
title: "Título"
|
||||
type: "Tipo"
|
||||
typeahead: "Autocompletar"
|
||||
uid: "Unique identifier"
|
||||
uid: "Identificador único"
|
||||
updated_at: "Actualizada el"
|
||||
updated_on: "Actualizada el"
|
||||
uploader: "Cargador"
|
||||
@@ -1863,7 +1863,7 @@ es:
|
||||
button_delete_watcher: "Eliminar observador %{name}"
|
||||
button_download: "Descargar"
|
||||
button_duplicate: "Duplicar"
|
||||
button_duplicate_and_follow: "Duplicate and follow"
|
||||
button_duplicate_and_follow: "Duplicar y seguir"
|
||||
button_edit: "Editar"
|
||||
button_edit_associated_wikipage: "Editar página Wiki asociada: %{page_title}"
|
||||
button_expand_all: "Expandir todos"
|
||||
@@ -1902,7 +1902,7 @@ es:
|
||||
button_unwatch: "No controlar más"
|
||||
button_update: "Actualizar"
|
||||
button_upgrade: "Mejorar"
|
||||
button_buy_now: "Buy now"
|
||||
button_buy_now: "Comprar ahora"
|
||||
button_upload: "Cargar"
|
||||
button_view: "Ver"
|
||||
button_watch: "Controlar"
|
||||
@@ -1947,13 +1947,13 @@ es:
|
||||
text:
|
||||
failed: 'No se pudo copiar el proyecto "%{source_project_name}" al proyecto "%{target_project_name}".'
|
||||
succeeded: 'Proyecto copiado de "%{source_project_name}" a "%{target_project_name}".'
|
||||
source_project_label: "Project copied"
|
||||
source_project_label: "Proyecto copiado"
|
||||
copy_options:
|
||||
dependencies_label: 'Copy from project'
|
||||
dependencies_label: 'Copiar desde el proyecto'
|
||||
create_project:
|
||||
template_label: "Use template"
|
||||
template_label: "Usar plantilla"
|
||||
copy_options:
|
||||
dependencies_label: 'Copy from template'
|
||||
dependencies_label: 'Copiar desde la plantilla'
|
||||
create_wiki_page: "Crear una nueva página wiki"
|
||||
create_wiki_page_button: "Página wiki"
|
||||
date:
|
||||
@@ -2128,7 +2128,7 @@ es:
|
||||
placeholder_users: Usuarios de marcadores de posición
|
||||
project_list_sharing: Compartición de la lista de proyectos
|
||||
readonly_work_packages: Paquetes de trabajo de solo lectura
|
||||
scim_api: SCIM server API
|
||||
scim_api: API del servidor SCIM
|
||||
sso_auth_providers: Inicio de sesión único
|
||||
team_planner_view: Vista del planificador de equipo
|
||||
virus_scanning: Análisis de antivirus
|
||||
@@ -2142,9 +2142,9 @@ es:
|
||||
plan_title: "Extensión Enterprise de %{plan}"
|
||||
plan_name: "Plan Enterprise %{plan}"
|
||||
plan_text_html: "Disponible a partir del %{plan_name}."
|
||||
unlimited: "Unlimited"
|
||||
unlimited: "Ilimitado"
|
||||
already_have_token: >
|
||||
Already have a token? Add it using the button below to upgrade to the booked Enterprise plan.
|
||||
¿Ya tiene un token? Añádalo utilizando el botón de abajo para actualizar al plan Enterprise reservado.
|
||||
hide_banner: "Ocultar este banner"
|
||||
homescreen_description: >
|
||||
Los planes Enterprise amplían la edición Community de OpenProject que está utilizando actualmente. Incluyen complementos, funciones adicionales y asistencia profesional que permiten a organizaciones de todos los tamaños lograr grandes cosas juntas.
|
||||
@@ -2152,15 +2152,15 @@ es:
|
||||
baseline_comparison:
|
||||
description: Resalte los cambios realizados en esta lista desde cualquier momento en el pasado.
|
||||
benefits:
|
||||
description: "What are the benefits of the Enterprise on-premises edition?"
|
||||
high_security: "Security features"
|
||||
high_security_text: "Single sign on (SAML, OpenID Connect, CAS), LDAP groups."
|
||||
installation: "Installation support"
|
||||
installation_text: "Experienced software engineers guide you through the complete installation and setup process in your own infrastructure."
|
||||
premium_features: "Enterprise add-ons"
|
||||
premium_features_text: "Agile boards, custom theme and logo, graphs, intelligent workflows with custom actions, full text search for work package attachments and multi-select custom fields."
|
||||
professional_support: "Professional support"
|
||||
professional_support_text: "Get reliable, high-touch support from senior support engineers with expert knowledge about running OpenProject in business-critical environments."
|
||||
description: "¿Cuáles son las ventajas de Enterprise On-Premises?"
|
||||
high_security: "Funciones de seguridad"
|
||||
high_security_text: "Inicio de sesión único (SAML, OpenID Connect, CAS), grupos LDAP."
|
||||
installation: "Soporte de instalación"
|
||||
installation_text: "Nuestros experimentados ingenieros de software le guiarán en todo el proceso de instalación y configuración en su propia infraestructura."
|
||||
premium_features: "Extensiones Enterprise"
|
||||
premium_features_text: "Tableros Agile, tema y logotipo personalizados, gráficos, flujos de trabajo inteligentes con acciones personalizadas, búsqueda de texto completo en archivos adjuntos de paquetes de trabajo y campos personalizados de selección múltiple."
|
||||
professional_support: "Soporte profesional"
|
||||
professional_support_text: "Obtenga ayuda rápida y de confianza de nuestros agentes de soporte con conocimientos especializados sobre cómo ejecutar OpenProject en entornos empresariales críticos."
|
||||
work_package_subject_generation:
|
||||
description: "Cree asuntos generados automáticamente utilizando atributos y texto referenciados."
|
||||
customize_life_cycle:
|
||||
@@ -2196,8 +2196,8 @@ es:
|
||||
title: "Inicio de sesión único para almacenamiento en la nube"
|
||||
description: "Habilite una autenticación fluida y segura para su almacenamiento Nextcloud con el inicio de sesión único. Simplifique la gestión del acceso y mejore la comodidad de los usuarios."
|
||||
scim_api:
|
||||
title: "SCIM clients"
|
||||
description: "Automate user management in OpenProject by seamlessly integrating external identity services like Microsoft Entra or Keycloak through our SCIM Server API. Available starting with the Enterprise corporate plan."
|
||||
title: "Clientes SCIM"
|
||||
description: "Automatice la gestión de usuarios en OpenProject integrando a la perfección servicios de identidad externos como Microsoft Entra o Keycloak a través de nuestra API del servidor SCIM. Disponible a partir del plan corporativo Enterprise."
|
||||
virus_scanning:
|
||||
description: "Asegúrese de que los archivos cargados en OpenProject se analizan en busca de virus antes de que otros usuarios puedan acceder a ellos."
|
||||
placeholder_users:
|
||||
@@ -2212,29 +2212,29 @@ es:
|
||||
description: " "
|
||||
teaser:
|
||||
title:
|
||||
one: "One day left of %{trial_plan} trial token"
|
||||
other: "%{count} days left of %{trial_plan} trial token"
|
||||
description: "You have access to all %{trial_plan} features."
|
||||
one: "Queda un día del token de prueba de %{trial_plan}"
|
||||
other: "Quedan %{count} días del token de prueba de %{trial_plan}"
|
||||
description: "Tiene acceso a todas las funciones de %{trial_plan}."
|
||||
trial:
|
||||
not_found: "You have requested a trial token, but that request is no longer available. Please try again."
|
||||
wait_for_confirmation: "We sent you an email to confirm your address in order to retrieve a trial token."
|
||||
not_found: "Ha solicitado un token de prueba, pero esa solicitud ya no está disponible. Inténtelo de nuevo."
|
||||
wait_for_confirmation: "Le hemos enviado un correo electrónico para que confirme su dirección con el fin de recuperar un token de prueba."
|
||||
already_retrieved: >
|
||||
Your trial enterprise token was already retrieved. Please check your emails for the token being attached. Please reach out to our support team if you need a new one.
|
||||
successfully_saved: "Your trial enterprise token has been successfully retrieved."
|
||||
token_sent: "Trial token requested"
|
||||
request_again: "Request again"
|
||||
resend_action: "Resend confirmation email"
|
||||
welcome_title: "Quick feature overview"
|
||||
welcome_description: "Get a quick overview of project management and team collaboration with OpenProject Enterprise edition."
|
||||
Su token de prueba de Enterprise ya se ha recuperado. Busque en sus correos electrónicos el token adjuntado. Póngase en contacto con nuestro equipo de Soporte si necesita uno nuevo.
|
||||
successfully_saved: "Su token de prueba de Enterprise se ha recuperado correctamente."
|
||||
token_sent: "Token de prueba solicitado"
|
||||
request_again: "Solicitar de nuevo"
|
||||
resend_action: "Reenviar correo electrónico de confirmación"
|
||||
welcome_title: "Resumen rápido de funciones"
|
||||
welcome_description: "Obtenga un resumen rápido de la gestión de proyectos y la colaboración en equipo con Enterprise Edition de OpenProject."
|
||||
confirmation_info: >
|
||||
We sent you an email on %{date} to %{email} with all the information to start the free trial of OpenProject Enterprise. Please check your inbox and click the confirmation link provided to start your 14-day free trial.
|
||||
Le enviamos un correo electrónico el %{date} a la dirección %{email} con toda la información para iniciar la prueba gratuita de OpenProject Enterprise. Revise su bandeja de entrada y haga clic en el enlace de confirmación proporcionado para iniciar su prueba gratuita de 14 días.
|
||||
confirmation_subline: >
|
||||
Please, check your inbox and follow the steps to start your 14-day free trial.
|
||||
domain_caption: The token will be valid for your currently configured host name.
|
||||
Revise su bandeja de entrada y siga los pasos para iniciar su prueba gratuita de 14 días.
|
||||
domain_caption: El token será válido para su nombre de host configurado actualmente.
|
||||
receive_newsletter_html: >
|
||||
I want to receive the OpenProject <a target="_blank" href="https://www.openproject.org/newsletter/">newsletter</a>.
|
||||
Quiero recibir el <a target="_blank" href="https://www.openproject.org/newsletter/">boletín</a> de OpenProject.
|
||||
consent_html: >
|
||||
I agree with the <a target="_blank" href="https://www.openproject.org/terms-of-service/">terms of service</a> and the <a target="_blank" href="https://www.openproject.org/data-privacy-and-security/">privacy policy</a>.
|
||||
Acepto los <a target="_blank" href="https://www.openproject.org/terms-of-service/">términos del servicio</a> y la <a target="_blank" href="https://www.openproject.org/data-privacy-and-security/">política de privacidad</a>.
|
||||
enumeration_activities: "Actividades de seguimiento del tiempo"
|
||||
enumeration_work_package_priorities: "Prioridades del paquete de trabajo"
|
||||
enumeration_reported_project_statuses: "Estatus del proyecto reportado"
|
||||
@@ -2694,7 +2694,7 @@ es:
|
||||
label_select_main_menu_item: Seleccione nuevo elemento de menú principal
|
||||
label_required_disk_storage: "Almacenamiento en disco requerido"
|
||||
label_send_invitation: Enviar invitación
|
||||
label_calculated_value: "Calculated value"
|
||||
label_calculated_value: "Valor calculado"
|
||||
label_change_plural: "Cambios"
|
||||
label_change_properties: "Cambiar propiedades"
|
||||
label_change_status: "Cambiar estado"
|
||||
@@ -2830,7 +2830,7 @@ es:
|
||||
label_follows: "sigue"
|
||||
label_force_user_language_to_default: "Configurar idioma de usuarios que tienen un lenguaje no permitido por defecto"
|
||||
label_form_configuration: "Configuración del formato"
|
||||
label_formula: "Formula"
|
||||
label_formula: "Fórmula"
|
||||
label_gantt_chart: "Diagrama de Gantt"
|
||||
label_gantt_chart_plural: "Diagramas de Gantt"
|
||||
label_general: "General"
|
||||
@@ -2867,7 +2867,7 @@ es:
|
||||
label_information_plural: "Información"
|
||||
label_installation_guides: "Guías de instalación"
|
||||
label_integer: "Número entero"
|
||||
label_interface: "Interface"
|
||||
label_interface: "Interfaz"
|
||||
label_internal: "Interno"
|
||||
label_introduction_video: "Vídeo de introducción"
|
||||
label_invite_user: "Invitar usuario"
|
||||
@@ -3228,7 +3228,7 @@ es:
|
||||
label_work_package_attachments: "Datos adjuntos de paquete de trabajo"
|
||||
label_work_package_category_new: "Nueva categoría"
|
||||
label_work_package_category_plural: "Categorías de paquete de trabajo"
|
||||
label_work_package_comments: "Work package comments"
|
||||
label_work_package_comments: "Comentarios del paquete de trabajo"
|
||||
label_work_package_hierarchy: "Jerarquía del paquete de trabajo"
|
||||
label_work_package_new: "Nuevo paquete de trabajo"
|
||||
label_work_package_edit: "Editar paquete de trabajo %{name}"
|
||||
@@ -3569,7 +3569,7 @@ es:
|
||||
permission_comment_news: "Comentar noticias"
|
||||
permission_commit_access: "Permiso de escritura sobre el repositorio (commit)"
|
||||
permission_copy_projects: "Copiar proyectos"
|
||||
permission_copy_work_packages: "Duplicate work packages"
|
||||
permission_copy_work_packages: "Duplicar paquetes de trabajo"
|
||||
permission_create_backup: "Crear copias de seguridad"
|
||||
permission_delete_work_package_watchers: "Eliminar los observadores"
|
||||
permission_delete_work_packages: "Eliminar paquetes de trabajo"
|
||||
@@ -4089,7 +4089,7 @@ es:
|
||||
phase_gates: "Puertas de fase"
|
||||
new:
|
||||
description: "Los cambios en esta fase del proyecto se reflejarán en todos los proyectos en los que esté activada."
|
||||
heading: "New phase"
|
||||
heading: "Nueva fase"
|
||||
both_gate: "Puerta inicial y final"
|
||||
no_gate: "Sin puerta"
|
||||
start_gate: "Puerta de inicio"
|
||||
@@ -4392,7 +4392,7 @@ es:
|
||||
reminders:
|
||||
label_remind_at: "Fecha"
|
||||
note_placeholder: "¿Por qué establece este recordatorio?"
|
||||
create_success_message: "Reminder set successfully. You will receive a notification for this work package %{reminder_time}."
|
||||
create_success_message: "Recordatorio establecido correctamente. Recibirá una notificación para este paquete de trabajo %{reminder_time}."
|
||||
success_update_message: "Recordatorio actualizado con éxito."
|
||||
success_deletion_message: "Recordatorio eliminado correctamente."
|
||||
sharing:
|
||||
@@ -4464,12 +4464,12 @@ es:
|
||||
message: "Compartir listas de proyectos con usuarios individuales es una extensión Enterprise."
|
||||
working_days:
|
||||
info: >
|
||||
Days that are not selected are skipped when scheduling work packages and project life cycles (and not included in the day count). These can be overridden at a work-package level.
|
||||
Los días no seleccionados se omiten al programar los paquetes de trabajo y ciclos de vida de proyectos (y no se incluyen en el recuento de días). Esto puede anularse a nivel de paquete de trabajo.
|
||||
instance_wide_info: >
|
||||
Las fechas añadidas a la lista siguiente se consideran no laborables y se omiten al programar los paquetes de trabajo.
|
||||
change_button: "Cambiar los días laborables"
|
||||
warning: >
|
||||
Changing which days of the week are considered working days or non-working days can affect the start and finish days of all work packages and life cycles in all projects in this instance.
|
||||
Cambiar qué días de la semana se consideran laborables y cuáles no, puede afectar a las fechas de inicio y fin de todos los paquetes de trabajo y ciclos de vida en todos los proyectos de esta instancia.
|
||||
journal_note:
|
||||
changed: _**Días laborales** cambiados (%{changes})._
|
||||
days:
|
||||
|
||||
@@ -642,7 +642,8 @@ de:
|
||||
title: "Geben Sie Ihr Kennwort ein, um fortzufahren"
|
||||
pagination:
|
||||
no_other_page: "Sie befinden sich auf der einzigen Seite."
|
||||
pagination_controls: "Paginierungs-Steuerung"
|
||||
page_navigation: "Pagination navigation"
|
||||
per_page_navigation: 'Items per page selection'
|
||||
pages:
|
||||
next: "Nächste Seite"
|
||||
previous: "Vorherige Seite"
|
||||
|
||||
@@ -212,9 +212,10 @@ es:
|
||||
change_button: "Guardar y reprogramar"
|
||||
change_title: "Cambiar los días laborables"
|
||||
removed_title: "Eliminará los siguientes días de la lista de días no laborables:"
|
||||
change_description: "Changing which days of the week are considered working days or non-working days can affect the start and finish days of all work packages and life cycles in all projects in this instance."
|
||||
change_description: "Cambiar qué días de la semana se consideran laborables y cuáles no, puede afectar a las fechas de inicio y fin de todos los paquetes de trabajo y ciclos de vida en todos los proyectos de esta instancia."
|
||||
warning: >
|
||||
|
||||
Los cambios pueden tardar algún tiempo en surtir efecto. Se le notificará cuando todos los paquetes de trabajo pertinentes se hayan actualizado.
|
||||
¿Seguro que desea continuar?
|
||||
work_packages_settings:
|
||||
warning_progress_calculation_mode_change_from_status_to_field_html: >-
|
||||
Cambiar el modo de cálculo del progreso de basado en el estado a basado en el trabajo hará que el campo <i>% completado</i> se pueda editar libremente. Si introduce opcionalmente valores para <i>Trabajo</i> o <i>Trabajo restante</i>, también se vincularán a <i>% completado</i>. Cambiar <i>Trabajo restante</i> puede entonces actualizar <i>% completado</i>.
|
||||
@@ -297,7 +298,7 @@ es:
|
||||
"16_2":
|
||||
standard:
|
||||
new_features_html: >
|
||||
The release contains various new features and improvements, such as <br> <ul class="%{list_styling_class}"> <li>New header and sidebar with improved navigation and design.</li> <li>Smoother experience with Custom fields, Relations, Reminders, Meetings, and My time tracking.</li> <li>Non-latin languages and emojis supported in PDF exports.</li> <li>Option to disable keyboard shortcuts for better accessibility.</li> <li>SCIM server via API (Enterprise add-on).</li> <li>API support for internal comments.</li> </ul>
|
||||
El lanzamiento contiene varias funciones y mejoras nuevas, como <br>. <ul class="%{list_styling_class}"> <li>Nuevo encabezado y barra lateral con navegación y diseño mejorados.</li> <li>Experiencia mejorada con Campos personalizados, Relaciones, Recordatorios, Reuniones y Seguimiento de mi tiempo.</li> <li>Idiomas no latinos y emojis compatibles en las exportaciones a PDF.</li> <li>Opción de desactivar los atajos de teclado para mejorar la accesibilidad.</li> <li>Servidor SCIM mediante API (extensión Enterprise).</li> <li>Soporte de API para comentarios internos.</li> </ul>
|
||||
ical_sharing_modal:
|
||||
title: "Suscribirse al calendario"
|
||||
inital_setup_error_message: "Se ha producido un error al obtener los datos."
|
||||
@@ -323,7 +324,7 @@ es:
|
||||
label_added_time_by: 'Agregado por <a href="%{authorLink}">%{author}</a> a las %{age}'
|
||||
label_ago: "días antes"
|
||||
label_all: "todos"
|
||||
label_all_projects: "All projects"
|
||||
label_all_projects: "Todos los proyectos"
|
||||
label_all_uppercase: "Todos"
|
||||
label_all_work_packages: "todos los paquetes de trabajo"
|
||||
label_and: "y"
|
||||
@@ -642,13 +643,13 @@ es:
|
||||
title: "Confirma tu contraseña para continuar"
|
||||
pagination:
|
||||
no_other_page: "Usted está en la única página."
|
||||
page_navigation: "Pagination navigation"
|
||||
per_page_navigation: 'Items per page selection'
|
||||
page_navigation: "Navegación de paginación"
|
||||
per_page_navigation: 'Selección de elementos por página'
|
||||
pages:
|
||||
next: "Next page"
|
||||
previous: "Previous page"
|
||||
page_number: Page %{number}
|
||||
show_per_page: Show %{number} per page
|
||||
next: "Página siguiente"
|
||||
previous: "Página anterior"
|
||||
page_number: Página %{number}
|
||||
show_per_page: Mostrar %{number} por página
|
||||
placeholders:
|
||||
default: "-"
|
||||
subject: "Escriba aquí el asunto"
|
||||
@@ -696,7 +697,7 @@ es:
|
||||
first_day: "Primer día"
|
||||
last_day: "Último día"
|
||||
text_are_you_sure: "¿Estás seguro?"
|
||||
breadcrumb: "Breadcrumb"
|
||||
breadcrumb: "Ruta de navegación"
|
||||
text_data_lost: "Todos los datos introducidos se perderán."
|
||||
text_user_wrote: "%{value} escribió:"
|
||||
types:
|
||||
@@ -781,7 +782,7 @@ es:
|
||||
autocompleter:
|
||||
placeholder: "Escriba para buscar"
|
||||
notFoundText: "No se encontraron elementos"
|
||||
search: "Search"
|
||||
search: "Buscar"
|
||||
project:
|
||||
placeholder: "Seleccionar proyecto"
|
||||
repositories:
|
||||
@@ -829,7 +830,7 @@ es:
|
||||
bulk_actions:
|
||||
edit: "Edición en masa"
|
||||
delete: "Eliminación en masa"
|
||||
duplicate: "Bulk duplicate"
|
||||
duplicate: "Duplicado en masa"
|
||||
move: "Cambio masivo del proyecto"
|
||||
button_clear: "Eliminar"
|
||||
comment_added: "El comentario fue añadido con éxito."
|
||||
@@ -875,7 +876,7 @@ es:
|
||||
header_with_parent: "Nuevo %{type} (Sub-elemento de %{parent_type} #%{id})"
|
||||
button: "Crear"
|
||||
duplicate:
|
||||
title: "Duplicate work package"
|
||||
title: "Duplicar paquete de trabajo"
|
||||
hierarchy:
|
||||
show: "Mostrar modo de jerarquía"
|
||||
hide: "Ocultar modo de jerarquía"
|
||||
|
||||
@@ -297,7 +297,7 @@ pt-BR:
|
||||
"16_2":
|
||||
standard:
|
||||
new_features_html: >
|
||||
A versão contém vários novos recursos e aprimoramentos, como <br> <ul class="%{list_styling_class}"> <li>Novo cabeçalho e barra lateral com navegação e design aprimorados.</li> <li>Experiência mais suave com campos personalizados, relações, lembretes, reuniões e controle do meu tempo.</li> <li>Suporte a idiomas não latinos e emojis nas exportações de PDF.</li> <li>Opção para desativar os atalhos de teclado para melhorar a acessibilidade.</li> <li>Servidor SCIM via API (complemento Enterprise).</li> <li>Suporte à API para comentários internos.</li> </ul>
|
||||
A versão inclui vários novos recursos e melhorias, tais como <br> <ul class="%{list_styling_class}"> <li>Novo cabeçalho e barra lateral com navegação e design aprimorados.</li> <li>Experiência mais fluida com Campos personalizados, Relações, Lembretes, Reuniões e Meu acompanhamento de tempo.</li> <li>Suporte a idiomas não latinos e emojis em exportações PDF.</li> <li>Opção para desativar atalhos de teclado para melhor acessibilidade.</li> <li>Servidor SCIM via API (add-on Enterprise).</li> <li>Suporte à API para comentários internos.</li> </ul>
|
||||
ical_sharing_modal:
|
||||
title: "Assinar calendário"
|
||||
inital_setup_error_message: "Ocorreu um erro ao buscar dados."
|
||||
@@ -642,12 +642,13 @@ pt-BR:
|
||||
title: "Confirme sua senha para continuar"
|
||||
pagination:
|
||||
no_other_page: "Você está na página única."
|
||||
pagination_controls: "Controles de paginação"
|
||||
page_navigation: "Pagination navigation"
|
||||
per_page_navigation: 'Items per page selection'
|
||||
pages:
|
||||
next: "Próxima página"
|
||||
previous: "Página anterior"
|
||||
page_number: Página %{number}
|
||||
show_per_page: Mostrar %{number} por página
|
||||
show_per_page: Exibir %{number} por página
|
||||
placeholders:
|
||||
default: "-"
|
||||
subject: "Informe o assunto aqui"
|
||||
@@ -695,7 +696,7 @@ pt-BR:
|
||||
first_day: "Primeiro dia"
|
||||
last_day: "Último dia"
|
||||
text_are_you_sure: "Você tem certeza?"
|
||||
breadcrumb: "Rota de navegação"
|
||||
breadcrumb: "Trilha de navegação"
|
||||
text_data_lost: "Todos os dados inseridos serão perdidos."
|
||||
text_user_wrote: "%{value} escreveu:"
|
||||
types:
|
||||
|
||||
@@ -298,7 +298,7 @@ pt-PT:
|
||||
"16_2":
|
||||
standard:
|
||||
new_features_html: >
|
||||
The release contains various new features and improvements, such as <br> <ul class="%{list_styling_class}"> <li>New header and sidebar with improved navigation and design.</li> <li>Smoother experience with Custom fields, Relations, Reminders, Meetings, and My time tracking.</li> <li>Non-latin languages and emojis supported in PDF exports.</li> <li>Option to disable keyboard shortcuts for better accessibility.</li> <li>SCIM server via API (Enterprise add-on).</li> <li>API support for internal comments.</li> </ul>
|
||||
A versão contém várias novas funcionalidades e melhorias, tais como <br> <ul class="%{list_styling_class}"> <li>Novo cabeçalho e barra lateral com navegação e design melhorados.</li> <li>Experiência mais fácil com campos personalizados, relações, lembretes, reuniões e controlo do meu tempo.</li> <li>Línguas não latinas e emojis suportados nas exportações de PDF.</li> <li>Opção para desativar os atalhos de teclado para melhor acessibilidade.</li> <li>Servidor SCIM através de API (complemento Enterprise).</li> <li>Suporte da API para comentários internos.</li> </ul>
|
||||
ical_sharing_modal:
|
||||
title: "Subscrever o calendário"
|
||||
inital_setup_error_message: "Ocorreu um erro ao recuperar os dados."
|
||||
@@ -324,7 +324,7 @@ pt-PT:
|
||||
label_added_time_by: 'Adicionado por <a href="%{authorLink}">%{author}</a> a %{age}'
|
||||
label_ago: "dias atrás"
|
||||
label_all: "todos"
|
||||
label_all_projects: "All projects"
|
||||
label_all_projects: "Todos os projetos"
|
||||
label_all_uppercase: "Todas"
|
||||
label_all_work_packages: "todas as tarefas"
|
||||
label_and: "e"
|
||||
@@ -643,13 +643,13 @@ pt-PT:
|
||||
title: "Confirme a sua palavra-passe para continuar"
|
||||
pagination:
|
||||
no_other_page: "Mais nenhuma página a apresentar."
|
||||
page_navigation: "Pagination navigation"
|
||||
per_page_navigation: 'Items per page selection'
|
||||
page_navigation: "Navegação por página"
|
||||
per_page_navigation: 'Seleção de elementos por página'
|
||||
pages:
|
||||
next: "Next page"
|
||||
previous: "Previous page"
|
||||
page_number: Page %{number}
|
||||
show_per_page: Show %{number} per page
|
||||
next: "Próxima página"
|
||||
previous: "Página anterior"
|
||||
page_number: Página %{number}
|
||||
show_per_page: Mostrar %{number} por página
|
||||
placeholders:
|
||||
default: "-"
|
||||
subject: "Introduzir assunto aqui"
|
||||
@@ -697,7 +697,7 @@ pt-PT:
|
||||
first_day: "Primeiro dia"
|
||||
last_day: "Último dia"
|
||||
text_are_you_sure: "Tem a certeza?"
|
||||
breadcrumb: "Breadcrumb"
|
||||
breadcrumb: "Estrutural"
|
||||
text_data_lost: "Todos os dados inseridos serão perdidos."
|
||||
text_user_wrote: "%{value} escreveu:"
|
||||
types:
|
||||
@@ -830,7 +830,7 @@ pt-PT:
|
||||
bulk_actions:
|
||||
edit: "Editar vários"
|
||||
delete: "Apagar vários"
|
||||
duplicate: "Bulk duplicate"
|
||||
duplicate: "Duplicado em massa"
|
||||
move: "Mudança em massa de projeto"
|
||||
button_clear: "Limpar"
|
||||
comment_added: "O comentário foi adicionado com sucesso."
|
||||
@@ -876,7 +876,7 @@ pt-PT:
|
||||
header_with_parent: "Nova %{type} (Filho de %{parent_type} #%{id})"
|
||||
button: "Criar"
|
||||
duplicate:
|
||||
title: "Duplicate work package"
|
||||
title: "Duplicar pacote de trabalho"
|
||||
hierarchy:
|
||||
show: "Mostrar modo de hierarquia"
|
||||
hide: "Ocultar modo de hierarquia"
|
||||
|
||||
@@ -644,8 +644,8 @@ ru:
|
||||
title: "Для продолжения введите свой пароль"
|
||||
pagination:
|
||||
no_other_page: "Вы находитесь на единственной странице."
|
||||
page_navigation: "Pagination navigation"
|
||||
per_page_navigation: 'Items per page selection'
|
||||
page_navigation: "Навигация по страницам"
|
||||
per_page_navigation: 'Количество элементов на страницу'
|
||||
pages:
|
||||
next: "Следующая страница"
|
||||
previous: "Предыдущая страница"
|
||||
@@ -831,7 +831,7 @@ ru:
|
||||
bulk_actions:
|
||||
edit: "Массовое изменение"
|
||||
delete: "Массовое удаление"
|
||||
duplicate: "Bulk duplicate"
|
||||
duplicate: "Групповое дублирование"
|
||||
move: "Массовое изменение проекта"
|
||||
button_clear: "Очистить"
|
||||
comment_added: "Комментарий успешно добавлен."
|
||||
@@ -877,7 +877,7 @@ ru:
|
||||
header_with_parent: "Новый %{type} (дочерний пакет работ %{parent_type} № %{id})"
|
||||
button: "Создать"
|
||||
duplicate:
|
||||
title: "Duplicate work package"
|
||||
title: "Дублировать пакет работ"
|
||||
hierarchy:
|
||||
show: "Скрыть режим иерархии"
|
||||
hide: "Скрыть режим иерархии"
|
||||
|
||||
@@ -316,7 +316,7 @@ uk:
|
||||
label_add_column_before: "Додати стовпець перед"
|
||||
label_add_columns: "Додати стовпці"
|
||||
label_add_comment: "Додати коментар"
|
||||
label_add_comment_title: "Коментар і введіть @, щоб повідомити інших людей"
|
||||
label_add_comment_title: "Напишіть коментар і введіть @, щоб повідомити інших людей"
|
||||
label_add_row_after: "Додати рядок після"
|
||||
label_add_row_before: "Додати рядок перед"
|
||||
label_add_selected_columns: "Додати вибрані стовпці"
|
||||
@@ -348,12 +348,12 @@ uk:
|
||||
label_description: "Опис"
|
||||
label_details: "Детальніше"
|
||||
label_display: "Вигляд"
|
||||
label_cancel_comment: "Скасувати коментар"
|
||||
label_cancel_comment: "Скасувати додавання коментаря"
|
||||
label_closed_work_packages: "закрито"
|
||||
label_collapse: "Згорнути"
|
||||
label_collapsed: "згорнуто"
|
||||
label_collapse_all: "Згорнути все"
|
||||
label_comment: "Коментування"
|
||||
label_comment: "Коментар"
|
||||
label_committed_at: "%{committed_revision_link}, %{date}"
|
||||
label_committed_link: "зафіксовано редакцію %{revision_identifier}"
|
||||
label_contains: "містить"
|
||||
@@ -450,14 +450,14 @@ uk:
|
||||
label_sum: "Сума"
|
||||
label_sum_for: "Сума для"
|
||||
label_total_sum: "Загальна сума"
|
||||
label_subject: "Заголовок"
|
||||
label_subject: "Тема"
|
||||
label_this_week: "цього тижня"
|
||||
label_today: "Сьогодні"
|
||||
label_time_entry_plural: "Витрачений час"
|
||||
label_up: "Вгору"
|
||||
label_user_plural: "Користувачі"
|
||||
label_activity_show_only_comments: "Показувати заходи лише з коментарями"
|
||||
label_activity_show_all: "Показати всю активність"
|
||||
label_activity_show_only_comments: "Показувати лише дії з коментарями"
|
||||
label_activity_show_all: "Показати всі дії"
|
||||
label_total_progress: "%{percent}% загального прогресу"
|
||||
label_total_amount: "Всього: %{amount}"
|
||||
label_updated_on: "оновлено"
|
||||
@@ -530,8 +530,8 @@ uk:
|
||||
drag: "Перевпорядкуйте картки в межах даного списку, перетягуючи їх, або перемістіть їх до іншого списку. Щоб переглянути докладні відомості, натисніть значок інформації (i) у верхньому правому куті або двічі натисніть картку."
|
||||
wp:
|
||||
toggler: "Тепер погляньмо на розділ <b>робочих пакетів</b>, де можна дізнатися більше про вашу роботу."
|
||||
list: "Цей огляд пакета <b>робіт</b> містить список всіх робіт у вашому проєкті, наприклад завдання, віхи, етапи тощо. <br> Пакети робіт можна створювати й редагувати безпосередньо в цьому поданні. Щоб отримати доступ до інформації про певний пакет робіт, просто двічі натисніть його рядок."
|
||||
full_view: "Подання <b>Деталі пакета робіт</b> містить усю відповідну інформацію, пов’язану з даним пакетом робіт, наприклад його опис, статус, пріоритет, діяльність, залежності й коментарі."
|
||||
list: "Цей огляд пакета <b>робіт</b> містить список усіх робіт у вашому проєкті, таких як завдання, контрольні точки, етапи тощо. <br> Пакети робіт можна створювати й редагувати безпосередньо в цьому поданні. Щоб отримати доступ до інформації про певний пакет робіт, просто двічі натисніть його рядок."
|
||||
full_view: "Подання <b>деталей пакета робіт</b> містить усю відповідну інформацію, пов’язану з відповідним пакетом робіт, наприклад його опис, статус, пріоритет, дії, залежності й коментарі."
|
||||
back_button: "Щоб вийти й повернутися в список пакета робіт, скористайтеся стрілкою повернення у верхньому лівому куті."
|
||||
create_button: "Кнопка <b>+ Створити</b> додасть новий пакет робіт до вашого проєкту."
|
||||
gantt_menu: "Легко створюйте розклади й часові шкали проєктів за допомогою модуля «Діаграма Ґанта»."
|
||||
@@ -835,9 +835,9 @@ uk:
|
||||
duplicate: "Bulk duplicate"
|
||||
move: "Групова зміна проєкту"
|
||||
button_clear: "Очистити"
|
||||
comment_added: "Коментар був успішно доданий."
|
||||
comment_added: "Коментар успішно додано."
|
||||
comment_send_failed: "Сталася помилка. Не вдалося додати коментар."
|
||||
comment_updated: "Коментар був успішно оновлений."
|
||||
comment_updated: "Коментар успішно оновлено."
|
||||
confirm_edit_cancel: "Ви дійсно бажаєте скасувати редагування пакету робіт?"
|
||||
description_filter: "Фільтр"
|
||||
description_enter_text: "Введіть текст"
|
||||
@@ -857,7 +857,7 @@ uk:
|
||||
message_view_spent_time: "Показувати час, витрачений на цей робочий пакет"
|
||||
message_work_package_read_only: "Робочий пакет заблоковано в цьому статусі. Атрибут, відмінний від статусу, може бути змінений."
|
||||
message_work_package_status_blocked: "Пакет робіт недоступний для запису, оскільки призначено закритий статус або закриту версію."
|
||||
placeholder_filter_by_text: "Тема, опис, коментарі, ..."
|
||||
placeholder_filter_by_text: "Тема, опис, коментарі…"
|
||||
progress:
|
||||
title: "Оцінки й прогрес виконання роботи"
|
||||
baseline:
|
||||
@@ -894,7 +894,7 @@ uk:
|
||||
description: Або нічого не було створено, або всі пакети робіт відфільтровані.
|
||||
limited_results: 'Показати в ручному режимі сортування можна лише стільки робочих пакетів: %{count}. Зменште кількість результатів за допомогою фільтрації або виберіть автоматичне сортування.'
|
||||
property_groups:
|
||||
details: "Детальніше"
|
||||
details: "Деталі"
|
||||
people: "Люди"
|
||||
estimatesAndTime: "Оцінки та час"
|
||||
other: "Інше"
|
||||
@@ -918,7 +918,7 @@ uk:
|
||||
responsible: "Відповідальний"
|
||||
startDate: "Дата початку"
|
||||
status: "Стан"
|
||||
subject: "Заголовок"
|
||||
subject: "Тема"
|
||||
subproject: "Підпроєкт"
|
||||
title: "Назва "
|
||||
type: "Тип"
|
||||
@@ -930,7 +930,7 @@ uk:
|
||||
remainingTime: "Залишок роботи"
|
||||
default_queries:
|
||||
manually_sorted: "Новий, відсортований уручну запит"
|
||||
latest_activity: "Останні дії"
|
||||
latest_activity: "Остання активність"
|
||||
created_by_me: "Створений мною"
|
||||
assigned_to_me: "Призначено мені"
|
||||
recently_created: "Нещодавно створений"
|
||||
@@ -1028,7 +1028,7 @@ uk:
|
||||
filter_work_packages_by_relation_type: "Фільтрувати пакети робіт за типом зв’язку"
|
||||
tabs:
|
||||
overview: Огляд
|
||||
activity: Дія
|
||||
activity: Активність
|
||||
relations: Зв'язки
|
||||
watchers: Спостерігачі
|
||||
files: Файли
|
||||
|
||||
@@ -99,12 +99,12 @@ pl:
|
||||
upgrade: "Zmień plan teraz"
|
||||
contact: "Skontaktuj się z nami, aby uzyskać demo"
|
||||
status:
|
||||
expired: "Expired"
|
||||
expiring_soon: "Expiring soon"
|
||||
in_grace_period: "In grace period"
|
||||
invalid_domain: "Invalid domain"
|
||||
not_active: "Not active"
|
||||
trial: "Trial"
|
||||
expired: "Ważność wygasła"
|
||||
expiring_soon: "Wkrótce wygasa"
|
||||
in_grace_period: "W okresie karencji"
|
||||
invalid_domain: "Nieprawidłowa domena"
|
||||
not_active: "Nieaktywny"
|
||||
trial: "Wersja próbna"
|
||||
jemalloc_allocator: Alokator pamięci Jemalloc
|
||||
journal_aggregation:
|
||||
explanation:
|
||||
@@ -112,21 +112,21 @@ pl:
|
||||
link: "webhook"
|
||||
scim_clients:
|
||||
authentication_methods:
|
||||
sso: "JWT from identity provider"
|
||||
oauth2_client: "OAuth 2.0 client credentials"
|
||||
oauth2_token: "Static access token"
|
||||
sso: "JWT od dostawcy tożsamości"
|
||||
oauth2_client: "Poświadczenia klienta OAuth 2.0"
|
||||
oauth2_token: "Statyczny token dostępu"
|
||||
created_client_credentials_dialog_component:
|
||||
title: "Client credentials created"
|
||||
heading: "Client credentials have been generated"
|
||||
one_time_hint: "This is the only time you will see the client secret. Make sure to copy it now."
|
||||
title: "Utworzono poświadczenia klienta"
|
||||
heading: "Wygenerowano poświadczenia klienta"
|
||||
one_time_hint: "To jedyny raz, kiedy zobaczysz klucz tajny klienta. Koniecznie skopiuj go teraz."
|
||||
created_token_dialog_component:
|
||||
title: "Token created"
|
||||
heading: "An token has been generated"
|
||||
title: "Utworzono token"
|
||||
heading: "Token został wygenerowany"
|
||||
label_token: "Token"
|
||||
one_time_hint: "This is the only time you will see this token. Make sure to copy it now."
|
||||
one_time_hint: "Jest to jedyny raz, gdy zobaczysz ten token. Koniecznie skopiuj go teraz."
|
||||
delete_scim_client_dialog_component:
|
||||
title: "Delete SCIM client"
|
||||
heading: "Are you sure you want to delete this SCIM client?"
|
||||
title: "Usuń klienta SCIM"
|
||||
heading: "Czy na pewno chcesz usunąć tego klienta SCIM?"
|
||||
description: "Users managed by this SCIM client can no longer be updated by it."
|
||||
edit:
|
||||
label_delete_scim_client: "Delete SCIM client"
|
||||
|
||||
@@ -43,7 +43,7 @@ pt-BR:
|
||||
commented: "comentou"
|
||||
internal_comment: Comentário interno
|
||||
internal_journal: Comentários internos são visíveis apenas para um grupo restrito de membros.
|
||||
unsaved_changes_confirmation_message: Você tem alterações não salvas. Tem certeza que deseja fechar o editor?
|
||||
unsaved_changes_confirmation_message: Há alterações não salvas. Tem certeza de que deseja fechar o editor?
|
||||
internal_comment_confirmation:
|
||||
title: "Tornar este comentário público?"
|
||||
heading: "Tornar este comentário público?"
|
||||
@@ -100,11 +100,11 @@ pt-BR:
|
||||
contact: "Contate-nos para uma demonstração"
|
||||
status:
|
||||
expired: "Expirado"
|
||||
expiring_soon: "Expirando em breve"
|
||||
in_grace_period: "Em período de carência"
|
||||
expiring_soon: "Prestes a expirar"
|
||||
in_grace_period: "Dentro do período de carência"
|
||||
invalid_domain: "Domínio inválido"
|
||||
not_active: "Inativo"
|
||||
trial: "Período de avaliação"
|
||||
trial: "Avaliação"
|
||||
jemalloc_allocator: Alocador de memória Jemalloc
|
||||
journal_aggregation:
|
||||
explanation:
|
||||
@@ -120,45 +120,45 @@ pt-BR:
|
||||
heading: "As credenciais do cliente foram geradas"
|
||||
one_time_hint: "Esta é a única vez que você verá o segredo do cliente. Certifique-se de copiá-lo agora."
|
||||
created_token_dialog_component:
|
||||
title: "Novo token criado"
|
||||
heading: "Uma ficha foi gerada"
|
||||
label_token: "Ficha"
|
||||
one_time_hint: "Esta é a única vez que você verá essa ficha, certifique-se de copiá-la agora."
|
||||
title: "Token criado"
|
||||
heading: "Uma token foi gerado"
|
||||
label_token: "Token"
|
||||
one_time_hint: "Esta é a única vez que você verá este token. Certifique-se de copiá-lo agora."
|
||||
delete_scim_client_dialog_component:
|
||||
title: "Excluir cliente SCIM"
|
||||
heading: "Tem certeza que deseja excluir este cliente SCIM?"
|
||||
description: "Os usuários gerenciados por este cliente SCIM não podem mais ser atualizados por ele."
|
||||
heading: "Tem certeza de que deseja excluir este cliente SCIM?"
|
||||
description: "Usuários gerenciados por este cliente SCIM não poderão mais ser atualizados por ele."
|
||||
edit:
|
||||
label_delete_scim_client: "Excluir cliente SCIM"
|
||||
form:
|
||||
auth_provider_description: "Este é o serviço que os usuários adicionados pelo provedor SCIM usarão para autenticar no OpenProject."
|
||||
authentication_method_description_html: "É assim que o cliente SCIM se autentica no OpenProject. Por favor, certifique-se de que as fichas OAuth incluam o escopo <code>scim_v2</code>."
|
||||
description_html: 'Por favor, consulte nossa <a href="%{docs_url}"> documentação sobre a configuração de clientes SCIM</a> para mais informações sobre essas opções de configuração.'
|
||||
jwt_sub_description_html: 'Por exemplo, para Keycloak, esse é o UUID da conta de serviço associada com o cliente SCIM. Consulte <a href="%{docs_url}">nossa documentação</a> para saber como encontrar a reivindicação do sujeito para sua utilização.'
|
||||
name_description: "Escolha um nome que ajudará outros administradores a entender melhor porque esse cliente foi configurado."
|
||||
auth_provider_description: "Este é o serviço que os usuários adicionados pelo provedor SCIM usarão para autenticar-se no OpenProject."
|
||||
authentication_method_description_html: "O cliente SCIM autentica-se no OpenProject dessa forma. Verifique se os tokens OAuth contêm o escopo <code>scim_v2</code>."
|
||||
description_html: 'Consulte nossa <a href="%{docs_url}">documentação sobre configuração de clientes SCIM</a> para obter mais informações sobre essas opções de configuração.'
|
||||
jwt_sub_description_html: 'Por exemplo, para o Keycloak, este é o UUID da conta de serviço associada ao cliente SCIM. Consulte <a href="%{docs_url}">nossa documentação</a> para saber como encontrar a declaração de sujeito para o seu caso de uso.'
|
||||
name_description: "Escolha um nome que ajude outros administradores a entender melhor o motivo pelo qual este cliente foi configurado."
|
||||
index:
|
||||
description: "Clientes SCIM configurados aqui podem interagir com a API do servidor OpenProject SCIM para provisionar, atualizar e desaprovisionar contas e grupos de usurários."
|
||||
description: "Clientes SCIM configurados aqui podem interagir com a API do servidor SCIM do OpenProject para provisionar, atualizar e desprovisionar contas de usuários e grupos."
|
||||
label_create_button: "Adicionar cliente SCIM"
|
||||
new:
|
||||
title: "Novo cliente SCIM"
|
||||
revoke_static_token_dialog_component:
|
||||
confirm_button: "Revogar"
|
||||
title: "Revogar token estático"
|
||||
heading: "Tem certeza que deseja revogar essa ficha?"
|
||||
description: "Os clientes SCIM que usam esse token não poderão mais acessar a API do servidor SCIM do OpenProject."
|
||||
heading: "Tem certeza de que deseja revogar este token?"
|
||||
description: "O cliente SCIM que usa este token não terá mais acesso à API do servidor SCIM do OpenProject."
|
||||
table_component:
|
||||
blank_slate:
|
||||
title: "Nenhum cliente SCIM foi configurado ainda"
|
||||
title: "Nenhum cliente SCIM configurado até o momento"
|
||||
description: "Adicione clientes para vê-los aqui"
|
||||
user_count: "Usuários"
|
||||
token_list_component:
|
||||
description: "Os tokens que você gerar aqui podem ser enviados por um cliente SCIM para acessar a API do OPenProject SCIM."
|
||||
description: "Os tokens gerados aqui podem ser transmitido por um cliente SCIM para acessar a API SCIM do OpenProject."
|
||||
heading: "Tokens"
|
||||
label_add_token: "Token"
|
||||
label_aria_add_token: "Adicionar token"
|
||||
token_table_component:
|
||||
blank_slate:
|
||||
title: "Nenhum token foi criado ainda"
|
||||
title: "Nenhum token criado até o momento"
|
||||
description: "Você pode criar um agora"
|
||||
expired: "Expirado em %{date}"
|
||||
revoked: "Revogado em %{date}"
|
||||
@@ -197,9 +197,9 @@ pt-BR:
|
||||
sync_failed: "Não foi possível sincronizar do LDAP: %{message}."
|
||||
back_to_index: "Clique aqui para voltar para a lista de conexão."
|
||||
technical_warning_html: |
|
||||
Este formulário LDAP requer conhecimento técnico da configuração de seu LDAP / Active Directory.
|
||||
Este formulário LDAP requer conhecimento técnico da sua configuração LDAP / Active Directory.
|
||||
<br>
|
||||
<a href="https://www.openproject.org/help/administration/manage-ldap-authentication/">Por favor, visite a nossa documentação para instruções mais detalhadas</a>.
|
||||
<a href="https://www.openproject.org/help/administration/manage-ldap-authentication/">Consulte nossa documentação para obter instruções detalhadas</a>.
|
||||
attribute_texts:
|
||||
name: Nome arbitrário da conexão LDAP
|
||||
host: Nome ou endereço IP do host LDAP
|
||||
@@ -207,7 +207,7 @@ pt-BR:
|
||||
generic_map: A chave de atributo no LDAP que é mapeada para o atributo `%{attribute}` do OpenProject
|
||||
admin_map_html: "Opcional: A chave de atributo no LDAP que <strong> se presente </strong> marca o usuário do OpenProject como administrador. Deixe vazio se estiver em dúvida."
|
||||
system_user_dn_html: |
|
||||
Digite o DN do usuário do sistema usado para acesso somente leitura.
|
||||
Informe o DN do usuário do sistema usado para acesso somente leitura.
|
||||
<br>
|
||||
Exemplo: uid=openproject,ou=system,dc=example,dc=com
|
||||
system_user_password: Digite a senha de vinculação do usuário do sistema
|
||||
@@ -328,7 +328,7 @@ pt-BR:
|
||||
admin_only: "Marque esta opção para tornar este atributo visível apenas para os administradores. Os usuários sem permissões de administrador não poderão vê-lo ou editá-lo."
|
||||
is_filter: >
|
||||
Permita que o campo personalizado seja utilizado num filtro nas visualizações do pacote de trabalho. Note que apenas com a opção "Para todos os projetos" selecionada, o campo personalizado irá aparecer nas visualizações globais.
|
||||
formula: "Adicione valores numéricos ou digite / para pesquisar por um atributo ou um operador matemático."
|
||||
formula: "Adicione valores numéricos ou digite / para buscar um atributo ou um operador matemático."
|
||||
tab:
|
||||
no_results_title_text: Atualmente, não há campos personalizados.
|
||||
no_results_content_text: Criar um novo campo personalizado
|
||||
@@ -353,8 +353,8 @@ pt-BR:
|
||||
parent:
|
||||
not_descendant: "deve ser um descendente da raiz da hierarquia."
|
||||
rules:
|
||||
copy_workflow_from: "Tipo para cópia do fluxo de trabalho"
|
||||
enabled: "Ativado"
|
||||
copy_workflow_from: "Tipo de cópia do fluxo de trabalho"
|
||||
enabled: "Habilitado"
|
||||
depth: "Profundidade"
|
||||
item: "Item"
|
||||
label: "Rótulo"
|
||||
@@ -419,7 +419,7 @@ pt-BR:
|
||||
text: "Esta ação não excluirá nenhum projeto contido na lista. Você tem certeza de que deseja excluir esta lista de projetos?"
|
||||
settings:
|
||||
header_details: Detalhes básicos
|
||||
header_status: Situação do projeto
|
||||
header_status: Status do projeto
|
||||
header_relations: Relações do projeto
|
||||
button_update_details: Atualizar detalhes
|
||||
button_update_status_description: Atualizar descrição do status
|
||||
@@ -686,7 +686,7 @@ pt-BR:
|
||||
tab: Projetos
|
||||
enable_all: Habilitar para todos os projetos
|
||||
select_projects: Selecionar projetos
|
||||
select_projects_description: Selecione os projetos nos quais você gostaria de usar esse tipo.
|
||||
select_projects_description: Selecione os projetos nos quais deseja usar esse tipo.
|
||||
settings:
|
||||
tab: "Configurações"
|
||||
type_color_text: A cor selecionada distingue os tipos diferentes nas tabelas de Gantt ou tabelas dos pacotes de trabalho. Recomenda-se o uso de uma cor forte.
|
||||
@@ -707,16 +707,16 @@ pt-BR:
|
||||
pattern:
|
||||
label: "Padrão de assunto"
|
||||
caption_with_supported_attributes_link: Crie padrões adicionando texto, ou digite "/" para pesquisar por %{link}.
|
||||
insert_as_text: "Nenhum atributo encontrado. Digite um texto como: \"%{word}\""
|
||||
insert_as_text: "Nenhum atributo encontrado. Adicione como texto: “%{word}”"
|
||||
supported_attributes_link: "atributos compatíveis"
|
||||
export_configuration:
|
||||
tab: "Gerar PDF"
|
||||
intro: "Selecione quais modelos disponíveis você deseja ativar para este tipo. O modelo define o design e os atributos visíveis no PDF exportado de um pacote de trabalho desse tipo. O primeiro modelo da lista é selecionado por padrão."
|
||||
pdf_export_templates:
|
||||
label: "Modelos de Exportação PDF"
|
||||
label: "Modelos de exportação de PDF"
|
||||
actions:
|
||||
label_enable_all: "Habilitar todos"
|
||||
label_disable_all: "Desativar tudo"
|
||||
label_disable_all: "Desabilitar todos"
|
||||
versions:
|
||||
overview:
|
||||
work_packages_in_archived_projects: "A versão é compartilhada com projetos arquivados que ainda possuem pacotes de trabalho atribuídos a esta versão. Eles são contados, mas não aparecerão nas exibições vinculadas."
|
||||
@@ -734,20 +734,20 @@ pt-BR:
|
||||
datepicker_modal:
|
||||
banner:
|
||||
description:
|
||||
automatic_mobile: "Data de início derivada."
|
||||
click_on_show_relations_to_open_gantt: 'Clique em "%{button_name}" para ter uma visão geral do diagrama de Gantt.'
|
||||
manual_mobile: "Ignorando relações."
|
||||
manual_gap_between_predecessors: "Existe uma lacuna entre isto e todos os antecessores."
|
||||
manual_overlap_with_predecessors: "Sobrepor com pelo menos um antecessor."
|
||||
manual_with_children: "Há pacotes de trabalho filhos na hierarquia, mas suas datas de início serão ignoradas."
|
||||
automatic_mobile: "Data de início calculada."
|
||||
click_on_show_relations_to_open_gantt: 'Clique em “%{button_name}” para exibir a visão geral do Gráfico de Gantt.'
|
||||
manual_mobile: "Ignorar relações."
|
||||
manual_gap_between_predecessors: "Existe um intervalo entre este item e todos os predecessores."
|
||||
manual_overlap_with_predecessors: "Sobrepõe-se a pelo menos um predecessor."
|
||||
manual_with_children: "Este pacote de trabalho possui pacotes de trabalho filho, mas as datas de início deles estão sendo ignoradas."
|
||||
title:
|
||||
automatic_mobile: "Agendado automaticamente."
|
||||
automatic_with_children: "As datas são definidas pelos pacotes de trabalho filhos na hierarquia."
|
||||
automatic_with_predecessor: "A data de início é definida por um antecessor."
|
||||
automatic_with_children: "As datas são determinadas pelos pacotes de trabalho filhos."
|
||||
automatic_with_predecessor: "A data de início é definida por uma tarefa predecessora."
|
||||
manual_mobile: "Agendado manualmente."
|
||||
manually_scheduled: "Agendado manualmente. Datas não afetadas pelas relações."
|
||||
manually_scheduled: "Agendamento manual. Datas não são afetadas por relações."
|
||||
blankslate:
|
||||
title: "Nenhum antecessor"
|
||||
title: "Sem predecessores"
|
||||
description: "Para ativar o agendamento automático, este pacote de trabalho precisa ter pelo menos um predecessor. Ele será então agendado automaticamente para começar após o predecessor mais próximo."
|
||||
ignore_non_working_days:
|
||||
title: "Apenas dias úteis"
|
||||
@@ -758,20 +758,20 @@ pt-BR:
|
||||
show_relations: "Exibir relações"
|
||||
update_inputs_aria_live_message: "Seletor de data atualizado. %{message}"
|
||||
tabs:
|
||||
aria_label: "Abas de seletor de datas"
|
||||
aria_label: "Abas do seletor de data"
|
||||
children: "Filhos"
|
||||
dates: "Datas"
|
||||
predecessors: "Predecessores"
|
||||
successors: "Sucessores"
|
||||
blankslate:
|
||||
predecessors:
|
||||
title: "Nenhum antecessor"
|
||||
description: "Este pacote de trabalho não tem antecessores."
|
||||
title: "Sem predecessores"
|
||||
description: "Este pacote de trabalho não tem predecessores."
|
||||
successors:
|
||||
title: "Nenhum sucessor"
|
||||
title: "Sem sucessores"
|
||||
description: "Este pacote de trabalho não tem sucessores."
|
||||
children:
|
||||
title: "Sem filhos na hierarquia"
|
||||
title: "Sem filhos"
|
||||
description: "Este pacote de trabalho não tem filhos."
|
||||
x_descendants:
|
||||
one: "Um pacote de trabalho descendente"
|
||||
@@ -844,8 +844,8 @@ pt-BR:
|
||||
follows_description: "O pacote de trabalho relacionado deve ser finalizado antes que este possa ser iniciado"
|
||||
label_child_singular: "filho"
|
||||
label_child_plural: "filhos"
|
||||
new_child: "Novo filho"
|
||||
new_child_description: "Cria um pacote de trabalho relacionado como um subpacote do pacote de trabalho atual (pai)"
|
||||
new_child: "Criar novo filho"
|
||||
new_child_description: "Cria um pacote de trabalho relacionado como subitem do pacote de trabalho atual (pai)"
|
||||
child: "Filho"
|
||||
child_description: "Torna o pacote de trabalho relacionado uma subtarefa do pacote de trabalho atual (pai)"
|
||||
parent: "Pai"
|
||||
@@ -997,7 +997,7 @@ pt-BR:
|
||||
widgets: "Widgets"
|
||||
journal:
|
||||
notes: "Anotações"
|
||||
cause_type: "Tipo de Causa"
|
||||
cause_type: "Tipo de causa"
|
||||
ldap_auth_source:
|
||||
account: "Conta"
|
||||
attr_firstname: "Atributo de nome"
|
||||
@@ -1014,7 +1014,7 @@ pt-BR:
|
||||
member:
|
||||
roles: "Papéis"
|
||||
notification:
|
||||
read_ian: "Ler no aplicativo"
|
||||
read_ian: "Lido no aplicativo"
|
||||
resource: "Recurso"
|
||||
oauth_client:
|
||||
client: "ID do cliente"
|
||||
@@ -1066,28 +1066,28 @@ pt-BR:
|
||||
finish_gate: "Controle de fase final"
|
||||
finish_gate_name: "Nome do controle de fase final"
|
||||
query:
|
||||
sums: "Somas"
|
||||
sums: "Totais"
|
||||
columns: "Colunas"
|
||||
column_names: "Colunas"
|
||||
relations_to_type_column: "Relações com %{type}"
|
||||
relations_of_type_column: "Relações de %{type}"
|
||||
child_work_packages: "Pacotes de trabalho filhos"
|
||||
group_by: "Agrupar resultados por"
|
||||
sort_by: "Ordenar resultados por"
|
||||
sort_by: "Classificar resultados por"
|
||||
filters: "Filtros"
|
||||
timeline_labels: "Rótulos de linha do tempo"
|
||||
timeline_visible: "Exibir gráfico de Gantt"
|
||||
timeline_zoom_level: "Nível de zoom do diagrama de Gantt"
|
||||
timestamps: "Carimbo de data/hora base da linha de base"
|
||||
sort_criteria: "Critérios de ordenação"
|
||||
timeline_zoom_level: "Nível de zoom do gráfico de Gantt"
|
||||
timestamps: "Carimbos de data/hora da linha de base"
|
||||
sort_criteria: "Critérios de classificação"
|
||||
highlighted_attributes: "Atributos destacados"
|
||||
highlighting_mode: "Modo de destaque"
|
||||
display_representation: "Modo de exibição"
|
||||
show_hierarchies: "Modo de exibição"
|
||||
starred: "Favorito"
|
||||
hidden: "Ocultado"
|
||||
manual_sorting: "Ordenação manual"
|
||||
ordered_work_packages: "Ordem dos pacotes de trabalho"
|
||||
hidden: "Oculto"
|
||||
manual_sorting: "Classificação manual"
|
||||
ordered_work_packages: "Classificação dos pacotes de trabalho"
|
||||
include_subprojects: "Incluir subprojetos"
|
||||
results: "Resultados"
|
||||
relation:
|
||||
@@ -1109,7 +1109,7 @@ pt-BR:
|
||||
scim_client:
|
||||
auth_provider: "Provedor de autenticação"
|
||||
authentication_method: "Método de autenticação"
|
||||
jwt_sub: "Reivindicação do sujeito"
|
||||
jwt_sub: "Declaração de sujeito"
|
||||
status:
|
||||
is_closed: "Pacote de trabalho fechado"
|
||||
is_readonly: "Pacote de trabalho somente leitura"
|
||||
@@ -1117,7 +1117,7 @@ pt-BR:
|
||||
default_done_ratio: "% de conclusão"
|
||||
token/ical:
|
||||
calendar: "Calendário"
|
||||
ical_token_query_assignment: "Atribuição da consulta"
|
||||
ical_token_query_assignment: "Atribuição de consulta"
|
||||
time_entry:
|
||||
activity: "Atividade"
|
||||
hours: "horas"
|
||||
@@ -1133,7 +1133,7 @@ pt-BR:
|
||||
color: "Cor"
|
||||
patterns: "Padrões"
|
||||
remote_identity:
|
||||
auth_source: "Fonte de Autenticação"
|
||||
auth_source: "Fonte de autenticação"
|
||||
integration: "Integração"
|
||||
user: "Usuário"
|
||||
user:
|
||||
@@ -1145,7 +1145,7 @@ pt-BR:
|
||||
force_password_change: "Forçar alteração de senha no próximo acesso"
|
||||
language: "Idioma"
|
||||
last_login_on: "Último acesso"
|
||||
failed_login_count: "Tentativas de falhas de acesso"
|
||||
failed_login_count: "Tentativas de logon com falha"
|
||||
first_name: "Nome"
|
||||
last_name: "Sobrenome"
|
||||
first_login: "Primeiro acesso"
|
||||
@@ -1155,22 +1155,22 @@ pt-BR:
|
||||
group:
|
||||
identity_url: "URL de identidade"
|
||||
user_preference:
|
||||
header_look_and_feel: "Aparência e usabilidade"
|
||||
header_look_and_feel: "Aparência e comportamento"
|
||||
header_alerts: "Alertas"
|
||||
button_update_look_and_feel: "Atualizar aparência e usabilidade"
|
||||
button_update_look_and_feel: "Atualizar aparência e comportamento"
|
||||
button_update_alerts: "Atualizar alertas"
|
||||
comments_sorting: "Exibir atividade do pacote de trabalho ordenada por"
|
||||
comments_sorting: "Exibir atividade do pacote de trabalho classificada por"
|
||||
disable_keyboard_shortcuts: "Desativar atalhos de teclado"
|
||||
disable_keyboard_shortcuts_caption_html: |-
|
||||
Você pode optar por desativar os <a href="%{href}">atalhos de teclado</a> padrão se utilizar um leitor de tela ou quiser evitar acionar uma ação acidentalmente com um atalho
|
||||
Você pode optar por desativar os <a href="%{href}">atalhos de teclado</a> padrão caso use leitor de tela ou queira evitar acionar ações acidentalmente com atalhos.
|
||||
dismissed_enterprise_banners: "Banners empresariais ocultos"
|
||||
impaired: "Modo de acessibilidade"
|
||||
auto_hide_popups: "Ocultar automaticamente banners concluídos"
|
||||
auto_hide_popups_caption: "Quando habilitado, os banners de conclusão verdes desaparecerão automaticamente após 5 segundos."
|
||||
auto_hide_popups: "Ocultar automaticamente as notificações de sucesso"
|
||||
auto_hide_popups_caption: "Quando habilitado, as notificações verdes de sucesso desaparecem automaticamente após 5 segundos."
|
||||
warn_on_leaving_unsaved: "Alertar-me ao sair de um pacote de trabalho sem salvar as alterações"
|
||||
theme: "Modo de cor"
|
||||
time_zone: "Fuso horário"
|
||||
mode_guideline: "Alguns modos substituem as cores personalizadas do tema para melhorar a acessibilidade e a legibilidade. Selecione o modo Claro para ter suporte completo a temas personalizados."
|
||||
mode_guideline: "Alguns modos substituirão as cores personalizadas do tema para melhorar acessibilidade e legibilidade. Selecione o modo Claro para suporte total ao tema personalizado."
|
||||
daily_reminders: "Lembretes diários"
|
||||
workdays: "Dias úteis"
|
||||
version:
|
||||
@@ -1335,10 +1335,10 @@ pt-BR:
|
||||
not_match_configured: "não corresponde a escopos disponíveis."
|
||||
enterprise_trial:
|
||||
already_used: "já foi usado para criar uma avaliação."
|
||||
failed_to_create: "A avaliação não pôde ser criada (%{status})"
|
||||
general_consent: "Por favor, aceite os termos e condições."
|
||||
failed_to_create: "Não foi possível criar a avaliação (%{status})"
|
||||
general_consent: "Aceite os termos e condições."
|
||||
enterprise_token:
|
||||
only_one_trial: "Apenas um token de avaliação pode estar ativo. Por favor, exclua o token de avaliação anterior antes de adicionar outro."
|
||||
only_one_trial: "Apenas um token de avaliação pode estar ativo. Exclua o token de avaliação anterior antes de adicionar outro."
|
||||
unreadable: "não pode ser lido. Tem certeza que é um token de suporte?"
|
||||
already_added: "Este token já foi adicionado."
|
||||
grids/grid:
|
||||
@@ -1697,7 +1697,7 @@ pt-BR:
|
||||
added_date: "definido para %{date}"
|
||||
changed_date: "alterado de %{from} para %{to}"
|
||||
deactivated: "desativado"
|
||||
deleted_project_phase: "Excluir fase de projeto"
|
||||
deleted_project_phase: "Fase do projeto excluída"
|
||||
phase_and_both_gates: "%{phase_message}. %{start_gate_message} e %{finish_gate_message}"
|
||||
phase_and_one_gate: "%{phase_message}. %{gate_message}"
|
||||
removed_date: "data excluída %{date}"
|
||||
@@ -1710,7 +1710,7 @@ pt-BR:
|
||||
actor: "Ator"
|
||||
action: "Ação"
|
||||
author: "Autor"
|
||||
avatar: "Imagem do perfil"
|
||||
avatar: "Avatar"
|
||||
base: "Erro geral:"
|
||||
body: "Mensagem"
|
||||
blocks_ids: "IDs dos pacotes de trabalho bloqueados"
|
||||
@@ -1725,7 +1725,7 @@ pt-BR:
|
||||
custom_options: "Valores possíveis"
|
||||
custom_values: "Campos personalizados"
|
||||
date: "Data"
|
||||
dates_interval: "Período"
|
||||
dates_interval: "Intervalo de datas"
|
||||
default_columns: "Colunas padrão"
|
||||
description: "Descrição"
|
||||
derived_due_date: "Data de término derivada"
|
||||
@@ -1760,10 +1760,10 @@ pt-BR:
|
||||
mail: "E-mail"
|
||||
name: "Nome"
|
||||
note: "Nota"
|
||||
notes: "Anotações"
|
||||
notes: "Observações"
|
||||
number: "Número"
|
||||
options: "Opções"
|
||||
operator: "Operador(a)"
|
||||
operator: "Operador"
|
||||
password: "Senha"
|
||||
priority: "Prioridade"
|
||||
project: "Projeto"
|
||||
@@ -1781,7 +1781,7 @@ pt-BR:
|
||||
status: "Status"
|
||||
state: "Estado"
|
||||
subject: "Assunto"
|
||||
slug: "Link encurtado"
|
||||
slug: "Identificador simples"
|
||||
summary: "Sumário"
|
||||
time_zone: "Fuso horário"
|
||||
text: "Texto"
|
||||
@@ -1951,7 +1951,7 @@ pt-BR:
|
||||
copy_options:
|
||||
dependencies_label: 'Copiar do projeto'
|
||||
create_project:
|
||||
template_label: "Utilizar modelo"
|
||||
template_label: "Usar modelo"
|
||||
copy_options:
|
||||
dependencies_label: 'Copiar do modelo'
|
||||
create_wiki_page: "Criar nova página wiki"
|
||||
@@ -2197,7 +2197,7 @@ pt-BR:
|
||||
description: "Ative a autenticação segura e contínua para o seu armazenamento Nextcloud com Autenticação única. Simplifique o gerenciamento de acessos e melhore a experiência do usuário."
|
||||
scim_api:
|
||||
title: "Clientes SCIM"
|
||||
description: "Automatize o gerenciamento de usuários no OpenProject integrando de forma suave os serviços de identidade externos como Microsoft Entra ou Keycloak através da nossa API do servidor SCIM. Disponível começando com o Plano corporativo empresarial."
|
||||
description: "Automatize o gerenciamento de usuários no OpenProject integrando de forma contínua serviços de identidade externos como Microsoft Entra ou Keycloak por meio da nossa API SCIM Server. Disponível a partir do plano corporativo Enterprise."
|
||||
virus_scanning:
|
||||
description: "Garantir que os arquivos carregados no OpenProject sejam verificados quanto à presença de vírus antes de serem acessados por outros usuários."
|
||||
placeholder_users:
|
||||
|
||||
@@ -101,7 +101,7 @@ pt-PT:
|
||||
status:
|
||||
expired: "Expirado"
|
||||
expiring_soon: "Expira em breve"
|
||||
in_grace_period: "In grace period"
|
||||
in_grace_period: "Em período de carência"
|
||||
invalid_domain: "Domínio inválido"
|
||||
not_active: "Não ativo"
|
||||
trial: "Teste"
|
||||
@@ -112,57 +112,57 @@ pt-PT:
|
||||
link: "webhook"
|
||||
scim_clients:
|
||||
authentication_methods:
|
||||
sso: "JWT from identity provider"
|
||||
oauth2_client: "OAuth 2.0 client credentials"
|
||||
oauth2_token: "Static access token"
|
||||
sso: "JWT do fornecedor de identidade"
|
||||
oauth2_client: "Credenciais do cliente OAuth 2.0"
|
||||
oauth2_token: "Token de acesso estático"
|
||||
created_client_credentials_dialog_component:
|
||||
title: "Client credentials created"
|
||||
heading: "Client credentials have been generated"
|
||||
one_time_hint: "This is the only time you will see the client secret. Make sure to copy it now."
|
||||
title: "Credenciais de cliente criadas"
|
||||
heading: "As credenciais do cliente foram geradas"
|
||||
one_time_hint: "Esta é a única vez que verá o segredo do cliente. Não se esqueça de o copiar agora."
|
||||
created_token_dialog_component:
|
||||
title: "Token created"
|
||||
heading: "An token has been generated"
|
||||
title: "Token criado"
|
||||
heading: "Foi gerado um token"
|
||||
label_token: "Token"
|
||||
one_time_hint: "This is the only time you will see this token. Make sure to copy it now."
|
||||
one_time_hint: "Esta é a única vez que verá este token. Não se esqueça de o copiar agora."
|
||||
delete_scim_client_dialog_component:
|
||||
title: "Delete SCIM client"
|
||||
heading: "Are you sure you want to delete this SCIM client?"
|
||||
description: "Users managed by this SCIM client can no longer be updated by it."
|
||||
title: "Eliminar o cliente SCIM"
|
||||
heading: "Tem a certeza de que quer eliminar este cliente SCIM?"
|
||||
description: "Os utilizadores geridos por este cliente SCIM já não podem ser atualizados por ele."
|
||||
edit:
|
||||
label_delete_scim_client: "Delete SCIM client"
|
||||
label_delete_scim_client: "Eliminar o cliente SCIM"
|
||||
form:
|
||||
auth_provider_description: "This is the service that users added by the SCIM provider will use to authenticate in OpenProject."
|
||||
authentication_method_description_html: "This is how the SCIM client authenticates at OpenProject. Please ensure that OAuth tokens include the <code>scim_v2</code> scope."
|
||||
description_html: 'Please refer to our <a href="%{docs_url}">documentation on configuring SCIM clients</a> for more information on these configuration options.'
|
||||
jwt_sub_description_html: 'For example, for Keycloak, this is the UUID of the service account associated with the SCIM client. Consult <a href="%{docs_url}">our documentation</a> to learn how to find the subject claim for your use case.'
|
||||
name_description: "Choose a name that will help other admins better understand why this client was configured."
|
||||
auth_provider_description: "Este é o serviço que os utilizadores adicionados pelo fornecedor SCIM utilizarão para se autenticarem no OpenProject."
|
||||
authentication_method_description_html: "É assim que o cliente SCIM se autentica no OpenProject. Garanta que os tokens OAuth incluem o âmbito <code>scim_v2</code>."
|
||||
description_html: 'Consulte a nossa <a href="%{docs_url}">documentação sobre a configuração de clientes SCIM</a> para mais informações sobre estas opções de configuração.'
|
||||
jwt_sub_description_html: 'Por exemplo, para o Keycloak, este é o UUID da conta de serviço associada ao cliente SCIM. Consulte <a href="%{docs_url}">a nossa documentação</a> para saber como encontrar a declaração de assunto para o seu caso de utilização.'
|
||||
name_description: "Escolha um nome que ajude os outros administradores a perceber melhor porque é que este cliente foi configurado."
|
||||
index:
|
||||
description: "SCIM clients configured here are able to interact with OpenProject SCIM server API to provision, update, and deprovision user accounts and groups."
|
||||
label_create_button: "Add SCIM client"
|
||||
description: "Os clientes SCIM aqui configurados conseguem interagir com a API do servidor OpenProject SCIM para prover, atualizar e desprover contas e grupos de utilizadores."
|
||||
label_create_button: "Adicionar cliente SCIM"
|
||||
new:
|
||||
title: "New SCIM client"
|
||||
title: "Novo cliente SCIM"
|
||||
revoke_static_token_dialog_component:
|
||||
confirm_button: "Revoke"
|
||||
title: "Revoke static token"
|
||||
heading: "Are you sure you want to revoke this token?"
|
||||
description: "The SCIM client that uses this token will no longer be able to access OpenProject's SCIM server API."
|
||||
confirm_button: "Revogar"
|
||||
title: "Revogar token estático"
|
||||
heading: "Tem a certeza de que deseja revogar este token?"
|
||||
description: "O cliente SCIM que utiliza este token deixará de poder aceder à API do servidor SCIM do OpenProject."
|
||||
table_component:
|
||||
blank_slate:
|
||||
title: "No SCIM clients configured yet"
|
||||
description: "Add clients to see them here"
|
||||
user_count: "Users"
|
||||
title: "Ainda não há clientes SCIM configurados"
|
||||
description: "Adicione clientes para os ver aqui"
|
||||
user_count: "Utilizadores"
|
||||
token_list_component:
|
||||
description: "The tokens you generate here can be passed by a SCIM client to access the OpenProject SCIM API."
|
||||
description: "Os tokens que gerar aqui podem ser passados por um cliente SCIM para aceder à API do OpenProject SCIM."
|
||||
heading: "Tokens"
|
||||
label_add_token: "Token"
|
||||
label_aria_add_token: "Add token"
|
||||
label_aria_add_token: "Adicionar token"
|
||||
token_table_component:
|
||||
blank_slate:
|
||||
title: "No tokens have been created yet"
|
||||
description: "You can create one now"
|
||||
expired: "Expired on %{date}"
|
||||
revoked: "Revoked on %{date}"
|
||||
title: "Access token table"
|
||||
title: "Ainda não foram criados tokens"
|
||||
description: "Pode criar um agora"
|
||||
expired: "Expirou em %{date}"
|
||||
revoked: "Revogado em %{date}"
|
||||
title: "Tabela de tokens de acesso"
|
||||
authentication:
|
||||
login_and_registration: "Iniciar sessão e registo"
|
||||
announcements:
|
||||
@@ -327,7 +327,7 @@ pt-PT:
|
||||
admin_only: "Selecione para tornar este atributo visível apenas para os administradores. Os utilizadores sem direitos de administrador não o poderão ver ou editar."
|
||||
is_filter: >
|
||||
Permita que o campo personalizado seja utilizado num filtro nas vistas do pacote de trabalho. Note que apenas com a opção "Para todos os projetos" selecionada, o campo personalizado irá aparecer nas vistas globais.
|
||||
formula: "Add numeric values or type / to search for an attribute or a mathematical operator."
|
||||
formula: "Adicione valores numéricos ou escreva / para procurar um atributo ou um operador matemático."
|
||||
tab:
|
||||
no_results_title_text: Atualmente, não existem campos personalizados.
|
||||
no_results_content_text: Criar um novo campo personalizado
|
||||
@@ -352,7 +352,7 @@ pt-PT:
|
||||
parent:
|
||||
not_descendant: "tem de ser um descendente da raiz da hierarquia."
|
||||
rules:
|
||||
copy_workflow_from: "Type for workflow copy"
|
||||
copy_workflow_from: "Tipo de cópia do fluxo de trabalho"
|
||||
enabled: "Ativado"
|
||||
depth: "Profundidade"
|
||||
item: "Item"
|
||||
@@ -684,9 +684,9 @@ pt-PT:
|
||||
tab: "Configuração do formulário"
|
||||
projects:
|
||||
tab: Projetos
|
||||
enable_all: Enable for all projects
|
||||
select_projects: Select projects
|
||||
select_projects_description: Select the projects in which you would like to use this type.
|
||||
enable_all: Ativar para todos os projetos
|
||||
select_projects: Selecionar projetos
|
||||
select_projects_description: Selecione os projetos em que pretende utilizar este tipo.
|
||||
settings:
|
||||
tab: "Definições"
|
||||
type_color_text: A cor selecionada distingue diferentes tipos em gráficos de Gantt ou tabelas de pacotes de trabalho. Portanto é recomendado usar uma cor forte.
|
||||
@@ -941,7 +941,7 @@ pt-PT:
|
||||
attribute_name: "Atributo"
|
||||
help_text: "Texto de ajuda"
|
||||
auth_provider:
|
||||
scim_clients: "SCIM clients"
|
||||
scim_clients: "Clientes SCIM"
|
||||
capability:
|
||||
context: "Contexto"
|
||||
changeset:
|
||||
@@ -955,7 +955,7 @@ pt-PT:
|
||||
default_value: "Valor predefinido"
|
||||
editable: "Editável"
|
||||
field_format: "Formato"
|
||||
formula: "Formula"
|
||||
formula: "Fórmula"
|
||||
is_filter: "Usado como filtro"
|
||||
is_for_all: "Para todos os projetos"
|
||||
is_required: "Obrigatório"
|
||||
@@ -1107,9 +1107,9 @@ pt-PT:
|
||||
role:
|
||||
permissions: "Permissões"
|
||||
scim_client:
|
||||
auth_provider: "Authentication provider"
|
||||
authentication_method: "Authentication method"
|
||||
jwt_sub: "Subject claim"
|
||||
auth_provider: "Fornecedor de autenticação"
|
||||
authentication_method: "Método de autenticação"
|
||||
jwt_sub: "Reivindicação do sujeito"
|
||||
status:
|
||||
is_closed: "Pacote de trabalho fechado"
|
||||
is_readonly: "Pacote de trabalho só de leitura"
|
||||
@@ -1126,7 +1126,7 @@ pt-PT:
|
||||
ongoing: "Em curso"
|
||||
type:
|
||||
description: "Texto padrão para a descrição"
|
||||
attribute_groups: "Form configuration"
|
||||
attribute_groups: "Configuração do formulário"
|
||||
is_in_roadmap: "Apresentado no roteiro por defeito"
|
||||
is_default: "Ativado para novos projetos por padrão"
|
||||
is_milestone: "É um marco"
|
||||
@@ -1153,20 +1153,20 @@ pt-PT:
|
||||
password_confirmation: "Confirmação"
|
||||
consented_at: "Consentiu em"
|
||||
group:
|
||||
identity_url: "Identity URL"
|
||||
identity_url: "URL de identidade"
|
||||
user_preference:
|
||||
header_look_and_feel: "Look and feel"
|
||||
header_look_and_feel: "Aspeto e sensação"
|
||||
header_alerts: "Alertas"
|
||||
button_update_look_and_feel: "Update look and feel"
|
||||
button_update_look_and_feel: "Atualizar o aspeto e a sensação"
|
||||
button_update_alerts: "Atualizar alertas"
|
||||
comments_sorting: "Exibir a atividade do pacote de trabalho ordenada por"
|
||||
disable_keyboard_shortcuts: "Desativar atalhos de teclado"
|
||||
disable_keyboard_shortcuts_caption_html: |-
|
||||
You can choose to disable default <a href="%{href}">keyboard shortcuts</a> if you use a screen reader or want to avoid accidentally triggering an action with a shortcut.
|
||||
Pode optar por desativar <a href="%{href}">atalhos de teclado</a> predefinidos se utilizar um leitor de ecrã, ou se quiser evitar desencadear acidentalmente uma ação com um atalho.
|
||||
dismissed_enterprise_banners: "Faixas de empresas ocultas"
|
||||
impaired: "Modo de acessibilidade"
|
||||
auto_hide_popups: "Automatically hide success banners"
|
||||
auto_hide_popups_caption: "When enabled, the green success banners will automatically disappear after 5 seconds."
|
||||
auto_hide_popups: "Ocultar automaticamente os banners de sucesso"
|
||||
auto_hide_popups_caption: "Quando ativado, os banners verdes de sucesso desaparecerão automaticamente após 5 segundos."
|
||||
warn_on_leaving_unsaved: "Alertar-me quando sair de um pacote de trabalho sem guardar as alterações"
|
||||
theme: "Modo de cor"
|
||||
time_zone: "Fuso horário"
|
||||
@@ -1239,7 +1239,7 @@ pt-PT:
|
||||
error_unauthorized: "não pode ser acedido."
|
||||
error_readonly: "tentou escrever, mas não é gravável."
|
||||
error_conflict: "A informação foi atualizada entretanto por pelo menos por um outro utilizador."
|
||||
error_not_found: "not found."
|
||||
error_not_found: "não encontrado."
|
||||
email: "não é um endereço de correio válido."
|
||||
empty: "não pode estar em branco."
|
||||
enterprise_plan_required: "requer pelo menos o %{plan_name}."
|
||||
@@ -1257,7 +1257,7 @@ pt-PT:
|
||||
inclusion: "não está definido como um dos valores permitidos."
|
||||
inclusion_nested: "não está definido como um dos valores permitidos no caminho '%{path}'."
|
||||
invalid: "é inválido."
|
||||
invalid_characters: "contains invalid characters."
|
||||
invalid_characters: "contém caracteres inválidos."
|
||||
invalid_url: "não é um URL válido."
|
||||
invalid_url_scheme: "não é um protocolo suportado (permitido: %{allowed_schemes})."
|
||||
less_than_or_equal_to: "deve ser menor ou igual a %{count}."
|
||||
@@ -1635,8 +1635,8 @@ pt-PT:
|
||||
one: "Função"
|
||||
other: "Papel"
|
||||
scim_client:
|
||||
one: "SCIM client"
|
||||
other: "SCIM clients"
|
||||
one: "Cliente SCIM"
|
||||
other: "Clientes SCIM"
|
||||
status: "Estado do pacote de trabalho"
|
||||
token/api:
|
||||
one: Token de acesso
|
||||
@@ -1739,7 +1739,7 @@ pt-PT:
|
||||
estimated_time: "Trabalho"
|
||||
email: "E-mail"
|
||||
entity_type: "Entidade"
|
||||
expires_at: "Expires on"
|
||||
expires_at: "Expira em"
|
||||
firstname: "Primeiro nome"
|
||||
filter: "Filtro"
|
||||
group: "Grupo"
|
||||
@@ -1788,7 +1788,7 @@ pt-PT:
|
||||
title: "Título"
|
||||
type: "Tipo"
|
||||
typeahead: "Preenchimento automático"
|
||||
uid: "Unique identifier"
|
||||
uid: "Identificador único"
|
||||
updated_at: "Atualizado em"
|
||||
updated_on: "Atualizado em"
|
||||
uploader: "Enviado por"
|
||||
@@ -1863,7 +1863,7 @@ pt-PT:
|
||||
button_delete_watcher: "Eliminar observador %{name}"
|
||||
button_download: "Transferir"
|
||||
button_duplicate: "Duplicar"
|
||||
button_duplicate_and_follow: "Duplicate and follow"
|
||||
button_duplicate_and_follow: "Duplicar e seguir"
|
||||
button_edit: "Editar"
|
||||
button_edit_associated_wikipage: "Editar página Wiki associada: %{page_title}"
|
||||
button_expand_all: "Expandir Tudo"
|
||||
@@ -1947,13 +1947,13 @@ pt-PT:
|
||||
text:
|
||||
failed: 'Não foi possível copiar o projecto "%{source_project_name}" para o projecto "%{target_project_name}".'
|
||||
succeeded: 'Projecto "%{source_project_name}" copiado para "%{target_project_name}".'
|
||||
source_project_label: "Project copied"
|
||||
source_project_label: "Projeto copiado"
|
||||
copy_options:
|
||||
dependencies_label: 'Copy from project'
|
||||
dependencies_label: 'Copiar do projeto'
|
||||
create_project:
|
||||
template_label: "Use template"
|
||||
template_label: "Utilizar modelo"
|
||||
copy_options:
|
||||
dependencies_label: 'Copy from template'
|
||||
dependencies_label: 'Copiar do modelo'
|
||||
create_wiki_page: "Criar nova página wiki"
|
||||
create_wiki_page_button: "Página wiki"
|
||||
date:
|
||||
@@ -2071,7 +2071,7 @@ pt-PT:
|
||||
units:
|
||||
minute_abbreviated:
|
||||
one: "min"
|
||||
other: "mins"
|
||||
other: "min"
|
||||
hour:
|
||||
one: "hora"
|
||||
other: "horas"
|
||||
@@ -2128,7 +2128,7 @@ pt-PT:
|
||||
placeholder_users: Utilizadores de marcador de posição
|
||||
project_list_sharing: Partilha da lista de projetos
|
||||
readonly_work_packages: Pacotes de trabalho só de leitura
|
||||
scim_api: SCIM server API
|
||||
scim_api: API do servidor SCIM
|
||||
sso_auth_providers: Início de sessão único
|
||||
team_planner_view: Vista do planeador de equipas
|
||||
virus_scanning: Análise antivírus
|
||||
@@ -2144,7 +2144,7 @@ pt-PT:
|
||||
plan_text_html: "Disponível a partir do %{plan_name}."
|
||||
unlimited: "Ilimitado"
|
||||
already_have_token: >
|
||||
Already have a token? Add it using the button below to upgrade to the booked Enterprise plan.
|
||||
Já tem um token? Adicione-o com o botão abaixo para atualizar para o plano Enterprise reservado.
|
||||
hide_banner: "Ocultar esta faixa"
|
||||
homescreen_description: >
|
||||
Os planos Enterprise aumentam a Edição Community do OpenProject que está atualmente a usar. Eles incluem complementos, funcionalidades adicionais e assistência profissional que permite que organizações de todas as dimensões alcancem grandes coisas em conjunto.
|
||||
@@ -2152,15 +2152,15 @@ pt-PT:
|
||||
baseline_comparison:
|
||||
description: Destacar as alterações introduzidas nesta lista desde qualquer ponto no passado.
|
||||
benefits:
|
||||
description: "What are the benefits of the Enterprise on-premises edition?"
|
||||
description: "Quais são os benefícios da edição Empresarial no local?"
|
||||
high_security: "Funcionalidades de segurança"
|
||||
high_security_text: "Single sign on (SAML, OpenID Connect, CAS), LDAP groups."
|
||||
high_security_text: "Início de sessão único (SAML, OpenID Connect, CAS), grupos LDAP."
|
||||
installation: "Apoio à instalação"
|
||||
installation_text: "Engenheiros de software experientes darão orientações durante toda a instalação e processo de configuração na sua própria infraestrutura."
|
||||
premium_features: "Enterprise add-ons"
|
||||
premium_features_text: "Agile boards, custom theme and logo, graphs, intelligent workflows with custom actions, full text search for work package attachments and multi-select custom fields."
|
||||
professional_support: "Professional support"
|
||||
professional_support_text: "Get reliable, high-touch support from senior support engineers with expert knowledge about running OpenProject in business-critical environments."
|
||||
premium_features: "Complementos Enterprise"
|
||||
premium_features_text: "Agile boards, gráficos de temas e logótipos personalizados, fluxos de trabalho inteligentes com ações personalizadas, pesquisa de texto completo para anexos em pacotes de trabalho, e campos personalizados de multi-seleção."
|
||||
professional_support: "Apoio profissional"
|
||||
professional_support_text: "Receba apoio fiável e de alto nível de engenheiros séniores de suporte, com conhecimento especializado sobre a gestão do OpenProject em ambientes de negócios críticos."
|
||||
work_package_subject_generation:
|
||||
description: "Crie temas gerados automaticamente utilizando atributos e texto referenciados."
|
||||
customize_life_cycle:
|
||||
@@ -2196,8 +2196,8 @@ pt-PT:
|
||||
title: "Início de sessão único para armazenamento Nextcloud"
|
||||
description: "Permita uma autenticação perfeita e segura para o seu armazenamento Nextcloud com o início de sessão único. Simplifique a gestão do acesso e aumente a conveniência do utilizador."
|
||||
scim_api:
|
||||
title: "SCIM clients"
|
||||
description: "Automate user management in OpenProject by seamlessly integrating external identity services like Microsoft Entra or Keycloak through our SCIM Server API. Available starting with the Enterprise corporate plan."
|
||||
title: "Clientes SCIM"
|
||||
description: "Automatize a gestão de utilizadores no OpenProject, integrando na perfeição serviços de identidade externos, como Microsoft Entra ou Keycloak, através da nossa API do Servidor SCIM. Disponível a partir do plano Enterprise empresarial."
|
||||
virus_scanning:
|
||||
description: "Assegure-se que os ficheiros carregados no OpenProject são verificados quanto à presença de vírus antes de serem acessíveis a outros utilizadores."
|
||||
placeholder_users:
|
||||
@@ -2212,29 +2212,29 @@ pt-PT:
|
||||
description: " "
|
||||
teaser:
|
||||
title:
|
||||
one: "One day left of %{trial_plan} trial token"
|
||||
other: "%{count} days left of %{trial_plan} trial token"
|
||||
description: "You have access to all %{trial_plan} features."
|
||||
one: "Um dia restante do token de teste %{trial_plan}"
|
||||
other: "F%{count} dias restantes do token de teste %{trial_plan}"
|
||||
description: "Tem acesso a todas as funcionalidades de %{trial_plan}."
|
||||
trial:
|
||||
not_found: "You have requested a trial token, but that request is no longer available. Please try again."
|
||||
wait_for_confirmation: "We sent you an email to confirm your address in order to retrieve a trial token."
|
||||
not_found: "Solicitou um token de teste, mas esse pedido já não está disponível. Tente novamente."
|
||||
wait_for_confirmation: "Enviámos um e-mail para confirmar o seu endereço, a fim de obter um token de avaliação."
|
||||
already_retrieved: >
|
||||
Your trial enterprise token was already retrieved. Please check your emails for the token being attached. Please reach out to our support team if you need a new one.
|
||||
successfully_saved: "Your trial enterprise token has been successfully retrieved."
|
||||
token_sent: "Trial token requested"
|
||||
request_again: "Request again"
|
||||
resend_action: "Resend confirmation email"
|
||||
welcome_title: "Quick feature overview"
|
||||
welcome_description: "Get a quick overview of project management and team collaboration with OpenProject Enterprise edition."
|
||||
O seu token de teste Enterprise já foi recuperado. Verifique os seus e-mails para ver se o token foi anexado. Contacte a nossa equipa de apoio se precisar de um novo token.
|
||||
successfully_saved: "O seu token de teste Enterprise foi recuperado com êxito."
|
||||
token_sent: "Token de teste solicitado"
|
||||
request_again: "Solicitar novamente"
|
||||
resend_action: "Reenviar e-mail de confirmação"
|
||||
welcome_title: "Síntese rápida das funcionalidades"
|
||||
welcome_description: "Obter uma síntese da gestão do projeto e colaboração em equipa com a edição OpenProject Enterprise."
|
||||
confirmation_info: >
|
||||
We sent you an email on %{date} to %{email} with all the information to start the free trial of OpenProject Enterprise. Please check your inbox and click the confirmation link provided to start your 14-day free trial.
|
||||
Enviámos-lhe um e-mail para %{date} para %{email} com todas as informações para iniciar o teste gratuito do OpenProject Enterprise. Verifique a sua caixa de entrada e clique na ligação de confirmação fornecida para iniciar o teste gratuito de 14 dias.
|
||||
confirmation_subline: >
|
||||
Please, check your inbox and follow the steps to start your 14-day free trial.
|
||||
domain_caption: The token will be valid for your currently configured host name.
|
||||
Verifique a sua caixa de correio eletrónico e siga os passos para iniciar o seu teste gratuito de 14 dias.
|
||||
domain_caption: O token será válido para o nome do anfitrião configurado atualmente.
|
||||
receive_newsletter_html: >
|
||||
I want to receive the OpenProject <a target="_blank" href="https://www.openproject.org/newsletter/">newsletter</a>.
|
||||
Quero receber a <a target="_blank" href="https://www.openproject.org/newsletter/">newsletter</a> do OpenProject.
|
||||
consent_html: >
|
||||
I agree with the <a target="_blank" href="https://www.openproject.org/terms-of-service/">terms of service</a> and the <a target="_blank" href="https://www.openproject.org/data-privacy-and-security/">privacy policy</a>.
|
||||
Concordo com os <a target="_blank" href="https://www.openproject.org/terms-of-service/">termos de serviço</a> e a <a target="_blank" href="https://www.openproject.org/data-privacy-and-security/">política de privacidade</a>.
|
||||
enumeration_activities: "Atividades de controlo de tempo"
|
||||
enumeration_work_package_priorities: "Prioridades do pacote de trabalho"
|
||||
enumeration_reported_project_statuses: "Estado do projeto reportado"
|
||||
@@ -2694,7 +2694,7 @@ pt-PT:
|
||||
label_select_main_menu_item: Seleccione novo item do menu principal
|
||||
label_required_disk_storage: "Espaço em disco necessário"
|
||||
label_send_invitation: Enviar convite
|
||||
label_calculated_value: "Calculated value"
|
||||
label_calculated_value: "Valor calculado"
|
||||
label_change_plural: "Alterações"
|
||||
label_change_properties: "Alterar propriedades"
|
||||
label_change_status: "Alterar estado"
|
||||
@@ -2830,7 +2830,7 @@ pt-PT:
|
||||
label_follows: "segue"
|
||||
label_force_user_language_to_default: "Idioma de utilizadores definida com uma linguagem não permitida como padrão"
|
||||
label_form_configuration: "Configuração do formulário"
|
||||
label_formula: "Formula"
|
||||
label_formula: "Fórmula"
|
||||
label_gantt_chart: "Gráfico de Gantt"
|
||||
label_gantt_chart_plural: "Gráficos de Gantt"
|
||||
label_general: "Geral"
|
||||
@@ -3228,7 +3228,7 @@ pt-PT:
|
||||
label_work_package_attachments: "Anexos do pacote de trabalho"
|
||||
label_work_package_category_new: "Nova categoria"
|
||||
label_work_package_category_plural: "Categorias de pacotes de trabalho"
|
||||
label_work_package_comments: "Work package comments"
|
||||
label_work_package_comments: "Comentários do pacote de trabalho"
|
||||
label_work_package_hierarchy: "Hierarquia de pacotes de trabalho"
|
||||
label_work_package_new: "Novo pacote de trabalho"
|
||||
label_work_package_edit: "Editar pacote de trabalho %{name}"
|
||||
@@ -3569,7 +3569,7 @@ pt-PT:
|
||||
permission_comment_news: "Comentar notícias"
|
||||
permission_commit_access: "Acesso de leitura/escrita ao repositório (comprometer)"
|
||||
permission_copy_projects: "Copiar projeto"
|
||||
permission_copy_work_packages: "Duplicate work packages"
|
||||
permission_copy_work_packages: "Duplicar pacotes de trabalho"
|
||||
permission_create_backup: "Criar cópias de segurança"
|
||||
permission_delete_work_package_watchers: "Eliminar observador"
|
||||
permission_delete_work_packages: "Eliminar pacotes de trabalho"
|
||||
@@ -4087,7 +4087,7 @@ pt-PT:
|
||||
phase_gates: "Portas de fases"
|
||||
new:
|
||||
description: "As alterações a esta fase do projeto serão refletidas em todos os projetos em que estiver ativada."
|
||||
heading: "New phase"
|
||||
heading: "Nova fase"
|
||||
both_gate: "Porta de início e conclusão"
|
||||
no_gate: "Sem porta"
|
||||
start_gate: "Porta de início"
|
||||
@@ -4391,7 +4391,7 @@ pt-PT:
|
||||
reminders:
|
||||
label_remind_at: "Data"
|
||||
note_placeholder: "Porque está a definir este lembrete?"
|
||||
create_success_message: "Reminder set successfully. You will receive a notification for this work package %{reminder_time}."
|
||||
create_success_message: "Lembrete definido com sucesso. Irá receber uma notificação para este pacote de trabalho %{reminder_time}."
|
||||
success_update_message: "Lembrete atualizado com sucesso."
|
||||
success_deletion_message: "Lembrete eliminado com sucesso."
|
||||
sharing:
|
||||
|
||||
@@ -1784,7 +1784,7 @@ ru:
|
||||
estimated_time: "Предполагаемое время"
|
||||
email: "Электронная почта"
|
||||
entity_type: "Тип сущности"
|
||||
expires_at: "Expires on"
|
||||
expires_at: "Истекает"
|
||||
firstname: "Имя"
|
||||
filter: "Фильтр"
|
||||
group: "Группа"
|
||||
@@ -1908,7 +1908,7 @@ ru:
|
||||
button_delete_watcher: "Удаление наблюдателя %{name}"
|
||||
button_download: "Скачать"
|
||||
button_duplicate: "Дублировать"
|
||||
button_duplicate_and_follow: "Duplicate and follow"
|
||||
button_duplicate_and_follow: "Дублировать и следить"
|
||||
button_edit: "Правка"
|
||||
button_edit_associated_wikipage: "Редактирование связанной wiki-страницы: %{page_title}"
|
||||
button_expand_all: "Развернуть все"
|
||||
@@ -3660,7 +3660,7 @@ ru:
|
||||
permission_comment_news: "Комментировать новость"
|
||||
permission_commit_access: "Возможность чтения/записи в репозиторий (commit)"
|
||||
permission_copy_projects: "Копировать проект"
|
||||
permission_copy_work_packages: "Duplicate work packages"
|
||||
permission_copy_work_packages: "Дублировать пакеты работ"
|
||||
permission_create_backup: "Создание резервных копий"
|
||||
permission_delete_work_package_watchers: "Удалить наблюдателей"
|
||||
permission_delete_work_packages: "Удалить пакеты работ"
|
||||
|
||||
@@ -464,13 +464,13 @@ uk:
|
||||
name: Відхилено
|
||||
time_entry_activities:
|
||||
item_0:
|
||||
name: Налаштування
|
||||
name: Керування
|
||||
item_1:
|
||||
name: Специфікація
|
||||
item_2:
|
||||
name: Розробка
|
||||
item_3:
|
||||
name: Перевірка
|
||||
name: Тестування
|
||||
item_4:
|
||||
name: Підтримка
|
||||
item_5:
|
||||
|
||||
+130
-130
@@ -99,12 +99,12 @@ uk:
|
||||
upgrade: "Оновити зараз"
|
||||
contact: "Зв’яжіться з нами, щоб отримати демоверсію"
|
||||
status:
|
||||
expired: "Expired"
|
||||
expiring_soon: "Expiring soon"
|
||||
in_grace_period: "In grace period"
|
||||
invalid_domain: "Invalid domain"
|
||||
not_active: "Not active"
|
||||
trial: "Trial"
|
||||
expired: "Термін дії минув"
|
||||
expiring_soon: "Термін дії незабаром мине"
|
||||
in_grace_period: "У пільговому періоді"
|
||||
invalid_domain: "Недійсний домен"
|
||||
not_active: "Неактивний"
|
||||
trial: "Пробний період"
|
||||
jemalloc_allocator: Розподіл пам'яті Jemalloc
|
||||
journal_aggregation:
|
||||
explanation:
|
||||
@@ -112,57 +112,57 @@ uk:
|
||||
link: "вебгука"
|
||||
scim_clients:
|
||||
authentication_methods:
|
||||
sso: "JWT from identity provider"
|
||||
oauth2_client: "OAuth 2.0 client credentials"
|
||||
oauth2_token: "Static access token"
|
||||
sso: "JWT від постачальника ідентифікаційних даних"
|
||||
oauth2_client: "Облікові дані клієнта OAuth 2.0"
|
||||
oauth2_token: "Постійний токен доступу"
|
||||
created_client_credentials_dialog_component:
|
||||
title: "Client credentials created"
|
||||
heading: "Client credentials have been generated"
|
||||
one_time_hint: "This is the only time you will see the client secret. Make sure to copy it now."
|
||||
title: "Облікові дані клієнта створено"
|
||||
heading: "Облікові дані клієнта згенеровано"
|
||||
one_time_hint: "Це єдиний раз, коли ви побачите секретний ключ клієнта. Обов’язково скопіюйте його зараз."
|
||||
created_token_dialog_component:
|
||||
title: "Token created"
|
||||
heading: "An token has been generated"
|
||||
label_token: "Token"
|
||||
one_time_hint: "This is the only time you will see this token. Make sure to copy it now."
|
||||
title: "Токен створено"
|
||||
heading: "Токен згенеровано"
|
||||
label_token: "Токен"
|
||||
one_time_hint: "Це єдиний раз, коли ви побачите цей токен. Обов’язково скопіюйте його зараз."
|
||||
delete_scim_client_dialog_component:
|
||||
title: "Delete SCIM client"
|
||||
heading: "Are you sure you want to delete this SCIM client?"
|
||||
description: "Users managed by this SCIM client can no longer be updated by it."
|
||||
title: "Видалити клієнт SCIM"
|
||||
heading: "Ви дійсно хочете видалити цей клієнт SCIM?"
|
||||
description: "Цей клієнт SCIM більше не може оновлювати користувачів, якими керує."
|
||||
edit:
|
||||
label_delete_scim_client: "Delete SCIM client"
|
||||
label_delete_scim_client: "Видалити клієнт SCIM"
|
||||
form:
|
||||
auth_provider_description: "This is the service that users added by the SCIM provider will use to authenticate in OpenProject."
|
||||
authentication_method_description_html: "This is how the SCIM client authenticates at OpenProject. Please ensure that OAuth tokens include the <code>scim_v2</code> scope."
|
||||
description_html: 'Please refer to our <a href="%{docs_url}">documentation on configuring SCIM clients</a> for more information on these configuration options.'
|
||||
jwt_sub_description_html: 'For example, for Keycloak, this is the UUID of the service account associated with the SCIM client. Consult <a href="%{docs_url}">our documentation</a> to learn how to find the subject claim for your use case.'
|
||||
name_description: "Choose a name that will help other admins better understand why this client was configured."
|
||||
auth_provider_description: "Це сервіс, який користувачі, додані SCIM-провайдером, будуть використовувати для автентифікації в OpenProject."
|
||||
authentication_method_description_html: "Ось так клієнт SCIM автентифікується в OpenProject. Переконайтеся, що токени OAuth включають область <code>scim_v2</code>."
|
||||
description_html: 'Щоб отримати додаткову інформацію про налаштування клієнтів SCIM, перегляньте <a href="%{docs_url}">документацію</a>.'
|
||||
jwt_sub_description_html: 'Наприклад, для Keycloak це UUID пов’язаного з клієнтом SCIM облікового запису сервісу. Інформацію про те, як знайти тему для свого варіанту використання, наведено в <a href="%{docs_url}">документації</a>.'
|
||||
name_description: "Виберіть ім’я, яке допоможе іншим адміністраторам чітко зрозуміти, навіщо налаштовано цей клієнт."
|
||||
index:
|
||||
description: "SCIM clients configured here are able to interact with OpenProject SCIM server API to provision, update, and deprovision user accounts and groups."
|
||||
label_create_button: "Add SCIM client"
|
||||
description: "Клієнти SCIM, налаштовані тут, можуть взаємодіяти з API сервера SCIM в OpenProject, щоб надавати, оновлювати й видаляти облікові записи й групи."
|
||||
label_create_button: "Додати клієнт SCIM"
|
||||
new:
|
||||
title: "New SCIM client"
|
||||
title: "Новий клієнт SCIM"
|
||||
revoke_static_token_dialog_component:
|
||||
confirm_button: "Revoke"
|
||||
title: "Revoke static token"
|
||||
heading: "Are you sure you want to revoke this token?"
|
||||
description: "The SCIM client that uses this token will no longer be able to access OpenProject's SCIM server API."
|
||||
confirm_button: "Анулювати"
|
||||
title: "Анулювати статичний токен"
|
||||
heading: "Ви дійсно хочете анулювати цей токен?"
|
||||
description: "Клієнт SCIM, який використовує цей токен, більше не зможе отримати доступ до API сервера SCIM в OpenProject."
|
||||
table_component:
|
||||
blank_slate:
|
||||
title: "No SCIM clients configured yet"
|
||||
description: "Add clients to see them here"
|
||||
user_count: "Users"
|
||||
title: "Немає налаштованих клієнтів SCIM"
|
||||
description: "Додайте клієнтів, щоб побачити їх тут"
|
||||
user_count: "Користувачі"
|
||||
token_list_component:
|
||||
description: "The tokens you generate here can be passed by a SCIM client to access the OpenProject SCIM API."
|
||||
heading: "Tokens"
|
||||
label_add_token: "Token"
|
||||
label_aria_add_token: "Add token"
|
||||
description: "Клієнт SCIM може передавати токени, які ви генеруєте тут, для доступу до API сервера SCIM в OpenProject."
|
||||
heading: "Токени"
|
||||
label_add_token: "Токен"
|
||||
label_aria_add_token: "Додати токен"
|
||||
token_table_component:
|
||||
blank_slate:
|
||||
title: "No tokens have been created yet"
|
||||
description: "You can create one now"
|
||||
expired: "Expired on %{date}"
|
||||
revoked: "Revoked on %{date}"
|
||||
title: "Access token table"
|
||||
title: "Ще не створено жодного токена"
|
||||
description: "Його можна створити прямо зараз"
|
||||
expired: "Термін дії минув %{date}"
|
||||
revoked: "Анульовано %{date}"
|
||||
title: "Таблиця токенів доступу"
|
||||
authentication:
|
||||
login_and_registration: "Вхід і реєстрація"
|
||||
announcements:
|
||||
@@ -325,7 +325,7 @@ uk:
|
||||
admin_only: "Установіть прапорець, щоб зробити цей атрибут видимим лише для адміністраторів. Користувачі без прав адміністратора не зможуть переглядати або редагувати його."
|
||||
is_filter: >
|
||||
Дозвольте відображати користувацьке поле у фільтрі в поданнях пакета робіт. Зверніть увагу: користувацьке поле відображатиметься в глобальних поданнях, лише якщо встановлено прапорець «Для всіх проєктів».
|
||||
formula: "Add numeric values or type / to search for an attribute or a mathematical operator."
|
||||
formula: "Додайте числові значення або введіть \"/\" для пошуку атрибута чи математичного оператора."
|
||||
tab:
|
||||
no_results_title_text: Наразі немає спеціальних полів.
|
||||
no_results_content_text: Створіть нове спеціальне поле
|
||||
@@ -339,7 +339,7 @@ uk:
|
||||
not_found: "не знайдено."
|
||||
rules:
|
||||
copy_workflow_from:
|
||||
workflow_missing: "has no own workflow."
|
||||
workflow_missing: "не має власного робочого процесу."
|
||||
item:
|
||||
root_item: "не може бути кореневим елементом."
|
||||
not_persisted: "має бути вже наявним елементом."
|
||||
@@ -350,8 +350,8 @@ uk:
|
||||
parent:
|
||||
not_descendant: "має бути нащадком кореня ієрархії."
|
||||
rules:
|
||||
copy_workflow_from: "Type for workflow copy"
|
||||
enabled: "Enabled"
|
||||
copy_workflow_from: "Тип копії робочого процесу"
|
||||
enabled: "Увімкнено"
|
||||
depth: "Глибина"
|
||||
item: "Елемент"
|
||||
label: "Мітка"
|
||||
@@ -898,7 +898,7 @@ uk:
|
||||
required_description: "Позначає цей робочий пакет як обовʼязковий для звʼязаного з ним"
|
||||
label_parent_singular: "батьківський елемент"
|
||||
label_parent_plural: "батьківський елемент"
|
||||
label_other_relations: "Other relations"
|
||||
label_other_relations: "Інші зв’язки"
|
||||
ghost_relation_title: "Пов’язаний пакет робіт"
|
||||
ghost_relation_description: "Ви не маєте дозволів на перегляд цього елемента."
|
||||
label_invitation: Запрошення
|
||||
@@ -953,7 +953,7 @@ uk:
|
||||
attribute_name: "Атрибут"
|
||||
help_text: "Довідковий текст"
|
||||
auth_provider:
|
||||
scim_clients: "SCIM clients"
|
||||
scim_clients: "Клієнти SCIM"
|
||||
capability:
|
||||
context: "Контекст"
|
||||
changeset:
|
||||
@@ -967,7 +967,7 @@ uk:
|
||||
default_value: "Значення за замовчуванням"
|
||||
editable: "Може бути відредаговане"
|
||||
field_format: "Формат"
|
||||
formula: "Formula"
|
||||
formula: "Формула"
|
||||
is_filter: "Використовується як фільтр"
|
||||
is_for_all: "Для всіх проєктів"
|
||||
is_required: "Обов'язкове поле"
|
||||
@@ -996,19 +996,19 @@ uk:
|
||||
enterprise_token:
|
||||
starts_at: "Дійсний з"
|
||||
subscriber: "Абонент"
|
||||
subscription: "Subscription"
|
||||
subscription: "Підписка"
|
||||
plan: "План"
|
||||
encoded_token: "Маркер підтримки підприємства"
|
||||
active_user_count_restriction: "Active users"
|
||||
active_user_count_restriction: "Активні користувачі"
|
||||
enterprise_trial:
|
||||
company: "Company"
|
||||
company: "Компанія"
|
||||
grids/grid:
|
||||
page: "Сторінка"
|
||||
row_count: "Кількість рядків"
|
||||
column_count: "Кількість стовпців"
|
||||
widgets: "Віджети"
|
||||
journal:
|
||||
notes: "Нотатки"
|
||||
notes: "Примітки"
|
||||
cause_type: "Тип причини"
|
||||
ldap_auth_source:
|
||||
account: "Обліковий запис"
|
||||
@@ -1119,9 +1119,9 @@ uk:
|
||||
role:
|
||||
permissions: "Дозволи"
|
||||
scim_client:
|
||||
auth_provider: "Authentication provider"
|
||||
authentication_method: "Authentication method"
|
||||
jwt_sub: "Subject claim"
|
||||
auth_provider: "Постачальник послуг автентифікації"
|
||||
authentication_method: "Метод автентифікації"
|
||||
jwt_sub: "Потрібна тема"
|
||||
status:
|
||||
is_closed: "Робочий пакет закритий"
|
||||
is_readonly: "Пакет робіт лише для перегляду"
|
||||
@@ -1138,7 +1138,7 @@ uk:
|
||||
ongoing: "Триває"
|
||||
type:
|
||||
description: "Текст за умовчанням для опису"
|
||||
attribute_groups: "Form configuration"
|
||||
attribute_groups: "Конфігурація форми"
|
||||
is_in_roadmap: "За умовчанням відображається в дорожній карті"
|
||||
is_default: "Активовано для нових проектів за замовчуванням"
|
||||
is_milestone: "Це віха"
|
||||
@@ -1165,24 +1165,24 @@ uk:
|
||||
password_confirmation: "Підтвердження"
|
||||
consented_at: "Згоден на"
|
||||
group:
|
||||
identity_url: "Identity URL"
|
||||
identity_url: "URL-адреса ідентичності"
|
||||
user_preference:
|
||||
header_look_and_feel: "Look and feel"
|
||||
header_alerts: "Alerts"
|
||||
button_update_look_and_feel: "Update look and feel"
|
||||
button_update_alerts: "Update alerts"
|
||||
comments_sorting: "Display work package activity sorted by"
|
||||
disable_keyboard_shortcuts: "Disable keyboard shortcuts"
|
||||
header_look_and_feel: "Оформлення"
|
||||
header_alerts: "Сповіщення"
|
||||
button_update_look_and_feel: "Оновити оформлення"
|
||||
button_update_alerts: "Сповіщення про оновлення"
|
||||
comments_sorting: "Відображати дії з пакетів робіт, відсортовані за"
|
||||
disable_keyboard_shortcuts: "Вимкнути комбінації клавіш"
|
||||
disable_keyboard_shortcuts_caption_html: |-
|
||||
You can choose to disable default <a href="%{href}">keyboard shortcuts</a> if you use a screen reader or want to avoid accidentally triggering an action with a shortcut.
|
||||
Ви можете вимкнути стандартні <a href="%{href}">комбінації клавіш</a>, якщо використовуєте невізуальний екран або хочете, щоб відповідні дії не запускалися, коли ви натискаєте ці комбінації клавіш випадково.
|
||||
dismissed_enterprise_banners: "Приховані банери Enterprise"
|
||||
impaired: "Режим спеціальних можливостей"
|
||||
auto_hide_popups: "Automatically hide success banners"
|
||||
auto_hide_popups_caption: "When enabled, the green success banners will automatically disappear after 5 seconds."
|
||||
warn_on_leaving_unsaved: "Попереджати мене, коли залишив робочий пакет із незбереженими змінами"
|
||||
theme: "Colour mode"
|
||||
theme: "Колірний режим"
|
||||
time_zone: "Часовий пояс"
|
||||
mode_guideline: "Some modes will overwrite custom theme colours for accessibility and legibility. Please select Light mode for full custom theme support."
|
||||
mode_guideline: "Деякі режими перезаписують користувацькі кольори теми, щоб поліпшити доступність і читабельність. Виберіть світлий режим, якщо потрібна повна підтримка користувацької теми."
|
||||
daily_reminders: "Щоденні нагадування"
|
||||
workdays: "Робочі дні"
|
||||
version:
|
||||
@@ -1251,7 +1251,7 @@ uk:
|
||||
error_unauthorized: "– можливо, немає доступу."
|
||||
error_readonly: "– було здійснено спробу запису, але елемент недоступний для запису."
|
||||
error_conflict: "Інформація була оновлена принаймні один інший користувач в той же час."
|
||||
error_not_found: "not found."
|
||||
error_not_found: "не знайдено."
|
||||
email: "– це не дійсна електронна адреса."
|
||||
empty: "не може бути порожнім."
|
||||
enterprise_plan_required: "потрібен принаймні план %{plan_name}."
|
||||
@@ -1269,7 +1269,7 @@ uk:
|
||||
inclusion: "не встановлено в одному з допустимих значень"
|
||||
inclusion_nested: "– не призначено одне з дозволених значень за шляхом «%{path}»."
|
||||
invalid: "зазначено невірно"
|
||||
invalid_characters: "contains invalid characters."
|
||||
invalid_characters: "містить недопустимі символи."
|
||||
invalid_url: "не є дійсною URL-адресою."
|
||||
invalid_url_scheme: "не є підтримуваним протоколом (дозволено: %{allowed_schemes})."
|
||||
less_than_or_equal_to: "має бути меншим або рівним %{count}"
|
||||
@@ -1346,13 +1346,13 @@ uk:
|
||||
scopes:
|
||||
not_match_configured: "не відповідає доступним областям."
|
||||
enterprise_trial:
|
||||
already_used: "was already used to create a trial."
|
||||
failed_to_create: "Trial could not be created (%{status})"
|
||||
general_consent: "Please accept the terms and conditions."
|
||||
already_used: "уже використано для створення пробної версії."
|
||||
failed_to_create: "Не вдалося створити пробну версію (%{status})"
|
||||
general_consent: "Будь ласка, прийміть правила й умови використання."
|
||||
enterprise_token:
|
||||
only_one_trial: "Only one trial token can be active. Please delete the previous trial token before adding another."
|
||||
only_one_trial: "Активним може бути лише один пробний токен. Видаліть попередній, перш ніж додавати новий."
|
||||
unreadable: "неможливо прочитати. Ви впевнені, що це маркер підтримки?"
|
||||
already_added: "This token has already been added."
|
||||
already_added: "Цей токен уже додано."
|
||||
grids/grid:
|
||||
overlaps: "перекриваються."
|
||||
outside: "знаходиться за межами сітки."
|
||||
@@ -1663,10 +1663,10 @@ uk:
|
||||
many: "Роль"
|
||||
other: "Роль"
|
||||
scim_client:
|
||||
one: "SCIM client"
|
||||
few: "SCIM clients"
|
||||
many: "SCIM clients"
|
||||
other: "SCIM clients"
|
||||
one: "Клієнт SCIM"
|
||||
few: "Клієнти SCIM"
|
||||
many: "Клієнти SCIM"
|
||||
other: "Клієнти SCIM"
|
||||
status: "Стан робочого пакета"
|
||||
token/api:
|
||||
one: Маркер доступу
|
||||
@@ -1756,7 +1756,7 @@ uk:
|
||||
blocks_ids: "Ідентифікатори заблокованих робочих пакетів"
|
||||
category: "Категорія"
|
||||
comment: "Коментар"
|
||||
comments: "Коментування"
|
||||
comments: "Коментар"
|
||||
content: "Контент"
|
||||
color: "Колір"
|
||||
creator: "Автор"
|
||||
@@ -1773,13 +1773,13 @@ uk:
|
||||
derived_start_date: "Отримана дата початку"
|
||||
direction: "Напрямок"
|
||||
display_sums: "Відображати суми"
|
||||
domain: "Domain"
|
||||
domain: "Домен"
|
||||
due_date: "Дата закінчення"
|
||||
estimated_hours: "Робота"
|
||||
estimated_time: "Робота"
|
||||
email: "Ел. адреса"
|
||||
entity_type: "Сутність"
|
||||
expires_at: "Expires on"
|
||||
expires_at: "Термін дії минає"
|
||||
firstname: "Ім'я"
|
||||
filter: "Фільтр"
|
||||
group: "Група"
|
||||
@@ -1793,14 +1793,14 @@ uk:
|
||||
#kept for backwards compatibility
|
||||
issue: "Робочий пакет"
|
||||
journal: "Журнал"
|
||||
journal_notes: "Коментування"
|
||||
journal_notes: "Коментар"
|
||||
lastname: "Прізвище"
|
||||
login: "Ім'я користувача"
|
||||
lock_version: "Заблокувати версію"
|
||||
mail: "Електронна пошта"
|
||||
name: "Назва"
|
||||
note: "Примітка"
|
||||
notes: "Нотатки"
|
||||
notes: "Примітки"
|
||||
number: "Число"
|
||||
options: "Опції"
|
||||
operator: "Оператор"
|
||||
@@ -1828,7 +1828,7 @@ uk:
|
||||
title: "Назва"
|
||||
type: "Тип"
|
||||
typeahead: "Автозаповнення"
|
||||
uid: "Unique identifier"
|
||||
uid: "Унікальний ідентифікатор"
|
||||
updated_at: "Оновлене"
|
||||
updated_on: "Оновлено"
|
||||
uploader: "Завантажувач"
|
||||
@@ -1903,7 +1903,7 @@ uk:
|
||||
button_delete_watcher: "Видалити спостерігача %{name}"
|
||||
button_download: "Завантажити"
|
||||
button_duplicate: "Дублювати"
|
||||
button_duplicate_and_follow: "Duplicate and follow"
|
||||
button_duplicate_and_follow: "Дублювати й стежити"
|
||||
button_edit: "Редагувати"
|
||||
button_edit_associated_wikipage: "Редагувати пов'язану Wiki сторінку: %{page_title}"
|
||||
button_expand_all: "Розгорнути все"
|
||||
@@ -1942,7 +1942,7 @@ uk:
|
||||
button_unwatch: "Перестати відстежувати"
|
||||
button_update: "Оновити"
|
||||
button_upgrade: "Оновити"
|
||||
button_buy_now: "Buy now"
|
||||
button_buy_now: "Придбати зараз"
|
||||
button_upload: "Завантажити"
|
||||
button_view: "Перегляд"
|
||||
button_watch: "Спостерігати"
|
||||
@@ -1987,13 +1987,13 @@ uk:
|
||||
text:
|
||||
failed: 'Не вдалося скопіювати проект "%{source_project_name}" до проекту "%{target_project_name}".'
|
||||
succeeded: 'Скопіював проект "%{source_project_name}" до "%{target_project_name}".'
|
||||
source_project_label: "Project copied"
|
||||
source_project_label: "Проєкт скопійовано"
|
||||
copy_options:
|
||||
dependencies_label: 'Copy from project'
|
||||
dependencies_label: 'Скопіювати з проєкту'
|
||||
create_project:
|
||||
template_label: "Use template"
|
||||
template_label: "Використовувати шаблон"
|
||||
copy_options:
|
||||
dependencies_label: 'Copy from template'
|
||||
dependencies_label: 'Копіювати з шаблону'
|
||||
create_wiki_page: "Створити нову вікісторінку"
|
||||
create_wiki_page_button: "Сторінка Wiki"
|
||||
date:
|
||||
@@ -2144,10 +2144,10 @@ uk:
|
||||
other: "%{count} с"
|
||||
units:
|
||||
minute_abbreviated:
|
||||
one: "min"
|
||||
few: "mins"
|
||||
many: "mins"
|
||||
other: "mins"
|
||||
one: "хв"
|
||||
few: "хв"
|
||||
many: "хв"
|
||||
other: "хв"
|
||||
hour:
|
||||
one: "година"
|
||||
few: "годин(и)"
|
||||
@@ -2176,7 +2176,7 @@ uk:
|
||||
description_category_reassign: "Оберіть категорію"
|
||||
description_message_content: "Зміст повідомлення"
|
||||
description_my_project: "Ви учасник"
|
||||
description_notes: "Нотатки"
|
||||
description_notes: "Примітки"
|
||||
description_parent_work_package: "Батьківський робочий пакет поточного"
|
||||
description_project_scope: "Область пошуку"
|
||||
description_query_sort_criteria_attribute: "Критерій сортування"
|
||||
@@ -2222,9 +2222,9 @@ uk:
|
||||
plan_title: "Доповнення версії Enterprise %{plan}"
|
||||
plan_name: "Версія Enterprise %{plan}"
|
||||
plan_text_html: "Доступно, починаючи з версії %{plan_name}."
|
||||
unlimited: "Unlimited"
|
||||
unlimited: "Без обмежень"
|
||||
already_have_token: >
|
||||
Already have a token? Add it using the button below to upgrade to the booked Enterprise plan.
|
||||
Уже маєте токен? Додайте його за допомогою кнопки нижче, щоб перейти на вже замовлений план Enterprise.
|
||||
hide_banner: "Приховати цей банер"
|
||||
homescreen_description: >
|
||||
Плани Enterprise розширюють можливості вашого випуску OpenProject Community. Вони включають доповнення, додаткові функції та професійну підтримку, що дає організаціям усіх розмірів досягати кращих результатів разом.
|
||||
@@ -2232,15 +2232,15 @@ uk:
|
||||
baseline_comparison:
|
||||
description: Виділяйте зміни, внесені в цей список із будь-якого моменту часу в минулому.
|
||||
benefits:
|
||||
description: "What are the benefits of the Enterprise on-premises edition?"
|
||||
high_security: "Security features"
|
||||
high_security_text: "Single sign on (SAML, OpenID Connect, CAS), LDAP groups."
|
||||
installation: "Installation support"
|
||||
installation_text: "Experienced software engineers guide you through the complete installation and setup process in your own infrastructure."
|
||||
premium_features: "Enterprise add-ons"
|
||||
premium_features_text: "Agile boards, custom theme and logo, graphs, intelligent workflows with custom actions, full text search for work package attachments and multi-select custom fields."
|
||||
professional_support: "Professional support"
|
||||
professional_support_text: "Get reliable, high-touch support from senior support engineers with expert knowledge about running OpenProject in business-critical environments."
|
||||
description: "Які переваги локальної версії Enterprise?"
|
||||
high_security: "Функції безпеки"
|
||||
high_security_text: "Єдиний вхід (SAML, OpenID Connect, CAS), групи LDAP."
|
||||
installation: "Підтримка під час установлення"
|
||||
installation_text: "Досвідчені інженери з програмного забезпечення допоможуть вам виконати повне встановлення й налаштувати процес у вашій інфраструктурі."
|
||||
premium_features: "Доповнення версії Enterprise"
|
||||
premium_features_text: "Дошки Agile, власні тема й логотип, діаграми, розумні робочі процеси з користувацькими діями, повнотекстовий пошук у вкладеннях пакетів робіт і користувацькі поля з множинним вибором."
|
||||
professional_support: "Професійна підтримка"
|
||||
professional_support_text: "Отримайте надійну, комплексну підтримку від старших інженерів, які знають усе про запуск OpenProject у критично важливих для бізнесу середовищах."
|
||||
work_package_subject_generation:
|
||||
description: "Створюйте автоматично згенеровані теми на основі атрибутів і тексту."
|
||||
customize_life_cycle:
|
||||
@@ -2292,22 +2292,22 @@ uk:
|
||||
description: " "
|
||||
teaser:
|
||||
title:
|
||||
one: "One day left of %{trial_plan} trial token"
|
||||
few: "%{count} days left of %{trial_plan} trial token"
|
||||
many: "%{count} days left of %{trial_plan} trial token"
|
||||
other: "%{count} days left of %{trial_plan} trial token"
|
||||
description: "You have access to all %{trial_plan} features."
|
||||
one: "Один день до завершення строку дії пробного токена %{trial_plan}"
|
||||
few: "%{count} дні до завершення строку дії пробного токена %{trial_plan}"
|
||||
many: "%{count} днів до завершення строку дії пробного токена %{trial_plan}"
|
||||
other: "%{count} дня до завершення строку дії пробного токена %{trial_plan}"
|
||||
description: "У вас є доступ до всіх функцій %{trial_plan}."
|
||||
trial:
|
||||
not_found: "You have requested a trial token, but that request is no longer available. Please try again."
|
||||
wait_for_confirmation: "We sent you an email to confirm your address in order to retrieve a trial token."
|
||||
not_found: "Ви запитали пробний токен, але цей запит більше не доступний. Спробуйте ще раз."
|
||||
wait_for_confirmation: "Ми надіслали вам електронного листа, щоб ви підтвердили свою адресу й могли отримати пробний токен."
|
||||
already_retrieved: >
|
||||
Your trial enterprise token was already retrieved. Please check your emails for the token being attached. Please reach out to our support team if you need a new one.
|
||||
successfully_saved: "Your trial enterprise token has been successfully retrieved."
|
||||
token_sent: "Trial token requested"
|
||||
request_again: "Request again"
|
||||
resend_action: "Resend confirmation email"
|
||||
welcome_title: "Quick feature overview"
|
||||
welcome_description: "Get a quick overview of project management and team collaboration with OpenProject Enterprise edition."
|
||||
Токен пробної версії Enterprise уже отримано. На вашу електронну пошту мав надійти лист із вкладеним токеном. Якщо вам потрібен новий токен, зверніться до нашої служби підтримки.
|
||||
successfully_saved: "Токен пробної версії Enterprise успішно отримано."
|
||||
token_sent: "Запитано пробний токен"
|
||||
request_again: "Надіслати запит ще раз"
|
||||
resend_action: "Ще раз надіслати електронного листа для підтвердження"
|
||||
welcome_title: "Стислий огляд функцій"
|
||||
welcome_description: "Отримайте стислий огляд функцій для управління проєктами й командної роботи у версії OpenProject Enterprise."
|
||||
confirmation_info: >
|
||||
We sent you an email on %{date} to %{email} with all the information to start the free trial of OpenProject Enterprise. Please check your inbox and click the confirmation link provided to start your 14-day free trial.
|
||||
confirmation_subline: >
|
||||
@@ -2346,7 +2346,7 @@ uk:
|
||||
error_enterprise_token_invalid_domain: "Версія Enterprise не активна. Домен вашого маркера Enterprise (%{actual}) не збігається з іменем хосту системи (%{expected})."
|
||||
error_failed_to_delete_entry: "Не вдалося видалити цей запис."
|
||||
error_in_dependent: "Помилка спроби змінити залежний об'єкт: %{dependent_class} %{related_id} - %{related_subject}: %{error}"
|
||||
error_in_new_dependent: "Не вдалося змінити залежний об’єкт: %{dependent_class} – %{related_subject}: %{error}"
|
||||
error_in_new_dependent: "Не вдалося створити залежний об’єкт: %{dependent_class} – %{related_subject}: %{error}"
|
||||
error_invalid_selected_value: "Недійсне вибране значення."
|
||||
error_journal_attribute_not_present: "Журнал не містить атрибута %{attribute}."
|
||||
error_pdf_export_too_many_columns: "Для експорту PDF-файлу вибрано занадто багато стовпців. Скоротіть кількість стовпців."
|
||||
@@ -2690,7 +2690,7 @@ uk:
|
||||
label_active: "Активні"
|
||||
label_activate_user: "Активні користувачі"
|
||||
label_active_in_new_projects: "Активна участь в нових проектах"
|
||||
label_activity: "Дія"
|
||||
label_activity: "Активність"
|
||||
label_add_edit_translations: "Додайте та редагуйте переклади"
|
||||
label_add_another_file: "Додати ще один файл"
|
||||
label_add_columns: "Додати вибрані стовпці"
|
||||
@@ -3310,7 +3310,7 @@ uk:
|
||||
label_work_package_attachments: "Додатки до робочого пакету"
|
||||
label_work_package_category_new: "Нова категорія"
|
||||
label_work_package_category_plural: "Категорії робочих пакетів"
|
||||
label_work_package_comments: "Work package comments"
|
||||
label_work_package_comments: "Коментарі до пакета робіт"
|
||||
label_work_package_hierarchy: "Ієрархія робочих пакетів"
|
||||
label_work_package_new: "Новий пакет робіт"
|
||||
label_work_package_edit: "Редагувати робочий пакет %{name}"
|
||||
@@ -3633,7 +3633,7 @@ uk:
|
||||
text_getting_started_description: "Отримайте стислий огляд управління проєктами та командної роботи в OpenProject. Це відео можна переглянути знову в меню довідки."
|
||||
welcome: "Вітаємо в додатку %{app_title}"
|
||||
select_language: "Будь ласка, виберіть мову"
|
||||
permission_add_work_package_comments: "Додати коментарі"
|
||||
permission_add_work_package_comments: "Додавання коментарів"
|
||||
permission_add_work_packages: "Додати робочі пакети"
|
||||
permission_add_messages: "Відправка повідомлень"
|
||||
permission_add_project: "Створення проєктів"
|
||||
@@ -3664,12 +3664,12 @@ uk:
|
||||
permission_delete_timelines: "Видалити графіки"
|
||||
permission_delete_wiki_pages: "Видалення wiki-сторінок"
|
||||
permission_delete_wiki_pages_attachments: "Видалення прикріплених файлів"
|
||||
permission_edit_work_package_comments: "Модерувати коментарі"
|
||||
permission_edit_work_package_comments: "Модерування коментарів"
|
||||
permission_edit_work_package_comments_explanation: "Увага! Користувачі із цим дозволом можуть редагувати будь-чиї коментарі."
|
||||
permission_edit_work_packages: "Редагувати робочі пакети"
|
||||
permission_edit_messages: "Редагування повідомлень"
|
||||
permission_edit_own_internal_comments: "Редагування власних внутрішніх коментарів"
|
||||
permission_edit_own_work_package_comments: "Редагувати власні коментарі"
|
||||
permission_edit_own_work_package_comments: "Редагування власних коментарів"
|
||||
permission_edit_own_messages: "Редагування власних повідомлень"
|
||||
permission_edit_own_time_entries: "Редагування власного обліку часу"
|
||||
permission_edit_others_internal_comments: "Модерування внутрішніх коментарів"
|
||||
@@ -3731,7 +3731,7 @@ uk:
|
||||
permission_view_wiki_pages: "Перегляд wiki"
|
||||
permission_work_package_assigned: "Стати виконавцем / відповідальним"
|
||||
permission_work_package_assigned_explanation: "Робочі пакети можуть бути призначені користувачам і групам, які володіють цією роллю у відповідному проекті"
|
||||
permission_view_project_activity: "Перегляд активності в проєкті"
|
||||
permission_view_project_activity: "Перегляд дій у проєкті"
|
||||
permission_view_project_attributes: "Переглянути атрибути проєкту"
|
||||
permission_view_project_phases: "Перегляд етапів проєкту"
|
||||
permission_save_bcf_queries: "Зберігання запитів BCF"
|
||||
@@ -3773,7 +3773,7 @@ uk:
|
||||
use_template: "Використовувати шаблон"
|
||||
make_template: "Установити як шаблон"
|
||||
remove_from_templates: "Вилучити із шаблонів"
|
||||
project_module_activity: "Дія"
|
||||
project_module_activity: "Активність"
|
||||
project_module_forums: "Форуми"
|
||||
project_module_work_package_tracking: "Пакети робіт"
|
||||
project_module_news: "Новини"
|
||||
|
||||
+8
-12
@@ -137,29 +137,25 @@ Rails.application.routes.draw do
|
||||
|
||||
get "/roles/workflow/:id/:role_id/:type_id" => "roles#workflow"
|
||||
|
||||
resources :types do
|
||||
resource :form_configuration, only: %i[edit update], controller: "work_package_types/form_configuration_tab"
|
||||
resource :projects, controller: "work_package_types/projects_tab", only: %i[update edit] do
|
||||
resources :types, module: "work_package_types", except: [:update] do
|
||||
resource :form_configuration, only: %i[edit update], controller: "form_configuration_tab"
|
||||
resource :projects, controller: "projects_tab", only: %i[update edit] do
|
||||
collection do
|
||||
post :enable_all, to: "work_package_types/projects_tab#enable_all_projects"
|
||||
post :enable_all, to: "projects_tab#enable_all_projects"
|
||||
end
|
||||
end
|
||||
resource :settings, controller: "work_package_types/settings_tab", only: %i[update edit]
|
||||
resource :subject_configuration, controller: "work_package_types/subject_configuration_tab", only: %i[update edit]
|
||||
|
||||
member do
|
||||
get "edit/:tab" => "types#edit", as: "edit_tab"
|
||||
match "update/:tab" => "types#update", as: "update_tab", via: %i[post patch]
|
||||
end
|
||||
resource :settings, controller: "settings_tab", only: %i[update edit]
|
||||
resource :subject_configuration, controller: "subject_configuration_tab", only: %i[update edit]
|
||||
|
||||
resources :pdf_export_template, only: %i[],
|
||||
controller: "work_package_types/pdf_export_template",
|
||||
controller: "pdf_export_template",
|
||||
path: "pdf_export_template" do
|
||||
member do
|
||||
post :toggle
|
||||
put :drop
|
||||
end
|
||||
collection do
|
||||
get :edit
|
||||
put :enable_all
|
||||
put :disable_all
|
||||
end
|
||||
|
||||
@@ -20,7 +20,6 @@ get:
|
||||
|
||||
+ id: Sort by the version id
|
||||
+ name: Sort by the version name using numeric collation, comparing sequences of decimal digits by their numeric value
|
||||
+ semver_name: Deprecated, use name instead
|
||||
example: '[["name", "desc"]]'
|
||||
in: query
|
||||
name: sortBy
|
||||
|
||||
@@ -19,6 +19,21 @@ All controllers live under `frontend/src/stimulus/controllers/`. The naming conv
|
||||
|
||||
If you want to add a common pattern, manually register the controller under `frontend/src/stimulus/setup.ts`. Often you'll want to have a dynamically loaded controller instead though.
|
||||
|
||||
### Adding a static controller from a plugin
|
||||
|
||||
If you want to add a stimulus controller from plugin code, you can do so by manually adding it to the preregister:
|
||||
|
||||
```typescript
|
||||
import { OpenProjectStimulusApplication } from 'core-app/stimulus/app';
|
||||
import { MyTestControllerClass } from './test/foo/my-test.controller';
|
||||
|
||||
|
||||
OpenProjectStimulusApplication.preregister(
|
||||
'test',
|
||||
MyTestControllerClass
|
||||
);
|
||||
```
|
||||
|
||||
### Dynamically loaded controllers
|
||||
|
||||
To dynamically load a controller, it needs to live under `frontend/src/stimulus/controllers/dynamic/<controller-name>.controller.ts`.
|
||||
@@ -41,6 +56,21 @@ If you want to organize your dynamic controllers in a subfolder, use the [double
|
||||
|
||||
You need to take care to prefix all actions, values etc. with the exact same pattern, e.g., `data-admin--settings-target="foobar"`.
|
||||
|
||||
#### Dynamically loading controllers from plugins
|
||||
|
||||
If you want to add a dynamic stimulus controller import from plugin code, you can do so by manually adding it to the preregister:
|
||||
|
||||
```typescript
|
||||
import { OpenProjectStimulusApplication } from 'core-app/stimulus/app';
|
||||
|
||||
OpenProjectStimulusApplication.preregisterDynamic(
|
||||
'test',
|
||||
() => import('./test.controller')
|
||||
);
|
||||
```
|
||||
|
||||
This ensures that the controller is loaded only when it is needed, and not at application startup. The controller will then be engaded when the `data-controller` attribute is present in the DOM through the same mechanism as for the core dynamic controllers.
|
||||
|
||||
### Requiring a page controller
|
||||
|
||||
If you have a single controller used in a partial, we have added a helper to use in a partial in order to append a controller to the `#content-wrapper` tag. This is useful if your template doesn't have a single DOM root. For example, to load the dynamic `project-storage-form` controller and provide a custom value to it:
|
||||
|
||||
@@ -49,7 +49,7 @@ You can configure the following options for webhooks:
|
||||
3. Freely choose an additional **description** to further identify the intent of the respective webhook.
|
||||
4. By defining a **Signature secret** you guarantee that the sender of the payload request is actually OpenProject. The client will then check this signature secret.
|
||||
5. **Enable** if **the webhook** should be active.
|
||||
6. **Set the events** for which the webhook should be activate, i.e. webhook for updating or creating projects or work packages, or for creating time entries.
|
||||
6. **Set the events** for which the webhook should be activate, i.e. webhook for updating or creating projects, work packages, work package comments, time entries and attachments.
|
||||
7. **Select for which projects the webhook should be active**. You can choose all projects or only specific projects. For example if you select the project "System admin guide", an event (ie. create a new time entry) will be fired via the webhook. This will only happen if a user logs time within the selected projects.
|
||||
8. Press the green **Create** button to save your changes (you may nee to scroll down to find it). There you can also cancel your input.
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 164 KiB After Width: | Height: | Size: 267 KiB |
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 452 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 92 KiB |
@@ -1,3 +1,6 @@
|
||||
<op-breadcrumbs
|
||||
[items]="breadcrumbItems()"
|
||||
></op-breadcrumbs>
|
||||
<div class="toolbar-container">
|
||||
<div class="toolbar">
|
||||
<div class="title-container">
|
||||
|
||||
@@ -41,6 +41,8 @@ import { GlobalSearchService } from 'core-app/core/global_search/services/global
|
||||
import { CurrentProjectService } from 'core-app/core/current-project/current-project.service';
|
||||
import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator';
|
||||
import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin';
|
||||
import { PathHelperService } from 'core-app/core/path-helper/path-helper.service';
|
||||
import { OpTitleService } from 'core-app/core/html/op-title.service';
|
||||
|
||||
@Component({
|
||||
selector: 'opce-global-search-title',
|
||||
@@ -71,6 +73,8 @@ export class GlobalSearchTitleComponent extends UntilDestroyedMixin implements O
|
||||
readonly globalSearchService:GlobalSearchService,
|
||||
readonly I18n:I18nService,
|
||||
readonly injector:Injector,
|
||||
protected readonly pathHelper:PathHelperService,
|
||||
protected readonly titleService:OpTitleService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
@@ -95,6 +99,23 @@ export class GlobalSearchTitleComponent extends UntilDestroyedMixin implements O
|
||||
});
|
||||
}
|
||||
|
||||
breadcrumbItems() {
|
||||
const items = [];
|
||||
items.push({
|
||||
href: this.pathHelper.homePath(),
|
||||
text: this.titleService.appTitle,
|
||||
});
|
||||
if (this.currentProjectService?.identifier) {
|
||||
items.push({
|
||||
href: this.pathHelper.projectPath(this.currentProjectService.identifier),
|
||||
text: this.currentProjectService.name,
|
||||
});
|
||||
}
|
||||
items.push(this.searchTitle);
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
private projectText(scope:string):string {
|
||||
const currentProjectName = this.currentProjectService.name ? this.currentProjectService.name : '';
|
||||
|
||||
|
||||
+25
@@ -37,4 +37,29 @@ import { WorkPackageCopyController } from 'core-app/features/work-packages/compo
|
||||
})
|
||||
export class WorkPackageCopyFullViewComponent extends WorkPackageCopyController {
|
||||
public successState = 'work-packages.show';
|
||||
|
||||
breadcrumbItems() {
|
||||
const items = [];
|
||||
items.push({
|
||||
href: this.pathHelper.homePath(),
|
||||
text: this.titleService.appTitle,
|
||||
});
|
||||
if (this.currentProjectService?.identifier) {
|
||||
items.push({
|
||||
href: this.pathHelper.projectPath(this.currentProjectService.identifier),
|
||||
text: this.currentProjectService.name,
|
||||
});
|
||||
}
|
||||
items.push({
|
||||
href: this.pathHelper.workPackagesPath(this.currentProjectService.identifier as string),
|
||||
text: this.I18n.t('js.label_work_package_plural'),
|
||||
});
|
||||
items.push({
|
||||
href: this.pathHelper.projectWorkPackagePath(this.currentProjectService.identifier as string, this.stateParams.copiedFromWorkPackageId as string),
|
||||
text: this.newWorkPackage.subject,
|
||||
});
|
||||
items.push(I18n.t('js.button_duplicate'));
|
||||
|
||||
return items;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -1,4 +1,3 @@
|
||||
import { KeyCodes } from 'core-app/shared/helpers/keyCodes.enum';
|
||||
import { TableEventComponent } from 'core-app/features/work-packages/components/wp-fast-table/handlers/table-handler-registry';
|
||||
import { WorkPackageTable } from '../wp-fast-table';
|
||||
|
||||
@@ -6,7 +5,7 @@ import { WorkPackageTable } from '../wp-fast-table';
|
||||
* Execute the callback if the given JQuery Event is either an ENTER key or a click
|
||||
*/
|
||||
export function onClickOrEnter(evt:JQuery.TriggeredEvent, callback:() => void) {
|
||||
if (evt.type === 'click' || (evt.type === 'keydown' && evt.which === KeyCodes.ENTER)) {
|
||||
if (evt.type === 'click' || (evt.type === 'keydown' && evt.key === 'Enter')) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -1,5 +1,4 @@
|
||||
import { Injector } from '@angular/core';
|
||||
import { KeyCodes } from 'core-app/shared/helpers/keyCodes.enum';
|
||||
import { TableEventComponent } from 'core-app/features/work-packages/components/wp-fast-table/handlers/table-handler-registry';
|
||||
import { ContextMenuHandler } from './context-menu-handler';
|
||||
|
||||
@@ -23,7 +22,7 @@ export class ContextMenuKeyboardHandler extends ContextMenuHandler {
|
||||
|
||||
const target = jQuery(evt.target);
|
||||
|
||||
if (!(evt.keyCode === KeyCodes.F10 && evt.shiftKey && evt.altKey)) {
|
||||
if (!(evt.key === 'F10' && evt.shiftKey && evt.altKey)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ import { OpTitleService } from 'core-app/core/html/op-title.service';
|
||||
import { WorkPackageCreateService } from './wp-create.service';
|
||||
import { HalError } from 'core-app/features/hal/services/hal-error';
|
||||
import idFromLink from 'core-app/features/hal/helpers/id-from-link';
|
||||
import { CurrentProjectService } from 'core-app/core/current-project/current-project.service';
|
||||
|
||||
@Directive()
|
||||
export class WorkPackageCreateComponent extends UntilDestroyedMixin implements OnInit {
|
||||
@@ -97,7 +98,9 @@ export class WorkPackageCreateComponent extends UntilDestroyedMixin implements O
|
||||
protected readonly wpTableFilters:WorkPackageViewFiltersService,
|
||||
protected readonly pathHelper:PathHelperService,
|
||||
protected readonly apiV3Service:ApiV3Service,
|
||||
protected readonly cdRef:ChangeDetectorRef) {
|
||||
protected readonly currentProjectService:CurrentProjectService,
|
||||
protected readonly cdRef:ChangeDetectorRef,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
|
||||
+21
@@ -37,4 +37,25 @@ import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
})
|
||||
export class WorkPackageNewFullViewComponent extends WorkPackageCreateComponent {
|
||||
public successState = this.$state.current.data.successState as string;
|
||||
|
||||
breadcrumbItems() {
|
||||
const items = [];
|
||||
items.push({
|
||||
href: this.pathHelper.homePath(),
|
||||
text: this.titleService.appTitle,
|
||||
});
|
||||
if (this.currentProjectService?.identifier) {
|
||||
items.push({
|
||||
href: this.pathHelper.projectPath(this.currentProjectService.identifier),
|
||||
text: this.currentProjectService.name,
|
||||
});
|
||||
}
|
||||
items.push({
|
||||
href: this.pathHelper.workPackagesPath(this.currentProjectService.identifier as string),
|
||||
text: this.I18n.t('js.label_work_package_plural'),
|
||||
});
|
||||
items.push(I18n.t('js.label_create_work_package'));
|
||||
|
||||
return items;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<div
|
||||
class="work-package--new-state work-packages--new work-packages--show-view toolbar-container"
|
||||
*ngIf="newWorkPackage">
|
||||
<op-breadcrumbs
|
||||
[items]="breadcrumbItems()"
|
||||
></op-breadcrumbs>
|
||||
<edit-form [resource]="newWorkPackage"
|
||||
[skippedFields]="['status', 'type']"
|
||||
[inEditMode]="true"
|
||||
|
||||
+1
-1
@@ -122,7 +122,7 @@ export class WorkPackageRelationRowComponent extends UntilDestroyedMixin impleme
|
||||
}
|
||||
|
||||
public handleDescriptionKey($event:JQuery.TriggeredEvent) {
|
||||
if ($event.which === 27) {
|
||||
if ($event.key === 'Escape') {
|
||||
this.cancelDescriptionEdit();
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -29,7 +29,6 @@
|
||||
import { Injector } from '@angular/core';
|
||||
import * as moment from 'moment';
|
||||
import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space';
|
||||
import { KeyCodes } from 'core-app/shared/helpers/keyCodes.enum';
|
||||
import { LoadingIndicatorService } from 'core-app/core/loading-indicator/loading-indicator.service';
|
||||
|
||||
import { HalResourceEditingService } from 'core-app/shared/components/fields/edit/services/hal-resource-editing.service';
|
||||
@@ -128,7 +127,7 @@ export function registerWorkPackageMouseHandler(this:void,
|
||||
|
||||
function keyPressFn(ev:JQuery.TriggeredEvent) {
|
||||
const kev:KeyboardEvent = ev.originalEvent as KeyboardEvent;
|
||||
if (kev.keyCode === KeyCodes.ESCAPE) {
|
||||
if (kev.key === 'Escape') {
|
||||
deactivate(null, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ export type BreadcrumbItem =
|
||||
})
|
||||
export class OpBreadcrumbsComponent {
|
||||
@Input() items:BreadcrumbItem[] = [];
|
||||
@Input() lastItemSection:string | null = null;
|
||||
@Input() lastItemSection?:string | null = null;
|
||||
|
||||
constructor(
|
||||
readonly I18n:I18nService,
|
||||
|
||||
+3
-3
@@ -36,7 +36,7 @@ import {
|
||||
ICKEditorWatchdog,
|
||||
} from 'core-app/shared/components/editor/components/ckeditor/ckeditor.types';
|
||||
import { CKEditorSetupService } from 'core-app/shared/components/editor/components/ckeditor/ckeditor-setup.service';
|
||||
import { KeyCodes } from 'core-app/shared/helpers/keyCodes.enum';
|
||||
import { KeyCodes } from 'core-app/shared/helpers/keycodes';
|
||||
import { debugLog } from 'core-app/shared/helpers/debug_output';
|
||||
import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin';
|
||||
|
||||
@@ -271,13 +271,13 @@ export class OpCkeditorComponent extends UntilDestroyedMixin implements OnInit,
|
||||
editor.editing.view.document,
|
||||
'keydown',
|
||||
(evt, data) => {
|
||||
if ((data.ctrlKey || data.metaKey) && data.keyCode === Number(KeyCodes.ENTER)) {
|
||||
if ((data.ctrlKey || data.metaKey) && data.keyCode === KeyCodes.ENTER) {
|
||||
debugLog('Sending save request from CKEditor.');
|
||||
this.saveRequested.emit();
|
||||
evt.stop();
|
||||
}
|
||||
|
||||
if (data.keyCode === Number(KeyCodes.ESCAPE)) {
|
||||
if (data.keyCode === KeyCodes.ESCAPE) {
|
||||
this.editorEscape.emit();
|
||||
evt.stop();
|
||||
}
|
||||
|
||||
+2
-3
@@ -26,7 +26,6 @@
|
||||
// See COPYRIGHT and LICENSE files for more details.
|
||||
//++
|
||||
|
||||
import { KeyCodes } from 'core-app/shared/helpers/keyCodes.enum';
|
||||
import { I18nService } from 'core-app/core/i18n/i18n.service';
|
||||
import { ConfigurationService } from 'core-app/core/config/configuration.service';
|
||||
import { Injector } from '@angular/core';
|
||||
@@ -139,7 +138,7 @@ export class HalResourceEditFieldHandler extends EditFieldHandler {
|
||||
public async handleUserKeydown(event:JQuery.TriggeredEvent, onlyCancel = false) {
|
||||
// Only handle submission in edit mode
|
||||
if (this.inEditMode && !onlyCancel) {
|
||||
if (event.which === KeyCodes.ENTER) {
|
||||
if (event.key === 'Enter') {
|
||||
await this.form.submit();
|
||||
return false;
|
||||
}
|
||||
@@ -147,7 +146,7 @@ export class HalResourceEditFieldHandler extends EditFieldHandler {
|
||||
}
|
||||
|
||||
// Escape editing when not in edit mode
|
||||
if (event.which === KeyCodes.ESCAPE) {
|
||||
if (event.key === 'Escape') {
|
||||
this.handleUserCancel();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
OpContextMenuLocalsToken,
|
||||
} from 'core-app/shared/components/op-context-menu/op-context-menu.types';
|
||||
import { OPContextMenuComponent } from 'core-app/shared/components/op-context-menu/op-context-menu.component';
|
||||
import { KeyCodes } from 'core-app/shared/helpers/keyCodes.enum';
|
||||
import { FocusHelperService } from 'core-app/shared/directives/focus/focus-helper';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
@@ -52,7 +51,7 @@ export class OPContextMenuService {
|
||||
|
||||
// Listen to keyups on window to close context menus
|
||||
jQuery(window).on('keydown', (evt:JQuery.TriggeredEvent) => {
|
||||
if (this.active && evt.which === KeyCodes.ESCAPE) {
|
||||
if (this.active && evt.key === 'Escape') {
|
||||
this.close(true);
|
||||
}
|
||||
|
||||
|
||||
+4
-5
@@ -11,7 +11,6 @@ import { IHALCollection } from 'core-app/core/apiv3/types/hal-collection.type';
|
||||
import { finalize, take } from 'rxjs/operators';
|
||||
import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { KeyCodes } from 'core-app/shared/helpers/keyCodes.enum';
|
||||
import { ID } from '@datorama/akita';
|
||||
import { IProjectData } from './project-data';
|
||||
import { CurrentProjectService } from 'core-app/core/current-project/current-project.service';
|
||||
@@ -85,16 +84,16 @@ export class SearchableProjectListService {
|
||||
this.selectedItemID$
|
||||
.pipe(take(1))
|
||||
.subscribe((activeID) => {
|
||||
switch (event.keyCode) {
|
||||
case KeyCodes.UP_ARROW:
|
||||
switch (event.key) {
|
||||
case 'ArrowUp':
|
||||
event.preventDefault();
|
||||
this.selectPreviousResult(activeID, projects);
|
||||
break;
|
||||
case KeyCodes.DOWN_ARROW:
|
||||
case 'ArrowDown':
|
||||
event.preventDefault();
|
||||
this.selectNextResult(activeID, projects);
|
||||
break;
|
||||
case KeyCodes.ENTER:
|
||||
case 'Enter':
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
this.activateSelectedResult(event);
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
export enum KeyCodes {
|
||||
BACKSPACE = 8,
|
||||
TAB = 9,
|
||||
ENTER = 13,
|
||||
SHIFT = 16,
|
||||
CTRL = 17,
|
||||
ALT = 18,
|
||||
PAUSE = 19,
|
||||
CAPS_LOCK = 20,
|
||||
ESCAPE = 27,
|
||||
SPACE = 32,
|
||||
PAGE_UP = 33,
|
||||
PAGE_DOWN = 34,
|
||||
END = 35,
|
||||
HOME = 36,
|
||||
LEFT_ARROW = 37,
|
||||
UP_ARROW = 38,
|
||||
RIGHT_ARROW = 39,
|
||||
DOWN_ARROW = 40,
|
||||
INSERT = 45,
|
||||
DELETE = 46,
|
||||
KEY_0 = 48,
|
||||
KEY_1 = 49,
|
||||
KEY_2 = 50,
|
||||
KEY_3 = 51,
|
||||
KEY_4 = 52,
|
||||
KEY_5 = 53,
|
||||
KEY_6 = 54,
|
||||
KEY_7 = 55,
|
||||
KEY_8 = 56,
|
||||
KEY_9 = 57,
|
||||
KEY_A = 65,
|
||||
KEY_B = 66,
|
||||
KEY_C = 67,
|
||||
KEY_D = 68,
|
||||
KEY_E = 69,
|
||||
KEY_F = 70,
|
||||
KEY_G = 71,
|
||||
KEY_H = 72,
|
||||
KEY_I = 73,
|
||||
KEY_J = 74,
|
||||
KEY_K = 75,
|
||||
KEY_L = 76,
|
||||
KEY_M = 77,
|
||||
KEY_N = 78,
|
||||
KEY_O = 79,
|
||||
KEY_P = 80,
|
||||
KEY_Q = 81,
|
||||
KEY_R = 82,
|
||||
KEY_S = 83,
|
||||
KEY_T = 84,
|
||||
KEY_U = 85,
|
||||
KEY_V = 86,
|
||||
KEY_W = 87,
|
||||
KEY_X = 88,
|
||||
KEY_Y = 89,
|
||||
KEY_Z = 90,
|
||||
LEFT_META = 91,
|
||||
RIGHT_META = 92,
|
||||
SELECT = 93,
|
||||
NUMPAD_0 = 96,
|
||||
NUMPAD_1 = 97,
|
||||
NUMPAD_2 = 98,
|
||||
NUMPAD_3 = 99,
|
||||
NUMPAD_4 = 100,
|
||||
NUMPAD_5 = 101,
|
||||
NUMPAD_6 = 102,
|
||||
NUMPAD_7 = 103,
|
||||
NUMPAD_8 = 104,
|
||||
NUMPAD_9 = 105,
|
||||
MULTIPLY = 106,
|
||||
ADD = 107,
|
||||
SUBTRACT = 109,
|
||||
DECIMAL = 110,
|
||||
DIVIDE = 111,
|
||||
F1 = 112,
|
||||
F2 = 113,
|
||||
F3 = 114,
|
||||
F4 = 115,
|
||||
F5 = 116,
|
||||
F6 = 117,
|
||||
F7 = 118,
|
||||
F8 = 119,
|
||||
F9 = 120,
|
||||
F10 = 121,
|
||||
F11 = 122,
|
||||
F12 = 123,
|
||||
NUM_LOCK = 144,
|
||||
SCROLL_LOCK = 145,
|
||||
SEMICOLON = 186,
|
||||
EQUALS = 187,
|
||||
COMMA = 188,
|
||||
DASH = 189,
|
||||
PERIOD = 190,
|
||||
FORWARD_SLASH = 191,
|
||||
GRAVE_ACCENT = 192,
|
||||
OPEN_BRACKET = 219,
|
||||
BACK_SLASH = 220,
|
||||
CLOSE_BRACKET = 221,
|
||||
SINGLE_QUOTE = 222,
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export const KeyCodes = {
|
||||
ENTER: 13,
|
||||
ESCAPE: 27,
|
||||
};
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
TemplateRef,
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import { KeyCodes } from 'core-app/shared/helpers/keyCodes.enum';
|
||||
import { I18nService } from 'core-app/core/i18n/i18n.service';
|
||||
import { findAllFocusableElementsWithin } from 'core-app/shared/helpers/focus-helpers';
|
||||
import { SpotDropModalTeleportationService } from './drop-modal-teleportation.service';
|
||||
@@ -218,7 +217,7 @@ export class SpotDropModalComponent implements OnDestroy {
|
||||
}
|
||||
|
||||
private escapeCallback = (evt:KeyboardEvent) => {
|
||||
if (evt.keyCode === KeyCodes.ESCAPE) {
|
||||
if (evt.key === 'Escape') {
|
||||
this.close();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
min-width: 25vw
|
||||
|
||||
pre
|
||||
background: #f1f1f1
|
||||
padding: 5px
|
||||
|
||||
|
||||
|
||||
@@ -53,10 +53,9 @@ RB.EditableInplace = (function ($) {
|
||||
j = $(this).parents('.model').first();
|
||||
that = j.data('this');
|
||||
|
||||
// 13 is the key code of Enter, 27 of ESC.
|
||||
if (e.which === 13) {
|
||||
if (e.key === 'Enter') {
|
||||
that.saveEdits();
|
||||
} else if (e.which === 27) {
|
||||
} else if (e.key === 'Escape') {
|
||||
that.cancelEdit();
|
||||
} else {
|
||||
return true;
|
||||
|
||||
@@ -121,7 +121,7 @@ RB.Model = (function ($) {
|
||||
},
|
||||
],
|
||||
close: function (e, ui) {
|
||||
if (e.which === 1 || e.which === 27) {
|
||||
if (e.type === 'click' || (e.type === 'keydown' && e.key === 'Escape')) {
|
||||
self.cancelEdit();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -49,7 +49,7 @@ export default class MenusSubtreeController extends Controller {
|
||||
|
||||
public toggle(event:MouseEvent|KeyboardEvent):void {
|
||||
// ignore the event if a key different from ENTER was pressed.
|
||||
if (event.type === 'keydown' && event.which !== 13) {
|
||||
if (event.type === 'keydown' && (event as KeyboardEvent).key !== 'Enter') {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import { PathHelperService } from 'core-app/core/path-helper/path-helper.service
|
||||
import moment from 'moment';
|
||||
import allLocales from '@fullcalendar/core/locales-all';
|
||||
import { renderStreamMessage } from '@hotwired/turbo';
|
||||
import { opStopwatchStopIconData, toDOMString } from '@openproject/octicons-angular';
|
||||
|
||||
export default class MyTimeTrackingController extends Controller {
|
||||
private turboRequests:TurboRequestsService;
|
||||
@@ -94,19 +95,28 @@ export default class MyTimeTrackingController extends Controller {
|
||||
eventMinHeight: 30,
|
||||
eventMaxStack: 2,
|
||||
nowIndicator: true,
|
||||
slotDuration: '00:15:00',
|
||||
slotLabelInterval: '01:00',
|
||||
businessHours: { daysOfWeek: this.workingDaysValue, startTime: '00:00', endTime: '24:00' },
|
||||
hiddenDays: this.hiddenDays(),
|
||||
firstDay: this.startOfWeekValue,
|
||||
eventClassNames(info) {
|
||||
return [
|
||||
const classes = [
|
||||
'calendar-time-entry-event',
|
||||
`__hl_type_${info.event.extendedProps.typeId}`,
|
||||
'__hl_border_top',
|
||||
'ellipsis',
|
||||
];
|
||||
|
||||
if (info.event.extendedProps.ongoing) {
|
||||
classes.push('calendar-time-entry-event-ongoing');
|
||||
}
|
||||
|
||||
return classes;
|
||||
},
|
||||
eventContent: (info) => {
|
||||
let timeDetails = '';
|
||||
let stopTimerButton = '';
|
||||
let duration = info.event.extendedProps.hours as number;
|
||||
|
||||
if (info.isResizing && info.event.start && info.event.end) {
|
||||
@@ -118,10 +128,14 @@ export default class MyTimeTrackingController extends Controller {
|
||||
timeDetails = `<div class="fc-event-times" title="${time}">${time}</div>`;
|
||||
}
|
||||
|
||||
if (info.event.extendedProps.ongoing) {
|
||||
stopTimerButton = toDOMString(opStopwatchStopIconData, 'small', { 'aria-hidden': 'true', class: 'octicon stop-timer-button' });
|
||||
}
|
||||
|
||||
return {
|
||||
html: `
|
||||
<div class="fc-event-main-frame">
|
||||
<div class="fc-event-time">${this.displayDuration(duration)}</div>
|
||||
<div class="fc-event-time">${stopTimerButton} ${this.displayDuration(duration)}</div>
|
||||
<div class="fc-event-title-container">
|
||||
<div class="fc-event-title fc-event-wp" title="${info.event.extendedProps.workPackageSubject}">
|
||||
<a class="Link--primary Link" href="${this.pathHelper.workPackageShortPath(info.event.extendedProps.workPackageId as string)}">
|
||||
@@ -188,6 +202,10 @@ export default class MyTimeTrackingController extends Controller {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (draggedEvent?.extendedProps.ongoing) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
@@ -210,6 +228,9 @@ export default class MyTimeTrackingController extends Controller {
|
||||
);
|
||||
}
|
||||
|
||||
// mark the event explicitly as resizable if it is not an all day event
|
||||
info.event.setProp('durationEditable', !info.event.allDay);
|
||||
|
||||
this.calendar.setOption('defaultTimedEventDuration', this.DEFAULT_TIMED_EVENT_DURATION);
|
||||
},
|
||||
eventClick: (info) => {
|
||||
|
||||
@@ -171,6 +171,12 @@ export default class TimeEntryController extends Controller {
|
||||
// Parse input through our chronic duration parser and then reformat as hours that can be nicely parsed on the
|
||||
// backend
|
||||
const hours = this.parsedHourInput();
|
||||
|
||||
if (hours === 0) {
|
||||
this.hoursInputTarget.value = '';
|
||||
return;
|
||||
}
|
||||
|
||||
this.hoursInputTarget.value = outputChronicDuration(hours, { format: 'hours_only' }) || '';
|
||||
|
||||
this.datesChanged(this.hoursInputTarget);
|
||||
|
||||
+41
-52
@@ -86,22 +86,14 @@ export default class IndexController extends Controller {
|
||||
declare showConflictFlashMessageUrlValue:string;
|
||||
declare unsavedChangesConfirmationMessageValue:string;
|
||||
|
||||
private handleWorkPackageUpdateBound:EventListener;
|
||||
private handleVisibilityChangeBound:EventListener;
|
||||
private rescueEditorContentBound:EventListener;
|
||||
private handleTurboSubmitStartBound:EventListener;
|
||||
private handleTurboSubmitEndBound:EventListener;
|
||||
|
||||
private onEscapeEditorBound:EventListener;
|
||||
private adjustMarginBound:EventListener;
|
||||
private onBlurEditorBound:EventListener;
|
||||
private onFocusEditorBound:EventListener;
|
||||
|
||||
private updateInProgress:boolean;
|
||||
private turboRequests:TurboRequestsService;
|
||||
|
||||
private apiV3Service:ApiV3Service;
|
||||
|
||||
private abortController = new AbortController();
|
||||
private ckEditorAbortController = new AbortController();
|
||||
|
||||
async connect() {
|
||||
const context = await window.OpenProject.getPluginContext();
|
||||
this.turboRequests = context.services.turboRequests;
|
||||
@@ -123,6 +115,7 @@ export default class IndexController extends Controller {
|
||||
disconnect() {
|
||||
this.rescueEditorContent();
|
||||
this.removeEventListeners();
|
||||
this.removeCkEditorEventListeners();
|
||||
this.stopPolling();
|
||||
this.markAsDisconnected();
|
||||
}
|
||||
@@ -145,30 +138,28 @@ export default class IndexController extends Controller {
|
||||
}
|
||||
|
||||
private setupEventListeners() {
|
||||
this.handleWorkPackageUpdateBound = () => { void this.handleWorkPackageUpdate(); };
|
||||
this.handleVisibilityChangeBound = () => { void this.handleVisibilityChange(); };
|
||||
this.rescueEditorContentBound = () => { void this.rescueEditorContent(); };
|
||||
const { signal } = this.abortController;
|
||||
|
||||
this.handleTurboSubmitStartBound = (event:Event) => { void this.handleTurboSubmitStart(event); };
|
||||
this.handleTurboSubmitEndBound = (event:Event) => { void this.handleTurboSubmitEnd(event); };
|
||||
const handlers = {
|
||||
workPackageUpdated: () => { void this.handleWorkPackageUpdate(); },
|
||||
workPackageNotificationsUpdated: () => { void this.handleWorkPackageUpdate(); },
|
||||
visibilityChange: () => { void this.handleVisibilityChange(); },
|
||||
beforeUnload: () => { void this.rescueEditorContent(); },
|
||||
turboSubmitStart: (event:Event) => { void this.handleTurboSubmitStart(event); },
|
||||
turboSubmitEnd: (event:Event) => { void this.handleTurboSubmitEnd(event); },
|
||||
};
|
||||
|
||||
document.addEventListener('work-package-updated', this.handleWorkPackageUpdateBound);
|
||||
document.addEventListener('work-package-notifications-updated', this.handleWorkPackageUpdateBound);
|
||||
document.addEventListener('visibilitychange', this.handleVisibilityChangeBound);
|
||||
document.addEventListener('beforeunload', this.rescueEditorContentBound);
|
||||
document.addEventListener('work-package-updated', handlers.workPackageUpdated, { signal });
|
||||
document.addEventListener('work-package-notifications-updated', handlers.workPackageNotificationsUpdated, { signal });
|
||||
document.addEventListener('visibilitychange', handlers.visibilityChange, { signal });
|
||||
document.addEventListener('beforeunload', handlers.beforeUnload, { signal });
|
||||
|
||||
this.element.addEventListener('turbo:submit-start', this.handleTurboSubmitStartBound);
|
||||
this.element.addEventListener('turbo:submit-end', this.handleTurboSubmitEndBound);
|
||||
this.element.addEventListener('turbo:submit-start', handlers.turboSubmitStart, { signal });
|
||||
this.element.addEventListener('turbo:submit-end', handlers.turboSubmitEnd, { signal });
|
||||
}
|
||||
|
||||
private removeEventListeners() {
|
||||
document.removeEventListener('work-package-updated', this.handleWorkPackageUpdateBound);
|
||||
document.removeEventListener('work-package-notifications-updated', this.handleWorkPackageUpdateBound);
|
||||
document.removeEventListener('visibilitychange', this.handleVisibilityChangeBound);
|
||||
document.removeEventListener('beforeunload', this.rescueEditorContentBound);
|
||||
|
||||
this.element.removeEventListener('turbo:submit-start', this.handleTurboSubmitStartBound);
|
||||
this.element.removeEventListener('turbo:submit-end', this.handleTurboSubmitEndBound);
|
||||
this.abortController.abort();
|
||||
}
|
||||
|
||||
private handleVisibilityChange() {
|
||||
@@ -555,34 +546,32 @@ export default class IndexController extends Controller {
|
||||
}
|
||||
}
|
||||
|
||||
private addEventListenersToCkEditorInstance() {
|
||||
this.onEscapeEditorBound = () => { void this.closeEditor(); };
|
||||
this.adjustMarginBound = () => { void this.adjustJournalContainerMargin(); };
|
||||
this.onBlurEditorBound = () => { void this.onBlurEditor(); };
|
||||
this.onFocusEditorBound = () => {
|
||||
void this.onFocusEditor();
|
||||
if (this.isMobile()) {
|
||||
void this.scrollInputContainerIntoView(200);
|
||||
}
|
||||
private addCkEditorEventListeners() {
|
||||
const { signal } = this.ckEditorAbortController;
|
||||
|
||||
const handlers = {
|
||||
onEscapeEditor: () => { void this.closeEditor(); },
|
||||
adjustMargin: () => { void this.adjustJournalContainerMargin(); },
|
||||
onBlurEditor: () => { void this.onBlurEditor(); },
|
||||
onFocusEditor: () => {
|
||||
void this.onFocusEditor();
|
||||
if (this.isMobile()) { void this.scrollInputContainerIntoView(200); }
|
||||
},
|
||||
};
|
||||
|
||||
const editorElement = this.getCkEditorElement();
|
||||
if (editorElement) {
|
||||
editorElement.addEventListener('editorEscape', this.onEscapeEditorBound);
|
||||
editorElement.addEventListener('editorKeyup', this.adjustMarginBound);
|
||||
editorElement.addEventListener('editorBlur', this.onBlurEditorBound);
|
||||
editorElement.addEventListener('editorFocus', this.onFocusEditorBound);
|
||||
editorElement.addEventListener('editorEscape', handlers.onEscapeEditor, { signal });
|
||||
editorElement.addEventListener('editorKeyup', handlers.adjustMargin, { signal });
|
||||
editorElement.addEventListener('editorBlur', handlers.onBlurEditor, { signal });
|
||||
editorElement.addEventListener('editorFocus', handlers.onFocusEditor, { signal });
|
||||
}
|
||||
}
|
||||
|
||||
private removeEventListenersFromCkEditorInstance() {
|
||||
const editorElement = this.getCkEditorElement();
|
||||
if (editorElement) {
|
||||
editorElement.removeEventListener('editorEscape', this.onEscapeEditorBound);
|
||||
editorElement.removeEventListener('editorKeyup', this.adjustMarginBound);
|
||||
editorElement.removeEventListener('editorBlur', this.onBlurEditorBound);
|
||||
editorElement.removeEventListener('editorFocus', this.onFocusEditorBound);
|
||||
}
|
||||
private removeCkEditorEventListeners() {
|
||||
this.ckEditorAbortController.abort();
|
||||
// Create a new AbortController for future CKEditor events
|
||||
this.ckEditorAbortController = new AbortController();
|
||||
}
|
||||
|
||||
private adjustJournalContainerMargin() {
|
||||
@@ -636,7 +625,7 @@ export default class IndexController extends Controller {
|
||||
this.formRowTarget.classList.remove('d-none');
|
||||
this.journalsContainerTarget?.classList.add('work-packages-activities-tab-index-component--journals-container_with-input-compensation');
|
||||
|
||||
this.addEventListenersToCkEditorInstance();
|
||||
this.addCkEditorEventListeners();
|
||||
|
||||
if (this.isMobile()) {
|
||||
this.focusEditor(0);
|
||||
@@ -670,7 +659,7 @@ export default class IndexController extends Controller {
|
||||
|
||||
hideEditor() {
|
||||
this.clearEditor(); // remove potentially empty lines
|
||||
this.removeEventListenersFromCkEditorInstance();
|
||||
this.removeCkEditorEventListeners();
|
||||
this.buttonRowTarget.classList.remove('d-none');
|
||||
this.formRowTarget.classList.add('d-none');
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { ControllerConstructor } from '@hotwired/stimulus/dist/types/core/contro
|
||||
import { ApplicationController } from 'stimulus-use';
|
||||
import { AttributeObserver } from '@hotwired/stimulus';
|
||||
import { debugLog } from 'core-app/shared/helpers/debug_output';
|
||||
import { OpenProjectStimulusApplication } from 'core-stimulus/openproject-stimulus-application';
|
||||
|
||||
export class OpApplicationController extends ApplicationController {
|
||||
private loaded = new Set<string>();
|
||||
@@ -30,19 +31,43 @@ export class OpApplicationController extends ApplicationController {
|
||||
const registered = this.application.router.modules.map((module) => module.definition.identifier);
|
||||
|
||||
controllers.forEach((controller) => {
|
||||
const path = this.derivePath(controller);
|
||||
if (!registered.includes(controller) && !this.loaded.has(controller)) {
|
||||
debugLog(`Loading controller ${controller}`);
|
||||
this.loaded.add(controller);
|
||||
void import(/* webpackChunkName: "[request]" */`./dynamic/${path}.controller`)
|
||||
.then((imported:{ default:ControllerConstructor }) => this.application.register(controller, imported.default))
|
||||
.catch((err:unknown) => {
|
||||
console.error('Failed to load dynamic controller chunk %O: %O', controller, err);
|
||||
|
||||
void this
|
||||
.importController(controller)
|
||||
.then((clazz) => {
|
||||
if (clazz) {
|
||||
this.application.register(controller, clazz);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async importController(controller:string):Promise<ControllerConstructor|null> {
|
||||
try {
|
||||
const imported = await this.fetchDynamicController(controller);
|
||||
return imported.default;
|
||||
} catch (err) {
|
||||
console.error('Failed to load dynamic controller chunk %O: %O', controller, err);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private async fetchDynamicController(controller:string) {
|
||||
if (OpenProjectStimulusApplication.dynamicImports.has(controller)) {
|
||||
return OpenProjectStimulusApplication.dynamicImports.get(controller)!();
|
||||
}
|
||||
|
||||
// Default: Try to load the controller from dynamic/ subfolder.
|
||||
const path = this.derivePath(controller);
|
||||
return await import(/* webpackChunkName: "[request]" */ `./dynamic/${path}.controller`) as Promise<{
|
||||
default:ControllerConstructor
|
||||
}>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive dynamic path from controller name.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
import { ControllerConstructor } from '@hotwired/stimulus/dist/types/core/controller';
|
||||
import { Application } from '@hotwired/stimulus';
|
||||
|
||||
export type DynamicControllerLoader = () => Promise<{ default:ControllerConstructor }>;
|
||||
|
||||
export class OpenProjectStimulusApplication extends Application {
|
||||
/** A map of controllers that have been preregistered. */
|
||||
static controllers = new Map<string, ControllerConstructor>();
|
||||
|
||||
/** A map of a dynamic controller loader to load when the controller name is found */
|
||||
static dynamicImports = new Map<string, DynamicControllerLoader>();
|
||||
|
||||
/**
|
||||
* Register a controller to be used in the application,
|
||||
* allowing it to be registered before the Stimulus application is being initialized.
|
||||
*
|
||||
* This is useful for plugins that execute code before we call setup.ts
|
||||
*
|
||||
* @param name the name/identifier of the controller
|
||||
* @param controller the controller class
|
||||
*/
|
||||
static preregister(name:string, controller:ControllerConstructor) {
|
||||
this.controllers.set(name, controller);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a dynamic controller to be imported using the given path,
|
||||
* allowing it to be defined somewhere else than within the dynamic/ subfolder.
|
||||
*
|
||||
* This is useful for plugins that want to define new dynamic controllers.
|
||||
* How to use this: In your plugin's main.ts, call this
|
||||
* @example
|
||||
* OpenProjectStimulusApplication.preregisterDynamic(
|
||||
* 'test',
|
||||
* () => import('./test.controller')
|
||||
* );
|
||||
* ```
|
||||
*
|
||||
* @param name the name/identifier of the controller
|
||||
* @param loader A callback to provide the controller asynchronously.
|
||||
*/
|
||||
static preregisterDynamic(name:string, loader:DynamicControllerLoader) {
|
||||
this.dynamicImports.set(name, loader);
|
||||
}
|
||||
|
||||
async start():Promise<void> {
|
||||
this.preregisteredControllers.forEach((controller, name) => {
|
||||
this.register(name, controller);
|
||||
});
|
||||
|
||||
await super.start();
|
||||
}
|
||||
|
||||
get preregisteredControllers() {
|
||||
return OpenProjectStimulusApplication.controllers;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
import { Application } from '@hotwired/stimulus';
|
||||
import { environment } from '../environments/environment';
|
||||
import { OpApplicationController } from './controllers/op-application.controller';
|
||||
import MainMenuController from './controllers/dynamic/menus/main.controller';
|
||||
@@ -22,6 +21,8 @@ import ScrollIntoViewController from './controllers/scroll-into-view.controller'
|
||||
import CkeditorFocusController from './controllers/ckeditor-focus.controller';
|
||||
|
||||
import AutoSubmit from '@stimulus-components/auto-submit';
|
||||
import { OpenProjectStimulusApplication } from 'core-stimulus/openproject-stimulus-application';
|
||||
import { Application } from '@hotwired/stimulus';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
@@ -29,35 +30,32 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
const instance = Application.start();
|
||||
OpenProjectStimulusApplication.preregister('application', OpApplicationController);
|
||||
OpenProjectStimulusApplication.preregister('async-dialog', AsyncDialogController);
|
||||
OpenProjectStimulusApplication.preregister('disable-when-checked', OpDisableWhenCheckedController);
|
||||
OpenProjectStimulusApplication.preregister('flash', FlashController);
|
||||
OpenProjectStimulusApplication.preregister('menus--main', MainMenuController);
|
||||
OpenProjectStimulusApplication.preregister('password-confirmation-dialog', PasswordConfirmationDialogController);
|
||||
OpenProjectStimulusApplication.preregister('poll-for-changes', PollForChangesController);
|
||||
OpenProjectStimulusApplication.preregister('print', PrintController);
|
||||
OpenProjectStimulusApplication.preregister('refresh-on-form-changes', RefreshOnFormChangesController);
|
||||
OpenProjectStimulusApplication.preregister('form-preview', FormPreviewController);
|
||||
OpenProjectStimulusApplication.preregister('hover-card-trigger', HoverCardTriggerController);
|
||||
OpenProjectStimulusApplication.preregister('show-when-checked', OpShowWhenCheckedController);
|
||||
OpenProjectStimulusApplication.preregister('show-when-value-selected', OpShowWhenValueSelectedController);
|
||||
OpenProjectStimulusApplication.preregister('table-highlighting', TableHighlightingController);
|
||||
OpenProjectStimulusApplication.preregister('projects-zen-mode', OpProjectsZenModeController);
|
||||
OpenProjectStimulusApplication.preregister('work-packages--date-picker--preview', PreviewController);
|
||||
OpenProjectStimulusApplication.preregister('keep-scroll-position', KeepScrollPositionController);
|
||||
OpenProjectStimulusApplication.preregister('pattern-input', PatternInputController);
|
||||
OpenProjectStimulusApplication.preregister('scroll-into-view', ScrollIntoViewController);
|
||||
OpenProjectStimulusApplication.preregister('ckeditor-focus', CkeditorFocusController);
|
||||
OpenProjectStimulusApplication.preregister('auto-submit', AutoSubmit);
|
||||
|
||||
const instance = OpenProjectStimulusApplication.start();
|
||||
window.Stimulus = instance;
|
||||
|
||||
instance.debug = !environment.production;
|
||||
instance.handleError = (error, message, detail) => {
|
||||
console.warn(error, message, detail);
|
||||
};
|
||||
|
||||
instance.register('async-dialog', AsyncDialogController);
|
||||
instance.register('disable-when-checked', OpDisableWhenCheckedController);
|
||||
instance.register('flash', FlashController);
|
||||
instance.register('menus--main', MainMenuController);
|
||||
instance.register('password-confirmation-dialog', PasswordConfirmationDialogController);
|
||||
instance.register('poll-for-changes', PollForChangesController);
|
||||
instance.register('print', PrintController);
|
||||
instance.register('refresh-on-form-changes', RefreshOnFormChangesController);
|
||||
instance.register('form-preview', FormPreviewController);
|
||||
instance.register('hover-card-trigger', HoverCardTriggerController);
|
||||
instance.register('show-when-checked', OpShowWhenCheckedController);
|
||||
instance.register('show-when-value-selected', OpShowWhenValueSelectedController);
|
||||
instance.register('table-highlighting', TableHighlightingController);
|
||||
instance.register('projects-zen-mode', OpProjectsZenModeController);
|
||||
instance.register('work-packages--date-picker--preview', PreviewController);
|
||||
instance.register('keep-scroll-position', KeepScrollPositionController);
|
||||
instance.register('pattern-input', PatternInputController);
|
||||
instance.register('scroll-into-view', ScrollIntoViewController);
|
||||
instance.register('ckeditor-focus', CkeditorFocusController);
|
||||
instance.register('auto-submit', AutoSubmit);
|
||||
|
||||
// Application controller must be registered last, as it tries to automatically load other controllers
|
||||
// not yet registered.
|
||||
instance.register('application', OpApplicationController);
|
||||
|
||||
@@ -10,6 +10,7 @@ import { addTurboGlobalListeners } from './turbo-global-listeners';
|
||||
import { applyTurboNavigationPatch } from './turbo-navigation-patch';
|
||||
import { debugLog, whenDebugging } from 'core-app/shared/helpers/debug_output';
|
||||
import { TURBO_EVENTS } from './constants';
|
||||
import { StreamActions } from '@hotwired/turbo';
|
||||
|
||||
Turbo.session.drive = true;
|
||||
Turbo.setProgressBarDelay(100);
|
||||
@@ -34,6 +35,10 @@ registerFlashStreamAction();
|
||||
registerLiveRegionStreamAction();
|
||||
registerInputCaptionStreamAction();
|
||||
|
||||
StreamActions.reloadPage = function reloadPage() {
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
// Apply navigational patch
|
||||
// https://github.com/hotwired/turbo/issues/1300
|
||||
applyTurboNavigationPatch();
|
||||
|
||||
@@ -35,7 +35,7 @@ module API
|
||||
code 500
|
||||
|
||||
def initialize(message = I18n.t("api_v3.errors.code_500_outbound_request_failure", status_code: 403))
|
||||
super
|
||||
super(message || I18n.t("api_v3.errors.code_500_outbound_request_failure", status_code: 403))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -35,7 +35,7 @@ module API
|
||||
code 500
|
||||
|
||||
def initialize(message = I18n.t("api_v3.errors.code_500_outbound_request_failure", status_code: 404))
|
||||
super
|
||||
super(message || I18n.t("api_v3.errors.code_500_outbound_request_failure", status_code: 404))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) the OpenProject GmbH
|
||||
@@ -33,7 +35,7 @@ module API
|
||||
code 401
|
||||
|
||||
def initialize(message = I18n.t("api_v3.errors.code_401"))
|
||||
super
|
||||
super(message || I18n.t("api_v3.errors.code_401"))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -37,7 +37,7 @@ module API
|
||||
end
|
||||
|
||||
def href_callback
|
||||
query_params = "sortBy=#{to_query [%i(semver_name asc)]}&pageSize=-1"
|
||||
query_params = "sortBy=#{to_query [%i(name asc)]}&pageSize=-1"
|
||||
|
||||
if filter.project.nil?
|
||||
"#{api_v3_paths.versions}?#{query_params}"
|
||||
|
||||
@@ -57,7 +57,7 @@ module FullCalendar
|
||||
"title" => title,
|
||||
"url" => url,
|
||||
"classNames" => class_names
|
||||
}.merge(additional_attributes).compact_blank.as_json
|
||||
}.merge(additional_attributes).compact.as_json
|
||||
end
|
||||
|
||||
def to_json(*)
|
||||
|
||||
@@ -1,42 +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.
|
||||
#++
|
||||
#
|
||||
|
||||
module OpenProject::Patches::SecureHeadersTurboAwareNonce
|
||||
def content_security_policy_script_nonce(request)
|
||||
if request.env["HTTP_TURBO_REFERRER"].present?
|
||||
request.env[SecureHeaders::NONCE_KEY] ||= request.env["HTTP_X_TURBO_NONCE"]
|
||||
end
|
||||
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
OpenProject::Patches.patch_gem_version "secure_headers", "7.1.0" do
|
||||
SecureHeaders.singleton_class.prepend OpenProject::Patches::SecureHeadersTurboAwareNonce
|
||||
end
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user