Revision 912:5e80956cc792 lib/redmine

View differences:

lib/redmine/access_control.rb
1
# redMine - project management software
2
# Copyright (C) 2006-2007  Jean-Philippe Lang
1
# Redmine - project management software
2
# Copyright (C) 2006-2011  Jean-Philippe Lang
3 3
#
4 4
# This program is free software; you can redistribute it and/or
5 5
# modify it under the terms of the GNU General Public License
6 6
# as published by the Free Software Foundation; either version 2
7 7
# of the License, or (at your option) any later version.
8
# 
8
#
9 9
# This program is distributed in the hope that it will be useful,
10 10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 12
# GNU General Public License for more details.
13
# 
13
#
14 14
# You should have received a copy of the GNU General Public License
15 15
# along with this program; if not, write to the Free Software
16 16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 17

  
18 18
module Redmine
19 19
  module AccessControl
20
    
20

  
21 21
    class << self
22 22
      def map
23 23
        mapper = Mapper.new
......
25 25
        @permissions ||= []
26 26
        @permissions += mapper.mapped_permissions
27 27
      end
28
      
28

  
29 29
      def permissions
30 30
        @permissions
31 31
      end
32
      
32

  
33 33
      # Returns the permission of given name or nil if it wasn't found
34 34
      # Argument should be a symbol
35 35
      def permission(name)
36 36
        permissions.detect {|p| p.name == name}
37 37
      end
38
      
38

  
39 39
      # Returns the actions that are allowed by the permission of given name
40 40
      def allowed_actions(permission_name)
41 41
        perm = permission(permission_name)
42 42
        perm ? perm.actions : []
43 43
      end
44
      
44

  
45 45
      def public_permissions
46 46
        @public_permissions ||= @permissions.select {|p| p.public?}
47 47
      end
48
      
48

  
49 49
      def members_only_permissions
50 50
        @members_only_permissions ||= @permissions.select {|p| p.require_member?}
51 51
      end
52
      
52

  
53 53
      def loggedin_only_permissions
54 54
        @loggedin_only_permissions ||= @permissions.select {|p| p.require_loggedin?}
55 55
      end
56
      
56

  
57 57
      def available_project_modules
58 58
        @available_project_modules ||= @permissions.collect(&:project_module).uniq.compact
59 59
      end
60
      
60

  
61 61
      def modules_permissions(modules)
62 62
        @permissions.select {|p| p.project_module.nil? || modules.include?(p.project_module.to_s)}
63 63
      end
64 64
    end
65
    
65

  
66 66
    class Mapper
67 67
      def initialize
68 68
        @project_module = nil
69 69
      end
70
      
70

  
71 71
      def permission(name, hash, options={})
72 72
        @permissions ||= []
73 73
        options.merge!(:project_module => @project_module)
74 74
        @permissions << Permission.new(name, hash, options)
75 75
      end
76
      
76

  
77 77
      def project_module(name, options={})
78 78
        @project_module = name
79 79
        yield self
80 80
        @project_module = nil
81 81
      end
82
      
82

  
83 83
      def mapped_permissions
84 84
        @permissions
85 85
      end
86 86
    end
87
    
87

  
88 88
    class Permission
89 89
      attr_reader :name, :actions, :project_module
90
      
90

  
91 91
      def initialize(name, hash, options)
92 92
        @name = name
93 93
        @actions = []
......
103 103
        end
104 104
        @actions.flatten!
105 105
      end
106
      
106

  
107 107
      def public?
108 108
        @public
109 109
      end
110
      
110

  
111 111
      def require_member?
112 112
        @require && @require == :member
113 113
      end
114
      
114

  
115 115
      def require_loggedin?
116 116
        @require && (@require == :member || @require == :loggedin)
117 117
      end
118
    end    
118
    end
119 119
  end
120 120
end
lib/redmine/access_keys.rb
1
# redMine - project management software
2
# Copyright (C) 2006-2008  Jean-Philippe Lang
1
# Redmine - project management software
2
# Copyright (C) 2006-2011  Jean-Philippe Lang
3 3
#
4 4
# This program is free software; you can redistribute it and/or
5 5
# modify it under the terms of the GNU General Public License
6 6
# as published by the Free Software Foundation; either version 2
7 7
# of the License, or (at your option) any later version.
8
# 
8
#
9 9
# This program is distributed in the hope that it will be useful,
10 10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 12
# GNU General Public License for more details.
13
# 
13
#
14 14
# You should have received a copy of the GNU General Public License
15 15
# along with this program; if not, write to the Free Software
16 16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
......
23 23
                  :search => '4',
24 24
                  :new_issue => '7'
25 25
                 }.freeze unless const_defined?(:ACCESSKEYS)
26
                 
26

  
27 27
    def self.key_for(action)
28 28
      ACCESSKEYS[action]
29 29
    end
lib/redmine/activity.rb
1 1
# Redmine - project management software
2
# Copyright (C) 2006-2008  Jean-Philippe Lang
2
# Copyright (C) 2006-2011  Jean-Philippe Lang
3 3
#
4 4
# This program is free software; you can redistribute it and/or
5 5
# modify it under the terms of the GNU General Public License
6 6
# as published by the Free Software Foundation; either version 2
7 7
# of the License, or (at your option) any later version.
8
# 
8
#
9 9
# This program is distributed in the hope that it will be useful,
10 10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 12
# GNU General Public License for more details.
13
# 
13
#
14 14
# You should have received a copy of the GNU General Public License
15 15
# along with this program; if not, write to the Free Software
16 16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 17

  
18 18
module Redmine
19 19
  module Activity
20
  
20

  
21 21
    mattr_accessor :available_event_types, :default_event_types, :providers
22
    
22

  
23 23
    @@available_event_types = []
24 24
    @@default_event_types = []
25 25
    @@providers = Hash.new {|h,k| h[k]=[] }
......
28 28
      def map(&block)
29 29
        yield self
30 30
      end
31
      
31

  
32 32
      # Registers an activity provider
33 33
      def register(event_type, options={})
34 34
        options.assert_valid_keys(:class_name, :default)
35
        
35

  
36 36
        event_type = event_type.to_s
37 37
        providers = options[:class_name] || event_type.classify
38 38
        providers = ([] << providers) unless providers.is_a?(Array)
39
        
39

  
40 40
        @@available_event_types << event_type unless @@available_event_types.include?(event_type)
41 41
        @@default_event_types << event_type unless options[:default] == false
42 42
        @@providers[event_type] += providers
lib/redmine/activity/fetcher.rb
1 1
# Redmine - project management software
2
# Copyright (C) 2006-2008  Jean-Philippe Lang
2
# Copyright (C) 2006-2011  Jean-Philippe Lang
3 3
#
4 4
# This program is free software; you can redistribute it and/or
5 5
# modify it under the terms of the GNU General Public License
6 6
# as published by the Free Software Foundation; either version 2
7 7
# of the License, or (at your option) any later version.
8
# 
8
#
9 9
# This program is distributed in the hope that it will be useful,
10 10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 12
# GNU General Public License for more details.
13
# 
13
#
14 14
# You should have received a copy of the GNU General Public License
15 15
# along with this program; if not, write to the Free Software
16 16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
......
20 20
    # Class used to retrieve activity events
