Revision 1298:4f746d8966dd .svn/pristine/4c

View differences:

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

  
18
module Redmine
19
  module Platform
20
    class << self
21
      def mswin?
22
        (RUBY_PLATFORM =~ /(:?mswin|mingw)/) ||
23
           (RUBY_PLATFORM == 'java' && (ENV['OS'] || ENV['os']) =~ /windows/i)
24
      end
25
    end
26
  end
27
end
.svn/pristine/4c/4c336c258123fb4e9b45bc7fc7cd3f56f7293d30.svn-base
1
# encoding: utf-8
2
#
3
# Redmine - project management software
4
# Copyright (C) 2006-2013  Jean-Philippe Lang
5
#
6
# This program is free software; you can redistribute it and/or
7
# modify it under the terms of the GNU General Public License
8
# as published by the Free Software Foundation; either version 2
9
# of the License, or (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
# GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19

  
20
module IssueCategoriesHelper
21
end
.svn/pristine/4c/4c46450a69eddda0d211771bcf055604ac13c9ff.svn-base
1
class Namespace::SharedPluginController < ApplicationController
2
  def an_action
3
    render_class_and_action 'from alpha_plugin'
4
  end
5
end
.svn/pristine/4c/4c59ba20a8f0cc2dfb2dedfa1e4641821d4c6a38.svn-base
1
// ** I18N
2

  
3
// Calendar NO language (Norwegian/Norsk bokmål)
4
// Author: Kai Olav Fredriksen <k@i.fredriksen.net>
5

  
6
// full day names
7
Calendar._DN = new Array
8
("Søndag",
9
 "Mandag",
10
 "Tirsdag",
11
 "Onsdag",
12
 "Torsdag",
13
 "Fredag",
14
 "Lørdag",
15
 "Søndag");
16

  
17
Calendar._SDN_len = 3; // short day name length
18
Calendar._SMN_len = 3; // short month name length
19

  
20
// First day of the week. "0" means display Sunday first, "1" means display
21
// Monday first, etc.
22
Calendar._FD = 1;
23

  
24
// full month names
25
Calendar._MN = new Array
26
("Januar",
27
 "Februar",
28
 "Mars",
29
 "April",
30
 "Mai",
31
 "Juni",
32
 "Juli",
33
 "August",
34
 "September",
35
 "Oktober",
36
 "November",
37
 "Desember");
38

  
39
// tooltips
40
Calendar._TT = {};
41
Calendar._TT["INFO"] = "Om kalenderen";
42

  
43
Calendar._TT["ABOUT"] =
44
"DHTML Date/Time Selector\n" +
45
"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-)
46
"For latest version visit: http://www.dynarch.com/projects/calendar/\n" +
47
"Distributed under GNU LGPL.  See http://gnu.org/licenses/lgpl.html for details." +
48
"\n\n" +
49
"Date selection:\n" +
50
"- Use the \xab, \xbb buttons to select year\n" +
51
"- Use the " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " buttons to select month\n" +
52
"- Hold mouse button on any of the above buttons for faster selection.";
53
Calendar._TT["ABOUT_TIME"] = "\n\n" +
54
"Time selection:\n" +
55
"- Click on any of the time parts to increase it\n" +
56
"- or Shift-click to decrease it\n" +
57
"- or click and drag for faster selection.";
58

  
59
Calendar._TT["PREV_YEAR"] = "Forrige år (hold for meny)";
60
Calendar._TT["PREV_MONTH"] = "Forrige måned (hold for meny)";
61
Calendar._TT["GO_TODAY"] = "Gå til idag";
62
Calendar._TT["NEXT_MONTH"] = "Neste måned (hold for meny)";
63
Calendar._TT["NEXT_YEAR"] = "Neste år (hold for meny)";
64
Calendar._TT["SEL_DATE"] = "Velg dato";
65
Calendar._TT["DRAG_TO_MOVE"] = "Dra for å flytte";
66
Calendar._TT["PART_TODAY"] = " (idag)";
67

  
68
// the following is to inform that "%s" is to be the first day of week
69
// %s will be replaced with the day name.
70
Calendar._TT["DAY_FIRST"] = "Vis %s først";
71

  
72
// This may be locale-dependent.  It specifies the week-end days, as an array
73
// of comma-separated numbers.  The numbers are from 0 to 6: 0 means Sunday, 1
74
// means Monday, etc.
75
Calendar._TT["WEEKEND"] = "0,6";
76

  
77
Calendar._TT["CLOSE"] = "Lukk";
78
Calendar._TT["TODAY"] = "Idag";
79
Calendar._TT["TIME_PART"] = "(Shift-)Klikk eller dra for å endre verdi";
80

  
81
// date formats
82
Calendar._TT["DEF_DATE_FORMAT"] = "%%d.%m.%Y";
83
Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e";
84

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

  
18
require File.expand_path('../../../test_helper', __FILE__)
19

  
20
class ApiTest::ProjectsTest < ActionController::IntegrationTest
21
  fixtures :projects, :versions, :users, :roles, :members, :member_roles, :issues, :journals, :journal_details,
22
           :trackers, :projects_trackers, :issue_statuses, :enabled_modules, :enumerations, :boards, :messages,
23
           :attachments, :custom_fields, :custom_values, :time_entries, :issue_categories
24

  
25
  def setup
26
    Setting.rest_api_enabled = '1'
27
    set_tmp_attachments_directory
28
  end
29

  
30
  context "GET /projects" do
31
    context ".xml" do
32
      should "return projects" do
33
        get '/projects.xml'
