Project

General

Profile

Feature #29664 » full patch based on 5.1-stable.patch

Jan Catrysse, 2025-12-02 16:24

View differences:

Gemfile (revision f9673061812c2f08e3775f61597c44826a430ea3) → Gemfile (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da)
15 15
gem 'rbpdf', '~> 1.21.3'
16 16
gem 'addressable'
17 17
gem 'rubyzip', '~> 2.3.0'
18
gem 'rest-client', '~> 2.1'
18 19

  
19 20
#  Ruby Standard Gems
20 21
gem 'csv', '~> 3.2.6'
/dev/null (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) → app/controllers/webhooks_controller.rb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da)
1
# frozen_string_literal: true
2

  
3
class WebhooksController < ApplicationController
4
  self.main_menu = false
5

  
6
  before_action :require_login
7
  before_action :check_enabled
8
  before_action :authorize
9
  before_action :find_webhook, :only => [:edit, :update, :destroy]
10

  
11
  require_sudo_mode :create, :update, :destroy
12

  
13
  helper :projects
14

  
15
  def index
16
    @webhooks = webhooks_scope.order(:url)
17
  end
18

  
19
  def new
20
    @webhook = User.current.webhooks.build
21
  end
22

  
23
  def edit
24
  end
25

  
26
  def create
27
    @webhook = User.current.webhooks.build(webhook_params)
28
    if @webhook.save
29
      redirect_to webhooks_path
30
    else
31
      render :new
32
    end
33
  end
34

  
35
  def update
36
    if @webhook.update(webhook_params)
37
      redirect_to webhooks_path
38
    else
39
      render :edit
40
    end
41
  end
42

  
43
  def destroy
44
    @webhook.destroy
45
    redirect_to webhooks_path
46
  end
47

  
48
  private
49

  
50
  def webhooks_scope
51
    if User.current.admin?
52
      Webhook.includes(:user).references(:users)
53
    else
54
      User.current.webhooks
55
    end
56
  end
57

  
58
  def webhook_params
59
    attrs = params.require(:webhook).permit(:url, :secret, :active, :events => [], :project_ids => [], :tracker_ids => [])
60
    attrs[:events] = Array(attrs[:events]).reject(&:blank?)
61
    attrs[:project_ids] = Array(attrs[:project_ids]).reject(&:blank?)
62
    attrs[:tracker_ids] = Array(attrs[:tracker_ids]).reject(&:blank?)
63
    attrs
64
  end
65

  
66
  def find_webhook
67
    @webhook = webhooks_scope.find_by(:id => params[:id])
68
    render_404 unless @webhook
69
  end
70

  
71
  def authorize
72
    deny_access unless User.current.allowed_to?(:use_webhooks, nil, :global => true)
73
  end
74

  
75
  def check_enabled
76
    render_403 unless Webhook.enabled?
77
  end
78
end
/dev/null (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) → app/jobs/webhook_job.rb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da)
1
# frozen_string_literal: true
2

  
3
class WebhookJob < ApplicationJob
4
  def perform(hook_id, payload_json)
5
    previous_user = User.current
6
    hook = Webhook.find_by(:id => hook_id)
7
    return unless hook&.user&.active?
8

  
9
    User.current = hook.user
10
    hook.call(payload_json)
11
  ensure
12
    User.current = previous_user
13
  end
14
end
app/models/issue.rb (revision f9673061812c2f08e3775f61597c44826a430ea3) → app/models/issue.rb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da)
127 127
  # https://api.rubyonrails.org/v5.2.3/classes/ActiveSupport/Callbacks/ClassMethods.html#method-i-set_callback
128 128
  after_create_commit :send_notification
129 129
  after_create_commit :add_auto_watcher
130
  after_create_commit  -> {Webhook.trigger('issue.created', self)}
131
  after_update_commit  :trigger_issue_webhooks
132
  after_destroy_commit -> {Webhook.trigger('issue.deleted', self)}
130 133
  after_commit :create_parent_issue_journal
131 134

  
132 135
  # Returns a SQL conditions string used to find all issues visible by the specified user
......
164 167
    end
165 168
  end
166 169

  
170
  def trigger_issue_webhooks
171
    Webhook.trigger('issue.updated', self)
172

  
173
    return unless saved_change_to_status_id?
174

  
175
    previous_status = IssueStatus.find_by(:id => saved_change_to_status_id.first)
176
    current_status = status
177

  
178
    return unless current_status&.is_closed?
179
    return if previous_status&.is_closed?
180

  
181
    Webhook.trigger('issue.closed', self)
182
  end
183

  
167 184
  # Returns true if usr or current user is allowed to view the issue
168 185
  def visible?(usr=nil)
169 186
    (usr || User.current).allowed_to?(:view_issues, self.project) do |role, user|
app/models/project.rb (revision f9673061812c2f08e3775f61597c44826a430ea3) → app/models/project.rb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da)
47 47
  has_many :queries, :dependent => :destroy
48 48
  has_many :documents, :dependent => :destroy
49 49
  has_many :news, lambda {includes(:author)}, :dependent => :destroy
50
  has_and_belongs_to_many :webhooks
50 51
  has_many :issue_categories, lambda {order(:name)}, :dependent => :delete_all
51 52
  has_many :boards, lambda {order(:position)}, :inverse_of => :project, :dependent => :destroy
52 53
  has_one :repository, lambda {where(:is_default => true)}
app/models/user.rb (revision f9673061812c2f08e3775f61597c44826a430ea3) → app/models/user.rb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da)
91 91
  has_one :api_token, lambda {where "#{table.name}.action='api'"}, :class_name => 'Token'
92 92
  has_one :email_address, lambda {where :is_default => true}, :autosave => true
93 93
  has_many :email_addresses, :dependent => :delete_all
94
  has_many :webhooks, :dependent => :destroy
94 95
  belongs_to :auth_source
95 96

  
96 97
  scope :logged, lambda {where("#{User.table_name}.status <> #{STATUS_ANONYMOUS}")}
/dev/null (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) → app/models/webhook.rb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da)
1
# frozen_string_literal: true
2

  
3
require 'rest-client'
4

  
5
class Webhook < ApplicationRecord
6
  belongs_to :user
7
  has_and_belongs_to_many :projects
8
  has_and_belongs_to_many :trackers
9

  
10
  serialize :events, Array
11

  
12
  validates :url, :presence => true,
13
                  :length => {:maximum => 2000},
14
                  :webhook_endpoint => true
15
  validates :secret, :length => {:maximum => 255}, :allow_blank => true