21 21
    class Fetcher
22 22
      attr_reader :user, :project, :scope
23
      
23

  
24 24
      # Needs to be unloaded in development mode
25 25
      @@constantized_providers = Hash.new {|h,k| h[k] = Redmine::Activity.providers[k].collect {|t| t.constantize } }
26
      
26

  
27 27
      def initialize(user, options={})
28 28
        options.assert_valid_keys(:project, :with_subprojects, :author)
29 29
        @user = user
30 30
        @project = options[:project]
31 31
        @options = options
32
        
32

  
33 33
        @scope = event_types
34 34
      end
35
      
35

  
36 36
      # Returns an array of available event types
37 37
      def event_types
38 38
        return @event_types unless @event_types.nil?
39
        
39

  
40 40
        @event_types = Redmine::Activity.available_event_types
41 41
        @event_types = @event_types.select {|o| @project.self_and_descendants.detect {|p| @user.allowed_to?("view_#{o}".to_sym, p)}} if @project
42 42
        @event_types
43 43
      end
44
      
44

  
45 45
      # Yields to filter the activity scope
46 46
      def scope_select(&block)
47 47
        @scope = @scope.select {|t| yield t }
48 48
      end
49
      
49

  
50 50
      # Sets the scope
51 51
      # Argument can be :all, :default or an array of event types
52 52
      def scope=(s)
......
59 59
          @scope = s & event_types
60 60
        end
61 61
      end
62
      
62

  
63 63
      # Resets the scope to the default scope
64 64
      def default_scope!
65 65
        @scope = Redmine::Activity.default_event_types
66 66
      end
67
      
67

  
68 68
      # Returns an array of events for the given date range
69 69
      # sorted in reverse chronological order
70 70
      def events(from = nil, to = nil, options={})
71 71
        e = []
72 72
        @options[:limit] = options[:limit]
73
        
73

  
74 74
        @scope.each do |event_type|
75 75
          constantized_providers(event_type).each do |provider|
76 76
            e += provider.find_events(event_type, @user, from, to, @options)
77 77
          end
78 78
        end
79
        
79

  
80 80
        e.sort! {|a,b| b.event_datetime <=> a.event_datetime}
81
        
81

  
82 82
        if options[:limit]
83 83
          e = e.slice(0, options[:limit])
84 84
        end
85 85
        e
86 86
      end
87
      
87

  
88 88
      private
89
      
89

  
90 90
      def constantized_providers(event_type)
91 91
        @@constantized_providers[event_type]
92 92
      end
lib/redmine/ciphering.rb
17 17

  
18 18
module Redmine
19 19
  module Ciphering
20
    def self.included(base) 
20
    def self.included(base)
21 21
      base.extend ClassMethods
22 22
    end
23
    
23

  
24 24
    class << self
25 25
      def encrypt_text(text)
26
        if cipher_key.blank?
26
        if cipher_key.blank? || text.blank?
27 27
          text
28 28
        else
29 29
          c = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
......
36 36
          "aes-256-cbc:" + [e, iv].map {|v| Base64.encode64(v).strip}.join('--')
37 37
        end
38 38
      end
39
      
39

  
40 40
      def decrypt_text(text)
41 41
        if text && match = text.match(/\Aaes-256-cbc:(.+)\Z/)
42
          if cipher_key.blank?
43
            logger.error "Attempt to decrypt a ciphered text with no cipher key configured in config/configuration.yml" if logger
44
            return text
45
          end
42 46
          text = match[1]
43 47
          c = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
44 48
          e, iv = text.split("--").map {|s| Base64.decode64(s)}
......
51 55
          text
52 56
        end
53 57
      end
54
      
58

  
55 59
      def cipher_key
56 60
        key = Redmine::Configuration['database_cipher_key'].to_s
57 61
        key.blank? ? nil : Digest::SHA256.hexdigest(key)
58 62
      end
63
      
64
      def logger
65
        Rails.logger
66
      end
59 67
    end
60
  
68

  
61 69
    module ClassMethods
62 70
      def encrypt_all(attribute)
63 71
        transaction do
......
68 76
          end
69 77
        end ? true : false
70 78
      end
71
      
79

  
72 80
      def decrypt_all(attribute)
73 81
        transaction do
74 82
          all.each do |object|
......
79 87
        end
80 88
      end ? true : false
81 89
    end
82
    
90

  
83 91
    private
84
    
92

  
85 93
    # Returns the value of the given ciphered attribute
86 94
    def read_ciphered_attribute(attribute)
87 95
      Redmine::Ciphering.decrypt_text(read_attribute(attribute))
88 96
    end
89
    
97

  
90 98
    # Sets the value of the given ciphered attribute
91 99
    def write_ciphered_attribute(attribute, value)
92 100
      write_attribute(attribute, Redmine::Ciphering.encrypt_text(value))
lib/redmine/codeset_util.rb
11 11
          str = str.encode("US-ASCII", :invalid => :replace,
12 12
                :undef => :replace, :replace => '?').encode("UTF-8")
13 13
        end
14
      elsif RUBY_PLATFORM == 'java'
15
        begin
16
          ic = Iconv.new('UTF-8', 'UTF-8')
17
          str = ic.iconv(str)
18
        rescue
19
          str = str.gsub(%r{[^\r\n\t\x20-\x7e]}, '?')
20
        end
14 21
      else
15 22
        ic = Iconv.new('UTF-8', 'UTF-8')
16 23
        txtar = ""
......
27 34
      end
28 35
      str
29 36
    end
37

  
38
    def self.to_utf8(str, encoding)
39
      return str if str.nil?
40
      str.force_encoding("ASCII-8BIT") if str.respond_to?(:force_encoding)
41
      if str.empty?
42
        str.force_encoding("UTF-8") if str.respond_to?(:force_encoding)
43
        return str
44
      end
45
      enc = encoding.blank? ? "UTF-8" : encoding
46
      if str.respond_to?(:force_encoding)
47
        if enc.upcase != "UTF-8"
48
          str.force_encoding(enc)
49
          str = str.encode("UTF-8", :invalid => :replace,
50
                :undef => :replace, :replace => '?')
51
        else
52
          str.force_encoding("UTF-8")
53
          if ! str.valid_encoding?
54
            str = str.encode("US-ASCII", :invalid => :replace,
55
                  :undef => :replace, :replace => '?').encode("UTF-8")
56
          end
57
        end
58
      elsif RUBY_PLATFORM == 'java'
59
        begin
60
          ic = Iconv.new('UTF-8', enc)
61
          str = ic.iconv(str)
62
        rescue
63
          str = str.gsub(%r{[^\r\n\t\x20-\x7e]}, '?')
64
        end
65
      else
66
        ic = Iconv.new('UTF-8', enc)