34
        assert_response :success
35
        assert_equal 'application/xml', @response.content_type
36

  
37
        assert_tag :tag => 'projects',
38
          :child => {:tag => 'project', :child => {:tag => 'id', :content => '1'}}
39
      end
40
    end
41

  
42
    context ".json" do
43
      should "return projects" do
44
        get '/projects.json'
45
        assert_response :success
46
        assert_equal 'application/json', @response.content_type
47

  
48
        json = ActiveSupport::JSON.decode(response.body)
49
        assert_kind_of Hash, json
50
        assert_kind_of Array, json['projects']
51
        assert_kind_of Hash, json['projects'].first
52
        assert json['projects'].first.has_key?('id')
53
      end
54
    end
55
  end
56

  
57
  context "GET /projects/:id" do
58
    context ".xml" do
59
      # TODO: A private project is needed because should_allow_api_authentication
60
      # actually tests that authentication is *required*, not just allowed
61
      should_allow_api_authentication(:get, "/projects/2.xml")
62

  
63
      should "return requested project" do
64
        get '/projects/1.xml'
65
        assert_response :success
66
        assert_equal 'application/xml', @response.content_type
67

  
68
        assert_tag :tag => 'project',
69
          :child => {:tag => 'id', :content => '1'}
70
        assert_tag :tag => 'custom_field',
71
          :attributes => {:name => 'Development status'}, :content => 'Stable'
72

  
73
        assert_no_tag 'trackers'
74
        assert_no_tag 'issue_categories'
75
      end
76

  
77
      context "with hidden custom fields" do
78
        setup do
79
          ProjectCustomField.find_by_name('Development status').update_attribute :visible, false
80
        end
81

  
82
        should "not display hidden custom fields" do
83
          get '/projects/1.xml'
84
          assert_response :success
85
          assert_equal 'application/xml', @response.content_type
86

  
87
          assert_no_tag 'custom_field',
88
            :attributes => {:name => 'Development status'}
89
        end
90
      end
91

  
92
      should "return categories with include=issue_categories" do
93
        get '/projects/1.xml?include=issue_categories'
94
        assert_response :success
95
        assert_equal 'application/xml', @response.content_type
96

  
97
        assert_tag 'issue_categories',
98
          :attributes => {:type => 'array'},
99
          :child => {
100
            :tag => 'issue_category',
101
            :attributes => {
102
              :id => '2',
103
              :name => 'Recipes'
104
            }
105
          }
106
      end
107

  
108
      should "return trackers with include=trackers" do
109
        get '/projects/1.xml?include=trackers'
110
        assert_response :success
111
        assert_equal 'application/xml', @response.content_type
112

  
113
        assert_tag 'trackers',
114
          :attributes => {:type => 'array'},
115
          :child => {
116
            :tag => 'tracker',
117
            :attributes => {
118
              :id => '2',
119
              :name => 'Feature request'
120
            }
121
          }
122
      end
123
    end
124

  
125
    context ".json" do
126
      should_allow_api_authentication(:get, "/projects/2.json")
127

  
128
      should "return requested project" do
129
        get '/projects/1.json'
130

  
131
        json = ActiveSupport::JSON.decode(response.body)
132
        assert_kind_of Hash, json
133
        assert_kind_of Hash, json['project']
134
        assert_equal 1, json['project']['id']
135
      end
136
    end
137
  end
138

  
139
  context "POST /projects" do
140
    context "with valid parameters" do
141
      setup do
142
        Setting.default_projects_modules = ['issue_tracking', 'repository']
143
        @parameters = {:project => {:name => 'API test', :identifier => 'api-test'}}
144
      end
145

  
146
      context ".xml" do
147
        should_allow_api_authentication(:post,
148
                                        '/projects.xml',
149
                                        {:project => {:name => 'API test', :identifier => 'api-test'}},
150
                                        {:success_code => :created})
151

  
152

  
153
        should "create a project with the attributes" do
154
          assert_difference('Project.count') do
155
            post '/projects.xml', @parameters, :authorization => credentials('admin')
156
          end
157

  
158
          project = Project.first(:order => 'id DESC')
159
          assert_equal 'API test', project.name
160
          assert_equal 'api-test', project.identifier
161
          assert_equal ['issue_tracking', 'repository'], project.enabled_module_names.sort
162
          assert_equal Tracker.all.size, project.trackers.size
163

  
164
          assert_response :created
165
          assert_equal 'application/xml', @response.content_type
166
          assert_tag 'project', :child => {:tag => 'id', :content => project.id.to_s}
167
        end
168

  
169
        should "accept enabled_module_names attribute" do
170
          @parameters[:project].merge!({:enabled_module_names => ['issue_tracking', 'news', 'time_tracking']})
171

  
172
          assert_difference('Project.count') do
173
            post '/projects.xml', @parameters, :authorization => credentials('admin')
174
          end
175

  
176
          project = Project.first(:order => 'id DESC')
177
          assert_equal ['issue_tracking', 'news', 'time_tracking'], project.enabled_module_names.sort
178
        end
179

  
180
        should "accept tracker_ids attribute" do
181
          @parameters[:project].merge!({:tracker_ids => [1, 3]})
182

  
183
          assert_difference('Project.count') do
184
            post '/projects.xml', @parameters, :authorization => credentials('admin')
185
          end
186

  
187
          project = Project.first(:order => 'id DESC')
188
          assert_equal [1, 3], project.trackers.map(&:id).sort
189
        end
190
      end
191
    end
192

  
193
    context "with invalid parameters" do