16
  validate :validate_events
17
  validate :validate_projects
18
  validate :validate_trackers
19

  
20
  scope :active, -> {where(:active => true)}
21

  
22
  before_validation :filter_projects
23
  before_validation :filter_trackers
24
  after_initialize :ensure_events_array
25

  
26
  def self.enabled?
27
    Setting.webhooks_enabled?
28
  end
29

  
30
  def self.trigger(event, object)
31
    return unless enabled?
32

  
33
    hooks_for(event, object).each do |hook|
34
      payload = hook.payload(event, object)
35
      WebhookJob.perform_later(hook.id, payload.to_json)
36
    end
37
  end
38

  
39
  def self.hooks_for(event, object)
40
    project = object.respond_to?(:project) ? object.project : nil
41
    return [] unless project
42

  
43
    active
44
      .joins(:projects, :user)
45
      .where(:projects => {:id => project.id})
46
      .where(:users => {:status => User::STATUS_ACTIVE})
47
      .select do |hook|
48
        hook.events.include?(event) &&
49
          (!object.respond_to?(:visible?) || object.visible?(hook.user)) &&
50
          hook.user.allowed_to?(:use_webhooks, project) &&
51
          hook.tracker_allowed?(object)
52
      end
53
  end
54

  
55
  def setable_projects
56
    member = user || User.current
57
    return Project.none unless member
58

  
59
    Project.allowed_to(member, :use_webhooks).sorted
60
  end
61

  
62
  def setable_events
63
    WebhookPayload::EVENTS
64
  end
65

  
66
  def setable_event_names
67
    setable_events.flat_map {|type, actions| actions.map {|action| "#{type}.#{action}"}}
68
  end
69

  
70
  def setable_trackers
71
    project_trackers = setable_projects.includes(:trackers).flat_map(&:trackers)
72
    project_trackers.uniq.sort_by(&:position)
73
  end
74

  
75
  def payload(event, object)
76
    WebhookPayload.new(event, object, user).to_h
77
  end
78

  
79
  def call(payload_json)
80
    Executor.new(url, payload_json, secret).call
81
    true
82
  rescue => e
83
    Rails.logger.warn do
84
      "Webhook delivery failed: #{e.class}: #{e.message}\n#{Array(e.backtrace).join("\n")}"
85
    end
86
    false
87
  end
88

  
89
  class Executor
90
    def initialize(url, payload, secret)
91
      @url = url
92
      @payload = payload
93
      @secret = secret
94
    end
95

  
96
    def call
97
      raise URI::BadURIError unless WebhookEndpointValidator.safe_webhook_uri?(@url)
98

  
99
      headers = {
100
        :accept => '*/*',
101
        :content_type => :json,
102
        :user_agent => 'Redmine'
103
      }
104
      headers['X-Redmine-Signature-256'] = compute_signature if @secret.present?
105

  
106
      Rails.logger.debug {"Webhook: POST #{@url}"}
107
      RestClient.post(@url, @payload, headers)
108
    end
109

  
110
    private
111

  
112
    def compute_signature
113
      'sha256=' + OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), @secret, @payload)
114
    end
115
  end
116

  
117
  def tracker_allowed?(object)
118
    return true unless object.respond_to?(:tracker_id)
119
    tracker_ids.blank? || tracker_ids.include?(object.tracker_id)
120
  end
121

  
122
  private
123

  
124
  def ensure_events_array
125
    self.events ||= []
126
  end
127

  
128
  def filter_projects
129
    return if project_ids.blank?
130

  
131
    allowed_ids = setable_projects.map(&:id)
132
    self.project_ids = project_ids & allowed_ids
133
  end
134

  
135
  def filter_trackers
136
    return if tracker_ids.blank?
137

  
138
    allowed_ids = setable_trackers.map(&:id)
139
    self.tracker_ids = tracker_ids & allowed_ids
140
  end
141

  
142
  def validate_events
143
    self.events = Array(events).reject(&:blank?)
144
    if events.blank? || (events - setable_event_names).any?
145
      errors.add(:events, :invalid)
146
    end
147
  end
148

  
149
  def validate_projects
150
    if project_ids.blank?
151
      errors.add(:projects, :blank)
152
    end
153
  end
154

  
155
  def validate_trackers
156
    self.tracker_ids = tracker_ids & setable_trackers.map(&:id)
157

  
158
    if issue_events_selected? && tracker_ids.blank?
159
      errors.add(:trackers, :blank)
160
    end
161
  end
162

  
163
  def issue_events_selected?
164
    Array(events).any? {|event| event.to_s.start_with?('issue.')}
165
  end
166
end
/dev/null (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) → app/models/webhook_payload.rb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da)
1
# frozen_string_literal: true
2

  
3
class WebhookPayload
4
  EVENTS = {
5
    :issue => %w[created updated closed deleted],
6
    :wiki_page => %w[created updated deleted]
7
  }.freeze
8

  
9
  attr_reader :event, :object, :user
10

  
11
  def initialize(event, object, user)
12
    @event = event
13
    @object = object
14
    @user = user
15
  end
16

  
17
  def to_h
18
    type, action = event.split('.')
19
    type = type&.to_sym
20
    unless EVENTS[type]&.include?(action)
21
      raise ArgumentError, "Unsupported webhook event: #{event}"
22
    end
23

  
24
    payload, timestamp = send("#{type}_payload", action)
25
    timestamp ||= Time.current
26
    {
27
      :type => event,
28
      :timestamp => timestamp.utc.iso8601,
29
      :data => payload
30
    }
31
  end
32

  
33
  private
34

  
35
  def issue_payload(action)
36
    issue = object
37
    journal = %w[updated closed].include?(action) ? issue.current_journal : nil
38

  
39
    payload = {
40
      :issue => issue_hash(issue)
41
    }
42
    payload[:journal] = journal_hash(journal) if journal
43

  
44
    timestamp =
45
      case action
46
      when 'created'
47
        issue.created_on
48
      when 'closed'
49
        issue.closed_on || Time.current
50
      when 'deleted'
51
        Time.current
52
      else
53
        journal&.created_on || issue.updated_on
54
      end
55

  
56
    [payload, timestamp]
57
  end
58

  
59
  def wiki_page_payload(action)
60
    page, content = wiki_page_and_content
61
    payload = {
62
      :wiki_page => wiki_page_hash(page)
63
    }
64
    payload[:content] = wiki_content_hash(content) if content
65

  
66
    timestamp =
67
      case action
68
      when 'created'
69
        content&.created_on || Time.current