67
        txtar = ""
68
        begin
69
          txtar += ic.iconv(str)
70
        rescue Iconv::IllegalSequence
71
          txtar += $!.success
72
          str = '?' + $!.failed[1,$!.failed.length]
73
          retry
74
        rescue
75
          txtar += $!.success
76
        end
77
        str = txtar
78
      end
79
      str
80
    end
81

  
82
    def self.to_utf8_by_setting(str)
83
      return str if str.nil?
84
      str = self.to_utf8_by_setting_internal(str)
85
      if str.respond_to?(:force_encoding)
86
        str.force_encoding('UTF-8')
87
      end
88
      str
89
    end
90

  
91
    def self.to_utf8_by_setting_internal(str)
92
      return str if str.nil?
93
      if str.respond_to?(:force_encoding)
94
        str.force_encoding('ASCII-8BIT')
95
      end
96
      return str if str.empty?
97
      return str if /\A[\r\n\t\x20-\x7e]*\Z/n.match(str) # for us-ascii
98
      if str.respond_to?(:force_encoding)
99
        str.force_encoding('UTF-8')
100
      end
101
      encodings = Setting.repositories_encodings.split(',').collect(&:strip)
102
      encodings.each do |encoding|
103
        begin
104
          return Iconv.conv('UTF-8', encoding, str)
105
        rescue Iconv::Failure
106
          # do nothing here and try the next encoding
107
        end
108
      end
109
      str = self.replace_invalid_utf8(str)
110
      if str.respond_to?(:force_encoding)
111
        str.force_encoding('UTF-8')
112
      end
113
      str
114
    end
115

  
116
    def self.from_utf8(str, encoding)
117
      str ||= ''
118
      if str.respond_to?(:force_encoding)
119
        str.force_encoding('UTF-8')
120
        if encoding.upcase != 'UTF-8'
121
          str = str.encode(encoding, :invalid => :replace,
122
                           :undef => :replace, :replace => '?')
123
        else
124
          str = self.replace_invalid_utf8(str)
125
        end
126
      elsif RUBY_PLATFORM == 'java'
127
        begin
128
          ic = Iconv.new(encoding, 'UTF-8')
129
          str = ic.iconv(str)
130
        rescue
131
          str = str.gsub(%r{[^\r\n\t\x20-\x7e]}, '?')
132
        end
133
      else
134
        ic = Iconv.new(encoding, 'UTF-8')
135
        txtar = ""
136
        begin
137
          txtar += ic.iconv(str)
138
        rescue Iconv::IllegalSequence
139
          txtar += $!.success
140
          str = '?' + $!.failed[1, $!.failed.length]
141
          retry
142
        rescue
143
          txtar += $!.success
144
        end
145
        str = txtar
146
      end
147
    end
30 148
  end
31 149
end
lib/redmine/configuration.rb
5 5
# modify it under the terms of the GNU General Public License
6 6
# as published by the Free Software Foundation; either version 2
7 7
# of the License, or (at your option) any later version.
8
# 
8
#
9 9
# This program is distributed in the hope that it will be useful,
10 10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 12
# GNU General Public License for more details.
13
# 
13
#
14 14
# You should have received a copy of the GNU General Public License
15 15
# along with this program; if not, write to the Free Software
16 16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 17

  
18 18
module Redmine
19 19
  module Configuration
20
    
20

  
21 21
    # Configuration default values
22 22
    @defaults = {
23 23
      'email_delivery' => nil
24 24
    }
25
    
25

  
26 26
    @config = nil
27
    
27

  
28 28
    class << self
29 29
      # Loads the Redmine configuration file
30 30
      # Valid options:
31 31
      # * <tt>:file</tt>: the configuration file to load (default: config/configuration.yml)
32
      # * <tt>:env</tt>: the environment to load the configuration for (default: Rails.env) 
32
      # * <tt>:env</tt>: the environment to load the configuration for (default: Rails.env)
33 33
      def load(options={})
34 34
        filename = options[:file] || File.join(Rails.root, 'config', 'configuration.yml')
35 35
        env = options[:env] || Rails.env
36
        
36

  
37 37
        @config = @defaults.dup
38
        
38

  
39 39
        load_deprecated_email_configuration(env)
40 40
        if File.file?(filename)
41 41
          @config.merge!(load_from_yaml(filename, env))
42 42
        end
43
        
43

  
44 44
        # Compatibility mode for those who copy email.yml over configuration.yml
45 45
        %w(delivery_method smtp_settings sendmail_settings).each do |key|
46 46
          if value = @config.delete(key)
......
48 48
            @config['email_delivery'][key] = value
49 49
          end
50 50
        end
51
        
51

  
52 52
        if @config['email_delivery']
53 53
          ActionMailer::Base.perform_deliveries = true
54 54
          @config['email_delivery'].each do |k, v|
......
56 56
            ActionMailer::Base.send("#{k}=", v)
57 57
          end
58 58
        end
59
          
59

  
60 60
        @config
61 61
      end
62
      
62

  
63 63
      # Returns a configuration setting
64 64
      def [](name)
65 65
        load unless @config
66 66
        @config[name]
67 67
      end
68
      
68

  
69 69
      # Yields a block with the specified hash configuration settings
70 70
      def with(settings)
71 71
        settings.stringify_keys!
......
75 75
        yield if block_given?
76 76
        @config.merge! was
77 77
      end
78
      
78

  
79 79
      private
80
      
80

  
81 81
      def load_from_yaml(filename, env)
82
        yaml = YAML::load_file(filename)
82
        yaml = nil
83
        begin
84
          yaml = YAML::load_file(filename)
85
        rescue ArgumentError
86
          $stderr.puts "Your Redmine configuration file located at #{filename} is not a valid YAML file and could not be loaded."
87
          exit 1
88
        end
83 89
        conf = {}
84 90
        if yaml.is_a?(Hash)
85 91
          if yaml['default']
......
89 95
            conf.merge!(yaml[env])
90 96
          end
91 97
        else
92
          $stderr.puts "#{filename} is not a valid Redmine configuration file"
98
          $stderr.puts "Your Redmine configuration file located at #{filename} is not a valid Redmine configuration file."
93 99
          exit 1
94 100
        end
95 101
        conf
96 102
      end
97
      
103

  
98 104
      def load_deprecated_email_configuration(env)
99 105
        deprecated_email_conf = File.join(Rails.root, 'config', 'email.yml')
100 106
        if File.file?(deprecated_email_conf)
lib/redmine/core_ext/string/conversions.rb
1
# redMine - project management software
2
# Copyright (C) 2008  Jean-Philippe Lang
1
# Redmine - project management software
2
# Copyright (C) 2006-2011  Jean-Philippe Lang
3 3
#
4 4
# This program is free software; you can redistribute it and/or
5 5
# modify it under the terms of the GNU General Public License
6 6
# as published by the Free Software Foundation; either version 2
7 7
# of the License, or (at your option) any later version.
8
# 
8
#
9 9
# This program is distributed in the hope that it will be useful,
10 10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 12
# GNU General Public License for more details.
13
# 
13
#
14 14
# You should have received a copy of the GNU General Public License
15 15
# along with this program; if not, write to the Free Software
16 16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
......
36 36
          s.gsub!(',', '.')