194
      setup do
195
        @parameters = {:project => {:name => 'API test'}}
196
      end
197

  
198
      context ".xml" do
199
        should "return errors" do
200
          assert_no_difference('Project.count') do
201
            post '/projects.xml', @parameters, :authorization => credentials('admin')
202
          end
203

  
204
          assert_response :unprocessable_entity
205
          assert_equal 'application/xml', @response.content_type
206
          assert_tag 'errors', :child => {:tag => 'error', :content => "Identifier can't be blank"}
207
        end
208
      end
209
    end
210
  end
211

  
212
  context "PUT /projects/:id" do
213
    context "with valid parameters" do
214
      setup do
215
        @parameters = {:project => {:name => 'API update'}}
216
      end
217

  
218
      context ".xml" do
219
        should_allow_api_authentication(:put,
220
                                        '/projects/2.xml',
221
                                        {:project => {:name => 'API update'}},
222
                                        {:success_code => :ok})
223

  
224
        should "update the project" do
225
          assert_no_difference 'Project.count' do
226
            put '/projects/2.xml', @parameters, :authorization => credentials('jsmith')
227
          end
228
          assert_response :ok
229
          assert_equal 'application/xml', @response.content_type
230
          project = Project.find(2)
231
          assert_equal 'API update', project.name
232
        end
233

  
234
        should "accept enabled_module_names attribute" do
235
          @parameters[:project].merge!({:enabled_module_names => ['issue_tracking', 'news', 'time_tracking']})
236

  
237
          assert_no_difference 'Project.count' do
238
            put '/projects/2.xml', @parameters, :authorization => credentials('admin')
239
          end
240
          assert_response :ok
241
          project = Project.find(2)
242
          assert_equal ['issue_tracking', 'news', 'time_tracking'], project.enabled_module_names.sort
243
        end
244

  
245
        should "accept tracker_ids attribute" do
246
          @parameters[:project].merge!({:tracker_ids => [1, 3]})
247

  
248
          assert_no_difference 'Project.count' do
249
            put '/projects/2.xml', @parameters, :authorization => credentials('admin')
250
          end
251
          assert_response :ok
252
          project = Project.find(2)
253
          assert_equal [1, 3], project.trackers.map(&:id).sort
254
        end
255
      end
256
    end
257

  
258
    context "with invalid parameters" do
259
      setup do
260
        @parameters = {:project => {:name => ''}}
261
      end
262

  
263
      context ".xml" do
264
        should "return errors" do
265
          assert_no_difference('Project.count') do
266
            put '/projects/2.xml', @parameters, :authorization => credentials('admin')
267
          end
268

  
269
          assert_response :unprocessable_entity
270
          assert_equal 'application/xml', @response.content_type
271
          assert_tag 'errors', :child => {:tag => 'error', :content => "Name can't be blank"}
272
        end
273
      end
274
    end
275
  end
276

  
277
  context "DELETE /projects/:id" do
278
    context ".xml" do
279
      should_allow_api_authentication(:delete,
280
                                      '/projects/2.xml',
281
                                      {},
282
                                      {:success_code => :ok})
283

  
284
      should "delete the project" do
285
        assert_difference('Project.count',-1) do
286
          delete '/projects/2.xml', {}, :authorization => credentials('admin')
287
        end
288
        assert_response :ok
289
        assert_nil Project.find_by_id(2)
290
      end
291
    end
292
  end
293

  
294
  def credentials(user, password=nil)
295
    ActionController::HttpAuthentication::Basic.encode_credentials(user, password || user)
296
  end
297
end
.svn/pristine/4c/4cde02772540ced7426b8c2828a1d2272fc2d8ad.svn-base
1
// ** I18N
2

  
3
// Calendar pt language
4
// Author: Adalberto Machado, <betosm@terra.com.br>
5
// Corrected by: Pedro Araújo <phcrva19@hotmail.com>
6
// Encoding: any
7
// Distributed under the same terms as the calendar itself.
8

  
9
// For translators: please use UTF-8 if possible.  We strongly believe that
10
// Unicode is the answer to a real internationalized world.  Also please
11
// include your contact information in the header, as can be seen above.
12

  
13
// full day names
14
Calendar._DN = new Array
15
("Domingo",
16
 "Segunda",
17
 "Terça",
18
 "Quarta",
19
 "Quinta",
20
 "Sexta",
21
 "Sábado",
22
 "Domingo");
23

  
24
// Please note that the following array of short day names (and the same goes
25
// for short month names, _SMN) isn't absolutely necessary.  We give it here
26
// for exemplification on how one can customize the short day names, but if
27
// they are simply the first N letters of the full name you can simply say:
28
//
29
//   Calendar._SDN_len = N; // short day name length
30
//   Calendar._SMN_len = N; // short month name length
31
//
32
// If N = 3 then this is not needed either since we assume a value of 3 if not
33
// present, to be compatible with translation files that were written before
34
// this feature.
35

  
36
// short day names
37
Calendar._SDN = new Array
38
("Dom",
39
 "Seg",
40
 "Ter",
41
 "Qua",
42
 "Qui",
43
 "Sex",
44
 "Sáb",
45
 "Dom");
46

  
47
// First day of the week. "0" means display Sunday first, "1" means display
48
// Monday first, etc.
49
Calendar._FD = 1;
50

  
51
// full month names
52
Calendar._MN = new Array
53
("Janeiro",
54
 "Fevereiro",
55
 "Março",
56
 "Abril",
57
 "Maio",
58
 "Junho",
59
 "Julho",
60
 "Agosto",
61
 "Setembro",
62
 "Outubro",
63
 "Novembro",
64
 "Dezembro");