70
      when 'deleted'
71
        Time.current
72
      else
73
        content&.updated_on || Time.current
74
      end
75

  
76
    [payload, timestamp]
77
  end
78

  
79
  def issue_hash(issue)
80
    hash = {
81
      :id => issue.id,
82
      :subject => issue.subject,
83
      :description => issue.description,
84
      :is_private => issue.is_private?,
85
      :project => project_hash(issue.project),
86
      :tracker => reference_hash(issue.tracker),
87
      :status => status_hash(issue.status),
88
      :priority => reference_hash(issue.priority),
89
      :author => user_hash(issue.author),
90
      :assigned_to => user_hash(issue.assigned_to),
91
      :category => reference_hash(issue.category),
92
      :fixed_version => reference_hash(issue.fixed_version),
93
      :parent => issue.parent_id ? {:id => issue.parent_id} : nil,
94
      :start_date => issue.start_date&.to_s,
95
      :due_date => issue.due_date&.to_s,
96
      :done_ratio => issue.done_ratio,
97
      :estimated_hours => issue.estimated_hours,
98
      :total_estimated_hours => issue.total_estimated_hours,
99
      :created_on => issue.created_on&.iso8601,
100
      :updated_on => issue.updated_on&.iso8601,
101
      :closed_on => issue.closed_on&.iso8601
102
    }
103

  
104
    if user&.allowed_to?(:view_time_entries, issue.project)
105
      hash[:spent_hours] = issue.spent_hours
106
      hash[:total_spent_hours] = issue.total_spent_hours
107
    end
108

  
109
    custom_fields = issue.visible_custom_field_values(user).map do |value|
110
      {
111
        :id => value.custom_field_id,
112
        :name => value.custom_field.name,
113
        :value => value.value
114
      }
115
    end
116
    hash[:custom_fields] = custom_fields if custom_fields.any?
117

  
118
    hash.compact
119
  end
120

  
121
  def journal_hash(journal)
122
    return unless journal
123

  
124
    {
125
      :id => journal.id,
126
      :notes => journal.notes,
127
      :created_on => journal.created_on&.iso8601,
128
      :user => user_hash(journal.user),
129
      :details => journal.visible_details(user).map do |detail|
130
        {
131
          :property => detail.property,
132
          :prop_key => detail.prop_key,
133
          :old_value => detail.old_value,
134
          :value => detail.value
135
        }
136
      end
137
    }
138
  end
139

  
140
  def wiki_page_hash(page)
141
    return {} unless page
142

  
143
    {
144
      :id => page.id,
145
      :title => page.title,
146
      :project => project_hash(page.project),
147
      :parent_id => page.parent_id,
148
      :created_on => page.created_on&.iso8601,
149
      :updated_on => page.updated_on&.iso8601
150
    }.compact
151
  end
152

  
153
  def wiki_content_hash(content)
154
    return unless content
155

  
156
    {
157
      :version => content.version,
158
      :text => content.text,
159
      :comments => content.comments,
160
      :author => user_hash(content.author),
161
      :created_on => content.created_on&.iso8601,
162
      :updated_on => content.updated_on&.iso8601
163
    }
164
  end
165

  
166
  def wiki_page_and_content
167
    if object.is_a?(WikiContent)
168
      [object.page, object]
169
    else
170
      [object, object.respond_to?(:content) ? object.content : nil]
171
    end
172
  end
173

  
174
  def project_hash(project)
175
    return unless project
176

  
177
    {
178
      :id => project.id,
179
      :identifier => project.identifier,
180
      :name => project.name
181
    }
182
  end
183

  
184
  def reference_hash(record)
185
    return unless record
186

  
187
    {:id => record.id, :name => record.name}
188
  end
189

  
190
  def status_hash(status)
191
    return unless status
192

  
193
    {:id => status.id, :name => status.name, :is_closed => status.is_closed?}
194
  end
195

  
196
  def user_hash(user_record)
197
    return unless user_record
198

  
199
    {:id => user_record.id, :name => user_record.name}
200
  end
201
end
app/models/wiki_content.rb (revision f9673061812c2f08e3775f61597c44826a430ea3) → app/models/wiki_content.rb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da)
33 33
  after_save :create_version
34 34
  after_create_commit :send_notification_create
35 35
  after_update_commit :send_notification_update
36
  after_create_commit :trigger_webhook_create
37
  after_update_commit :trigger_webhook_update
36 38

  
37 39
  scope :without_text, lambda {select(:id, :page_id, :version, :updated_on)}
38 40

  
......
101 103
      Mailer.deliver_wiki_content_updated(self)
102 104
    end
103 105
  end
106

  
107
  def trigger_webhook_create
108
    Webhook.trigger('wiki_page.created', self)
109
  end
110

  
111
  def trigger_webhook_update
112
    Webhook.trigger('wiki_page.updated', self)
113
  end
104 114
end
app/models/wiki_page.rb (revision f9673061812c2f08e3775f61597c44826a430ea3) → app/models/wiki_page.rb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da)
61 61
  before_destroy :delete_redirects
62 62
  before_save :handle_rename_or_move, :update_wiki_start_page
63 63
  after_save :handle_children_move, :delete_selected_attachments
64
  after_destroy_commit -> {Webhook.trigger('wiki_page.deleted', self)}
64 65

  
65 66
  # eager load information about last updates, without loading text
66 67
  scope :with_updated_on, lambda {preload(:content_without_text)}
/dev/null (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) → app/validators/webhook_endpoint_validator.rb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da)
1
# frozen_string_literal: true
2

  
3
require 'set'
4
require 'uri'
5
require 'resolv'
6
require 'ipaddr'
7

  
8
class WebhookEndpointValidator < ActiveModel::EachValidator
9
  BAD_PORTS = Set[25, 111, 135, 139, 445, 1433, 1521, 2049, 3306, 3389, 5985, 5986].freeze
10

  
11
  def validate_each(record, attribute, value)
12
    return if value.blank?
13

  
14
    valid, reason = self.class.validate_uri(value)
15
    record.errors.add(attribute, :invalid, message: reason && " (#{reason})") unless valid
16
  end
17

  
18
  class << self
19
    def safe_webhook_uri?(value)
20
      valid, = validate_uri(value)
21
      valid
22
    end
23

  
24
    def validate_uri(value)
25
      uri = value.is_a?(URI) ? value : URI.parse(value.to_s)
26

  
27
      return [false, 'must use http or https'] unless valid_scheme?(uri.scheme)
28

  
29
      host_reason = host_validation_error(uri.host)