37 37
          begin; Kernel.Float(s); rescue; nil; end
38 38
        end
39
        
39

  
40 40
        # Object#to_a removed in ruby1.9
41 41
        if RUBY_VERSION > '1.9'
42 42
          def to_a
lib/redmine/core_ext/string/inflections.rb
1 1
# Redmine - project management software
2
# Copyright (C) 2009  Jean-Philippe Lang
2
# Copyright (C) 2006-2011  Jean-Philippe Lang
3 3
#
4 4
# This program is free software; you can redistribute it and/or
5 5
# modify it under the terms of the GNU General Public License
6 6
# as published by the Free Software Foundation; either version 2
7 7
# of the License, or (at your option) any later version.
8
# 
8
#
9 9
# This program is distributed in the hope that it will be useful,
10 10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 12
# GNU General Public License for more details.
13
# 
13
#
14 14
# You should have received a copy of the GNU General Public License
15 15
# along with this program; if not, write to the Free Software
16 16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
lib/redmine/custom_field_format.rb
1 1
# Redmine - project management software
2
# Copyright (C) 2006-2009  Jean-Philippe Lang
2
# Copyright (C) 2006-2011  Jean-Philippe Lang
3 3
#
4 4
# This program is free software; you can redistribute it and/or
5 5
# modify it under the terms of the GNU General Public License
6 6
# as published by the Free Software Foundation; either version 2
7 7
# of the License, or (at your option) any later version.
8
# 
8
#
9 9
# This program is distributed in the hope that it will be useful,
10 10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 12
# GNU General Public License for more details.
13
# 
13
#
14 14
# You should have received a copy of the GNU General Public License
15 15
# along with this program; if not, write to the Free Software
16 16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
......
49 49
        return value
50 50
      }
51 51
    end
52
    
52

  
53 53
    ['user', 'version'].each do |name|
54 54
      define_method("format_as_#{name}") {|value|
55 55
        return value.blank? ? "" : name.classify.constantize.find_by_id(value.to_i).to_s
......
60 60
      def map(&block)
61 61
        yield self
62 62
      end
63
      
63

  
64 64
      # Registers a custom field format
65 65
      def register(custom_field_format, options={})
66 66
        @@available[custom_field_format.name] = custom_field_format unless @@available.keys.include?(custom_field_format.name)
......
100 100
        end
101 101
      end
102 102
    end
103
  end 
103
  end
104 104
end
lib/redmine/default_data/loader.rb
5 5
# modify it under the terms of the GNU General Public License
6 6
# as published by the Free Software Foundation; either version 2
7 7
# of the License, or (at your option) any later version.
8
# 
8
#
9 9
# This program is distributed in the hope that it will be useful,
10 10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 12
# GNU General Public License for more details.
13
# 
13
#
14 14
# You should have received a copy of the GNU General Public License
15 15
# along with this program; if not, write to the Free Software
16 16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
......
21 21

  
22 22
    module Loader
23 23
      include Redmine::I18n
24
    
24

  
25 25
      class << self
26 26
        # Returns true if no data is already loaded in the database
27 27
        # otherwise false
......
31 31
            !IssueStatus.find(:first) &&
32 32
            !Enumeration.find(:first)
33 33
        end
34
        
34

  
35 35
        # Loads the default data
36 36
        # Raises a RecordNotSaved exception if something goes wrong
37 37
        def load(lang=nil)
38 38
          raise DataAlreadyLoaded.new("Some configuration data is already loaded.") unless no_data?
39 39
          set_language_if_valid(lang)
40
          
40

  
41 41
          Role.transaction do
42 42
            # Roles
43
            manager = Role.create! :name => l(:default_role_manager), 
43
            manager = Role.create! :name => l(:default_role_manager),
44 44
                                   :issues_visibility => 'all',
45 45
                                   :position => 1
46 46
            manager.permissions = manager.setable_permissions.collect {|p| p.name}
47 47
            manager.save!
48
            
49
            developer = Role.create!  :name => l(:default_role_developer), 
50
                                      :position => 2, 
51
                                      :permissions => [:manage_versions, 
48

  
49
            developer = Role.create!  :name => l(:default_role_developer),
50
                                      :position => 2,
51
                                      :permissions => [:manage_versions,
52 52
                                                      :manage_categories,
53 53
                                                      :view_issues,
54 54
                                                      :add_issues,
......
74 74
                                                      :browse_repository,
75 75
                                                      :view_changesets,
76 76
                                                      :commit_access]
77
            
77

  
78 78
            reporter = Role.create! :name => l(:default_role_reporter),
79 79
                                    :position => 3,
80 80
                                    :permissions => [:view_issues,
......
94 94
                                                    :view_files,
95 95
                                                    :browse_repository,
96 96
                                                    :view_changesets]
97
                        
97

  
98 98
            Role.non_member.update_attribute :permissions, [:view_issues,
99 99
                                                            :add_issues,
100 100
                                                            :add_issue_notes,
......
110 110
                                                            :view_files,
111 111
                                                            :browse_repository,
112 112
                                                            :view_changesets]
113
          
113

  
114 114
            Role.anonymous.update_attribute :permissions, [:view_issues,
115 115
                                                           :view_gantt,
116 116
                                                           :view_calendar,
......
121 121
                                                           :view_files,
122 122
                                                           :browse_repository,
123 123
                                                           :view_changesets]
124
                                                             
124

  
125 125
            # Trackers
126 126
            Tracker.create!(:name => l(:default_tracker_bug),     :is_in_chlog => true,  :is_in_roadmap => false, :position => 1)
127 127
            Tracker.create!(:name => l(:default_tracker_feature), :is_in_chlog => true,  :is_in_roadmap => true,  :position => 2)
128 128
            Tracker.create!(:name => l(:default_tracker_support), :is_in_chlog => false, :is_in_roadmap => false, :position => 3)
129
            
129

  
130 130
            # Issue statuses
131 131
            new       = IssueStatus.create!(:name => l(:default_issue_status_new), :is_closed => false, :is_default => true, :position => 1)
132 132
            in_progress  = IssueStatus.create!(:name => l(:default_issue_status_in_progress), :is_closed => false, :is_default => false, :position => 2)
......
134 134
            feedback  = IssueStatus.create!(:name => l(:default_issue_status_feedback), :is_closed => false, :is_default => false, :position => 4)
135 135
            closed    = IssueStatus.create!(:name => l(:default_issue_status_closed), :is_closed => true, :is_default => false, :position => 5)
136 136
            rejected  = IssueStatus.create!(:name => l(:default_issue_status_rejected), :is_closed => true, :is_default => false, :position => 6)
137
            
137

  
138 138
            # Workflow
139 139
            Tracker.find(:all).each { |t|
140 140
              IssueStatus.find(:all).each { |os|
141 141
                IssueStatus.find(:all).each { |ns|
142 142
                  Workflow.create!(:tracker_id => t.id, :role_id => manager.id, :old_status_id => os.id, :new_status_id => ns.id) unless os == ns
143
                }        
144
              }      
143
                }
144
              }
145 145
            }