65

  
66
// short month names
67
Calendar._SMN = new Array
68
("Jan",
69
 "Fev",
70
 "Mar",
71
 "Abr",
72
 "Mai",
73
 "Jun",
74
 "Jul",
75
 "Ago",
76
 "Set",
77
 "Out",
78
 "Nov",
79
 "Dez");
80

  
81
// tooltips
82
Calendar._TT = {};
83
Calendar._TT["INFO"] = "Sobre o calendário";
84

  
85
Calendar._TT["ABOUT"] =
86
"DHTML Date/Time Selector\n" +
87
"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-)
88
"Última versão visite: http://www.dynarch.com/projects/calendar/\n" +
89
"Distribuído sobre a licença GNU LGPL.  Veja http://gnu.org/licenses/lgpl.html para detalhes." +
90
"\n\n" +
91
"Selecção de data:\n" +
92
"- Use os botões \xab, \xbb para seleccionar o ano\n" +
93
"- Use os botões " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " para seleccionar o mês\n" +
94
"- Segure o botão do rato em qualquer um desses botões para selecção rápida.";
95
Calendar._TT["ABOUT_TIME"] = "\n\n" +
96
"Selecção de hora:\n" +
97
"- Clique em qualquer parte da hora para incrementar\n" +
98
"- ou Shift-click para decrementar\n" +
99
"- ou clique e segure para selecção rápida.";
100

  
101
Calendar._TT["PREV_YEAR"] = "Ano ant. (segure para menu)";
102
Calendar._TT["PREV_MONTH"] = "Mês ant. (segure para menu)";
103
Calendar._TT["GO_TODAY"] = "Hoje";
104
Calendar._TT["NEXT_MONTH"] = "Prox. mês (segure para menu)";
105
Calendar._TT["NEXT_YEAR"] = "Prox. ano (segure para menu)";
106
Calendar._TT["SEL_DATE"] = "Seleccione a data";
107
Calendar._TT["DRAG_TO_MOVE"] = "Arraste para mover";
108
Calendar._TT["PART_TODAY"] = " (hoje)";
109

  
110
// the following is to inform that "%s" is to be the first day of week
111
// %s will be replaced with the day name.
112
Calendar._TT["DAY_FIRST"] = "Mostre %s primeiro";
113

  
114
// This may be locale-dependent.  It specifies the week-end days, as an array
115
// of comma-separated numbers.  The numbers are from 0 to 6: 0 means Sunday, 1
116
// means Monday, etc.
117
Calendar._TT["WEEKEND"] = "0,6";
118

  
119
Calendar._TT["CLOSE"] = "Fechar";
120
Calendar._TT["TODAY"] = "Hoje";
121
Calendar._TT["TIME_PART"] = "(Shift-)Click ou arraste para mudar valor";
122

  
123
// date formats
124
Calendar._TT["DEF_DATE_FORMAT"] = "%d/%m/%Y";
125
Calendar._TT["TT_DATE_FORMAT"] = "%a, %e %b";
126

  
127
Calendar._TT["WK"] = "sm";
128
Calendar._TT["TIME"] = "Hora:";
.svn/pristine/4c/4ce30d3521f9c879b76d2c58d967727fd036b9f7.svn-base
1
--- 
2
changesets_001: 
3
  commit_date: 2007-04-11
4
  committed_on: 2007-04-11 15:14:44 +02:00
5
  revision: 1
6
  id: 100
7
  comments: My very first commit
8
  repository_id: 10
9
  committer: dlopper
10
  user_id: 3
11
changesets_002: 
12
  commit_date: 2007-04-12
13
  committed_on: 2007-04-12 15:14:44 +02:00
14
  revision: 2
15
  id: 101
16
  comments: 'This commit fixes #1, #2 and references #1 & #3'
17
  repository_id: 10
18
  committer: dlopper
19
  user_id: 3
20
changesets_003: 
21
  commit_date: 2007-04-12
22
  committed_on: 2007-04-12 15:14:44 +02:00
23
  revision: 3
24
  id: 102
25
  comments: |-
26
    A commit with wrong issue ids
27
    IssueID #666 #3
28
  repository_id: 10
29
  committer: dlopper
30
  user_id: 3
31
changesets_004: 
32
  commit_date: 2007-04-12
33
  committed_on: 2007-04-12 15:14:44 +02:00
34
  revision: 4
35
  id: 103
36
  comments: |-
37
    A commit with an issue id of an other project
38
    IssueID 4 2
39
  repository_id: 10
40
  committer: dlopper
41
  user_id: 3
42
changesets_005: 
43
  commit_date: "2007-09-10"
44
  comments: Modified one file in the folder.
45
  committed_on: 2007-09-10 19:01:08
46
  revision: "5"
47
  id: 104
48
  scmid:
49
  user_id: 3
50
  repository_id: 10
51
  committer: dlopper
52
changesets_006: 
53
  commit_date: "2007-09-10"
54
  comments: Moved helloworld.rb from / to /folder.
55
  committed_on: 2007-09-10 19:01:47
56
  revision: "6"
57
  id: 105
58
  scmid:
59
  user_id: 3
60
  repository_id: 10
61
  committer: dlopper
62
changesets_007: 
63
  commit_date: "2007-09-10"
64
  comments: Removed one file.
65
  committed_on: 2007-09-10 19:02:16
66
  revision: "7"
67
  id: 106
68
  scmid:
69
  user_id: 3
70
  repository_id: 10
71
  committer: dlopper
72
changesets_008: 
73
  commit_date: "2007-09-10"
74
  comments: |-
75
    This commits references an issue.
76
    Refs #2
77
  committed_on: 2007-09-10 19:04:35
78
  revision: "8"
79
  id: 107
80
  scmid:
81
  user_id: 3
82
  repository_id: 10
83
  committer: dlopper
84
changesets_009: 
85
  commit_date: "2009-09-10"
86
  comments: One file added.
87
  committed_on: 2009-09-10 19:04:35
88
  revision: "9"
89
  id: 108
90
  scmid:
91
  user_id: 3
92
  repository_id: 10
93
  committer: dlopper
94
changesets_010: 
95
  commit_date: "2009-09-10"
96
  comments: Same file modified.
97
  committed_on: 2009-09-10 19:04:35
98
  revision: "10"
99
  id: 109
100
  scmid:
101
  user_id: 3
102
  repository_id: 10
103
  committer: dlopper
104
  
.svn/pristine/4c/4ce47e5153ddf18249e25aad233433aafed78ea8.svn-base
1
# Redmine - project management software
2
# Copyright (C) 2006-2013  Jean-Philippe Lang
3
#
4
# This program is free software; you can redistribute it and/or
5
# modify it under the terms of the GNU General Public License
6
# as published by the Free Software Foundation; either version 2
7
# of the License, or (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17

  
18
class Changeset < ActiveRecord::Base
19
  belongs_to :repository
20
  belongs_to :user
21
  has_many :filechanges, :class_name => 'Change', :dependent => :delete_all
22
  has_and_belongs_to_many :issues
23
  has_and_belongs_to_many :parents,
24
                          :class_name => "Changeset",
25
                          :join_table => "#{table_name_prefix}changeset_parents#{table_name_suffix}",
26
                          :association_foreign_key => 'parent_id', :foreign_key => 'changeset_id'
27
  has_and_belongs_to_many :children,
28
                          :class_name => "Changeset",
29
                          :join_table => "#{table_name_prefix}changeset_parents#{table_name_suffix}",
30
                          :association_foreign_key => 'changeset_id', :foreign_key => 'parent_id'
31

  
32
  acts_as_event :title => Proc.new {|o| o.title},
33
                :description => :long_comments,
34
                :datetime => :committed_on,
35
                :url => Proc.new {|o| {:controller => 'repositories', :action => 'revision', :id => o.repository.project, :repository_id => o.repository.identifier_param, :rev => o.identifier}}
36

  
37
  acts_as_searchable :columns => 'comments',
38
                     :include => {:repository => :project},
39
                     :project_key => "#{Repository.table_name}.project_id",
40
                     :date_column => 'committed_on'
41

  
42
  acts_as_activity_provider :timestamp => "#{table_name}.committed_on",
43
                            :author_key => :user_id,
44
                            :find_options => {:include => [:user, {:repository => :project}]}
45

  
46
  validates_presence_of :repository_id, :revision, :committed_on, :commit_date
47
  validates_uniqueness_of :revision, :scope => :repository_id
48
  validates_uniqueness_of :scmid, :scope => :repository_id, :allow_nil => true
49

  
50
  scope :visible, lambda {|*args|
51
    includes(:repository => :project).where(Project.allowed_to_condition(args.shift || User.current, :view_changesets, *args))
52
  }
53

  
54
  after_create :scan_for_issues
55
  before_create :before_create_cs
56

  
57
  def revision=(r)
58
    write_attribute :revision, (r.nil? ? nil : r.to_s)
59
  end
60

  
61
  # Returns the identifier of this changeset; depending on repository backends
62
  def identifier
63
    if repository.class.respond_to? :changeset_identifier
64
      repository.class.changeset_identifier self
65
    else
66
      revision.to_s
67
    end
68
  end
69

  
70
  def committed_on=(date)
71
    self.commit_date = date
72
    super
73
  end
74

  
75
  # Returns the readable identifier
76
  def format_identifier
77
    if repository.class.respond_to? :format_changeset_identifier
78
      repository.class.format_changeset_identifier self
79
    else
80
      identifier
81
    end
82
  end
83

  
84
  def project
85
    repository.project
86
  end
87

  
88
  def author
89
    user || committer.to_s.split('<').first
90
  end
91

  
92
  def before_create_cs
93
    self.committer = self.class.to_utf8(self.committer, repository.repo_log_encoding)
94
    self.comments  = self.class.normalize_comments(
95
                       self.comments, repository.repo_log_encoding)
96
    self.user = repository.find_committer_user(self.committer)
97
  end
98

  
99
  def scan_for_issues
100
    scan_comment_for_issue_ids
101
  end
102

  
103
  TIMELOG_RE = /
104
    (
105
    ((\d+)(h|hours?))((\d+)(m|min)?)?
106
    |
107
    ((\d+)(h|hours?|m|min))
108
    |
109
    (\d+):(\d+)
110
    |
111
    (\d+([\.,]\d+)?)h?
112
    )
113
    /x
114

  
115
  def scan_comment_for_issue_ids
116
    return if comments.blank?
117
    # keywords used to reference issues
118
    ref_keywords = Setting.commit_ref_keywords.downcase.split(",").collect(&:strip)
119
    ref_keywords_any = ref_keywords.delete('*')
120
    # keywords used to fix issues
121
    fix_keywords = Setting.commit_fix_keywords.downcase.split(",").collect(&:strip)
