Subject: [PATCH] Feature: webhook --- Index: Gemfile IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/Gemfile b/Gemfile --- a/Gemfile (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/Gemfile (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -15,6 +15,7 @@ gem 'rbpdf', '~> 1.21.3' gem 'addressable' gem 'rubyzip', '~> 2.3.0' +gem 'rest-client', '~> 2.1' # Ruby Standard Gems gem 'csv', '~> 3.2.6' Index: app/controllers/webhooks_controller.rb IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/app/controllers/webhooks_controller.rb b/app/controllers/webhooks_controller.rb new file mode 100644 --- /dev/null (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) +++ b/app/controllers/webhooks_controller.rb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -0,0 +1,78 @@ +# frozen_string_literal: true + +class WebhooksController < ApplicationController + self.main_menu = false + + before_action :require_login + before_action :check_enabled + before_action :authorize + before_action :find_webhook, :only => [:edit, :update, :destroy] + + require_sudo_mode :create, :update, :destroy + + helper :projects + + def index + @webhooks = webhooks_scope.order(:url) + end + + def new + @webhook = User.current.webhooks.build + end + + def edit + end + + def create + @webhook = User.current.webhooks.build(webhook_params) + if @webhook.save + redirect_to webhooks_path + else + render :new + end + end + + def update + if @webhook.update(webhook_params) + redirect_to webhooks_path + else + render :edit + end + end + + def destroy + @webhook.destroy + redirect_to webhooks_path + end + + private + + def webhooks_scope + if User.current.admin? + Webhook.includes(:user).references(:users) + else + User.current.webhooks + end + end + + def webhook_params + attrs = params.require(:webhook).permit(:url, :secret, :active, :events => [], :project_ids => [], :tracker_ids => []) + attrs[:events] = Array(attrs[:events]).reject(&:blank?) + attrs[:project_ids] = Array(attrs[:project_ids]).reject(&:blank?) + attrs[:tracker_ids] = Array(attrs[:tracker_ids]).reject(&:blank?) + attrs + end + + def find_webhook + @webhook = webhooks_scope.find_by(:id => params[:id]) + render_404 unless @webhook + end + + def authorize + deny_access unless User.current.allowed_to?(:use_webhooks, nil, :global => true) + end + + def check_enabled + render_403 unless Webhook.enabled? + end +end Index: app/jobs/webhook_job.rb IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/app/jobs/webhook_job.rb b/app/jobs/webhook_job.rb new file mode 100644 --- /dev/null (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) +++ b/app/jobs/webhook_job.rb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +class WebhookJob < ApplicationJob + def perform(hook_id, payload_json) + previous_user = User.current + hook = Webhook.find_by(:id => hook_id) + return unless hook&.user&.active? + + User.current = hook.user + hook.call(payload_json) + ensure + User.current = previous_user + end +end Index: app/models/issue.rb IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/app/models/issue.rb b/app/models/issue.rb --- a/app/models/issue.rb (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/app/models/issue.rb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -127,6 +127,9 @@ # https://api.rubyonrails.org/v5.2.3/classes/ActiveSupport/Callbacks/ClassMethods.html#method-i-set_callback after_create_commit :send_notification after_create_commit :add_auto_watcher + after_create_commit -> {Webhook.trigger('issue.created', self)} + after_update_commit :trigger_issue_webhooks + after_destroy_commit -> {Webhook.trigger('issue.deleted', self)} after_commit :create_parent_issue_journal # Returns a SQL conditions string used to find all issues visible by the specified user @@ -164,6 +167,20 @@ end end + def trigger_issue_webhooks + Webhook.trigger('issue.updated', self) + + return unless saved_change_to_status_id? + + previous_status = IssueStatus.find_by(:id => saved_change_to_status_id.first) + current_status = status + + return unless current_status&.is_closed? + return if previous_status&.is_closed? + + Webhook.trigger('issue.closed', self) + end + # Returns true if usr or current user is allowed to view the issue def visible?(usr=nil) (usr || User.current).allowed_to?(:view_issues, self.project) do |role, user| Index: app/models/project.rb IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/app/models/project.rb b/app/models/project.rb --- a/app/models/project.rb (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/app/models/project.rb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -47,6 +47,7 @@ has_many :queries, :dependent => :destroy has_many :documents, :dependent => :destroy has_many :news, lambda {includes(:author)}, :dependent => :destroy + has_and_belongs_to_many :webhooks has_many :issue_categories, lambda {order(:name)}, :dependent => :delete_all has_many :boards, lambda {order(:position)}, :inverse_of => :project, :dependent => :destroy has_one :repository, lambda {where(:is_default => true)} Index: app/models/user.rb IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/app/models/user.rb b/app/models/user.rb --- a/app/models/user.rb (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/app/models/user.rb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -91,6 +91,7 @@ has_one :api_token, lambda {where "#{table.name}.action='api'"}, :class_name => 'Token' has_one :email_address, lambda {where :is_default => true}, :autosave => true has_many :email_addresses, :dependent => :delete_all + has_many :webhooks, :dependent => :destroy belongs_to :auth_source scope :logged, lambda {where("#{User.table_name}.status <> #{STATUS_ANONYMOUS}")} Index: app/models/webhook.rb IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/app/models/webhook.rb b/app/models/webhook.rb new file mode 100644 --- /dev/null (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) +++ b/app/models/webhook.rb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -0,0 +1,166 @@ +# frozen_string_literal: true + +require 'rest-client' + +class Webhook < ApplicationRecord + belongs_to :user + has_and_belongs_to_many :projects + has_and_belongs_to_many :trackers + + serialize :events, Array + + validates :url, :presence => true, + :length => {:maximum => 2000}, + :webhook_endpoint => true + validates :secret, :length => {:maximum => 255}, :allow_blank => true + validate :validate_events + validate :validate_projects + validate :validate_trackers + + scope :active, -> {where(:active => true)} + + before_validation :filter_projects + before_validation :filter_trackers + after_initialize :ensure_events_array + + def self.enabled? + Setting.webhooks_enabled? + end + + def self.trigger(event, object) + return unless enabled? + + hooks_for(event, object).each do |hook| + payload = hook.payload(event, object) + WebhookJob.perform_later(hook.id, payload.to_json) + end + end + + def self.hooks_for(event, object) + project = object.respond_to?(:project) ? object.project : nil + return [] unless project + + active + .joins(:projects, :user) + .where(:projects => {:id => project.id}) + .where(:users => {:status => User::STATUS_ACTIVE}) + .select do |hook| + hook.events.include?(event) && + (!object.respond_to?(:visible?) || object.visible?(hook.user)) && + hook.user.allowed_to?(:use_webhooks, project) && + hook.tracker_allowed?(object) + end + end + + def setable_projects + member = user || User.current + return Project.none unless member + + Project.allowed_to(member, :use_webhooks).sorted + end + + def setable_events + WebhookPayload::EVENTS + end + + def setable_event_names + setable_events.flat_map {|type, actions| actions.map {|action| "#{type}.#{action}"}} + end + + def setable_trackers + project_trackers = setable_projects.includes(:trackers).flat_map(&:trackers) + project_trackers.uniq.sort_by(&:position) + end + + def payload(event, object) + WebhookPayload.new(event, object, user).to_h + end + + def call(payload_json) + Executor.new(url, payload_json, secret).call + true + rescue => e + Rails.logger.warn do + "Webhook delivery failed: #{e.class}: #{e.message}\n#{Array(e.backtrace).join("\n")}" + end + false + end + + class Executor + def initialize(url, payload, secret) + @url = url + @payload = payload + @secret = secret + end + + def call + raise URI::BadURIError unless WebhookEndpointValidator.safe_webhook_uri?(@url) + + headers = { + :accept => '*/*', + :content_type => :json, + :user_agent => 'Redmine' + } + headers['X-Redmine-Signature-256'] = compute_signature if @secret.present? + + Rails.logger.debug {"Webhook: POST #{@url}"} + RestClient.post(@url, @payload, headers) + end + + private + + def compute_signature + 'sha256=' + OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), @secret, @payload) + end + end + + def tracker_allowed?(object) + return true unless object.respond_to?(:tracker_id) + tracker_ids.blank? || tracker_ids.include?(object.tracker_id) + end + + private + + def ensure_events_array + self.events ||= [] + end + + def filter_projects + return if project_ids.blank? + + allowed_ids = setable_projects.map(&:id) + self.project_ids = project_ids & allowed_ids + end + + def filter_trackers + return if tracker_ids.blank? + + allowed_ids = setable_trackers.map(&:id) + self.tracker_ids = tracker_ids & allowed_ids + end + + def validate_events + self.events = Array(events).reject(&:blank?) + if events.blank? || (events - setable_event_names).any? + errors.add(:events, :invalid) + end + end + + def validate_projects + if project_ids.blank? + errors.add(:projects, :blank) + end + end + + def validate_trackers + self.tracker_ids = tracker_ids & setable_trackers.map(&:id) + + if issue_events_selected? && tracker_ids.blank? + errors.add(:trackers, :blank) + end + end + + def issue_events_selected? + Array(events).any? {|event| event.to_s.start_with?('issue.')} + end +end Index: app/models/webhook_payload.rb IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/app/models/webhook_payload.rb b/app/models/webhook_payload.rb new file mode 100644 --- /dev/null (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) +++ b/app/models/webhook_payload.rb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -0,0 +1,201 @@ +# frozen_string_literal: true + +class WebhookPayload + EVENTS = { + :issue => %w[created updated closed deleted], + :wiki_page => %w[created updated deleted] + }.freeze + + attr_reader :event, :object, :user + + def initialize(event, object, user) + @event = event + @object = object + @user = user + end + + def to_h + type, action = event.split('.') + type = type&.to_sym + unless EVENTS[type]&.include?(action) + raise ArgumentError, "Unsupported webhook event: #{event}" + end + + payload, timestamp = send("#{type}_payload", action) + timestamp ||= Time.current + { + :type => event, + :timestamp => timestamp.utc.iso8601, + :data => payload + } + end + + private + + def issue_payload(action) + issue = object + journal = %w[updated closed].include?(action) ? issue.current_journal : nil + + payload = { + :issue => issue_hash(issue) + } + payload[:journal] = journal_hash(journal) if journal + + timestamp = + case action + when 'created' + issue.created_on + when 'closed' + issue.closed_on || Time.current + when 'deleted' + Time.current + else + journal&.created_on || issue.updated_on + end + + [payload, timestamp] + end + + def wiki_page_payload(action) + page, content = wiki_page_and_content + payload = { + :wiki_page => wiki_page_hash(page) + } + payload[:content] = wiki_content_hash(content) if content + + timestamp = + case action + when 'created' + content&.created_on || Time.current + when 'deleted' + Time.current + else + content&.updated_on || Time.current + end + + [payload, timestamp] + end + + def issue_hash(issue) + hash = { + :id => issue.id, + :subject => issue.subject, + :description => issue.description, + :is_private => issue.is_private?, + :project => project_hash(issue.project), + :tracker => reference_hash(issue.tracker), + :status => status_hash(issue.status), + :priority => reference_hash(issue.priority), + :author => user_hash(issue.author), + :assigned_to => user_hash(issue.assigned_to), + :category => reference_hash(issue.category), + :fixed_version => reference_hash(issue.fixed_version), + :parent => issue.parent_id ? {:id => issue.parent_id} : nil, + :start_date => issue.start_date&.to_s, + :due_date => issue.due_date&.to_s, + :done_ratio => issue.done_ratio, + :estimated_hours => issue.estimated_hours, + :total_estimated_hours => issue.total_estimated_hours, + :created_on => issue.created_on&.iso8601, + :updated_on => issue.updated_on&.iso8601, + :closed_on => issue.closed_on&.iso8601 + } + + if user&.allowed_to?(:view_time_entries, issue.project) + hash[:spent_hours] = issue.spent_hours + hash[:total_spent_hours] = issue.total_spent_hours + end + + custom_fields = issue.visible_custom_field_values(user).map do |value| + { + :id => value.custom_field_id, + :name => value.custom_field.name, + :value => value.value + } + end + hash[:custom_fields] = custom_fields if custom_fields.any? + + hash.compact + end + + def journal_hash(journal) + return unless journal + + { + :id => journal.id, + :notes => journal.notes, + :created_on => journal.created_on&.iso8601, + :user => user_hash(journal.user), + :details => journal.visible_details(user).map do |detail| + { + :property => detail.property, + :prop_key => detail.prop_key, + :old_value => detail.old_value, + :value => detail.value + } + end + } + end + + def wiki_page_hash(page) + return {} unless page + + { + :id => page.id, + :title => page.title, + :project => project_hash(page.project), + :parent_id => page.parent_id, + :created_on => page.created_on&.iso8601, + :updated_on => page.updated_on&.iso8601 + }.compact + end + + def wiki_content_hash(content) + return unless content + + { + :version => content.version, + :text => content.text, + :comments => content.comments, + :author => user_hash(content.author), + :created_on => content.created_on&.iso8601, + :updated_on => content.updated_on&.iso8601 + } + end + + def wiki_page_and_content + if object.is_a?(WikiContent) + [object.page, object] + else + [object, object.respond_to?(:content) ? object.content : nil] + end + end + + def project_hash(project) + return unless project + + { + :id => project.id, + :identifier => project.identifier, + :name => project.name + } + end + + def reference_hash(record) + return unless record + + {:id => record.id, :name => record.name} + end + + def status_hash(status) + return unless status + + {:id => status.id, :name => status.name, :is_closed => status.is_closed?} + end + + def user_hash(user_record) + return unless user_record + + {:id => user_record.id, :name => user_record.name} + end +end Index: app/models/wiki_content.rb IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/app/models/wiki_content.rb b/app/models/wiki_content.rb --- a/app/models/wiki_content.rb (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/app/models/wiki_content.rb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -33,6 +33,8 @@ after_save :create_version after_create_commit :send_notification_create after_update_commit :send_notification_update + after_create_commit :trigger_webhook_create + after_update_commit :trigger_webhook_update scope :without_text, lambda {select(:id, :page_id, :version, :updated_on)} @@ -101,4 +103,12 @@ Mailer.deliver_wiki_content_updated(self) end end + + def trigger_webhook_create + Webhook.trigger('wiki_page.created', self) + end + + def trigger_webhook_update + Webhook.trigger('wiki_page.updated', self) + end end Index: app/models/wiki_page.rb IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb --- a/app/models/wiki_page.rb (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/app/models/wiki_page.rb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -61,6 +61,7 @@ before_destroy :delete_redirects before_save :handle_rename_or_move, :update_wiki_start_page after_save :handle_children_move, :delete_selected_attachments + after_destroy_commit -> {Webhook.trigger('wiki_page.deleted', self)} # eager load information about last updates, without loading text scope :with_updated_on, lambda {preload(:content_without_text)} Index: app/validators/webhook_endpoint_validator.rb IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/app/validators/webhook_endpoint_validator.rb b/app/validators/webhook_endpoint_validator.rb new file mode 100644 --- /dev/null (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) +++ b/app/validators/webhook_endpoint_validator.rb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -0,0 +1,178 @@ +# frozen_string_literal: true + +require 'set' +require 'uri' +require 'resolv' +require 'ipaddr' + +class WebhookEndpointValidator < ActiveModel::EachValidator + BAD_PORTS = Set[25, 111, 135, 139, 445, 1433, 1521, 2049, 3306, 3389, 5985, 5986].freeze + + def validate_each(record, attribute, value) + return if value.blank? + + valid, reason = self.class.validate_uri(value) + record.errors.add(attribute, :invalid, message: reason && " (#{reason})") unless valid + end + + class << self + def safe_webhook_uri?(value) + valid, = validate_uri(value) + valid + end + + def validate_uri(value) + uri = value.is_a?(URI) ? value : URI.parse(value.to_s) + + return [false, 'must use http or https'] unless valid_scheme?(uri.scheme) + + host_reason = host_validation_error(uri.host) + return [false, host_reason] if host_reason + + return [false, 'uses a disallowed port'] unless valid_port?(uri.port) + + [true, nil] + rescue URI::Error, ArgumentError => e + Rails.logger.warn {"Webhook endpoint rejected: #{value.inspect} (#{e.message})"} + [false, e.message] + end + + def valid_scheme?(scheme) + %w[http https].include?(scheme) + end + + def host_validation_error(host) + return 'host is missing' if host.blank? + + blocked = blocked_targets + allowed = allowed_targets + + host_allowlisted = allowed[:host_pattern]&.match?(host) + + return 'host is blocklisted' if host_blocked?(host, blocked) + if !allowlist_empty?(allowed) && !host_allowlisted && !allowlist_ip_allowed_for_host?(host, allowed) + return "host is not on the allowlist" + end + + addresses = ip_literal?(host) ? [host] : resolve_addresses(host) + return nil if addresses.blank? && allowlist_empty?(allowed) + + addresses.each do |ip| + ipaddr = IPAddr.new(ip) + allowlisted_ip = host_allowlisted || allowlist_ip_allowed?(ipaddr, allowed) + + return "address #{ipaddr} is blocklisted" if blocked[:ips].any? {|entry| entry.include?(ipaddr)} + return "address #{ipaddr} is not on the allowlist" if !allowlist_empty?(allowed) && !allowlisted_ip + return "address #{ipaddr} is not reachable from the public network" if !allowlisted_ip && (ipaddr.loopback? || ipaddr.link_local? || ipaddr.private?) + rescue IPAddr::Error + return "address #{ip.inspect} is invalid" + end + + nil + end + + def valid_port?(port) + port.present? && !BAD_PORTS.include?(port) + end + + def resolve_addresses(host) + addresses = [] + Resolv.each_address(host) {|ip| addresses << ip} + addresses + rescue Resolv::ResolvError + [] + end + + def ip_literal?(host) + IPAddr.new(host) + true + rescue IPAddr::Error + false + end + + def blocked_targets + @blocked_targets ||= begin + ips = [] + host_patterns = [] + Array(Redmine::Configuration['webhook_blocklist']).each do |entry| + entry = entry.to_s.strip + next if entry.empty? + + begin + ips << IPAddr.new(entry) + rescue IPAddr::Error + host_patterns << entry + end + end + { + :ips => ips.freeze, + :host_pattern => build_host_pattern(host_patterns) + } + end + end + + def allowed_targets + @allowed_targets ||= begin + ips = [] + host_patterns = [] + Array(Redmine::Configuration['webhook_allowlist']).each do |entry| + entry = entry.to_s.strip + next if entry.empty? + + begin + ips << IPAddr.new(entry) + rescue IPAddr::Error + host_patterns << entry + end + end + { + :ips => ips.freeze, + :host_pattern => build_host_pattern(host_patterns) + } + end + end + + def build_host_pattern(patterns) + return if patterns.empty? + + sources = patterns.map do |value| + if value.start_with?('*.') + "(?:.*\\.)?#{Regexp.escape(value.delete_prefix('*.'))}" + else + Regexp.escape(value) + end + end + + Regexp.new("\\A(?:#{sources.join('|')})\\z", Regexp::IGNORECASE) + end + + def host_blocked?(host, blocked) + blocked[:host_pattern]&.match?(host) + end + + def host_allowed_by_allowlist?(host, allowed) + return true if allowlist_empty?(allowed) + + allowed[:host_pattern]&.match?(host) || allowlist_ip_allowed_for_host?(host, allowed) + end + + def allowlist_empty?(allowed) + allowed[:ips].empty? && allowed[:host_pattern].nil? + end + + def allowlist_ip_allowed_for_host?(host, allowed) + addresses = ip_literal?(host) ? [host] : resolve_addresses(host) + return false if addresses.empty? + + addresses.any? do |ip| + allowlist_ip_allowed?(IPAddr.new(ip), allowed) + rescue IPAddr::Error + false + end + end + + def allowlist_ip_allowed?(ipaddr, allowed) + allowed[:ips].any? {|entry| entry.include?(ipaddr)} + end + end +end Index: app/views/my/_sidebar.html.erb IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/app/views/my/_sidebar.html.erb b/app/views/my/_sidebar.html.erb --- a/app/views/my/_sidebar.html.erb (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/app/views/my/_sidebar.html.erb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -31,6 +31,11 @@ <% else %> <%= l(:label_missing_api_access_key) %> <% end %> + +<% if Setting.webhooks_enabled? && User.current.allowed_to?(:use_webhooks, nil, :global => true) %> +

<%= l(:label_integrations) %>

+

<%= link_to l(:label_webhook_plural), webhooks_path, :class => 'icon icon-link' %>

+<% end %> (<%= link_to l(:button_reset), my_api_key_path, :method => :post %>)

<% end %> Index: app/views/settings/_api.html.erb IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/app/views/settings/_api.html.erb b/app/views/settings/_api.html.erb --- a/app/views/settings/_api.html.erb (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/app/views/settings/_api.html.erb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -4,6 +4,8 @@

<%= setting_check_box :rest_api_enabled %>

<%= setting_check_box :jsonp_enabled %>

+ +

<%= setting_check_box :webhooks_enabled %>

<%= submit_tag l(:button_save) %> Index: app/views/webhooks/_form.html.erb IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/app/views/webhooks/_form.html.erb b/app/views/webhooks/_form.html.erb new file mode 100644 --- /dev/null (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) +++ b/app/views/webhooks/_form.html.erb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -0,0 +1,68 @@ +<%= error_messages_for @webhook %> +
+
+
+

+ <%= f.text_field :url, :required => true, :size => 60 %> + <%= l(:webhook_url_info) %> +

+

+ <%= f.text_field :secret %> + <%= raw l(:webhook_secret_info_html) %> +

+

+ <%= f.check_box :active %> <%= l(:field_active) %> +

+
+ +

<%= l(:label_webhook_events) %>

+
+ <% @webhook.setable_events.keys.sort.each do |type| %> +
+ + <%= toggle_checkboxes_link("##{type}_events input") %> + <%= l_or_humanize(type, :prefix => 'webhook_events_') %> + + <% @webhook.setable_events[type].each do |action| %> + <% name = "#{type}.#{action}" %> + + <% end %> +
+ <% end %> +
<%= check_all_links 'events' %> + <%= hidden_field_tag 'webhook[events][]', '' %> +
+
+ +
+
+ + <%= toggle_checkboxes_link("#webhook_project_ids input[type=checkbox]") %> + <%= l(:label_project_plural) %> + + <% project_ids = @webhook.project_ids || [] %> + <%= render_project_nested_lists(@webhook.setable_projects) do |p| %> + <%= content_tag('label', check_box_tag('webhook[project_ids][]', p.id, project_ids.include?(p.id)) + ' ' + h(p)) %> + <% end %> + <%= hidden_field_tag 'webhook[project_ids][]', '' %> +
+ +
+ + <%= toggle_checkboxes_link("#webhook_tracker_ids input[type=checkbox]") %> + <%= l(:label_tracker_plural) %> + + <% tracker_ids = @webhook.tracker_ids || [] %> + <% @webhook.setable_trackers.each do |tracker| %> + + <% end %> + <%= hidden_field_tag 'webhook[tracker_ids][]', '' %> +
+
+
Index: app/views/webhooks/edit.html.erb IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/app/views/webhooks/edit.html.erb b/app/views/webhooks/edit.html.erb new file mode 100644 --- /dev/null (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) +++ b/app/views/webhooks/edit.html.erb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -0,0 +1,4 @@ +<%= labelled_form_for @webhook, :url => webhook_path(@webhook), :html => {:method => :patch} do |f| %> + <%= render :partial => 'form', :locals => {:f => f} %> +

<%= submit_tag l(:button_save) %>

+<% end %> Index: app/views/webhooks/index.html.erb IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/app/views/webhooks/index.html.erb b/app/views/webhooks/index.html.erb new file mode 100644 --- /dev/null (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) +++ b/app/views/webhooks/index.html.erb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -0,0 +1,43 @@ +
+ <%= link_to l(:label_webhook_new), new_webhook_path, :class => 'icon icon-add' %> +
+ +<%= title l(:label_webhook_plural) %> + +<% if @webhooks.any? %> +
+ + + + <% if User.current.admin? %> + + <% end %> + + + + + + + + + <% @webhooks.each do |hook| %> + + <% if User.current.admin? %> + + <% end %> + + + + + + + <% end %> + +
<%= l(:label_user) %><%= l(:field_active) %><%= l(:field_url) %><%= l(:label_webhook_events) %><%= l(:label_project_plural) %>
<%= h hook.user %><%= hook.active? ? l(:general_text_Yes) : l(:general_text_No) %><%= truncate(hook.url, :length => 50) %><%= safe_join(hook.events.map {|e| content_tag(:code, e)}, ', ') %><%= safe_join(hook.projects.visible.map {|p| link_to_project(p)}, ', ') %> + <%= link_to l(:button_edit), edit_webhook_path(hook), :class => 'icon icon-edit' %> + <%= link_to l(:button_delete), webhook_path(hook), :method => :delete, :data => {:confirm => l(:text_are_you_sure)}, :class => 'icon icon-del' %> +
+
+<% else %> +

<%= l(:label_no_data) %>

+<% end %> Index: app/views/webhooks/new.html.erb IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/app/views/webhooks/new.html.erb b/app/views/webhooks/new.html.erb new file mode 100644 --- /dev/null (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) +++ b/app/views/webhooks/new.html.erb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -0,0 +1,4 @@ +<%= labelled_form_for @webhook, :url => webhooks_path do |f| %> + <%= render :partial => 'form', :locals => {:f => f} %> +

<%= submit_tag l(:button_create) %>

+<% end %> Index: config/locales/ar.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/ar.yml b/config/locales/ar.yml --- a/config/locales/ar.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/ar.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1487,3 +1487,22 @@ text_default_active_job_queue_changed: Default queue adapter which is well suited only for dev/test changed twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/az.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/az.yml b/config/locales/az.yml --- a/config/locales/az.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/az.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1579,3 +1579,22 @@ text_default_active_job_queue_changed: Default queue adapter which is well suited only for dev/test changed twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/bg.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/bg.yml b/config/locales/bg.yml --- a/config/locales/bg.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/bg.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1432,3 +1432,22 @@ text_project_destroy_enter_identifier: За да потвърдите действието, въведете идентификатора на проекта (%{identifier}) по-долу. field_name_or_email_or_login: Име, e-mail или login име twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/bs.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/bs.yml b/config/locales/bs.yml --- a/config/locales/bs.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/bs.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1474,3 +1474,22 @@ text_default_active_job_queue_changed: Default queue adapter which is well suited only for dev/test changed twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/ca.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/ca.yml b/config/locales/ca.yml --- a/config/locales/ca.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/ca.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1475,3 +1475,22 @@ text_select_apply_issue_status: Marca l'estat de la incidència field_name_or_email_or_login: Nom, correu o login twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/cs.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/cs.yml b/config/locales/cs.yml --- a/config/locales/cs.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/cs.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1469,3 +1469,22 @@ text_default_active_job_queue_changed: Výchozí adaptér fronty, vhodný pouze pro vývoj/testování, byl změněn twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/da.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/da.yml b/config/locales/da.yml --- a/config/locales/da.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/da.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1504,3 +1504,22 @@ text_default_active_job_queue_changed: Default queue adapter which is well suited only for dev/test changed twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/de.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/de.yml b/config/locales/de.yml --- a/config/locales/de.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/de.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1455,3 +1455,22 @@ text_default_active_job_queue_changed: Default queue adapter which is well suited only for dev/test changed twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/el.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/el.yml b/config/locales/el.yml --- a/config/locales/el.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/el.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1487,3 +1487,22 @@ text_default_active_job_queue_changed: Default queue adapter which is well suited only for dev/test changed twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/en-GB.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/en-GB.yml b/config/locales/en-GB.yml --- a/config/locales/en-GB.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/en-GB.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1488,3 +1488,22 @@ text_default_active_job_queue_changed: Default queue adapter which is well suited only for dev/test changed twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/en.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/en.yml b/config/locales/en.yml --- a/config/locales/en.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/en.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -922,6 +922,24 @@ label_year: Year label_month: Month label_week: Week + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" label_date_from: From label_date_to: To label_language_based: Based on user's language Index: config/locales/es-PA.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/es-PA.yml b/config/locales/es-PA.yml --- a/config/locales/es-PA.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/es-PA.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1517,3 +1517,22 @@ text_default_active_job_queue_changed: Default queue adapter which is well suited only for dev/test changed twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/es.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/es.yml b/config/locales/es.yml --- a/config/locales/es.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/es.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1553,3 +1553,22 @@ text_default_active_job_queue_changed: Default queue adapter which is well suited only for dev/test changed twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/et.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/et.yml b/config/locales/et.yml --- a/config/locales/et.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/et.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1492,3 +1492,22 @@ text_default_active_job_queue_changed: Default queue adapter which is well suited only for dev/test changed twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/eu.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/eu.yml b/config/locales/eu.yml --- a/config/locales/eu.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/eu.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1488,3 +1488,22 @@ text_default_active_job_queue_changed: Default queue adapter which is well suited only for dev/test changed twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/fa.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/fa.yml b/config/locales/fa.yml --- a/config/locales/fa.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/fa.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1421,3 +1421,22 @@ field_name_or_email_or_login: نام، رایانامه یا شناسه کاربری text_default_active_job_queue_changed: آداپتور پیش‌فرض صف که به شکل مناسبی کار می‌کند تنها برای توسعه/آزمون تغییر کرد twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/fi.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/fi.yml b/config/locales/fi.yml --- a/config/locales/fi.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/fi.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1508,3 +1508,22 @@ text_default_active_job_queue_changed: Default queue adapter which is well suited only for dev/test changed twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/fr.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/fr.yml b/config/locales/fr.yml --- a/config/locales/fr.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/fr.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1471,3 +1471,22 @@ field_name_or_email_or_login: Nom, email ou login text_default_active_job_queue_changed: Gestionnaire de file par défaut le plus adapté twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/gl.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/gl.yml b/config/locales/gl.yml --- a/config/locales/gl.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/gl.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1492,3 +1492,22 @@ field_name_or_email_or_login: Nome, correo electrónico ou usuario text_default_active_job_queue_changed: Cambiado o adaptador do sistema de colas por defecto dado que só está pensado para desenvolvemento/probas twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/he.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/he.yml b/config/locales/he.yml --- a/config/locales/he.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/he.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1492,3 +1492,22 @@ text_default_active_job_queue_changed: Default queue adapter which is well suited only for dev/test changed twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/hr.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/hr.yml b/config/locales/hr.yml --- a/config/locales/hr.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/hr.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1484,3 +1484,22 @@ text_default_active_job_queue_changed: Default queue adapter which is well suited only for dev/test changed twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/hu.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/hu.yml b/config/locales/hu.yml --- a/config/locales/hu.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/hu.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1506,3 +1506,22 @@ text_default_active_job_queue_changed: Default queue adapter which is well suited only for dev/test changed twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/id.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/id.yml b/config/locales/id.yml --- a/config/locales/id.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/id.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1489,3 +1489,22 @@ text_default_active_job_queue_changed: Default queue adapter which is well suited only for dev/test changed twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/it.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/it.yml b/config/locales/it.yml --- a/config/locales/it.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/it.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1426,3 +1426,22 @@ text_project_destroy_enter_identifier: "Per confermare, inserisci l'identificativo del progetto (%{identifier}) qui sotto." field_name_or_email_or_login: Nome, email o login twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/ja.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/ja.yml b/config/locales/ja.yml --- a/config/locales/ja.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/ja.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1439,3 +1439,22 @@ field_name_or_email_or_login: 姓名・メールアドレス・ログインID text_default_active_job_queue_changed: キューアダプターがデフォルト (開発・テスト用) 以外のものに変更済み twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/ko.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/ko.yml b/config/locales/ko.yml --- a/config/locales/ko.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/ko.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1508,3 +1508,22 @@ text_default_active_job_queue_changed: Default queue adapter which is well suited only for dev/test changed twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/lt.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/lt.yml b/config/locales/lt.yml --- a/config/locales/lt.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/lt.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1448,3 +1448,22 @@ text_default_active_job_queue_changed: Default queue adapter which is well suited only for dev/test changed twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/lv.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/lv.yml b/config/locales/lv.yml --- a/config/locales/lv.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/lv.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1481,3 +1481,22 @@ text_default_active_job_queue_changed: Default queue adapter which is well suited only for dev/test changed twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/mk.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/mk.yml b/config/locales/mk.yml --- a/config/locales/mk.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/mk.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1487,3 +1487,22 @@ text_default_active_job_queue_changed: Default queue adapter which is well suited only for dev/test changed twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/mn.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/mn.yml b/config/locales/mn.yml --- a/config/locales/mn.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/mn.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1487,3 +1487,22 @@ text_default_active_job_queue_changed: Default queue adapter which is well suited only for dev/test changed twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/nl.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/nl.yml b/config/locales/nl.yml --- a/config/locales/nl.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/nl.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1462,3 +1462,22 @@ text_default_active_job_queue_changed: Default queue adapter which is well suited only for dev/test changed twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/no.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/no.yml b/config/locales/no.yml --- a/config/locales/no.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/no.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1477,3 +1477,22 @@ text_default_active_job_queue_changed: Default queue adapter which is well suited only for dev/test changed twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/pl.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/pl.yml b/config/locales/pl.yml --- a/config/locales/pl.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/pl.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1431,3 +1431,22 @@ text_default_active_job_queue_changed: Default queue adapter which is well suited only for dev/test changed twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/pt-BR.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml --- a/config/locales/pt-BR.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/pt-BR.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1492,3 +1492,22 @@ text_default_active_job_queue_changed: Adaptador de fila padrão que é bem adequado apenas para dev/test foi alterado twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/pt.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/pt.yml b/config/locales/pt.yml --- a/config/locales/pt.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/pt.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1480,3 +1480,22 @@ text_default_active_job_queue_changed: Default queue adapter which is well suited only for dev/test changed twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/ro.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/ro.yml b/config/locales/ro.yml --- a/config/locales/ro.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/ro.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1482,3 +1482,22 @@ text_default_active_job_queue_changed: Default queue adapter which is well suited only for dev/test changed twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/ru.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/ru.yml b/config/locales/ru.yml --- a/config/locales/ru.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/ru.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1557,3 +1557,22 @@ field_name_or_email_or_login: Имя, email или логин text_default_active_job_queue_changed: Изменен адаптер очереди по умолчанию, который хорошо подходит только для разработки/тестирования twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/sk.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/sk.yml b/config/locales/sk.yml --- a/config/locales/sk.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/sk.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1476,3 +1476,22 @@ text_default_active_job_queue_changed: Default queue adapter which is well suited only for dev/test changed twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/sl.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/sl.yml b/config/locales/sl.yml --- a/config/locales/sl.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/sl.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1487,3 +1487,22 @@ text_default_active_job_queue_changed: Default queue adapter which is well suited only for dev/test changed twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/sq.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/sq.yml b/config/locales/sq.yml --- a/config/locales/sq.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/sq.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1449,3 +1449,22 @@ text_default_active_job_queue_changed: Default queue adapter which is well suited only for dev/test changed twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/sr-YU.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/sr-YU.yml b/config/locales/sr-YU.yml --- a/config/locales/sr-YU.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/sr-YU.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1489,3 +1489,22 @@ text_default_active_job_queue_changed: Default queue adapter which is well suited only for dev/test changed twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/sr.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/sr.yml b/config/locales/sr.yml --- a/config/locales/sr.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/sr.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1488,3 +1488,22 @@ text_default_active_job_queue_changed: Default queue adapter which is well suited only for dev/test changed twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/sv.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/sv.yml b/config/locales/sv.yml --- a/config/locales/sv.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/sv.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1520,3 +1520,22 @@ text_default_active_job_queue_changed: Default queue adapter which is well suited only for dev/test changed twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/ta-IN.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/ta-IN.yml b/config/locales/ta-IN.yml --- a/config/locales/ta-IN.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/ta-IN.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1486,3 +1486,22 @@ text_default_active_job_queue_changed: Default queue adapter which is well suited only for dev/test changed twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/th.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/th.yml b/config/locales/th.yml --- a/config/locales/th.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/th.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1483,3 +1483,22 @@ text_default_active_job_queue_changed: Default queue adapter which is well suited only for dev/test changed twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/tr.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/tr.yml b/config/locales/tr.yml --- a/config/locales/tr.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/tr.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1486,3 +1486,22 @@ text_default_active_job_queue_changed: Default queue adapter which is well suited only for dev/test changed twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/uk.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/uk.yml b/config/locales/uk.yml --- a/config/locales/uk.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/uk.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1475,3 +1475,22 @@ text_default_active_job_queue_changed: Default queue adapter which is well suited only for dev/test changed twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/vi.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/vi.yml b/config/locales/vi.yml --- a/config/locales/vi.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/vi.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1492,3 +1492,22 @@ text_default_active_job_queue_changed: Default queue adapter which is well suited only for dev/test changed twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/zh-TW.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml --- a/config/locales/zh-TW.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/zh-TW.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1504,3 +1504,22 @@ field_name_or_email_or_login: 姓名, 電子郵件或帳戶名稱 text_default_active_job_queue_changed: 已變更僅適用於開發/測試環境的佇列適配器 (queue adapter) twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/locales/zh.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/locales/zh.yml b/config/locales/zh.yml --- a/config/locales/zh.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/locales/zh.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -1423,3 +1423,22 @@ field_name_or_email_or_login: 姓名、电子邮件或登录名 text_default_active_job_queue_changed: 仅适用于开发/测试环境的默认队列适配器已改变 twofa_already_setup: Two-factor authentication already set up + + label_webhook_plural: "Webhooks" + label_webhook_new: "New webhook" + label_webhook_edit: "Edit webhook" + label_webhook_events: "Events" + webhook_events_issue: "Issues" + webhook_events_issue_created: "Issue created" + webhook_events_issue_updated: "Issue updated" + webhook_events_issue_closed: "Issue closed" + webhook_events_issue_deleted: "Issue deleted" + webhook_events_wiki_page: "Wiki pages" + webhook_events_wiki_page_created: "Wiki page created" + webhook_events_wiki_page_updated: "Wiki page updated" + webhook_events_wiki_page_deleted: "Wiki page deleted" + webhook_url_info: "Redmine will send a POST request to this URL when one of the selected events in one of the selected projects occurs." + webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the X-Redmine-Signature-256 header." + permission_use_webhooks: "Use webhooks" + setting_webhooks_enabled: "Enable webhooks" + label_integrations: "Integrations" Index: config/routes.rb IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/routes.rb b/config/routes.rb --- a/config/routes.rb (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/routes.rb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -317,6 +317,7 @@ get 'attachments/download/:id', :to => 'attachments#download', :id => /\d+/ get 'attachments/thumbnail/:id(/:size)', :to => 'attachments#thumbnail', :id => /\d+/, :size => /\d+/, :as => 'thumbnail' resources :attachments, :only => [:show, :update, :destroy] + resources :webhooks, :except => [:show] # register plugin object types with ObjectTypeConstraint.register_object_type(PluginModel.name.underscore.pluralize') constraints Redmine::Acts::Attachable::ObjectTypeConstraint do Index: config/settings.yml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/config/settings.yml b/config/settings.yml --- a/config/settings.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/config/settings.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -322,6 +322,9 @@ jsonp_enabled: default: 0 security_notifications: 1 +webhooks_enabled: + default: 0 + security_notifications: 1 default_notification_option: default: 'only_my_events' emails_header: Index: db/migrate/20231120120000_create_webhooks.rb IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/db/migrate/20231120120000_create_webhooks.rb b/db/migrate/20231120120000_create_webhooks.rb new file mode 100644 --- /dev/null (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) +++ b/db/migrate/20231120120000_create_webhooks.rb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +class CreateWebhooks < ActiveRecord::Migration[6.1] + def change + create_table :webhooks do |t| + t.references :user, :null => false + t.string :url, :limit => 2000, :null => false + t.string :secret, :limit => 255 + t.boolean :active, :default => true, :null => false + t.text :events + t.timestamps + end + end +end Index: db/migrate/20231120120100_create_projects_webhooks.rb IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/db/migrate/20231120120100_create_projects_webhooks.rb b/db/migrate/20231120120100_create_projects_webhooks.rb new file mode 100644 --- /dev/null (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) +++ b/db/migrate/20231120120100_create_projects_webhooks.rb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class CreateProjectsWebhooks < ActiveRecord::Migration[6.1] + def change + create_table :projects_webhooks, :id => false do |t| + t.references :project, :null => false + t.references :webhook, :null => false + end + + add_index :projects_webhooks, [:project_id, :webhook_id], :unique => true, :name => 'index_projects_webhooks_on_project_and_webhook' + end +end Index: db/migrate/20231120120200_create_trackers_webhooks.rb IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/db/migrate/20231120120200_create_trackers_webhooks.rb b/db/migrate/20231120120200_create_trackers_webhooks.rb new file mode 100644 --- /dev/null (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) +++ b/db/migrate/20231120120200_create_trackers_webhooks.rb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +class CreateTrackersWebhooks < ActiveRecord::Migration[6.1] + def change + create_table :trackers_webhooks, :id => false do |t| + t.references :tracker, :null => false + t.references :webhook, :null => false + end + + add_index :trackers_webhooks, [:tracker_id, :webhook_id], :unique => true, + :name => 'index_trackers_webhooks_on_tracker_and_webhook' + end +end Index: doc/webhooks.md IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/doc/webhooks.md b/doc/webhooks.md new file mode 100644 --- /dev/null (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) +++ b/doc/webhooks.md (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -0,0 +1,109 @@ +# Webhooks + +Redmine can send webhooks whenever issues or wiki pages change. A webhook delivers +an HTTP POST request to an external service with a JSON payload that describes +the event. + +## Enabling webhooks +1. Go to **Administration → Settings → API** and enable **Enable webhooks**. +2. Ensure background jobs are running so delivery jobs can be processed. + Webhook deliveries are enqueued as Active Job jobs, so they need a queue + runner: + - **Default (development/test)**: Rails uses the `:async` adapter, which + runs jobs in background threads inside the Redmine process. No extra + setup is required, but jobs stop if the process stops. + - **Production**: Configure a persistent queue adapter (for example + `:delayed_job` or `:sidekiq`) and run a worker process. With + `:delayed_job`, start a worker using `bundle exec rake jobs:work` and + supervise it via systemd, a service manager, or a cron-launched wrapper + so it restarts if it exits. With Sidekiq, run the Sidekiq process and + point it at the same Redis instance you configured for Active Job. Remember + to restart Sidekiq after patching the code! + +## Permissions +Only users with the **Use webhooks** permission can create webhooks. The current +implementation grants this permission to project members. Each webhook also +respects the visibility of projects and issues for the webhook owner when +selecting projects and when delivering events. + +## Creating a webhook +1. Open **My account → Webhooks**. +2. Click **New webhook**, then fill in the URL, optional secret, events, + projects, and (for issue events) **trackers**. +3. Save the webhook. Active webhooks will enqueue a delivery job for each + matching event. + +### Tracker filters for issue events +- Trackers determine which issues will trigger `issue.*` events. If you select + any issue events, you must pick at least one tracker; issues using other + trackers are skipped. +- Choose only the trackers you care about to reduce traffic. When your project + list changes, the tracker list automatically narrows to the trackers used by + the projects you can select. + +### Secrets and signatures +If you set a secret, Redmine includes an `X-Redmine-Signature-256` header. It is +an HMAC SHA-256 signature of the request body using the provided secret. + +## Events and payload +Supported events: +- `issue.created`, `issue.updated`, `issue.closed`, `issue.deleted` +- `wiki_page.created`, `wiki_page.updated`, `wiki_page.deleted` + +### When does a webhook fire? +- The event is enqueued after the database transaction commits (so failed + saves do not send anything). +- The webhook must be **active** and belong to a **user who is still active**. +- The webhook’s user must have the **Use webhooks** permission on the project. +- The issue or wiki page must be **visible to the webhook owner**; if the owner + cannot see the record, the event is skipped. +- For issue events, the issue’s **tracker must be selected** on the webhook. +- `issue.closed` fires only when an issue changes from a non-closed status to a + closed status. +- Events are triggered regardless of which user performs the change. For + example, if Alice owns a webhook for Project A, and Bob updates or deletes an + issue in Project A, Alice’s webhook will still fire as long as she can see + the issue and has permission on that project. + +The request body is a JSON object with: +- `type`: the event name, for example `issue.updated` +- `timestamp`: ISO8601 timestamp of the event +- `data`: object with the payload + +Issue payloads include the Redmine API representation of the issue, and for +updates they also include the last journal entry with visible details for the +webhook owner. + +Wiki payloads include the page and, for updates, the version number. + +## Safety checks +Webhook endpoints must use HTTP(S) and cannot target loopback, link-local, or +private addresses unless they are explicitly allowlisted. You can further +restrict or block destinations: + +- **Blocklist** (`webhook_blocklist` in `config/configuration.yml`): a list of + hostnames (exact or `*.example.com` wildcards) or IP/CIDR entries that are + rejected. This is useful to prevent callbacks to internal hosts. +- **Allowlist** (`webhook_allowlist` in `config/configuration.yml`): if set, + only hostnames or IPs/CIDRs that match the list are accepted. A hostname + entry covers all IPs it resolves to (including private ranges if you choose + to allow them). Both lists use the same syntax, for example: + + ```yaml + production: + webhook_allowlist: + - hooks.example.com # exact host + - "*.hooks.example.org" # wildcard host + - 203.0.113.0/24 # CIDR range + - 10.254.80.0/24 # allow a private range (use with care) + webhook_blocklist: + - 10.0.0.0/8 + - intranet.example.com + ``` + +Leading and trailing whitespace around entries is ignored. If no allowlist is +defined, any public host passes unless blocked or private. + +Endpoints are validated when saved and again before delivery. The validation +error includes the reason (for example, blocked host, outside allowlist, or +private address). A failed delivery logs the error but does not raise to users. Index: lib/redmine/preparation.rb IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/lib/redmine/preparation.rb b/lib/redmine/preparation.rb --- a/lib/redmine/preparation.rb (revision f9673061812c2f08e3775f61597c44826a430ea3) +++ b/lib/redmine/preparation.rb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -48,6 +48,7 @@ # Queries map.permission :manage_public_queries, {:queries => [:new, :create, :edit, :update, :destroy]}, :require => :member map.permission :save_queries, {:queries => [:new, :create, :edit, :update, :destroy]}, :require => :loggedin + map.permission :use_webhooks, {}, :require => :member map.project_module :issue_tracking do |map| # Issues Index: test/functional/webhooks_controller_test.rb IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/test/functional/webhooks_controller_test.rb b/test/functional/webhooks_controller_test.rb new file mode 100644 --- /dev/null (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) +++ b/test/functional/webhooks_controller_test.rb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +require File.expand_path('../../test_helper', __FILE__) + +class WebhooksControllerTest < Redmine::ControllerTest + fixtures :projects, :users, :roles, :members, :member_roles, :trackers + + def setup + %w[Manager Developer].each do |role_name| + Role.find_by_name(role_name).add_permission! :use_webhooks + end + end + + def test_admin_can_view_all_webhooks + with_webhooks_enabled do + hook_one = create_webhook_for(User.find(2)) + hook_two = create_webhook_for(User.find(3), 'https://example.net') + + @request.session[:user_id] = 1 + get :index + + assert_response :success + assert_select 'table.list tbody tr', 2 + assert_select 'td.username', :text => hook_one.user.name + assert_select 'td.username', :text => hook_two.user.name + end + end + + def test_admin_can_edit_other_user_webhook + with_webhooks_enabled do + webhook = create_webhook_for(User.find(2)) + + @request.session[:user_id] = 1 + get :edit, :params => {:id => webhook.id} + + assert_response :success + assert_select 'form[action=?]', webhook_path(webhook) + end + end + + private + + def with_webhooks_enabled + with_settings :webhooks_enabled => '1' do + yield + end + end + + def create_webhook_for(user, url = 'https://example.com') + project = Project.find(1) + role = Role.find_by_name('Manager') + tracker = Tracker.find(1) + + unless user.member_of?(project) + Member.create!(:project => project, :principal => user, :roles => [role]) + end + + project.trackers << tracker unless project.trackers.include?(tracker) + + Webhook.create!( + :user => user, + :url => url, + :projects => [project], + :trackers => [tracker], + :events => ['issue.created'] + ) + end +end Index: test/unit/webhook_endpoint_validator_test.rb IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/test/unit/webhook_endpoint_validator_test.rb b/test/unit/webhook_endpoint_validator_test.rb new file mode 100644 --- /dev/null (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) +++ b/test/unit/webhook_endpoint_validator_test.rb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -0,0 +1,104 @@ +# frozen_string_literal: true + +require File.expand_path('../../test_helper', __FILE__) + +class WebhookEndpointValidatorTest < ActiveSupport::TestCase + def teardown + WebhookEndpointValidator.unstub(:blocked_targets) + WebhookEndpointValidator.unstub(:allowed_targets) + WebhookEndpointValidator.unstub(:resolve_addresses) + rescue Mocha::NotInitializedError + # ignore when nothing was stubbed + end + + def test_rejects_private_ip_addresses + WebhookEndpointValidator.stubs(:blocked_targets).returns(:ips => [], :host_pattern => nil) + WebhookEndpointValidator.stubs(:allowed_targets).returns(:ips => [], :host_pattern => nil) + WebhookEndpointValidator.stubs(:resolve_addresses).returns(['192.168.0.10']) + + assert_not WebhookEndpointValidator.safe_webhook_uri?('http://example.test') + end + + def test_allows_private_ip_addresses_on_allowlist + allowlist = {:ips => [IPAddr.new('10.254.80.0/24')], :host_pattern => nil} + WebhookEndpointValidator.stubs(:blocked_targets).returns(:ips => [], :host_pattern => nil) + WebhookEndpointValidator.stubs(:allowed_targets).returns(allowlist) + WebhookEndpointValidator.stubs(:resolve_addresses).returns(['10.254.80.25']) + + assert WebhookEndpointValidator.safe_webhook_uri?('https://fileservermonitoring.geoxyz.be') + end + + def test_allows_hosts_on_allowlist_pattern + WebhookEndpointValidator.stubs(:blocked_targets).returns(:ips => [], :host_pattern => nil) + WebhookEndpointValidator.stubs(:allowed_targets).returns( + :ips => [], + :host_pattern => Regexp.new('\\Aallowed\\.example\\.org\\z') + ) + WebhookEndpointValidator.stubs(:resolve_addresses).returns(['203.0.113.5']) + + assert WebhookEndpointValidator.safe_webhook_uri?('https://allowed.example.org') + assert_not WebhookEndpointValidator.safe_webhook_uri?('https://other.example.org') + end + + def test_allowlisted_hostname_does_not_require_ip_entry + allowlist = {:ips => [], :host_pattern => Regexp.new('\\Afileservermonitoring\\.geoxyz\\.be\\z')} + WebhookEndpointValidator.stubs(:blocked_targets).returns(:ips => [], :host_pattern => nil) + WebhookEndpointValidator.stubs(:allowed_targets).returns(allowlist) + WebhookEndpointValidator.stubs(:resolve_addresses).returns(['203.0.113.7']) + + assert WebhookEndpointValidator.safe_webhook_uri?('https://fileservermonitoring.geoxyz.be/redmine/webhook') + end + + def test_allows_hosts_with_ip_allowlist_entry + allowlist = {:ips => [IPAddr.new('203.0.113.0/24')], :host_pattern => nil} + WebhookEndpointValidator.stubs(:blocked_targets).returns(:ips => [], :host_pattern => nil) + WebhookEndpointValidator.stubs(:allowed_targets).returns(allowlist) + WebhookEndpointValidator.stubs(:resolve_addresses).returns(['203.0.113.99']) + + assert WebhookEndpointValidator.safe_webhook_uri?('https://example.net') + + WebhookEndpointValidator.stubs(:resolve_addresses).returns(['198.51.100.10']) + assert_not WebhookEndpointValidator.safe_webhook_uri?('https://example.net') + end + + def test_hostname_allowlist_accepts_resolved_ip + allowlist = {:ips => [], :host_pattern => Regexp.new('\\Aallowed\\.example\\.org\\z')} + WebhookEndpointValidator.stubs(:blocked_targets).returns(:ips => [], :host_pattern => nil) + WebhookEndpointValidator.stubs(:allowed_targets).returns(allowlist) + WebhookEndpointValidator.stubs(:resolve_addresses).returns(['203.0.113.10']) + + assert WebhookEndpointValidator.safe_webhook_uri?('https://allowed.example.org/webhook') + end + + def test_hostname_not_on_allowlist_is_rejected + allowlist = {:ips => [], :host_pattern => Regexp.new('\\Aallowed\\.example\\.org\\z')} + WebhookEndpointValidator.stubs(:blocked_targets).returns(:ips => [], :host_pattern => nil) + WebhookEndpointValidator.stubs(:allowed_targets).returns(allowlist) + WebhookEndpointValidator.stubs(:resolve_addresses).returns(['203.0.113.20']) + + assert_not WebhookEndpointValidator.safe_webhook_uri?('https://other.example.org/webhook') + end + + def test_hostname_allowlist_allows_private_ip_when_host_is_allowlisted + allowlist = {:ips => [], :host_pattern => Regexp.new('\\Ainternal\\.example\\.org\\z')} + WebhookEndpointValidator.stubs(:blocked_targets).returns(:ips => [], :host_pattern => nil) + WebhookEndpointValidator.stubs(:allowed_targets).returns(allowlist) + WebhookEndpointValidator.stubs(:resolve_addresses).returns(['192.168.50.10']) + + assert WebhookEndpointValidator.safe_webhook_uri?('https://internal.example.org/webhook') + end + + def test_allows_hostnames_with_whitespace_in_allowlist_entries + WebhookEndpointValidator.unstub(:allowed_targets) + WebhookEndpointValidator.instance_variable_set(:@allowed_targets, nil) + + Redmine::Configuration.stubs(:[]).with('webhook_allowlist').returns([' fileservermonitoring.geoxyz.be ']) + WebhookEndpointValidator.stubs(:blocked_targets).returns(:ips => [], :host_pattern => nil) + WebhookEndpointValidator.stubs(:resolve_addresses).returns(['203.0.113.5']) + + assert WebhookEndpointValidator.safe_webhook_uri?('https://fileservermonitoring.geoxyz.be/redmine/webhook') + ensure + WebhookEndpointValidator.instance_variable_set(:@allowed_targets, nil) + Redmine::Configuration.unstub(:[]) + end +end Index: test/unit/webhook_test.rb IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/test/unit/webhook_test.rb b/test/unit/webhook_test.rb new file mode 100644 --- /dev/null (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) +++ b/test/unit/webhook_test.rb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +require File.expand_path('../../test_helper', __FILE__) + +class WebhookTest < ActiveSupport::TestCase + fixtures :projects, :users, :members, :member_roles, :roles, :trackers, :issues, :issue_statuses + + def setup + %w[Manager Developer].each do |role_name| + Role.find_by_name(role_name).add_permission! :use_webhooks + end + end + + def test_issue_events_require_trackers + webhook = Webhook.new( + :user => User.find(2), + :url => 'https://example.com', + :projects => [Project.find(1)], + :events => ['issue.created'] + ) + + assert webhook.invalid? + assert_includes webhook.errors.attribute_names, :trackers + end + + def test_hooks_for_filters_by_tracker + webhook = Webhook.create!( + :user => User.find(1), + :url => 'https://example.com', + :projects => [Project.find(1)], + :trackers => [Tracker.find(1)], + :events => ['issue.created'] + ) + + allowed_issue = Issue.find(1) # tracker_id 1 + filtered_issue = Issue.find(2) # tracker_id 2 + + assert_equal [webhook], Webhook.hooks_for('issue.created', allowed_issue) + assert_equal [], Webhook.hooks_for('issue.created', filtered_issue) + end + + def test_trigger_enqueues_jobs_when_enabled + webhook = Webhook.create!( + :user => User.find(1), + :url => 'https://example.com', + :projects => [Project.find(1)], + :trackers => [Tracker.find(1)], + :events => ['issue.created'] + ) + + issue = Issue.find(1) + + with_settings :webhooks_enabled => '1' do + WebhookJob.expects(:perform_later).with(webhook.id, includes('issue.created')).once + + Webhook.trigger('issue.created', issue) + end + end + + def test_issue_closed_event_is_triggered_on_status_transition + issue = Issue.find(1) + + Webhook.expects(:trigger).with('issue.updated', issue).once + Webhook.expects(:trigger).with('issue.closed', issue).once + + issue.update_attribute(:status_id, IssueStatus.find_by(:is_closed => true).id) + end + + def test_issue_closed_event_not_triggered_without_status_change + issue = Issue.find(1) + + Webhook.expects(:trigger).with('issue.updated', issue).once + Webhook.expects(:trigger).with('issue.closed', anything).never + + issue.update_attribute(:subject, 'Changed without status update') + end +end