146
            
146

  
147 147
            Tracker.find(:all).each { |t|
148 148
              [new, in_progress, resolved, feedback].each { |os|
149 149
                [in_progress, resolved, feedback, closed].each { |ns|
150 150
                  Workflow.create!(:tracker_id => t.id, :role_id => developer.id, :old_status_id => os.id, :new_status_id => ns.id) unless os == ns
151
                }        
152
              }      
151
                }
152
              }
153 153
            }
154
            
154

  
155 155
            Tracker.find(:all).each { |t|
156 156
              [new, in_progress, resolved, feedback].each { |os|
157 157
                [closed].each { |ns|
158 158
                  Workflow.create!(:tracker_id => t.id, :role_id => reporter.id, :old_status_id => os.id, :new_status_id => ns.id) unless os == ns
159
                }        
159
                }
160 160
              }
161 161
              Workflow.create!(:tracker_id => t.id, :role_id => reporter.id, :old_status_id => resolved.id, :new_status_id => feedback.id)
162 162
            }
163
          
163

  
164 164
            # Enumerations
165 165
            DocumentCategory.create!(:name => l(:default_doc_category_user), :position => 1)
166 166
            DocumentCategory.create!(:name => l(:default_doc_category_tech), :position => 2)
167
          
167

  
168 168
            IssuePriority.create!(:name => l(:default_priority_low), :position => 1)
169 169
            IssuePriority.create!(:name => l(:default_priority_normal), :position => 2, :is_default => true)
170 170
            IssuePriority.create!(:name => l(:default_priority_high), :position => 3)
171 171
            IssuePriority.create!(:name => l(:default_priority_urgent), :position => 4)
172 172
            IssuePriority.create!(:name => l(:default_priority_immediate), :position => 5)
173
          
173

  
174 174
            TimeEntryActivity.create!(:name => l(:default_activity_design), :position => 1)
175 175
            TimeEntryActivity.create!(:name => l(:default_activity_development), :position => 2)
176 176
          end
lib/redmine/export/pdf.rb
18 18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19 19

  
20 20
require 'iconv'
21
require 'rfpdf/fpdf'
22 21
require 'fpdf/chinese'
23 22
require 'fpdf/japanese'
24 23
require 'fpdf/korean'
24
require 'core/rmagick'
25 25

  
26 26
module Redmine
27 27
  module Export
28 28
    module PDF
29 29
      include ActionView::Helpers::TextHelper
30 30
      include ActionView::Helpers::NumberHelper
31
      include IssuesHelper
31 32

  
32 33
      class ITCPDF < TCPDF
33 34
        include Redmine::I18n
34 35
        attr_accessor :footer_date
35 36

  
36 37
        def initialize(lang)
38
          @@k_path_cache = Rails.root.join('tmp', 'pdf')
39
          FileUtils.mkdir_p @@k_path_cache unless File::exist?(@@k_path_cache)
37 40
          set_language_if_valid lang
38 41
          pdf_encoding = l(:general_pdf_encoding).upcase
39
          if RUBY_VERSION < '1.9'
40
            @ic = Iconv.new(pdf_encoding, 'UTF-8')
41
          end
42 42
          super('P', 'mm', 'A4', (pdf_encoding == 'UTF-8'), pdf_encoding)
43 43
          case current_language.to_s.downcase
44 44
          when 'vi'
......
104 104
        end
105 105

  
106 106
        def fix_text_encoding(txt)
107
          RDMPdfEncoding::rdm_pdf_iconv(@ic, txt)
107
          RDMPdfEncoding::rdm_from_utf8(txt, l(:general_pdf_encoding))
108 108
        end
109 109

  
110 110
        def RDMCell(w ,h=0, txt='', border=0, ln=0, align='', fill=0, link='')
......
115 115
          MultiCell(w, h, fix_text_encoding(txt), border, align, fill, ln)
116 116
        end
117 117

  
118
        def RDMwriteHTMLCell(w, h, x, y, txt='', attachments=[], border=0, ln=1, fill=0)
119
          @attachments = attachments
120
          writeHTMLCell(w, h, x, y,
121
            fix_text_encoding(
122
              Redmine::WikiFormatting.to_html(Setting.text_formatting, txt)),
123
            border, ln, fill)
124
        end
125

  
126
        def getImageFilename(attrname)
127
          # attrname: general_pdf_encoding string file/uri name
128
          atta = RDMPdfEncoding.attach(@attachments, attrname, l(:general_pdf_encoding))
129
          if atta
130
            return atta.diskfile
131
          else
132
            return nil
133
          end
134
        end
135

  
118 136
        def Footer
119 137
          SetFont(@font_for_footer, 'I', 8)
120 138
          SetY(-15)
......
150 168
        col_width = []
151 169
        unless query.columns.empty?
152 170
          col_width = query.columns.collect do |c|
153
            (c.name == :subject || (c.is_a?(QueryCustomFieldColumn) && ['string', 'text'].include?(c.custom_field.field_format)))? 4.0 : 1.0
171
            (c.name == :subject || (c.is_a?(QueryCustomFieldColumn) &&
172
              ['string', 'text'].include?(c.custom_field.field_format))) ? 4.0 : 1.0
154 173
          end
155 174
          ratio = (table_width - col_id_width) / col_width.inject(0) {|s,w| s += w}
156 175
          col_width = col_width.collect {|w| w * ratio}
......
182 201
        pdf.SetFontStyle('',8)
183 202
        pdf.SetFillColor(255, 255, 255)
184 203
        previous_group = false
185
        issues.each do |issue|
204
        issue_list(issues) do |issue, level|
186 205
          if query.grouped? &&
187 206
               (group = query.group_by_column.value(issue)) != previous_group
188 207
            pdf.SetFontStyle('B',9)
......
199 218
              show_value(cv)
200 219
            else
201 220
              value = issue.send(column.name)
221
              if column.name == :subject
222
                value = "  " * level + value
223
              end
202 224
              if value.is_a?(Date)
203 225
                format_date(value)
204 226
              elsif value.is_a?(Time)
......
278 300
        pdf.footer_date = format_date(Date.today)
279 301
        pdf.AddPage
280 302
        pdf.SetFontStyle('B',11)
281
        pdf.RDMMultiCell(190,5,
282
             "#{issue.project} - #{issue.tracker} # #{issue.id}: #{issue.subject}")
303
        buf = "#{issue.project} - #{issue.tracker} # #{issue.id}"
304
        pdf.RDMMultiCell(190, 5, buf)
305
        pdf.Ln
306
        pdf.SetFontStyle('',8)
307
        base_x = pdf.GetX