30
      return [false, host_reason] if host_reason
31

  
32
      return [false, 'uses a disallowed port'] unless valid_port?(uri.port)
33

  
34
      [true, nil]
35
    rescue URI::Error, ArgumentError => e
36
      Rails.logger.warn {"Webhook endpoint rejected: #{value.inspect} (#{e.message})"}
37
      [false, e.message]
38
    end
39

  
40
    def valid_scheme?(scheme)
41
      %w[http https].include?(scheme)
42
    end
43

  
44
    def host_validation_error(host)
45
      return 'host is missing' if host.blank?
46

  
47
      blocked = blocked_targets
48
      allowed = allowed_targets
49

  
50
      host_allowlisted = allowed[:host_pattern]&.match?(host)
51

  
52
      return 'host is blocklisted' if host_blocked?(host, blocked)
53
      if !allowlist_empty?(allowed) && !host_allowlisted && !allowlist_ip_allowed_for_host?(host, allowed)
54
        return "host is not on the allowlist"
55
      end
56

  
57
      addresses = ip_literal?(host) ? [host] : resolve_addresses(host)
58
      return nil if addresses.blank? && allowlist_empty?(allowed)
59

  
60
      addresses.each do |ip|
61
        ipaddr = IPAddr.new(ip)
62
        allowlisted_ip = host_allowlisted || allowlist_ip_allowed?(ipaddr, allowed)
63

  
64
        return "address #{ipaddr} is blocklisted" if blocked[:ips].any? {|entry| entry.include?(ipaddr)}
65
        return "address #{ipaddr} is not on the allowlist" if !allowlist_empty?(allowed) && !allowlisted_ip
66
        return "address #{ipaddr} is not reachable from the public network" if !allowlisted_ip && (ipaddr.loopback? || ipaddr.link_local? || ipaddr.private?)
67
      rescue IPAddr::Error
68
        return "address #{ip.inspect} is invalid"
69
      end
70

  
71
      nil
72
    end
73

  
74
    def valid_port?(port)
75
      port.present? && !BAD_PORTS.include?(port)
76
    end
77

  
78
    def resolve_addresses(host)
79
      addresses = []
80
      Resolv.each_address(host) {|ip| addresses << ip}
81
      addresses
82
    rescue Resolv::ResolvError
83
      []
84
    end
85

  
86
    def ip_literal?(host)
87
      IPAddr.new(host)
88
      true
89
    rescue IPAddr::Error
90
      false
91
    end
92

  
93
    def blocked_targets
94
      @blocked_targets ||= begin
95
        ips = []
96
        host_patterns = []
97
        Array(Redmine::Configuration['webhook_blocklist']).each do |entry|
98
          entry = entry.to_s.strip
99
          next if entry.empty?
100

  
101
          begin
102
            ips << IPAddr.new(entry)
103
          rescue IPAddr::Error
104
            host_patterns << entry
105
          end
106
        end
107
        {
108
          :ips => ips.freeze,
109
          :host_pattern => build_host_pattern(host_patterns)
110
        }
111
      end
112
    end
113

  
114
    def allowed_targets
115
      @allowed_targets ||= begin
116
        ips = []
117
        host_patterns = []
118
        Array(Redmine::Configuration['webhook_allowlist']).each do |entry|
119
          entry = entry.to_s.strip
120
          next if entry.empty?
121

  
122
          begin
123
            ips << IPAddr.new(entry)
124
          rescue IPAddr::Error
125
            host_patterns << entry
126
          end
127
        end
128
        {
129
          :ips => ips.freeze,
130
          :host_pattern => build_host_pattern(host_patterns)
131
        }
132
      end
133
    end
134

  
135
    def build_host_pattern(patterns)
136
      return if patterns.empty?
137

  
138
      sources = patterns.map do |value|
139
        if value.start_with?('*.')
140
          "(?:.*\\.)?#{Regexp.escape(value.delete_prefix('*.'))}"
141
        else
142
          Regexp.escape(value)
143
        end
144
      end
145

  
146
      Regexp.new("\\A(?:#{sources.join('|')})\\z", Regexp::IGNORECASE)
147
    end
148

  
149
    def host_blocked?(host, blocked)
150
      blocked[:host_pattern]&.match?(host)
151
    end
152

  
153
    def host_allowed_by_allowlist?(host, allowed)
154
      return true if allowlist_empty?(allowed)
155

  
156
      allowed[:host_pattern]&.match?(host) || allowlist_ip_allowed_for_host?(host, allowed)
157
    end
158

  
159
    def allowlist_empty?(allowed)
160
      allowed[:ips].empty? && allowed[:host_pattern].nil?
161
    end
162

  
163
    def allowlist_ip_allowed_for_host?(host, allowed)
164
      addresses = ip_literal?(host) ? [host] : resolve_addresses(host)
165
      return false if addresses.empty?
166

  
167
      addresses.any? do |ip|
168
        allowlist_ip_allowed?(IPAddr.new(ip), allowed)
169
      rescue IPAddr::Error
170
        false
171
      end
172
    end
173

  
174
    def allowlist_ip_allowed?(ipaddr, allowed)
175
      allowed[:ips].any? {|entry| entry.include?(ipaddr)}
176
    end
177
  end
178
end
app/views/my/_sidebar.html.erb (revision f9673061812c2f08e3775f61597c44826a430ea3) → app/views/my/_sidebar.html.erb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da)
31 31
<% else %>
32 32
<%= l(:label_missing_api_access_key) %>
33 33
<% end %>
34

  
35
<% if Setting.webhooks_enabled? && User.current.allowed_to?(:use_webhooks, nil, :global => true) %>
36
  <h4><%= l(:label_integrations) %></h4>
37
  <p><%= link_to l(:label_webhook_plural), webhooks_path, :class => 'icon icon-link' %></p>
38
<% end %>
34 39
(<%= link_to l(:button_reset), my_api_key_path, :method => :post %>)
35 40
</p>
36 41
<% end %>
app/views/settings/_api.html.erb (revision f9673061812c2f08e3775f61597c44826a430ea3) → app/views/settings/_api.html.erb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da)
4 4
<p><%= setting_check_box :rest_api_enabled %></p>
5 5

  
6 6
<p><%= setting_check_box :jsonp_enabled %></p>
7

  
8
<p><%= setting_check_box :webhooks_enabled %></p>
7 9
</div>
8 10

  
9 11
<%= submit_tag l(:button_save) %>
/dev/null (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) → app/views/webhooks/_form.html.erb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da)
1
<%= error_messages_for @webhook %>
2
<div class="splitcontent">
3
  <div class="splitcontentleft">
