10-patches.rb 7.22 KB
Newer Older
jplang's avatar
jplang committed
1
require 'active_record'
2 3 4 5 6

module ActiveRecord
  class Base
    include Redmine::I18n
    # Translate attribute names for validation errors display
7 8
    def self.human_attribute_name(attr, options = {})
      prepared_attr = attr.to_s.sub(/_id$/, '').sub(/^.+\./, '')
9
      class_prefix = name.underscore.tr('/', '_')
10

jplang's avatar
jplang committed
11 12 13 14
      redmine_default = [
        :"field_#{class_prefix}_#{prepared_attr}",
        :"field_#{prepared_attr}"
      ]
15

jplang's avatar
jplang committed
16
      options[:default] = redmine_default + Array(options[:default])
17 18

      super
19 20
    end
  end
21 22 23

  # Undefines private Kernel#open method to allow using `open` scopes in models.
  # See Defect #11545 (http://www.redmine.org/issues/11545) for details.
24 25 26 27 28
  class Base
    class << self
      undef open
    end
  end
29
  class Relation ; undef open ; end
30 31 32 33 34 35 36 37 38 39 40 41 42
end

module ActionView
  module Helpers
    module DateHelper
      # distance_of_time_in_words breaks when difference is greater than 30 years
      def distance_of_date_in_words(from_date, to_date = 0, options = {})
        from_date = from_date.to_date if from_date.respond_to?(:to_date)
        to_date = to_date.to_date if to_date.respond_to?(:to_date)
        distance_in_days = (to_date - from_date).abs

        I18n.with_options :locale => options[:locale], :scope => :'datetime.distance_in_words' do |locale|
          case distance_in_days
43
            when 0..60     then locale.t :x_days,             :count => distance_in_days.round
44
            when 61..720   then locale.t :about_x_months,     :count => (distance_in_days / 30).round
45
            else                locale.t :over_x_years,       :count => (distance_in_days / 365).floor
46 47 48 49 50
          end
        end
      end
    end
  end
jplang's avatar
jplang committed
51 52 53 54

  class Resolver
    def find_all(name, prefix=nil, partial=false, details={}, key=nil, locals=[])
      cached(key, [name, prefix, partial], details, locals) do
55
        if (details[:formats] & [:xml, :json]).any?
jplang's avatar
jplang committed
56 57 58 59 60 61 62
          details = details.dup
          details[:formats] = details[:formats].dup + [:api]
        end
        find_templates(name, prefix, partial, details)
      end
    end
  end
63
end
64

65
ActionView::Base.field_error_proc = Proc.new{ |html_tag, instance| html_tag || ''.html_safe }
66

67 68 69
# HTML5: <option value=""></option> is invalid, use <option value="">&nbsp;</option> instead
module ActionView
  module Helpers
jplang's avatar
jplang committed
70 71 72
    module Tags
      class Base
        private
jplang's avatar
jplang committed
73 74
        alias :add_options_without_non_empty_blank_option :add_options
        def add_options(option_tags, options, value = nil)
jplang's avatar
jplang committed
75 76 77 78 79
          if options[:include_blank] == true
            options = options.dup
            options[:include_blank] = '&nbsp;'.html_safe
          end
          add_options_without_non_empty_blank_option(option_tags, options, value)
80 81 82 83 84
        end
      end
    end

    module FormTagHelper
jplang's avatar
jplang committed
85 86
      alias :select_tag_without_non_empty_blank_option :select_tag
      def select_tag(name, option_tags = nil, options = {})
87 88 89 90 91 92 93 94
        if options.delete(:include_blank)
          options[:prompt] = '&nbsp;'.html_safe
        end
        select_tag_without_non_empty_blank_option(name, option_tags, options)
      end
    end

    module FormOptionsHelper
jplang's avatar
jplang committed
95 96
      alias :options_for_select_without_non_empty_blank_option :options_for_select
      def options_for_select(container, selected = nil)
97 98 99 100 101 102 103 104 105
        if container.is_a?(Array)
          container = container.map {|element| element.blank? ? ["&nbsp;".html_safe, ""] : element}
        end
        options_for_select_without_non_empty_blank_option(container, selected)
      end
    end
  end
end

106 107 108 109 110 111 112 113 114 115 116 117 118
require 'mail'