308
        i = 1
309
        issue.ancestors.each do |ancestor|
310
          pdf.SetX(base_x + i)
311
          buf = "#{ancestor.tracker} # #{ancestor.id} (#{ancestor.status.to_s}): #{ancestor.subject}"
312
          pdf.RDMMultiCell(190 - i, 5, buf)
313
          i += 1 if i < 35
314
        end
283 315
        pdf.Ln
284 316

  
285 317
        pdf.SetFontStyle('B',9)
......
340 372
        pdf.SetFontStyle('B',9)
341 373
        pdf.RDMCell(35+155, 5, l(:field_description), "LRT", 1)
342 374
        pdf.SetFontStyle('',9)
343
        pdf.RDMMultiCell(35+155, 5, issue.description.to_s, "LRB")
375

  
376
        # Set resize image scale
377
        pdf.SetImageScale(1.6)
378
        pdf.RDMwriteHTMLCell(35+155, 5, 0, 0,
379
              issue.description.to_s, issue.attachments, "LRB")
380

  
381
        unless issue.leaf?
382
          # for CJK
383
          truncate_length = ( l(:general_pdf_encoding).upcase == "UTF-8" ? 90 : 65 )
384
  
385
          pdf.SetFontStyle('B',9)
386
          pdf.RDMCell(35+155,5, l(:label_subtask_plural) + ":", "LTR")
387
          pdf.Ln
388
          issue_list(issue.descendants.sort_by(&:lft)) do |child, level|
389
            buf = truncate("#{child.tracker} # #{child.id}: #{child.subject}",
390
                           :length => truncate_length)
391
            level = 10 if level >= 10
392
            pdf.SetFontStyle('',8)
393
            pdf.RDMCell(35+135,5, (level >=1 ? "  " * level : "") + buf, "L")
394
            pdf.SetFontStyle('B',8)
395
            pdf.RDMCell(20,5, child.status.to_s, "R")
396
            pdf.Ln
397
          end
398
        end
399

  
400
        relations = issue.relations.select { |r| r.other_issue(issue).visible? }
401
        unless relations.empty?
402
          # for CJK
403
          truncate_length = ( l(:general_pdf_encoding).upcase == "UTF-8" ? 80 : 60 )
404
  
405
          pdf.SetFontStyle('B',9)
406
          pdf.RDMCell(35+155,5, l(:label_related_issues) + ":", "LTR")
407
          pdf.Ln
408
          relations.each do |relation|
409
            buf = ""
410
            buf += "#{l(relation.label_for(issue))} "
411
            if relation.delay && relation.delay != 0
412
              buf += "(#{l('datetime.distance_in_words.x_days', :count => relation.delay)}) "
413
            end
414
            if Setting.cross_project_issue_relations?
415
              buf += "#{relation.other_issue(issue).project} - "
416
            end
417
            buf += "#{relation.other_issue(issue).tracker}" +
418
                   " # #{relation.other_issue(issue).id}: #{relation.other_issue(issue).subject}"
419
            buf = truncate(buf, :length => truncate_length)
420
            pdf.SetFontStyle('', 8)
421
            pdf.RDMCell(35+155-60, 5, buf, "L")
422
            pdf.SetFontStyle('B',8)
423
            pdf.RDMCell(20,5, relation.other_issue(issue).status.to_s, "")
424
            pdf.RDMCell(20,5, format_date(relation.other_issue(issue).start_date), "")
425
            pdf.RDMCell(20,5, format_date(relation.other_issue(issue).due_date), "R")
426
            pdf.Ln
427
          end
428
        end
429
        pdf.RDMCell(190,5, "", "T")
344 430
        pdf.Ln
345 431

  
346 432
        if issue.changesets.any? &&
......
356 442
            pdf.Ln
357 443
            unless changeset.comments.blank?
358 444
              pdf.SetFontStyle('',8)
359
              pdf.RDMMultiCell(190,5, changeset.comments.to_s)
445
              pdf.RDMwriteHTMLCell(190,5,0,0,
446
                    changeset.comments.to_s, issue.attachments, "")
360 447
            end
361 448
            pdf.Ln
362 449
          end
......
365 452
        pdf.SetFontStyle('B',9)
366 453
        pdf.RDMCell(190,5, l(:label_history), "B")
367 454
        pdf.Ln
455
        indice = 0
368 456
        for journal in issue.journals.find(
369 457
                          :all, :include => [:user, :details],
370 458
                          :order => "#{Journal.table_name}.created_on ASC")
459
          indice = indice + 1
371 460
          pdf.SetFontStyle('B',8)
372 461
          pdf.RDMCell(190,5,
373
             format_time(journal.created_on) + " - " + journal.user.name)
462
             "#" + indice.to_s +
463
             " - " + format_time(journal.created_on) +
464
             " - " + journal.user.name)
374 465
          pdf.Ln
375 466
          pdf.SetFontStyle('I',8)
376 467
          for detail in journal.details
......
379 470
          if journal.notes?
380 471
            pdf.Ln unless journal.details.empty?
381 472
            pdf.SetFontStyle('',8)
382
            pdf.RDMMultiCell(190,5, journal.notes.to_s)
473
            pdf.RDMwriteHTMLCell(190,5,0,0,
474
                  journal.notes.to_s, issue.attachments, "")
383 475
          end
384 476
          pdf.Ln
385 477
        end
......
400 492
        pdf.Output
401 493
      end
402 494

  
495
      # Returns a PDF string of a single wiki page
496
      def wiki_to_pdf(page, project)
497
        pdf = ITCPDF.new(current_language)
498
        pdf.SetTitle("#{project} - #{page.title}")
499
        pdf.alias_nb_pages
500
        pdf.footer_date = format_date(Date.today)
501
        pdf.AddPage
502
        pdf.SetFontStyle('B',11)
503
        pdf.RDMMultiCell(190,5,
504
             "#{project} - #{page.title} - # #{page.content.version}")
505
        pdf.Ln
506
        # Set resize image scale
507
        pdf.SetImageScale(1.6)
508
        pdf.SetFontStyle('',9)
509
        pdf.RDMwriteHTMLCell(190,5,0,0,
510
              page.content.text.to_s, page.attachments, "TLRB")
511
        if page.attachments.any?
512
          pdf.Ln
513
          pdf.SetFontStyle('B',9)
514
          pdf.RDMCell(190,5, l(:label_attachment_plural), "B")
515
          pdf.Ln
516
          for attachment in page.attachments
517
            pdf.SetFontStyle('',8)
518
            pdf.RDMCell(80,5, attachment.filename)
519
            pdf.RDMCell(20,5, number_to_human_size(attachment.filesize),0,0,"R")
520
            pdf.RDMCell(25,5, format_date(attachment.created_on),0,0,"R")
521
            pdf.RDMCell(65,5, attachment.author.name,0,0,"R")
522
            pdf.Ln
523
          end
524
        end
525
        pdf.Output
526
      end
527

  
403 528
      class RDMPdfEncoding