4
    <div class="box tabular">
5
      <p>
6
        <%= f.text_field :url, :required => true, :size => 60 %>
7
        <em class="info"><%= l(:webhook_url_info) %></em>
8
      </p>
9
      <p>
10
        <%= f.text_field :secret %>
11
        <em class="info"><%= raw l(:webhook_secret_info_html) %></em>
12
      </p>
13
      <p>
14
        <%= f.check_box :active %> <%= l(:field_active) %>
15
      </p>
16
    </div>
17

  
18
    <h3><%= l(:label_webhook_events) %></h3>
19
    <div class="box tabular" id="events">
20
      <% @webhook.setable_events.keys.sort.each do |type| %>
21
        <fieldset id="<%= type %>_events">
22
          <legend>
23
            <%= toggle_checkboxes_link("##{type}_events input") %>
24
            <%= l_or_humanize(type, :prefix => 'webhook_events_') %>
25
          </legend>
26
          <% @webhook.setable_events[type].each do |action| %>
27
            <% name = "#{type}.#{action}" %>
28
            <label class="floating">
29
              <%= check_box_tag 'webhook[events][]', name, @webhook.events.include?(name), :id => "webhook_events_#{name.tr('.', '_')}" %>
30
              <%= l_or_humanize("#{type}_#{action}", :prefix => 'webhook_events_') %>
31
            </label>
32
          <% end %>
33
        </fieldset>
34
      <% end %>
35
      <br /><%= check_all_links 'events' %>
36
      <%= hidden_field_tag 'webhook[events][]', '' %>
37
    </div>
38
  </div>
39

  
40
  <div class="splitcontentright">
41
    <fieldset class="box" id="webhook_project_ids">
42
      <legend>
43
        <%= toggle_checkboxes_link("#webhook_project_ids input[type=checkbox]") %>
44
        <%= l(:label_project_plural) %>
45
      </legend>
46
      <% project_ids = @webhook.project_ids || [] %>
47
      <%= render_project_nested_lists(@webhook.setable_projects) do |p| %>
48
        <%= content_tag('label', check_box_tag('webhook[project_ids][]', p.id, project_ids.include?(p.id)) + ' ' + h(p)) %>
49
      <% end %>
50
      <%= hidden_field_tag 'webhook[project_ids][]', '' %>
51
    </fieldset>
52

  
53
    <fieldset class="box" id="webhook_tracker_ids">
54
      <legend>
55
        <%= toggle_checkboxes_link("#webhook_tracker_ids input[type=checkbox]") %>
56
        <%= l(:label_tracker_plural) %>
57
      </legend>
58
      <% tracker_ids = @webhook.tracker_ids || [] %>
59
      <% @webhook.setable_trackers.each do |tracker| %>
60
        <label class="floating">
61
          <%= check_box_tag('webhook[tracker_ids][]', tracker.id, tracker_ids.include?(tracker.id)) %>
62
          <%= h(tracker.name) %>
63
        </label>
64
      <% end %>
65
      <%= hidden_field_tag 'webhook[tracker_ids][]', '' %>
66
    </fieldset>
67
  </div>
68
</div>
/dev/null (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) → app/views/webhooks/edit.html.erb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da)
1
<%= labelled_form_for @webhook, :url => webhook_path(@webhook), :html => {:method => :patch} do |f| %>
2
  <%= render :partial => 'form', :locals => {:f => f} %>
3
  <p><%= submit_tag l(:button_save) %></p>
4
<% end %>
/dev/null (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) → app/views/webhooks/index.html.erb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da)
1
<div class="contextual">
2
  <%= link_to l(:label_webhook_new), new_webhook_path, :class => 'icon icon-add' %>
3
</div>
4

  
5
<%= title l(:label_webhook_plural) %>
6

  
7
<% if @webhooks.any? %>
8
  <div class="autoscroll">
9
    <table class="list">
10
      <thead>
11
        <tr>
12
          <% if User.current.admin? %>
13
            <th><%= l(:label_user) %></th>
14
          <% end %>
15
          <th><%= l(:field_active) %></th>
16
          <th><%= l(:field_url) %></th>
17
          <th><%= l(:label_webhook_events) %></th>
18
          <th><%= l(:label_project_plural) %></th>
19
          <th></th>
20
        </tr>
21
      </thead>
22
      <tbody>
23
        <% @webhooks.each do |hook| %>
24
          <tr id="webhook_<%= hook.id %>" class="<%= cycle('odd', 'even') %>">
25
            <% if User.current.admin? %>
26
              <td class="username"><%= h hook.user %></td>
27
            <% end %>
28
            <td><%= hook.active? ? l(:general_text_Yes) : l(:general_text_No) %></td>
29
            <td><%= truncate(hook.url, :length => 50) %></td>
30
            <td><%= safe_join(hook.events.map {|e| content_tag(:code, e)}, ', ') %></td>
31
            <td><%= safe_join(hook.projects.visible.map {|p| link_to_project(p)}, ', ') %></td>
32
            <td class="buttons">
33
              <%= link_to l(:button_edit), edit_webhook_path(hook), :class => 'icon icon-edit' %>
34
              <%= link_to l(:button_delete), webhook_path(hook), :method => :delete, :data => {:confirm => l(:text_are_you_sure)}, :class => 'icon icon-del' %>
35
            </td>
36
          </tr>
37
        <% end %>
38
      </tbody>
39
    </table>
40
  </div>
41
<% else %>
42
  <p class="nodata"><%= l(:label_no_data) %></p>
43
<% end %>
/dev/null (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da) → app/views/webhooks/new.html.erb (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da)
1
<%= labelled_form_for @webhook, :url => webhooks_path do |f| %>
2
  <%= render :partial => 'form', :locals => {:f => f} %>
3
  <p><%= submit_tag l(:button_create) %></p>
4
<% end %>
config/locales/ar.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) → config/locales/ar.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da)
1487 1487
  text_default_active_job_queue_changed: Default queue adapter which is well suited
1488 1488
    only for dev/test changed
1489 1489
  twofa_already_setup: Two-factor authentication already set up
1490

  
1491
  label_webhook_plural: "Webhooks"
1492
  label_webhook_new: "New webhook"
1493
  label_webhook_edit: "Edit webhook"
1494
  label_webhook_events: "Events"
1495
  webhook_events_issue: "Issues"
1496
  webhook_events_issue_created: "Issue created"
1497
  webhook_events_issue_updated: "Issue updated"