module DeliveryMethods
  class AsyncSMTP < ::Mail::SMTP
    def deliver!(*args)
      Thread.start do
        super *args
      end
    end
  end

  class AsyncSendmail < ::Mail::Sendmail
    def deliver!(*args)
119
      Thread.start do
120
        super *args
121
      end
122 123
    end
  end
124

125 126 127 128 129 130 131 132
  class TmpFile
    def initialize(*args); end

    def deliver!(mail)
      dest_dir = File.join(Rails.root, 'tmp', 'emails')
      Dir.mkdir(dest_dir) unless File.directory?(dest_dir)
      File.open(File.join(dest_dir, mail.message_id.gsub(/[<>]/, '') + '.eml'), 'wb') {|f| f.write(mail.encoded) }
    end
133
  end
134 135
end

136 137 138
ActionMailer::Base.add_delivery_method :async_smtp, DeliveryMethods::AsyncSMTP
ActionMailer::Base.add_delivery_method :async_sendmail, DeliveryMethods::AsyncSendmail
ActionMailer::Base.add_delivery_method :tmp_file, DeliveryMethods::TmpFile
139

140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
# Changes how sent emails are logged
# Rails doesn't log cc and bcc which is misleading when using bcc only (#12090)
module ActionMailer
  class LogSubscriber < ActiveSupport::LogSubscriber
    def deliver(event)
      recipients = [:to, :cc, :bcc].inject("") do |s, header|
        r = Array.wrap(event.payload[header])
        if r.any?
          s << "\n  #{header}: #{r.join(', ')}"
        end
        s
      end
      info("\nSent email \"#{event.payload[:subject]}\" (%1.fms)#{recipients}" % event.duration)
      debug(event.payload[:mail])
    end
  end
end

jplang's avatar
jplang committed
158 159 160 161 162 163 164 165 166 167
# #deliver is deprecated in Rails 4.2
# Prevents massive deprecation warnings
module ActionMailer
  class MessageDelivery < Delegator
    def deliver
      deliver_now
    end
  end
end

jplang's avatar
jplang committed
168 169
module ActionController
  module MimeResponds
jplang's avatar
jplang committed
170
    class Collector
jplang's avatar
jplang committed
171 172 173 174 175 176
      def api(&block)
        any(:xml, :json, &block)
      end
    end
  end
end
177 178 179 180 181 182 183

module ActionController
  class Base
    # Displays an explicit message instead of a NoMethodError exception
    # when trying to start Redmine with an old session_store.rb
    # TODO: remove it in a later version
    def self.session=(*args)
184
      $stderr.puts "Please remove config/initializers/session_store.rb and run `rake generate_secret_token`.\n" +
jplang's avatar
jplang committed
185
        "Setting the session secret with ActionController.session= is no longer supported."
186 187 188 189
      exit 1
    end
  end
end
190 191 192 193 194 195 196 197 198 199 200 201 202 203

# Adds asset_id parameters to assets like Rails 3 to invalidate caches in browser
module ActionView
  module Helpers
    module AssetUrlHelper
      @@cache_asset_timestamps = Rails.env.production?
      @@asset_timestamps_cache = {}
      @@asset_timestamps_cache_guard = Mutex.new

      def asset_path_with_asset_id(source, options = {})
        asset_id = rails_asset_id(source, options)
        unless asset_id.blank?
          source += "?#{asset_id}"
        end
jplang's avatar
jplang committed
204
        asset_path(source, options.merge(skip_pipeline: true))
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
      end
      alias :path_to_asset :asset_path_with_asset_id

      def rails_asset_id(source, options = {})
        if asset_id = ENV["RAILS_ASSET_ID"]
          asset_id
        else
          if @@cache_asset_timestamps && (asset_id = @@asset_timestamps_cache[source])
            asset_id
          else
            extname = compute_asset_extname(source, options)
            path = File.join(Rails.public_path, "#{source}#{extname}")
            exist = false
            if File.exist? path
              exist = true
            else
jplang's avatar
jplang committed
221
              path = File.join(Rails.public_path, public_compute_asset_path("#{source}#{extname}", options))
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
              if File.exist? path
                exist = true
              end
            end
            asset_id = exist ? File.mtime(path).to_i.to_s : ''

            if @@cache_asset_timestamps
              @@asset_timestamps_cache_guard.synchronize do
                @@asset_timestamps_cache[source] = asset_id
              end
            end

            asset_id
          end
        end
      end
    end
  end
240
end