404
        include Redmine::I18n
405
        def self.rdm_pdf_iconv(ic, txt)
529
        def self.rdm_from_utf8(txt, encoding)
406 530
          txt ||= ''
531
          txt = Redmine::CodesetUtil.from_utf8(txt, encoding)
407 532
          if txt.respond_to?(:force_encoding)
408
            txt.force_encoding('UTF-8')
409
            if l(:general_pdf_encoding).upcase != 'UTF-8'
410
              txt = txt.encode(l(:general_pdf_encoding), :invalid => :replace,
411
                               :undef => :replace, :replace => '?')
412
            else
413
              txt = Redmine::CodesetUtil.replace_invalid_utf8(txt)
414
            end
415 533
            txt.force_encoding('ASCII-8BIT')
416
          elsif RUBY_PLATFORM == 'java'
417
            begin
418
              ic ||= Iconv.new(l(:general_pdf_encoding), 'UTF-8')
419
              txt = ic.iconv(txt)
420
            rescue
421
              txt = txt.gsub(%r{[^\r\n\t\x20-\x7e]}, '?')
422
            end
423
          else
424
            ic ||= Iconv.new(l(:general_pdf_encoding), 'UTF-8')
425
            txtar = ""
426
            begin
427
              txtar += ic.iconv(txt)
428
            rescue Iconv::IllegalSequence
429
              txtar += $!.success
430
              txt = '?' + $!.failed[1,$!.failed.length]
431
              retry
432
            rescue
433
              txtar += $!.success
434
            end
435
            txt = txtar
436 534
          end
437 535
          txt
438 536
        end
537

  
538
        def self.attach(attachments, filename, encoding)
539
          filename_utf8 = Redmine::CodesetUtil.to_utf8(filename, encoding)
540
          atta = nil