1498
  webhook_events_issue_closed: "Issue closed"
1499
  webhook_events_issue_deleted: "Issue deleted"
1500
  webhook_events_wiki_page: "Wiki pages"
1501
  webhook_events_wiki_page_created: "Wiki page created"
1502
  webhook_events_wiki_page_updated: "Wiki page updated"
1503
  webhook_events_wiki_page_deleted: "Wiki page deleted"
1504
  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."
1505
  webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the <code>X-Redmine-Signature-256</code> header."
1506
  permission_use_webhooks: "Use webhooks"
1507
  setting_webhooks_enabled: "Enable webhooks"
1508
  label_integrations: "Integrations"
config/locales/az.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) → config/locales/az.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da)
1579 1579
  text_default_active_job_queue_changed: Default queue adapter which is well suited
1580 1580
    only for dev/test changed
1581 1581
  twofa_already_setup: Two-factor authentication already set up
1582

  
1583
  label_webhook_plural: "Webhooks"
1584
  label_webhook_new: "New webhook"
1585
  label_webhook_edit: "Edit webhook"
1586
  label_webhook_events: "Events"
1587
  webhook_events_issue: "Issues"
1588
  webhook_events_issue_created: "Issue created"
1589
  webhook_events_issue_updated: "Issue updated"
1590
  webhook_events_issue_closed: "Issue closed"
1591
  webhook_events_issue_deleted: "Issue deleted"
1592
  webhook_events_wiki_page: "Wiki pages"
1593
  webhook_events_wiki_page_created: "Wiki page created"
1594
  webhook_events_wiki_page_updated: "Wiki page updated"
1595
  webhook_events_wiki_page_deleted: "Wiki page deleted"
1596
  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."
1597
  webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the <code>X-Redmine-Signature-256</code> header."
1598
  permission_use_webhooks: "Use webhooks"
1599
  setting_webhooks_enabled: "Enable webhooks"
1600
  label_integrations: "Integrations"
config/locales/bg.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) → config/locales/bg.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da)
1432 1432
  text_project_destroy_enter_identifier: За да потвърдите действието, въведете идентификатора на проекта (%{identifier}) по-долу.
1433 1433
  field_name_or_email_or_login: Име, e-mail или login име
1434 1434
  twofa_already_setup: Two-factor authentication already set up
1435

  
1436
  label_webhook_plural: "Webhooks"
1437
  label_webhook_new: "New webhook"
1438
  label_webhook_edit: "Edit webhook"
1439
  label_webhook_events: "Events"
1440
  webhook_events_issue: "Issues"
1441
  webhook_events_issue_created: "Issue created"
1442
  webhook_events_issue_updated: "Issue updated"
1443
  webhook_events_issue_closed: "Issue closed"
1444
  webhook_events_issue_deleted: "Issue deleted"
1445
  webhook_events_wiki_page: "Wiki pages"
1446
  webhook_events_wiki_page_created: "Wiki page created"
1447
  webhook_events_wiki_page_updated: "Wiki page updated"
1448
  webhook_events_wiki_page_deleted: "Wiki page deleted"
1449
  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."
1450
  webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the <code>X-Redmine-Signature-256</code> header."
1451
  permission_use_webhooks: "Use webhooks"
1452
  setting_webhooks_enabled: "Enable webhooks"
1453
  label_integrations: "Integrations"
config/locales/bs.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) → config/locales/bs.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da)
1474 1474
  text_default_active_job_queue_changed: Default queue adapter which is well suited
1475 1475
    only for dev/test changed
1476 1476
  twofa_already_setup: Two-factor authentication already set up
1477

  
1478
  label_webhook_plural: "Webhooks"
1479
  label_webhook_new: "New webhook"
1480
  label_webhook_edit: "Edit webhook"
1481
  label_webhook_events: "Events"
1482
  webhook_events_issue: "Issues"
1483
  webhook_events_issue_created: "Issue created"
1484
  webhook_events_issue_updated: "Issue updated"
1485
  webhook_events_issue_closed: "Issue closed"
1486
  webhook_events_issue_deleted: "Issue deleted"
1487
  webhook_events_wiki_page: "Wiki pages"
1488
  webhook_events_wiki_page_created: "Wiki page created"
1489
  webhook_events_wiki_page_updated: "Wiki page updated"
1490
  webhook_events_wiki_page_deleted: "Wiki page deleted"
1491
  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."
1492
  webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the <code>X-Redmine-Signature-256</code> header."
1493
  permission_use_webhooks: "Use webhooks"
1494
  setting_webhooks_enabled: "Enable webhooks"
1495
  label_integrations: "Integrations"
config/locales/ca.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) → config/locales/ca.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da)
1475 1475
  text_select_apply_issue_status: Marca l'estat de la incidència
1476 1476
  field_name_or_email_or_login: Nom, correu o login
1477 1477
  twofa_already_setup: Two-factor authentication already set up
1478

  
1479
  label_webhook_plural: "Webhooks"
1480
  label_webhook_new: "New webhook"
1481
  label_webhook_edit: "Edit webhook"
1482
  label_webhook_events: "Events"
1483
  webhook_events_issue: "Issues"
1484
  webhook_events_issue_created: "Issue created"
1485
  webhook_events_issue_updated: "Issue updated"
1486
  webhook_events_issue_closed: "Issue closed"
1487
  webhook_events_issue_deleted: "Issue deleted"
1488
  webhook_events_wiki_page: "Wiki pages"
1489
  webhook_events_wiki_page_created: "Wiki page created"
1490
  webhook_events_wiki_page_updated: "Wiki page updated"
1491
  webhook_events_wiki_page_deleted: "Wiki page deleted"
1492
  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."
1493
  webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the <code>X-Redmine-Signature-256</code> header."
1494
  permission_use_webhooks: "Use webhooks"
1495
  setting_webhooks_enabled: "Enable webhooks"
1496
  label_integrations: "Integrations"
config/locales/cs.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) → config/locales/cs.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da)
1469 1469
  text_default_active_job_queue_changed: Výchozí adaptér fronty, vhodný pouze pro
1470 1470
    vývoj/testování, byl změněn
1471 1471
  twofa_already_setup: Two-factor authentication already set up
1472

  
1473
  label_webhook_plural: "Webhooks"
1474
  label_webhook_new: "New webhook"
1475
  label_webhook_edit: "Edit webhook"
1476
  label_webhook_events: "Events"
1477
  webhook_events_issue: "Issues"
1478
  webhook_events_issue_created: "Issue created"
1479
  webhook_events_issue_updated: "Issue updated"
1480
  webhook_events_issue_closed: "Issue closed"
1481
  webhook_events_issue_deleted: "Issue deleted"
1482
  webhook_events_wiki_page: "Wiki pages"
1483
  webhook_events_wiki_page_created: "Wiki page created"
1484
  webhook_events_wiki_page_updated: "Wiki page updated"
1485
  webhook_events_wiki_page_deleted: "Wiki page deleted"
1486
  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."
1487
  webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the <code>X-Redmine-Signature-256</code> header."
1488
  permission_use_webhooks: "Use webhooks"
1489
  setting_webhooks_enabled: "Enable webhooks"
1490
  label_integrations: "Integrations"
config/locales/da.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) → config/locales/da.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da)
1504 1504
  text_default_active_job_queue_changed: Default queue adapter which is well suited
1505 1505
    only for dev/test changed
1506 1506
  twofa_already_setup: Two-factor authentication already set up
1507

  
1508
  label_webhook_plural: "Webhooks"
1509
  label_webhook_new: "New webhook"
1510
  label_webhook_edit: "Edit webhook"
1511
  label_webhook_events: "Events"
1512
  webhook_events_issue: "Issues"
1513
  webhook_events_issue_created: "Issue created"
1514
  webhook_events_issue_updated: "Issue updated"
1515
  webhook_events_issue_closed: "Issue closed"
1516
  webhook_events_issue_deleted: "Issue deleted"
1517
  webhook_events_wiki_page: "Wiki pages"
1518
  webhook_events_wiki_page_created: "Wiki page created"
1519
  webhook_events_wiki_page_updated: "Wiki page updated"
1520
  webhook_events_wiki_page_deleted: "Wiki page deleted"
1521
  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."
1522
  webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the <code>X-Redmine-Signature-256</code> header."
1523
  permission_use_webhooks: "Use webhooks"
1524
  setting_webhooks_enabled: "Enable webhooks"
1525
  label_integrations: "Integrations"
config/locales/de.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) → config/locales/de.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da)
1455 1455
  text_default_active_job_queue_changed: Default queue adapter which is well suited
1456 1456
    only for dev/test changed
1457 1457
  twofa_already_setup: Two-factor authentication already set up
1458

  
1459
  label_webhook_plural: "Webhooks"
1460
  label_webhook_new: "New webhook"
1461
  label_webhook_edit: "Edit webhook"
1462
  label_webhook_events: "Events"
1463
  webhook_events_issue: "Issues"
1464
  webhook_events_issue_created: "Issue created"
1465
  webhook_events_issue_updated: "Issue updated"
1466
  webhook_events_issue_closed: "Issue closed"
1467
  webhook_events_issue_deleted: "Issue deleted"
1468
  webhook_events_wiki_page: "Wiki pages"
1469
  webhook_events_wiki_page_created: "Wiki page created"
1470
  webhook_events_wiki_page_updated: "Wiki page updated"
1471
  webhook_events_wiki_page_deleted: "Wiki page deleted"
1472
  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."
1473
  webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the <code>X-Redmine-Signature-256</code> header."
1474
  permission_use_webhooks: "Use webhooks"
1475
  setting_webhooks_enabled: "Enable webhooks"
1476
  label_integrations: "Integrations"
config/locales/el.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) → config/locales/el.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da)
1487 1487
  text_default_active_job_queue_changed: Default queue adapter which is well suited
1488 1488
    only for dev/test changed
1489 1489
  twofa_already_setup: Two-factor authentication already set up
1490

  
1491
  label_webhook_plural: "Webhooks"
1492
  label_webhook_new: "New webhook"
1493
  label_webhook_edit: "Edit webhook"
1494
  label_webhook_events: "Events"
1495
  webhook_events_issue: "Issues"
1496
  webhook_events_issue_created: "Issue created"
1497
  webhook_events_issue_updated: "Issue updated"
1498
  webhook_events_issue_closed: "Issue closed"
1499
  webhook_events_issue_deleted: "Issue deleted"
1500
  webhook_events_wiki_page: "Wiki pages"
1501
  webhook_events_wiki_page_created: "Wiki page created"
1502
  webhook_events_wiki_page_updated: "Wiki page updated"
1503
  webhook_events_wiki_page_deleted: "Wiki page deleted"
1504
  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."
1505
  webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the <code>X-Redmine-Signature-256</code> header."
1506
  permission_use_webhooks: "Use webhooks"
1507
  setting_webhooks_enabled: "Enable webhooks"
1508
  label_integrations: "Integrations"
config/locales/en-GB.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) → config/locales/en-GB.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da)
1488 1488
  text_default_active_job_queue_changed: Default queue adapter which is well suited
1489 1489
    only for dev/test changed
1490 1490
  twofa_already_setup: Two-factor authentication already set up
1491

  
1492
  label_webhook_plural: "Webhooks"
1493
  label_webhook_new: "New webhook"
1494
  label_webhook_edit: "Edit webhook"
1495
  label_webhook_events: "Events"
1496
  webhook_events_issue: "Issues"
1497
  webhook_events_issue_created: "Issue created"
1498
  webhook_events_issue_updated: "Issue updated"
1499
  webhook_events_issue_closed: "Issue closed"
1500
  webhook_events_issue_deleted: "Issue deleted"
1501
  webhook_events_wiki_page: "Wiki pages"
1502
  webhook_events_wiki_page_created: "Wiki page created"
1503
  webhook_events_wiki_page_updated: "Wiki page updated"
1504
  webhook_events_wiki_page_deleted: "Wiki page deleted"
1505
  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."
1506
  webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the <code>X-Redmine-Signature-256</code> header."
1507
  permission_use_webhooks: "Use webhooks"
1508
  setting_webhooks_enabled: "Enable webhooks"
1509
  label_integrations: "Integrations"
config/locales/en.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) → config/locales/en.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da)
922 922
  label_year: Year
923 923
  label_month: Month
924 924
  label_week: Week
925
  label_webhook_plural: "Webhooks"
926
  label_webhook_new: "New webhook"
927
  label_webhook_edit: "Edit webhook"
928
  label_webhook_events: "Events"
929
  webhook_events_issue: "Issues"
930
  webhook_events_issue_created: "Issue created"
931
  webhook_events_issue_updated: "Issue updated"
932
  webhook_events_issue_closed: "Issue closed"
933
  webhook_events_issue_deleted: "Issue deleted"
934
  webhook_events_wiki_page: "Wiki pages"
935
  webhook_events_wiki_page_created: "Wiki page created"
936
  webhook_events_wiki_page_updated: "Wiki page updated"
937
  webhook_events_wiki_page_deleted: "Wiki page deleted"
938
  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."
939
  webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the <code>X-Redmine-Signature-256</code> header."
940
  permission_use_webhooks: "Use webhooks"
941
  setting_webhooks_enabled: "Enable webhooks"
942
  label_integrations: "Integrations"
925 943
  label_date_from: From
926 944
  label_date_to: To
927 945
  label_language_based: Based on user's language
config/locales/es-PA.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) → config/locales/es-PA.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da)
1517 1517
  text_default_active_job_queue_changed: Default queue adapter which is well suited
1518 1518
    only for dev/test changed
1519 1519
  twofa_already_setup: Two-factor authentication already set up
1520

  
1521
  label_webhook_plural: "Webhooks"
1522
  label_webhook_new: "New webhook"
1523
  label_webhook_edit: "Edit webhook"
1524
  label_webhook_events: "Events"
1525
  webhook_events_issue: "Issues"
1526
  webhook_events_issue_created: "Issue created"
1527
  webhook_events_issue_updated: "Issue updated"
1528
  webhook_events_issue_closed: "Issue closed"
1529
  webhook_events_issue_deleted: "Issue deleted"
1530
  webhook_events_wiki_page: "Wiki pages"
1531
  webhook_events_wiki_page_created: "Wiki page created"
1532
  webhook_events_wiki_page_updated: "Wiki page updated"
1533
  webhook_events_wiki_page_deleted: "Wiki page deleted"
1534
  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."
1535
  webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the <code>X-Redmine-Signature-256</code> header."
1536
  permission_use_webhooks: "Use webhooks"
1537
  setting_webhooks_enabled: "Enable webhooks"
1538
  label_integrations: "Integrations"
config/locales/es.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) → config/locales/es.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da)
1553 1553
  text_default_active_job_queue_changed: Default queue adapter which is well suited
1554 1554
    only for dev/test changed
1555 1555
  twofa_already_setup: Two-factor authentication already set up
1556

  
1557
  label_webhook_plural: "Webhooks"
1558
  label_webhook_new: "New webhook"
1559
  label_webhook_edit: "Edit webhook"
1560
  label_webhook_events: "Events"
1561
  webhook_events_issue: "Issues"
1562
  webhook_events_issue_created: "Issue created"
1563
  webhook_events_issue_updated: "Issue updated"
1564
  webhook_events_issue_closed: "Issue closed"
1565
  webhook_events_issue_deleted: "Issue deleted"
1566
  webhook_events_wiki_page: "Wiki pages"
1567
  webhook_events_wiki_page_created: "Wiki page created"
1568
  webhook_events_wiki_page_updated: "Wiki page updated"
1569
  webhook_events_wiki_page_deleted: "Wiki page deleted"
1570
  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."
1571
  webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the <code>X-Redmine-Signature-256</code> header."
1572
  permission_use_webhooks: "Use webhooks"
1573
  setting_webhooks_enabled: "Enable webhooks"
1574
  label_integrations: "Integrations"
config/locales/et.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) → config/locales/et.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da)
1492 1492
  text_default_active_job_queue_changed: Default queue adapter which is well suited
1493 1493
    only for dev/test changed
1494 1494
  twofa_already_setup: Two-factor authentication already set up
1495

  
1496
  label_webhook_plural: "Webhooks"
1497
  label_webhook_new: "New webhook"
1498
  label_webhook_edit: "Edit webhook"
1499
  label_webhook_events: "Events"
1500
  webhook_events_issue: "Issues"
1501
  webhook_events_issue_created: "Issue created"
1502
  webhook_events_issue_updated: "Issue updated"
1503
  webhook_events_issue_closed: "Issue closed"
1504
  webhook_events_issue_deleted: "Issue deleted"
1505
  webhook_events_wiki_page: "Wiki pages"
1506
  webhook_events_wiki_page_created: "Wiki page created"
1507
  webhook_events_wiki_page_updated: "Wiki page updated"
1508
  webhook_events_wiki_page_deleted: "Wiki page deleted"
1509
  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."
1510
  webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the <code>X-Redmine-Signature-256</code> header."
1511
  permission_use_webhooks: "Use webhooks"
1512
  setting_webhooks_enabled: "Enable webhooks"
1513
  label_integrations: "Integrations"
config/locales/eu.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) → config/locales/eu.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da)
1488 1488
  text_default_active_job_queue_changed: Default queue adapter which is well suited
1489 1489
    only for dev/test changed
1490 1490
  twofa_already_setup: Two-factor authentication already set up
1491

  
1492
  label_webhook_plural: "Webhooks"
1493
  label_webhook_new: "New webhook"
1494
  label_webhook_edit: "Edit webhook"
1495
  label_webhook_events: "Events"
1496
  webhook_events_issue: "Issues"
1497
  webhook_events_issue_created: "Issue created"
1498
  webhook_events_issue_updated: "Issue updated"
1499
  webhook_events_issue_closed: "Issue closed"
1500
  webhook_events_issue_deleted: "Issue deleted"
1501
  webhook_events_wiki_page: "Wiki pages"
1502
  webhook_events_wiki_page_created: "Wiki page created"
1503
  webhook_events_wiki_page_updated: "Wiki page updated"
1504
  webhook_events_wiki_page_deleted: "Wiki page deleted"
1505
  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."
1506
  webhook_secret_info_html: "If set, Redmine will include an HMAC signature in the <code>X-Redmine-Signature-256</code> header."
1507
  permission_use_webhooks: "Use webhooks"
1508
  setting_webhooks_enabled: "Enable webhooks"
1509
  label_integrations: "Integrations"
config/locales/fa.yml (revision f9673061812c2f08e3775f61597c44826a430ea3) → config/locales/fa.yml (revision 833d7e3d85eb74ec9bc1d45a64a445f6c577a7da)
1421 1421
  field_name_or_email_or_login: نام، رایانامه یا شناسه کاربری
1422 1422
  text_default_active_job_queue_changed: آداپتور پیش‌فرض صف که به شکل مناسبی کار می‌کند تنها برای توسعه/آزمون تغییر کرد
1423 1423
  twofa_already_setup: Two-factor authentication already set up
1424

  
1425
  label_webhook_plural: "Webhooks"
1426
  label_webhook_new: "New webhook"
1427
  label_webhook_edit: "Edit webhook"
... This diff was truncated because it exceeds the maximum size that can be displayed.
(14-14/15)