122

  
123
    kw_regexp = (ref_keywords + fix_keywords).collect{|kw| Regexp.escape(kw)}.join("|")
124

  
125
    referenced_issues = []
126

  
127
    comments.scan(/([\s\(\[,-]|^)((#{kw_regexp})[\s:]+)?(#\d+(\s+@#{TIMELOG_RE})?([\s,;&]+#\d+(\s+@#{TIMELOG_RE})?)*)(?=[[:punct:]]|\s|<|$)/i) do |match|
128
      action, refs = match[2], match[3]
129
      next unless action.present? || ref_keywords_any
130

  
131
      refs.scan(/#(\d+)(\s+@#{TIMELOG_RE})?/).each do |m|
132
        issue, hours = find_referenced_issue_by_id(m[0].to_i), m[2]
133
        if issue
134
          referenced_issues << issue
135
          fix_issue(issue) if fix_keywords.include?(action.to_s.downcase)
136
          log_time(issue, hours) if hours && Setting.commit_logtime_enabled?
137
        end
138
      end
139
    end
140

  
141
    referenced_issues.uniq!
142
    self.issues = referenced_issues unless referenced_issues.empty?
143
  end
144

  
145
  def short_comments
146
    @short_comments || split_comments.first
147
  end
148

  
149
  def long_comments
150
    @long_comments || split_comments.last
151
  end
152

  
153
  def text_tag(ref_project=nil)
154
    tag = if scmid?
155
      "commit:#{scmid}"
156
    else
157
      "r#{revision}"
158
    end
159
    if repository && repository.identifier.present?
160
      tag = "#{repository.identifier}|#{tag}"
161
    end
162
    if ref_project && project && ref_project != project
163
      tag = "#{project.identifier}:#{tag}"
164
    end
165
    tag
166
  end
167

  
168
  # Returns the title used for the changeset in the activity/search results
169
  def title
170
    repo = (repository && repository.identifier.present?) ? " (#{repository.identifier})" : ''
171
    comm = short_comments.blank? ? '' : (': ' + short_comments)
172
    "#{l(:label_revision)} #{format_identifier}#{repo}#{comm}"
173
  end
174

  
175
  # Returns the previous changeset
176
  def previous
177
    @previous ||= Changeset.where(["id < ? AND repository_id = ?", id, repository_id]).order('id DESC').first
178
  end
179

  
180
  # Returns the next changeset
181
  def next
182
    @next ||= Changeset.where(["id > ? AND repository_id = ?", id, repository_id]).order('id ASC').first
183
  end
184

  
185
  # Creates a new Change from it's common parameters
186
  def create_change(change)
187
    Change.create(:changeset     => self,
188
                  :action        => change[:action],
189
                  :path          => change[:path],
190
                  :from_path     => change[:from_path],
191
                  :from_revision => change[:from_revision])
192
  end
193

  
194
  # Finds an issue that can be referenced by the commit message
195
  def find_referenced_issue_by_id(id)
196
    return nil if id.blank?
197
    issue = Issue.find_by_id(id.to_i, :include => :project)
198
    if Setting.commit_cross_project_ref?
199
      # all issues can be referenced/fixed
200
    elsif issue
201
      # issue that belong to the repository project, a subproject or a parent project only
202
      unless issue.project &&
203
                (project == issue.project || project.is_ancestor_of?(issue.project) ||
204
                 project.is_descendant_of?(issue.project))
205
        issue = nil
206
      end
207
    end
208
    issue
209
  end
210

  
211
  private
212

  
213
  def fix_issue(issue)
214
    status = IssueStatus.find_by_id(Setting.commit_fix_status_id.to_i)
215
    if status.nil?
216
      logger.warn("No status matches commit_fix_status_id setting (#{Setting.commit_fix_status_id})") if logger
217
      return issue
218
    end
219

  
220
    # the issue may have been updated by the closure of another one (eg. duplicate)
221
    issue.reload
222
    # don't change the status is the issue is closed
223
    return if issue.status && issue.status.is_closed?
224

  
225
    journal = issue.init_journal(user || User.anonymous, ll(Setting.default_language, :text_status_changed_by_changeset, text_tag(issue.project)))
226
    issue.status = status
227
    unless Setting.commit_fix_done_ratio.blank?
228
      issue.done_ratio = Setting.commit_fix_done_ratio.to_i
229
    end
230
    Redmine::Hook.call_hook(:model_changeset_scan_commit_for_issue_ids_pre_issue_update,
231
                            { :changeset => self, :issue => issue })
232
    unless issue.save
233
      logger.warn("Issue ##{issue.id} could not be saved by changeset #{id}: #{issue.errors.full_messages}") if logger
234
    end
235
    issue
236
  end
237

  
238
  def log_time(issue, hours)
239
    time_entry = TimeEntry.new(
240
      :user => user,
241
      :hours => hours,
242
      :issue => issue,
243
      :spent_on => commit_date,
244
      :comments => l(:text_time_logged_by_changeset, :value => text_tag(issue.project),
245
                     :locale => Setting.default_language)
246
      )
247
    time_entry.activity = log_time_activity unless log_time_activity.nil?
248

  
249
    unless time_entry.save
250
      logger.warn("TimeEntry could not be created by changeset #{id}: #{time_entry.errors.full_messages}") if logger
251
    end
252
    time_entry
253
  end
254

  
255
  def log_time_activity
256
    if Setting.commit_logtime_activity_id.to_i > 0
257
      TimeEntryActivity.find_by_id(Setting.commit_logtime_activity_id.to_i)
258
    end
259
  end
260

  
261
  def split_comments
262
    comments =~ /\A(.+?)\r?\n(.*)$/m
263
    @short_comments = $1 || comments
264
    @long_comments = $2.to_s.strip
265
    return @short_comments, @long_comments
266
  end
267

  
268
  public
269

  
270
  # Strips and reencodes a commit log before insertion into the database
271
  def self.normalize_comments(str, encoding)
272
    Changeset.to_utf8(str.to_s.strip, encoding)
273
  end
274

  
275
  def self.to_utf8(str, encoding)
276
    Redmine::CodesetUtil.to_utf8(str, encoding)
277
  end
278
end
.svn/pristine/4c/4ce572a386a0b87f49f89a4bc090bd7724a6e4b0.svn-base
1
class SharedPluginModel < ActiveRecord::Base  
2
  def self.report_location; TestHelper::report_location(__FILE__); end
3
end
.svn/pristine/4c/4cf321de05d6993523d4f53f4c3078dcaf49b46c.svn-base
1
# Redmine - project management software
2
# Copyright (C) 2006-2013  Jean-Philippe Lang
3
#
4
# This program is free software; you can redistribute it and/or
5
# modify it under the terms of the GNU General Public License
6
# as published by the Free Software Foundation; either version 2
7
# of the License, or (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17

  
18
require File.expand_path('../../test_helper', __FILE__)
19

  
20
class SearchControllerTest < ActionController::TestCase
21
  fixtures :projects, :enabled_modules, :roles, :users, :members, :member_roles,
22
           :issues, :trackers, :issue_statuses, :enumerations,
23
           :custom_fields, :custom_values,
24
           :repositories, :changesets
25

  
26
  def setup
27
    User.current = nil
28
  end
29

  
30
  def test_search_for_projects
31
    get :index
32
    assert_response :success
33
    assert_template 'index'
34

  
35
    get :index, :q => "cook"
36
    assert_response :success
37
    assert_template 'index'
38
    assert assigns(:results).include?(Project.find(1))
39
  end
40

  
41
  def test_search_all_projects
42
    get :index, :q => 'recipe subproject commit', :all_words => ''
43
    assert_response :success
44
    assert_template 'index'
45

  
46
    assert assigns(:results).include?(Issue.find(2))
47
    assert assigns(:results).include?(Issue.find(5))
48
    assert assigns(:results).include?(Changeset.find(101))
49
    assert_tag :dt, :attributes => { :class => /issue/ },
50
                    :child => { :tag => 'a',  :content => /Add ingredients categories/ },
51
                    :sibling => { :tag => 'dd', :content => /should be classified by categories/ }
52

  
53
    assert assigns(:results_by_type).is_a?(Hash)
54
    assert_equal 5, assigns(:results_by_type)['changesets']
55
    assert_tag :a, :content => 'Changesets (5)'
56
  end
57

  
58
  def test_search_issues
59
    get :index, :q => 'issue', :issues => 1
60
    assert_response :success
61
    assert_template 'index'
62

  
63
    assert_equal true, assigns(:all_words)
64
    assert_equal false, assigns(:titles_only)
65
    assert assigns(:results).include?(Issue.find(8))
66
    assert assigns(:results).include?(Issue.find(5))
67
    assert_tag :dt, :attributes => { :class => /issue closed/ },
68
                    :child => { :tag => 'a',  :content => /Closed/ }
69
  end
70

  
71
  def test_search_issues_should_search_notes
72
    Journal.create!(:journalized => Issue.find(2), :notes => 'Issue notes with searchkeyword')
73

  
74
    get :index, :q => 'searchkeyword', :issues => 1
75
    assert_response :success
76
    assert_include Issue.find(2), assigns(:results)
77
  end
78

  
79
  def test_search_issues_with_multiple_matches_in_journals_should_return_issue_once
80
    Journal.create!(:journalized => Issue.find(2), :notes => 'Issue notes with searchkeyword')
81
    Journal.create!(:journalized => Issue.find(2), :notes => 'Issue notes with searchkeyword')
82

  
83
    get :index, :q => 'searchkeyword', :issues => 1
84
    assert_response :success
85
    assert_include Issue.find(2), assigns(:results)
86
    assert_equal 1, assigns(:results).size
87
  end
88

  
89
  def test_search_issues_should_search_private_notes_with_permission_only
90
    Journal.create!(:journalized => Issue.find(2), :notes => 'Private notes with searchkeyword', :private_notes => true)
91
    @request.session[:user_id] = 2
92

  
93
    Role.find(1).add_permission! :view_private_notes
94
    get :index, :q => 'searchkeyword', :issues => 1
95
    assert_response :success
96
    assert_include Issue.find(2), assigns(:results)
97

  
98
    Role.find(1).remove_permission! :view_private_notes
99
    get :index, :q => 'searchkeyword', :issues => 1
100
    assert_response :success
101
    assert_not_include Issue.find(2), assigns(:results)
102
  end
103

  
104
  def test_search_all_projects_with_scope_param
105
    get :index, :q => 'issue', :scope => 'all'
106
    assert_response :success
107
    assert_template 'index'
108
    assert assigns(:results).present?
109
  end
110

  
111
  def test_search_my_projects
112
    @request.session[:user_id] = 2
113
    get :index, :id => 1, :q => 'recipe subproject', :scope => 'my_projects', :all_words => ''
114
    assert_response :success
115
    assert_template 'index'
116
    assert assigns(:results).include?(Issue.find(1))
117
    assert !assigns(:results).include?(Issue.find(5))
118
  end
119

  
120
  def test_search_my_projects_without_memberships
121
    # anonymous user has no memberships
122
    get :index, :id => 1, :q => 'recipe subproject', :scope => 'my_projects', :all_words => ''
123
    assert_response :success
124
    assert_template 'index'
125
    assert assigns(:results).empty?
126
  end
127

  
128
  def test_search_project_and_subprojects
129
    get :index, :id => 1, :q => 'recipe subproject', :scope => 'subprojects', :all_words => ''
130
    assert_response :success
131
    assert_template 'index'
132
    assert assigns(:results).include?(Issue.find(1))
133
    assert assigns(:results).include?(Issue.find(5))
134
  end
135

  
136
  def test_search_without_searchable_custom_fields
137
    CustomField.update_all "searchable = #{ActiveRecord::Base.connection.quoted_false}"
138

  
139
    get :index, :id => 1
140
    assert_response :success
141
    assert_template 'index'
142
    assert_not_nil assigns(:project)
143

  
144
    get :index, :id => 1, :q => "can"
145
    assert_response :success
146
    assert_template 'index'
147
  end
148

  
149
  def test_search_with_searchable_custom_fields
150
    get :index, :id => 1, :q => "stringforcustomfield"
151
    assert_response :success
152
    results = assigns(:results)
153
    assert_not_nil results
154
    assert_equal 1, results.size
155
    assert results.include?(Issue.find(7))
156
  end
157

  
158
  def test_search_all_words
159
    # 'all words' is on by default
160
    get :index, :id => 1, :q => 'recipe updating saving', :all_words => '1'
161
    assert_equal true, assigns(:all_words)
162
    results = assigns(:results)
163
    assert_not_nil results
164
    assert_equal 1, results.size
165
    assert results.include?(Issue.find(3))
166
  end
167

  
168
  def test_search_one_of_the_words
169
    get :index, :id => 1, :q => 'recipe updating saving', :all_words => ''
170
    assert_equal false, assigns(:all_words)
171
    results = assigns(:results)
172
    assert_not_nil results
173
    assert_equal 3, results.size
174
    assert results.include?(Issue.find(3))
175
  end
176

  
177
  def test_search_titles_only_without_result
178
    get :index, :id => 1, :q => 'recipe updating saving', :titles_only => '1'
179
    results = assigns(:results)
180
    assert_not_nil results
181
    assert_equal 0, results.size
182
  end
183

  
184
  def test_search_titles_only
185
    get :index, :id => 1, :q => 'recipe', :titles_only => '1'
186
    assert_equal true, assigns(:titles_only)
187
    results = assigns(:results)
188
    assert_not_nil results
189
    assert_equal 2, results.size
190
  end
191

  
192
  def test_search_content
193
    Issue.update_all("description = 'This is a searchkeywordinthecontent'", "id=1")
194

  
195
    get :index, :id => 1, :q => 'searchkeywordinthecontent', :titles_only => ''
196
    assert_equal false, assigns(:titles_only)
197
    results = assigns(:results)
198
    assert_not_nil results
199
    assert_equal 1, results.size
200
  end
201

  
202
  def test_search_with_offset
203
    get :index, :q => 'coo', :offset => '20080806073000'
204
    assert_response :success
205
    results = assigns(:results)
206
    assert results.any?
207
    assert results.map(&:event_datetime).max < '20080806T073000'.to_time
208
  end
209

  
210
  def test_search_previous_with_offset
211
    get :index, :q => 'coo', :offset => '20080806073000', :previous => '1'
212
    assert_response :success
213
    results = assigns(:results)
214
    assert results.any?
215
    assert results.map(&:event_datetime).min >= '20080806T073000'.to_time
216
  end
217

  
218
  def test_search_with_invalid_project_id
219
    get :index, :id => 195, :q => 'recipe'
220
    assert_response 404
221
    assert_nil assigns(:results)
222
  end
223

  
224
  def test_quick_jump_to_issue
225
    # issue of a public project
226
    get :index, :q => "3"
227
    assert_redirected_to '/issues/3'
228

  
229
    # issue of a private project
230
    get :index, :q => "4"
231
    assert_response :success
232
    assert_template 'index'
233
  end
234

  
235
  def test_large_integer
236
    get :index, :q => '4615713488'
237
    assert_response :success
238
    assert_template 'index'
239
  end
240

  
241
  def test_tokens_with_quotes
242
    get :index, :id => 1, :q => '"good bye" hello "bye bye"'
243
    assert_equal ["good bye", "hello", "bye bye"], assigns(:tokens)
244
  end
245

  
246
  def test_results_should_be_escaped_once
247
    assert Issue.find(1).update_attributes(:subject => '<subject> escaped_once', :description => '<description> escaped_once')
248
    get :index, :q => 'escaped_once'
249
    assert_response :success
250
    assert_select '#search-results' do
251
      assert_select 'dt.issue a', :text => /&lt;subject&gt;/
252
      assert_select 'dd', :text => /&lt;description&gt;/
253
    end
254
  end
255

  
256
  def test_keywords_should_be_highlighted
257
    assert Issue.find(1).update_attributes(:subject => 'subject highlighted', :description => 'description highlighted')
258
    get :index, :q => 'highlighted'
259
    assert_response :success
260
    assert_select '#search-results' do
261
      assert_select 'dt.issue a span.highlight', :text => 'highlighted'
262
      assert_select 'dd span.highlight', :text => 'highlighted'
263
    end
264
  end
265
end

Also available in: Unified diff