541
          if filename_utf8 =~ /^[^\/"]+\.(gif|jpg|jpe|jpeg|png)$/i
542
            atta = Attachment.latest_attach(attachments, filename_utf8)
543
          end
544
          if atta && atta.readable? && atta.visible?
545
            return atta
546
          else
547
            return nil
548
          end
549
        end
439 550
      end
440 551
    end
441 552
  end
lib/redmine/helpers/calendar.rb
1
# redMine - project management software
2
# Copyright (C) 2006-2007  Jean-Philippe Lang
1
# Redmine - project management software
2
# Copyright (C) 2006-2011  Jean-Philippe Lang
3 3
#
4 4
# This program is free software; you can redistribute it and/or
5 5
# modify it under the terms of the GNU General Public License
6 6
# as published by the Free Software Foundation; either version 2
7 7
# of the License, or (at your option) any later version.
8
# 
8
#
9 9
# This program is distributed in the hope that it will be useful,
10 10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 12
# GNU General Public License for more details.
13
# 
13
#
14 14
# You should have received a copy of the GNU General Public License
15 15
# along with this program; if not, write to the Free Software
16 16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 17

  
18 18
module Redmine
19 19
  module Helpers
20
    
20

  
21 21
    # Simple class to compute the start and end dates of a calendar
22 22
    class Calendar
23 23
      include Redmine::I18n
24 24
      attr_reader :startdt, :enddt
25
      
25

  
26 26
      def initialize(date, lang = current_language, period = :month)
27 27
        @date = date
28 28
        @events = []
29 29
        @ending_events_by_days = {}
30 30
        @starting_events_by_days = {}
31
        set_language_if_valid lang        
31
        set_language_if_valid lang
32 32
        case period
33 33
        when :month
34 34
          @startdt = Date.civil(date.year, date.month, 1)
......
44 44
          raise 'Invalid period'
45 45
        end
46 46
      end
47
      
47

  
48 48
      # Sets calendar events
49 49
      def events=(events)
50 50
        @events = events
51 51
        @ending_events_by_days = @events.group_by {|event| event.due_date}
52 52
        @starting_events_by_days = @events.group_by {|event| event.start_date}
53 53
      end
54
      
54

  
55 55
      # Returns events for the given day
56 56
      def events_on(day)
57 57
        ((@ending_events_by_days[day] || []) + (@starting_events_by_days[day] || [])).uniq
58 58
      end
59
      
59

  
60 60
      # Calendar current month
61 61
      def month
62 62
        @date.month
63 63
      end
64
      
64

  
65 65
      # Return the first day of week
66 66
      # 1 = Monday ... 7 = Sunday
67 67
      def first_wday
......
76 76
          @first_dow ||= (l(:general_first_day_of_week).to_i - 1)%7 + 1
77 77
        end
78 78
      end
79
      
79

  
80 80
      def last_wday
81 81
        @last_dow ||= (first_wday + 5)%7 + 1
82 82
      end
83
    end    
83
    end
84 84
  end
85 85
end
lib/redmine/helpers/diff.rb
5 5
# modify it under the terms of the GNU General Public License
6 6
# as published by the Free Software Foundation; either version 2
7 7
# of the License, or (at your option) any later version.
8
# 
8
#
9 9
# This program is distributed in the hope that it will be useful,
10 10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 12
# GNU General Public License for more details.
13
# 
13
#
14 14
# You should have received a copy of the GNU General Public License
15 15
# along with this program; if not, write to the Free Software
16 16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
......
22 22
      include ActionView::Helpers::TagHelper
23 23
      include ActionView::Helpers::TextHelper
24 24
      attr_reader :diff, :words
25
      
25

  
26 26
      def initialize(content_to, content_from)
27 27
        @words = content_to.to_s.split(/(\s+)/)
28 28
        @words = @words.select {|word| word != ' '}
29 29
        words_from = content_from.to_s.split(/(\s+)/)
30
        words_from = words_from.select {|word| word != ' '}    
30
        words_from = words_from.select {|word| word != ' '}
31 31
        @diff = words_from.diff @words
32 32
      end
33
  
33

  
34 34
      def to_html
35 35
        words = self.words.collect{|word| h(word)}
36 36
        words_add = 0
......
41 41
          add_at = nil
42 42
          add_to = nil
43 43
          del_at = nil
44
          deleted = ""      
44
          deleted = ""
45 45
          diff.each do |change|
46 46
            pos = change[1]
47 47
            if change[0] == "+"
......
65 65
            words_del = 0
66 66
          end
67 67
        end
68
        words.join(' ')
68
        words.join(' ').html_safe
69 69
      end
70 70
    end
71 71
  end
lib/redmine/helpers/gantt.rb
260 260
      def subject_for_project(project, options)
261 261
        case options[:format]
262 262
        when :html
263
          subject = "<span class='icon icon-projects #{project.overdue? ? 'project-overdue' : ''}'>"
264
          subject << view.link_to_project(project)
265
          subject << '</span>'
263
          subject = "<span class='icon icon-projects #{project.overdue? ? 'project-overdue' : ''}'>".html_safe
264
          subject << view.link_to_project(project).html_safe
265
          subject << '</span>'.html_safe
266 266
          html_subject(options, subject, :css => "project-name")
267 267
        when :image
268 268
          image_subject(options, project.name)
......
298 298
      def subject_for_version(version, options)
299 299
        case options[:format]
300 300
        when :html
301
          subject = "<span class='icon icon-package #{version.behind_schedule? ? 'version-behind-schedule' : ''} #{version.overdue? ? 'version-overdue' : ''}'>"
302
          subject << view.link_to_version(version)
303
          subject << '</span>'
301
          subject = "<span class='icon icon-package #{version.behind_schedule? ? 'version-behind-schedule' : ''} #{version.overdue? ? 'version-overdue' : ''}'>".html_safe
302
          subject << view.link_to_version(version).html_safe
303
          subject << '</span>'.html_safe
304 304
          html_subject(options, subject, :css => "version-name")
305 305
        when :image
306 306
          image_subject(options, version.to_s_with_project)
......
347 347
          css_classes << ' issue-behind-schedule' if issue.behind_schedule?
348 348
          css_classes << ' icon icon-issue' unless Setting.gravatar_enabled? && issue.assigned_to
349 349

  
350
          subject = "<span class='#{css_classes}'>"
350
          subject = "<span class='#{css_classes}'>".html_safe
351 351
          if issue.assigned_to.present?
352 352
            assigned_string = l(:field_assigned_to) + ": " + issue.assigned_to.name
353
            subject << view.avatar(issue.assigned_to, :class => 'gravatar icon-gravatar', :size => 10, :title => assigned_string).to_s
353
            subject << view.avatar(issue.assigned_to, :class => 'gravatar icon-gravatar', :size => 10, :title => assigned_string).to_s.html_safe
354 354
          end
355
          subject << view.link_to_issue(issue)
356
          subject << '</span>'
355
          subject << view.link_to_issue(issue).html_safe
356
          subject << '</span>'.html_safe
357 357
          html_subject(options, subject, :css => "issue-subject", :title => issue.subject) + "\n"
358 358
        when :image
359 359
          image_subject(options, issue.subject)
......
737 737
        output = ''
738 738
        # Renders the task bar, with progress and late
739 739
        if coords[:bar_start] && coords[:bar_end]
740
          output << "<div style='top:#{ params[:top] }px;left:#{ coords[:bar_start] }px;width:#{ coords[:bar_end] - coords[:bar_start] - 2}px;' class='#{options[:css]} task_todo'>&nbsp;</div>"
740
          output << "<div style='top:#{ params[:top] }px;left:#{ coords[:bar_start] }px;width:#{ coords[:bar_end] - coords[:bar_start] - 2}px;' class='#{options[:css]} task_todo'>&nbsp;</div>".html_safe
741 741

  
742 742
          if coords[:bar_late_end]
743
            output << "<div style='top:#{ params[:top] }px;left:#{ coords[:bar_start] }px;width:#{ coords[:bar_late_end] - coords[:bar_start] - 2}px;' class='#{options[:css]} task_late'>&nbsp;</div>"
743
            output << "<div style='top:#{ params[:top] }px;left:#{ coords[:bar_start] }px;width:#{ coords[:bar_late_end] - coords[:bar_start] - 2}px;' class='#{options[:css]} task_late'>&nbsp;</div>".html_safe
744 744
          end
745 745
          if coords[:bar_progress_end]
746
            output << "<div style='top:#{ params[:top] }px;left:#{ coords[:bar_start] }px;width:#{ coords[:bar_progress_end] - coords[:bar_start] - 2}px;' class='#{options[:css]} task_done'>&nbsp;</div>"
746
            output << "<div style='top:#{ params[:top] }px;left:#{ coords[:bar_start] }px;width:#{ coords[:bar_progress_end] - coords[:bar_start] - 2}px;' class='#{options[:css]} task_done'>&nbsp;</div>".html_safe
747 747
          end
748 748
        end
749 749
        # Renders the markers
750 750
        if options[:markers]
751 751
          if coords[:start]
752
            output << "<div style='top:#{ params[:top] }px;left:#{ coords[:start] }px;width:15px;' class='#{options[:css]} marker starting'>&nbsp;</div>"
752
            output << "<div style='top:#{ params[:top] }px;left:#{ coords[:start] }px;width:15px;' class='#{options[:css]} marker starting'>&nbsp;</div>".html_safe
753 753
          end
754 754
          if coords[:end]
755
            output << "<div style='top:#{ params[:top] }px;left:#{ coords[:end] + params[:zoom] }px;width:15px;' class='#{options[:css]} marker ending'>&nbsp;</div>"
755
            output << "<div style='top:#{ params[:top] }px;left:#{ coords[:end] + params[:zoom] }px;width:15px;' class='#{options[:css]} marker ending'>&nbsp;</div>".html_safe
756 756
          end
757 757
        end
758 758
        # Renders the label on the right
759 759
        if options[:label]
760
          output << "<div style='top:#{ params[:top] }px;left:#{ (coords[:bar_end] || 0) + 8 }px;' class='#{options[:css]} label'>"
760
          output << "<div style='top:#{ params[:top] }px;left:#{ (coords[:bar_end] || 0) + 8 }px;' class='#{options[:css]} label'>".html_safe
761 761
          output << options[:label]
762
          output << "</div>"
762
          output << "</div>".html_safe
763 763
        end
764 764
        # Renders the tooltip
765 765
        if options[:issue] && coords[:bar_start] && coords[:bar_end]
766
          output << "<div class='tooltip' style='position: absolute;top:#{ params[:top] }px;left:#{ coords[:bar_start] }px;width:#{ coords[:bar_end] - coords[:bar_start] }px;height:12px;'>"
767
          output << '<span class="tip">'
768
          output << view.render_issue_tooltip(options[:issue])
769
          output << "</span></div>"
766
          output << "<div class='tooltip' style='position: absolute;top:#{ params[:top] }px;left:#{ coords[:bar_start] }px;width:#{ coords[:bar_end] - coords[:bar_start] }px;height:12px;'>".html_safe
767
          output << '<span class="tip">'.html_safe
768
          output << view.render_issue_tooltip(options[:issue]).html_safe
769
          output << "</span></div>".html_safe
770 770
        end
771 771
        @lines << output
772 772
        output
lib/redmine/hook.rb
1 1
# Redmine - project management software
2
# Copyright (C) 2006-2008  Jean-Philippe Lang
2
# Copyright (C) 2006-2011  Jean-Philippe Lang
3 3
#
4 4
# This program is free software; you can redistribute it and/or
5 5
# modify it under the terms of the GNU General Public License
6 6
# as published by the Free Software Foundation; either version 2
7 7
# of the License, or (at your option) any later version.
8
# 
8
#
9 9
# This program is distributed in the hope that it will be useful,
10 10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 12
# GNU General Public License for more details.
13
# 
13
#
14 14
# You should have received a copy of the GNU General Public License
15 15
# along with this program; if not, write to the Free Software
16 16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
......
22 22
    @@listener_classes = []
23 23
    @@listeners = nil
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff