Revision 1298:4f746d8966dd .svn/pristine/4c
| .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 => /<subject>/ |
|
| 252 |
assert_select 'dd', :text => /<description>/ |
|
| 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