##// END OF EJS Templates
wiki branch merged into trunk...
Jean-Philippe Lang -
r320:c514316a2efc
parent child
Show More
@@ -0,0 +1,111
1 # redMine - project management software
2 # Copyright (C) 2006-2007 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 WikiController < ApplicationController
19 layout 'base'
20 before_filter :find_wiki, :check_project_privacy, :except => [:preview]
21
22 # display a page (in editing mode if it doesn't exist)
23 def index
24 page_title = params[:page]
25 @page = @wiki.find_or_new_page(page_title)
26 if @page.new_record?
27 edit
28 render :action => 'edit' and return
29 end
30 @content = (params[:version] ? @page.content.versions.find_by_version(params[:version]) : @page.content)
31 if params[:export] == 'html'
32 export = render_to_string :action => 'export', :layout => false
33 send_data(export, :type => 'text/html', :filename => "#{@page.title}.html")
34 return
35 elsif params[:export] == 'txt'
36 send_data(@content.text, :type => 'text/plain', :filename => "#{@page.title}.txt")
37 return
38 end
39 render :action => 'show'
40 end
41
42 # edit an existing page or a new one
43 def edit
44 @page = @wiki.find_or_new_page(params[:page])
45 @page.content = WikiContent.new(:page => @page) if @page.new_record?
46 @content = @page.content
47 @content.text = "h1. #{@page.pretty_title}" if @content.text.empty?
48 # don't keep previous comment
49 @content.comment = nil
50 if request.post?
51 if @content.text == params[:content][:text]
52 # don't save if text wasn't changed
53 redirect_to :action => 'index', :id => @project, :page => @page.title
54 return
55 end
56 @content.text = params[:content][:text]
57 @content.comment = params[:content][:comment]
58 @content.author = logged_in_user
59 # if page is new @page.save will also save content, but not if page isn't a new record
60 if (@page.new_record? ? @page.save : @content.save)
61 redirect_to :action => 'index', :id => @project, :page => @page.title
62 end
63 end
64 end
65
66 # show page history
67 def history
68 @page = @wiki.find_page(params[:page])
69 # don't load text
70 @versions = @page.content.versions.find :all,
71 :select => "id, author_id, comment, updated_on, version",
72 :order => 'version DESC'
73 end
74
75 # display special pages
76 def special
77 page_title = params[:page].downcase
78 case page_title
79 # show pages index, sorted by title
80 when 'page_index'
81 # eager load information about last updates, without loading text
82 @pages = @wiki.pages.find :all, :select => "wiki_pages.*, wiki_contents.updated_on",
83 :joins => "LEFT JOIN wiki_contents ON wiki_contents.page_id = wiki_pages.id",
84 :order => 'title'
85 # export wiki to a single html file
86 when 'export'
87 @pages = @wiki.pages.find :all, :order => 'title'
88 export = render_to_string :action => 'export_multiple', :layout => false
89 send_data(export, :type => 'text/html', :filename => "wiki.html")
90 return
91 else
92 # requested special page doesn't exist, redirect to default page
93 redirect_to :action => 'index', :id => @project, :page => nil and return
94 end
95 render :action => "special_#{page_title}"
96 end
97
98 def preview
99 @text = params[:content][:text]
100 render :partial => 'preview'
101 end
102
103 private
104
105 def find_wiki
106 @project = Project.find(params[:id])
107 @wiki = @project.wiki
108 rescue ActiveRecord::RecordNotFound
109 render_404
110 end
111 end
@@ -0,0 +1,19
1 # redMine - project management software
2 # Copyright (C) 2006-2007 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 WikiHelper
19 end
@@ -0,0 +1,44
1 # redMine - project management software
2 # Copyright (C) 2006-2007 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 Wiki < ActiveRecord::Base
19 belongs_to :project
20 has_many :pages, :class_name => 'WikiPage', :dependent => :destroy
21
22 validates_presence_of :project_id, :start_page
23
24 # find the page with the given title
25 # if page doesn't exist, return a new page
26 def find_or_new_page(title)
27 title = Wiki.titleize(title || start_page)
28 find_page(title) || WikiPage.new(:wiki => self, :title => title)
29 end
30
31 # find the page with the given title
32 def find_page(title)
33 pages.find_by_title(Wiki.titleize(title || start_page))
34 end
35
36 # turn a string into a valid page title
37 def self.titleize(title)
38 # replace spaces with _ and remove unwanted caracters
39 title = title.gsub(/\s+/, '_').delete(',;|') if title
40 # upcase the first letter
41 title = title[0..0].upcase + title[1..-1] if title
42 title
43 end
44 end
@@ -0,0 +1,58
1 # redMine - project management software
2 # Copyright (C) 2006-2007 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 'zlib'
19
20 class WikiContent < ActiveRecord::Base
21 belongs_to :page, :class_name => 'WikiPage', :foreign_key => 'page_id'
22 belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
23 validates_presence_of :text
24
25 acts_as_versioned
26 class Version
27 belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
28 attr_protected :data
29
30 def text=(plain)
31 case Setting.wiki_compression
32 when 'gzip'
33 begin
34 self.data = Zlib::Deflate.deflate(plain, Zlib::BEST_COMPRESSION)
35 self.compression = 'gzip'
36 rescue
37 self.data = plain
38 self.compression = ''
39 end
40 else
41 self.data = plain
42 self.compression = ''
43 end
44 plain
45 end
46
47 def text
48 @text ||= case compression
49 when 'gzip'
50 Zlib::Inflate.inflate(data)
51 else
52 # uncompressed data
53 data
54 end
55 end
56 end
57
58 end
@@ -0,0 +1,34
1 # redMine - project management software
2 # Copyright (C) 2006-2007 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 WikiPage < ActiveRecord::Base
19 belongs_to :wiki
20 has_one :content, :class_name => 'WikiContent', :foreign_key => 'page_id', :dependent => :destroy
21
22 validates_presence_of :title
23 validates_format_of :title, :with => /^[^,\s]*$/
24 validates_uniqueness_of :title, :scope => :wiki_id, :case_sensitive => false
25 validates_associated :content
26
27 def before_save
28 self.title = Wiki.titleize(title)
29 end
30
31 def pretty_title
32 title.tr '_', ' '
33 end
34 end
@@ -0,0 +1,3
1 <fieldset class="preview"><legend><%= l(:label_preview) %></legend>
2 <%= textilizable @text %>
3 </fieldset>
@@ -0,0 +1,39
1 <div class="contextual">
2 <%= link_to(l(:label_page_index), {:action => 'special', :page => 'Page_index'}, :class => 'icon icon-index') %>
3 </div>
4
5 <h2><%= @page.pretty_title %></h2>
6
7 <% form_for :content, @content, :url => {:action => 'edit', :page => @page.title}, :html => {:id => 'wiki_form'} do |f| %>
8 <%= error_messages_for 'content' %>
9 <p><%= f.text_area :text, :cols => 100, :rows => 25, :style => "width:99%;" %></p>
10 <p><label><%= l(:field_comment) %></label><br /><%= f.text_field :comment, :size => 120 %></p>
11 <p><%= submit_tag l(:button_save) %>
12 <%= link_to_remote l(:label_preview),
13 { :url => { :controller => 'wiki', :action => 'preview' },
14 :method => 'get',
15 :update => 'preview',
16 :with => "Form.serialize('wiki_form')",
17 :loading => "Element.show('indicator')",
18 :loaded => "Element.hide('indicator')"
19 } %>
20 <span id="indicator" style="display:none"><%= image_tag "loading.gif", :align => "absmiddle" %></span>
21 </p>
22
23 <% end %>
24
25 <% if Setting.text_formatting == 'textile' %>
26 <%= javascript_include_tag 'jstoolbar' %>
27 <script type="text/javascript">
28 //<![CDATA[
29 if (document.getElementById) {
30 if (document.getElementById('content_text')) {
31 var commentTb = new jsToolBar(document.getElementById('content_text'));
32 commentTb.draw();
33 }
34 }
35 //]]>
36 </script>
37 <% end %>
38
39 <div id="preview" class="wiki"></div> No newline at end of file
@@ -0,0 +1,14
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
3 <head>
4 <title><%=h @page.pretty_title %></title>
5 <meta http-equiv="content-type" content="text/html; charset=utf-8" />
6 <style>
7 body { font:80% Verdana,Tahoma,Arial,sans-serif; }
8 h1, h2, h3, h4 { font-family: Trebuchet MS,Georgia,"Times New Roman",serif; }
9 </style>
10 </head>
11 <body>
12 <%= textilizable @content.text, :wiki_links => :local %>
13 </body>
14 </html>
@@ -0,0 +1,26
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
3 <head>
4 <title><%=h @wiki.project.name %></title>
5 <meta http-equiv="content-type" content="text/html; charset=utf-8" />
6 <style>
7 body { font:80% Verdana,Tahoma,Arial,sans-serif; }
8 h1, h2, h3, h4 { font-family: Trebuchet MS,Georgia,"Times New Roman",serif; }
9 </style>
10 </head>
11 <body>
12
13 <strong><%= l(:label_page_index) %></strong>
14 <ul>
15 <% @pages.each do |page| %>
16 <li><a href="#<%= page.title %>"><%= page.pretty_title %></a></li>
17 <% end %>
18 </ul>
19
20 <% @pages.each do |page| %>
21 <hr />
22 <%= textilizable page.content.text, :wiki_links => :anchor %>
23 <% end %>
24
25 </body>
26 </html>
@@ -0,0 +1,28
1 <div class="contextual">
2 <%= link_to(l(:label_page_index), {:action => 'special', :page => 'Page_index'}, :class => 'icon icon-index') %>
3 </div>
4
5 <h2><%= @page.pretty_title %></h2>
6
7 <h3><%= l(:label_history) %></h3>
8
9 <table class="list">
10 <thead><tr>
11 <th>#</th>
12 <th><%= l(:field_updated_on) %></th>
13 <th><%= l(:field_author) %></th>
14 <th><%= l(:field_comment) %></th>
15 </tr></thead>
16 <tbody>
17 <% @versions.each do |ver| %>
18 <tr class="<%= cycle("odd", "even") %>">
19 <th align="center"><%= link_to ver.version, :action => 'index', :page => @page.title, :version => ver.version %></th>
20 <td align="center"><%= format_time(ver.updated_on) %></td>
21 <td><em><%= ver.author ? ver.author.name : "anonyme" %></em></td>
22 <td><%=h ver.comment %></td>
23 </tr>
24 <% end %>
25 </tbody>
26 </table>
27
28 <p><%= link_to l(:button_back), :action => 'index', :page => @page.title %></p> No newline at end of file
@@ -0,0 +1,30
1 <div class="contextual">
2 <%= link_to(l(:button_edit), {:action => 'edit', :page => @page.title}, :class => 'icon icon-edit') if @content.version == @page.content.version %>
3 <%= link_to(l(:label_history), {:action => 'history', :page => @page.title}, :class => 'icon icon-history') %>
4 <%= link_to(l(:label_page_index), {:action => 'special', :page => 'Page_index'}, :class => 'icon icon-index') %>
5 </div>
6
7 <% if @content.version != @page.content.version %>
8 <p>
9 <%= link_to(('&#171; ' + l(:label_previous)), :action => 'index', :page => @page.title, :version => (@content.version - 1)) + " - " if @content.version > 1 %>
10 <%= "#{l(:label_version)} #{@content.version}/#{@page.content.version}" %> -
11 <%= link_to((l(:label_next) + ' &#187;'), :action => 'index', :page => @page.title, :version => (@content.version + 1)) + " - " if @content.version < @page.content.version %>
12 <%= link_to(l(:label_current_version), :action => 'index', :page => @page.title) %>
13 <br />
14 <em><%= @content.author ? @content.author.name : "anonyme" %>, <%= format_time(@content.updated_on) %> </em><br />
15 <%=h @content.comment %>
16 </p>
17 <hr />
18 <% end %>
19
20 <div class="wiki">
21 <% cache "wiki/show/#{@page.id}/#{@content.version}" do %>
22 <%= textilizable @content.text %>
23 <% end %>
24 </div>
25
26 <div class="contextual">
27 <%= l(:label_export_to) %>
28 <%= link_to 'HTML', {:export => 'html', :version => @content.version}, :class => 'icon icon-html' %>,
29 <%= link_to 'TXT', {:export => 'txt', :version => @content.version}, :class => 'icon icon-txt' %>
30 </div> No newline at end of file
@@ -0,0 +1,13
1 <div class="contextual">
2 <% unless @pages.empty? %>
3 <%= l(:label_export_to) %> <%= link_to 'HTML', {:action => 'special', :page => 'export'}, :class => 'icon icon-html' %>
4 <% end %>
5 </div>
6
7 <h2><%= l(:label_page_index) %></h2>
8
9 <% if @pages.empty? %><p><i><%= l(:label_no_data) %></i></p><% end %>
10 <ul><% @pages.each do |page| %>
11 <li><%= link_to page.pretty_title, :action => 'index', :page => page.title %> -
12 <%= l(:label_last_updates) %>: <%= format_time(page.updated_on) %></li>
13 <% end %></ul> No newline at end of file
@@ -0,0 +1,14
1 class CreateWikis < ActiveRecord::Migration
2 def self.up
3 create_table :wikis do |t|
4 t.column :project_id, :integer, :null => false
5 t.column :start_page, :string, :limit => 255, :null => false
6 t.column :status, :integer, :default => 1, :null => false
7 end
8 add_index :wikis, :project_id, :name => :wikis_project_id
9 end
10
11 def self.down
12 drop_table :wikis
13 end
14 end
@@ -0,0 +1,14
1 class CreateWikiPages < ActiveRecord::Migration
2 def self.up
3 create_table :wiki_pages do |t|
4 t.column :wiki_id, :integer, :null => false
5 t.column :title, :string, :limit => 255, :null => false
6 t.column :created_on, :datetime, :null => false
7 end
8 add_index :wiki_pages, [:wiki_id, :title], :name => :wiki_pages_wiki_id_title
9 end
10
11 def self.down
12 drop_table :wiki_pages
13 end
14 end
@@ -0,0 +1,30
1 class CreateWikiContents < ActiveRecord::Migration
2 def self.up
3 create_table :wiki_contents do |t|
4 t.column :page_id, :integer, :null => false
5 t.column :author_id, :integer
6 t.column :text, :text, :default => "", :null => false
7 t.column :comment, :string, :limit => 255, :default => ""
8 t.column :updated_on, :datetime, :null => false
9 t.column :version, :integer, :null => false
10 end
11 add_index :wiki_contents, :page_id, :name => :wiki_contents_page_id
12
13 create_table :wiki_content_versions do |t|
14 t.column :wiki_content_id, :integer, :null => false
15 t.column :page_id, :integer, :null => false
16 t.column :author_id, :integer
17 t.column :data, :binary
18 t.column :compression, :string, :limit => 6, :default => ""
19 t.column :comment, :string, :limit => 255, :default => ""
20 t.column :updated_on, :datetime, :null => false
21 t.column :version, :integer, :null => false
22 end
23 add_index :wiki_content_versions, :wiki_content_id, :name => :wiki_content_versions_wcid
24 end
25
26 def self.down
27 drop_table :wiki_contents
28 drop_table :wiki_content_versions
29 end
30 end
1 NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
@@ -0,0 +1,40
1 ---
2 wiki_content_versions_001:
3 updated_on: 2007-03-07 00:08:07 +01:00
4 page_id: 1
5 id: 1
6 version: 1
7 author_id: 1
8 comment: Page creation
9 wiki_content_id: 1
10 compression: ""
11 data: |-
12 h1. CookBook documentation
13
14
15
16 Some [[documentation]] here...
17 wiki_content_versions_002:
18 updated_on: 2007-03-07 00:08:34 +01:00
19 page_id: 1
20 id: 2
21 version: 2
22 author_id: 1
23 comment: Small update
24 wiki_content_id: 1
25 compression: ""
26 data: |-
27 h1. CookBook documentation
28
29
30
31 Some updated [[documentation]] here...
32 wiki_content_versions_003:
33 updated_on: 2007-03-07 00:10:51 +01:00
34 page_id: 1
35 id: 3
36 version: 3
37 author_id: 1
38 comment: ""
39 wiki_content_id: 1
40 compression: ""
@@ -0,0 +1,12
1 ---
2 wiki_contents_001:
3 text: |-
4 h1. CookBook documentation
5
6
7
8 Some updated [[documentation]] here with gzipped history
9 updated_on: 2007-03-07 00:10:51 +01:00
10 page_id: 1
11 id: 1
12 version: 3
@@ -0,0 +1,6
1 ---
2 wiki_pages_001:
3 created_on: 2007-03-07 00:08:07 +01:00
4 title: CookBook_documentation
5 id: 1
6 wiki_id: 1
@@ -0,0 +1,6
1 ---
2 wikis_001:
3 status: 1
4 start_page: CookBook documentation
5 project_id: 1
6 id: 1
@@ -0,0 +1,60
1 # redMine - project management software
2 # Copyright (C) 2006-2007 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.dirname(__FILE__) + '/../test_helper'
19
20 class WikiContentTest < Test::Unit::TestCase
21 fixtures :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions, :users
22
23 def setup
24 @wiki = Wiki.find(1)
25 @page = @wiki.pages.first
26 end
27
28 def test_create
29 page = WikiPage.new(:wiki => @wiki, :title => "Page")
30 page.content = WikiContent.new(:text => "Content text", :author => User.find(1), :comment => "My comment")
31 assert page.save
32 page.reload
33
34 content = page.content
35 assert_kind_of WikiContent, content
36 assert_equal 1, content.version
37 assert_equal 1, content.versions.length
38 assert_equal "Content text", content.text
39 assert_equal "My comment", content.comment
40 assert_equal User.find(1), content.author
41 assert_equal content.text, content.versions.last.text
42 end
43
44 def test_update
45 content = @page.content
46 version_count = content.version
47 content.text = "My new content"
48 assert content.save
49 content.reload
50 assert_equal version_count+1, content.version
51 assert_equal version_count+1, content.versions.length
52 end
53
54 def test_fetch_history
55 assert !@page.content.versions.empty?
56 @page.content.versions.each do |version|
57 assert_kind_of String, version.text
58 end
59 end
60 end
@@ -0,0 +1,50
1 # redMine - project management software
2 # Copyright (C) 2006-2007 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.dirname(__FILE__) + '/../test_helper'
19
20 class WikiPageTest < Test::Unit::TestCase
21 fixtures :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions
22
23 def setup
24 @wiki = Wiki.find(1)
25 @page = @wiki.pages.first
26 end
27
28 def test_create
29 page = WikiPage.new(:wiki => @wiki)
30 assert !page.save
31 assert_equal 1, page.errors.count
32
33 page.title = "Page"
34 assert page.save
35 page.reload
36
37 @wiki.reload
38 assert @wiki.pages.include?(page)
39 end
40
41 def test_find_or_new_page
42 page = @wiki.find_or_new_page("CookBook documentation")
43 assert_kind_of WikiPage, page
44 assert !page.new_record?
45
46 page = @wiki.find_or_new_page("Non existing page")
47 assert_kind_of WikiPage, page
48 assert page.new_record?
49 end
50 end
@@ -0,0 +1,39
1 # redMine - project management software
2 # Copyright (C) 2006-2007 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.dirname(__FILE__) + '/../test_helper'
19
20 class WikiTest < Test::Unit::TestCase
21 fixtures :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions
22
23 def test_create
24 wiki = Wiki.new(:project => Project.find(2))
25 assert !wiki.save
26 assert_equal 1, wiki.errors.count
27
28 wiki.start_page = "Start page"
29 assert wiki.save
30 end
31
32 def test_update
33 @wiki = Wiki.find(1)
34 @wiki.start_page = "Another start page"
35 assert @wiki.save
36 @wiki.reload
37 assert_equal "Another start page", @wiki.start_page
38 end
39 end
@@ -0,0 +1,74
1 *SVN* (version numbers are overrated)
2
3 * (5 Oct 2006) Allow customization of #versions association options [Dan Peterson]
4
5 *0.5.1*
6
7 * (8 Aug 2006) Versioned models now belong to the unversioned model. @article_version.article.class => Article [Aslak Hellesoy]
8
9 *0.5* # do versions even matter for plugins?
10
11 * (21 Apr 2006) Added without_locking and without_revision methods.
12
13 Foo.without_revision do
14 @foo.update_attributes ...
15 end
16
17 *0.4*
18
19 * (28 March 2006) Rename non_versioned_fields to non_versioned_columns (old one is kept for compatibility).
20 * (28 March 2006) Made explicit documentation note that string column names are required for non_versioned_columns.
21
22 *0.3.1*
23
24 * (7 Jan 2006) explicitly set :foreign_key option for the versioned model's belongs_to assocation for STI [Caged]
25 * (7 Jan 2006) added tests to prove has_many :through joins work
26
27 *0.3*
28
29 * (2 Jan 2006) added ability to share a mixin with versioned class
30 * (2 Jan 2006) changed the dynamic version model to MyModel::Version
31
32 *0.2.4*
33
34 * (27 Nov 2005) added note about possible destructive behavior of if_changed? [Michael Schuerig]
35
36 *0.2.3*
37
38 * (12 Nov 2005) fixed bug with old behavior of #blank? [Michael Schuerig]
39 * (12 Nov 2005) updated tests to use ActiveRecord Schema
40
41 *0.2.2*
42
43 * (3 Nov 2005) added documentation note to #acts_as_versioned [Martin Jul]
44
45 *0.2.1*
46
47 * (6 Oct 2005) renamed dirty? to changed? to keep it uniform. it was aliased to keep it backwards compatible.
48
49 *0.2*
50
51 * (6 Oct 2005) added find_versions and find_version class methods.
52
53 * (6 Oct 2005) removed transaction from create_versioned_table().
54 this way you can specify your own transaction around a group of operations.
55
56 * (30 Sep 2005) fixed bug where find_versions() would order by 'version' twice. (found by Joe Clark)
57
58 * (26 Sep 2005) added :sequence_name option to acts_as_versioned to set the sequence name on the versioned model
59
60 *0.1.3* (18 Sep 2005)
61
62 * First RubyForge release
63
64 *0.1.2*
65
66 * check if module is already included when acts_as_versioned is called
67
68 *0.1.1*
69
70 * Adding tests and rdocs
71
72 *0.1*
73
74 * Initial transfer from Rails ticket: http://dev.rubyonrails.com/ticket/1974 No newline at end of file
@@ -0,0 +1,20
1 Copyright (c) 2005 Rick Olson
2
3 Permission is hereby granted, free of charge, to any person obtaining
4 a copy of this software and associated documentation files (the
5 "Software"), to deal in the Software without restriction, including
6 without limitation the rights to use, copy, modify, merge, publish,
7 distribute, sublicense, and/or sell copies of the Software, and to
8 permit persons to whom the Software is furnished to do so, subject to
9 the following conditions:
10
11 The above copyright notice and this permission notice shall be
12 included in all copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. No newline at end of file
@@ -0,0 +1,28
1 = acts_as_versioned
2
3 This library adds simple versioning to an ActiveRecord module. ActiveRecord is required.
4
5 == Resources
6
7 Install
8
9 * gem install acts_as_versioned
10
11 Rubyforge project
12
13 * http://rubyforge.org/projects/ar-versioned
14
15 RDocs
16
17 * http://ar-versioned.rubyforge.org
18
19 Subversion
20
21 * http://techno-weenie.net/svn/projects/acts_as_versioned
22
23 Collaboa
24
25 * http://collaboa.techno-weenie.net/repository/browse/acts_as_versioned
26
27 Special thanks to Dreamer on ##rubyonrails for help in early testing. His ServerSideWiki (http://serversidewiki.com)
28 was the first project to use acts_as_versioned <em>in the wild</em>. No newline at end of file
@@ -0,0 +1,41
1 == Creating the test database
2
3 The default name for the test databases is "activerecord_versioned". If you
4 want to use another database name then be sure to update the connection
5 adapter setups you want to test with in test/connections/<your database>/connection.rb.
6 When you have the database online, you can import the fixture tables with
7 the test/fixtures/db_definitions/*.sql files.
8
9 Make sure that you create database objects with the same user that you specified in i
10 connection.rb otherwise (on Postgres, at least) tests for default values will fail.
11
12 == Running with Rake
13
14 The easiest way to run the unit tests is through Rake. The default task runs
15 the entire test suite for all the adapters. You can also run the suite on just
16 one adapter by using the tasks test_mysql_ruby, test_ruby_mysql, test_sqlite,
17 or test_postresql. For more information, checkout the full array of rake tasks with "rake -T"
18
19 Rake can be found at http://rake.rubyforge.org
20
21 == Running by hand
22
23 Unit tests are located in test directory. If you only want to run a single test suite,
24 or don't want to bother with Rake, you can do so with something like:
25
26 cd test; ruby -I "connections/native_mysql" base_test.rb
27
28 That'll run the base suite using the MySQL-Ruby adapter. Change the adapter
29 and test suite name as needed.
30
31 == Faster tests
32
33 If you are using a database that supports transactions, you can set the
34 "AR_TX_FIXTURES" environment variable to "yes" to use transactional fixtures.
35 This gives a very large speed boost. With rake:
36
37 rake AR_TX_FIXTURES=yes
38
39 Or, by hand:
40
41 AR_TX_FIXTURES=yes ruby -I connections/native_sqlite3 base_test.rb
@@ -0,0 +1,182
1 require 'rubygems'
2
3 Gem::manage_gems
4
5 require 'rake/rdoctask'
6 require 'rake/packagetask'
7 require 'rake/gempackagetask'
8 require 'rake/testtask'
9 require 'rake/contrib/rubyforgepublisher'
10
11 PKG_NAME = 'acts_as_versioned'
12 PKG_VERSION = '0.3.1'
13 PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
14 PROD_HOST = "technoweenie@bidwell.textdrive.com"
15 RUBY_FORGE_PROJECT = 'ar-versioned'
16 RUBY_FORGE_USER = 'technoweenie'
17
18 desc 'Default: run unit tests.'
19 task :default => :test
20
21 desc 'Test the calculations plugin.'
22 Rake::TestTask.new(:test) do |t|
23 t.libs << 'lib'
24 t.pattern = 'test/**/*_test.rb'
25 t.verbose = true
26 end
27
28 desc 'Generate documentation for the calculations plugin.'
29 Rake::RDocTask.new(:rdoc) do |rdoc|
30 rdoc.rdoc_dir = 'rdoc'
31 rdoc.title = "#{PKG_NAME} -- Simple versioning with active record models"
32 rdoc.options << '--line-numbers --inline-source'
33 rdoc.rdoc_files.include('README', 'CHANGELOG', 'RUNNING_UNIT_TESTS')
34 rdoc.rdoc_files.include('lib/**/*.rb')
35 end
36
37 spec = Gem::Specification.new do |s|
38 s.name = PKG_NAME
39 s.version = PKG_VERSION
40 s.platform = Gem::Platform::RUBY
41 s.summary = "Simple versioning with active record models"
42 s.files = FileList["{lib,test}/**/*"].to_a + %w(README MIT-LICENSE CHANGELOG RUNNING_UNIT_TESTS)
43 s.files.delete "acts_as_versioned_plugin.sqlite.db"
44 s.files.delete "acts_as_versioned_plugin.sqlite3.db"
45 s.files.delete "test/debug.log"
46 s.require_path = 'lib'
47 s.autorequire = 'acts_as_versioned'
48 s.has_rdoc = true
49 s.test_files = Dir['test/**/*_test.rb']
50 s.add_dependency 'activerecord', '>= 1.10.1'
51 s.add_dependency 'activesupport', '>= 1.1.1'
52 s.author = "Rick Olson"
53 s.email = "technoweenie@gmail.com"
54 s.homepage = "http://techno-weenie.net"
55 end
56
57 Rake::GemPackageTask.new(spec) do |pkg|
58 pkg.need_tar = true
59 end
60
61 desc "Publish the API documentation"
62 task :pdoc => [:rdoc] do
63 Rake::RubyForgePublisher.new(RUBY_FORGE_PROJECT, RUBY_FORGE_USER).upload
64 end
65
66 desc 'Publish the gem and API docs'
67 task :publish => [:pdoc, :rubyforge_upload]
68
69 desc "Publish the release files to RubyForge."
70 task :rubyforge_upload => :package do
71 files = %w(gem tgz).map { |ext| "pkg/#{PKG_FILE_NAME}.#{ext}" }
72
73 if RUBY_FORGE_PROJECT then
74 require 'net/http'
75 require 'open-uri'
76
77 project_uri = "http://rubyforge.org/projects/#{RUBY_FORGE_PROJECT}/"
78 project_data = open(project_uri) { |data| data.read }
79 group_id = project_data[/[?&]group_id=(\d+)/, 1]
80 raise "Couldn't get group id" unless group_id
81
82 # This echos password to shell which is a bit sucky
83 if ENV["RUBY_FORGE_PASSWORD"]
84 password = ENV["RUBY_FORGE_PASSWORD"]
85 else
86 print "#{RUBY_FORGE_USER}@rubyforge.org's password: "
87 password = STDIN.gets.chomp
88 end
89
90 login_response = Net::HTTP.start("rubyforge.org", 80) do |http|
91 data = [
92 "login=1",
93 "form_loginname=#{RUBY_FORGE_USER}",
94 "form_pw=#{password}"
95 ].join("&")
96 http.post("/account/login.php", data)
97 end
98
99 cookie = login_response["set-cookie"]
100 raise "Login failed" unless cookie
101 headers = { "Cookie" => cookie }
102
103 release_uri = "http://rubyforge.org/frs/admin/?group_id=#{group_id}"
104 release_data = open(release_uri, headers) { |data| data.read }
105 package_id = release_data[/[?&]package_id=(\d+)/, 1]
106 raise "Couldn't get package id" unless package_id
107
108 first_file = true
109 release_id = ""
110
111 files.each do |filename|
112 basename = File.basename(filename)
113 file_ext = File.extname(filename)
114 file_data = File.open(filename, "rb") { |file| file.read }
115
116 puts "Releasing #{basename}..."
117
118 release_response = Net::HTTP.start("rubyforge.org", 80) do |http|
119 release_date = Time.now.strftime("%Y-%m-%d %H:%M")
120 type_map = {
121 ".zip" => "3000",
122 ".tgz" => "3110",
123 ".gz" => "3110",
124 ".gem" => "1400"
125 }; type_map.default = "9999"
126 type = type_map[file_ext]
127 boundary = "rubyqMY6QN9bp6e4kS21H4y0zxcvoor"
128
129 query_hash = if first_file then
130 {
131 "group_id" => group_id,
132 "package_id" => package_id,
133 "release_name" => PKG_FILE_NAME,
134 "release_date" => release_date,
135 "type_id" => type,
136 "processor_id" => "8000", # Any
137 "release_notes" => "",
138 "release_changes" => "",
139 "preformatted" => "1",
140 "submit" => "1"
141 }
142 else
143 {
144 "group_id" => group_id,
145 "release_id" => release_id,
146 "package_id" => package_id,
147 "step2" => "1",
148 "type_id" => type,
149 "processor_id" => "8000", # Any
150 "submit" => "Add This File"
151 }
152 end
153
154 query = "?" + query_hash.map do |(name, value)|
155 [name, URI.encode(value)].join("=")
156 end.join("&")
157
158 data = [
159 "--" + boundary,
160 "Content-Disposition: form-data; name=\"userfile\"; filename=\"#{basename}\"",
161 "Content-Type: application/octet-stream",
162 "Content-Transfer-Encoding: binary",
163 "", file_data, ""
164 ].join("\x0D\x0A")
165
166 release_headers = headers.merge(
167 "Content-Type" => "multipart/form-data; boundary=#{boundary}"
168 )
169
170 target = first_file ? "/frs/admin/qrs.php" : "/frs/admin/editrelease.php"
171 http.post(target + query, data, release_headers)
172 end
173
174 if first_file then
175 release_id = release_response.body[/release_id=(\d+)/, 1]
176 raise("Couldn't get release id") unless release_id
177 end
178
179 first_file = false
180 end
181 end
182 end No newline at end of file
@@ -0,0 +1,1
1 require 'acts_as_versioned' No newline at end of file
This diff has been collapsed as it changes many lines, (511 lines changed) Show them Hide them
@@ -0,0 +1,511
1 # Copyright (c) 2005 Rick Olson
2 #
3 # Permission is hereby granted, free of charge, to any person obtaining
4 # a copy of this software and associated documentation files (the
5 # "Software"), to deal in the Software without restriction, including
6 # without limitation the rights to use, copy, modify, merge, publish,
7 # distribute, sublicense, and/or sell copies of the Software, and to
8 # permit persons to whom the Software is furnished to do so, subject to
9 # the following conditions:
10 #
11 # The above copyright notice and this permission notice shall be
12 # included in all copies or substantial portions of the Software.
13 #
14 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22 module ActiveRecord #:nodoc:
23 module Acts #:nodoc:
24 # Specify this act if you want to save a copy of the row in a versioned table. This assumes there is a
25 # versioned table ready and that your model has a version field. This works with optimisic locking if the lock_version
26 # column is present as well.
27 #
28 # The class for the versioned model is derived the first time it is seen. Therefore, if you change your database schema you have to restart
29 # your container for the changes to be reflected. In development mode this usually means restarting WEBrick.
30 #
31 # class Page < ActiveRecord::Base
32 # # assumes pages_versions table
33 # acts_as_versioned
34 # end
35 #
36 # Example:
37 #
38 # page = Page.create(:title => 'hello world!')
39 # page.version # => 1
40 #
41 # page.title = 'hello world'
42 # page.save
43 # page.version # => 2
44 # page.versions.size # => 2
45 #
46 # page.revert_to(1) # using version number
47 # page.title # => 'hello world!'
48 #
49 # page.revert_to(page.versions.last) # using versioned instance
50 # page.title # => 'hello world'
51 #
52 # See ActiveRecord::Acts::Versioned::ClassMethods#acts_as_versioned for configuration options
53 module Versioned
54 CALLBACKS = [:set_new_version, :save_version_on_create, :save_version?, :clear_changed_attributes]
55 def self.included(base) # :nodoc:
56 base.extend ClassMethods
57 end
58
59 module ClassMethods
60 # == Configuration options
61 #
62 # * <tt>class_name</tt> - versioned model class name (default: PageVersion in the above example)
63 # * <tt>table_name</tt> - versioned model table name (default: page_versions in the above example)
64 # * <tt>foreign_key</tt> - foreign key used to relate the versioned model to the original model (default: page_id in the above example)
65 # * <tt>inheritance_column</tt> - name of the column to save the model's inheritance_column value for STI. (default: versioned_type)
66 # * <tt>version_column</tt> - name of the column in the model that keeps the version number (default: version)
67 # * <tt>sequence_name</tt> - name of the custom sequence to be used by the versioned model.
68 # * <tt>limit</tt> - number of revisions to keep, defaults to unlimited
69 # * <tt>if</tt> - symbol of method to check before saving a new version. If this method returns false, a new version is not saved.
70 # For finer control, pass either a Proc or modify Model#version_condition_met?
71 #
72 # acts_as_versioned :if => Proc.new { |auction| !auction.expired? }
73 #
74 # or...
75 #
76 # class Auction
77 # def version_condition_met? # totally bypasses the <tt>:if</tt> option
78 # !expired?
79 # end
80 # end
81 #
82 # * <tt>if_changed</tt> - Simple way of specifying attributes that are required to be changed before saving a model. This takes
83 # either a symbol or array of symbols. WARNING - This will attempt to overwrite any attribute setters you may have.
84 # Use this instead if you want to write your own attribute setters (and ignore if_changed):
85 #
86 # def name=(new_name)
87 # write_changed_attribute :name, new_name
88 # end
89 #
90 # * <tt>extend</tt> - Lets you specify a module to be mixed in both the original and versioned models. You can also just pass a block
91 # to create an anonymous mixin:
92 #
93 # class Auction
94 # acts_as_versioned do
95 # def started?
96 # !started_at.nil?
97 # end
98 # end
99 # end
100 #
101 # or...
102 #
103 # module AuctionExtension
104 # def started?
105 # !started_at.nil?
106 # end
107 # end
108 # class Auction
109 # acts_as_versioned :extend => AuctionExtension
110 # end
111 #
112 # Example code:
113 #
114 # @auction = Auction.find(1)
115 # @auction.started?
116 # @auction.versions.first.started?
117 #
118 # == Database Schema
119 #
120 # The model that you're versioning needs to have a 'version' attribute. The model is versioned
121 # into a table called #{model}_versions where the model name is singlular. The _versions table should
122 # contain all the fields you want versioned, the same version column, and a #{model}_id foreign key field.
123 #
124 # A lock_version field is also accepted if your model uses Optimistic Locking. If your table uses Single Table inheritance,
125 # then that field is reflected in the versioned model as 'versioned_type' by default.
126 #
127 # Acts_as_versioned comes prepared with the ActiveRecord::Acts::Versioned::ActMethods::ClassMethods#create_versioned_table
128 # method, perfect for a migration. It will also create the version column if the main model does not already have it.
129 #
130 # class AddVersions < ActiveRecord::Migration
131 # def self.up
132 # # create_versioned_table takes the same options hash
133 # # that create_table does
134 # Post.create_versioned_table
135 # end
136 #
137 # def self.down
138 # Post.drop_versioned_table
139 # end
140 # end
141 #
142 # == Changing What Fields Are Versioned
143 #
144 # By default, acts_as_versioned will version all but these fields:
145 #
146 # [self.primary_key, inheritance_column, 'version', 'lock_version', versioned_inheritance_column]
147 #
148 # You can add or change those by modifying #non_versioned_columns. Note that this takes strings and not symbols.
149 #
150 # class Post < ActiveRecord::Base
151 # acts_as_versioned
152 # self.non_versioned_columns << 'comments_count'
153 # end
154 #
155 def acts_as_versioned(options = {}, &extension)
156 # don't allow multiple calls
157 return if self.included_modules.include?(ActiveRecord::Acts::Versioned::ActMethods)
158
159 send :include, ActiveRecord::Acts::Versioned::ActMethods
160
161 cattr_accessor :versioned_class_name, :versioned_foreign_key, :versioned_table_name, :versioned_inheritance_column,
162 :version_column, :max_version_limit, :track_changed_attributes, :version_condition, :version_sequence_name, :non_versioned_columns,
163 :version_association_options
164
165 # legacy
166 alias_method :non_versioned_fields, :non_versioned_columns
167 alias_method :non_versioned_fields=, :non_versioned_columns=
168
169 class << self
170 alias_method :non_versioned_fields, :non_versioned_columns
171 alias_method :non_versioned_fields=, :non_versioned_columns=
172 end
173
174 send :attr_accessor, :changed_attributes
175
176 self.versioned_class_name = options[:class_name] || "Version"
177 self.versioned_foreign_key = options[:foreign_key] || self.to_s.foreign_key
178 self.versioned_table_name = options[:table_name] || "#{table_name_prefix}#{base_class.name.demodulize.underscore}_versions#{table_name_suffix}"
179 self.versioned_inheritance_column = options[:inheritance_column] || "versioned_#{inheritance_column}"
180 self.version_column = options[:version_column] || 'version'
181 self.version_sequence_name = options[:sequence_name]
182 self.max_version_limit = options[:limit].to_i
183 self.version_condition = options[:if] || true
184 self.non_versioned_columns = [self.primary_key, inheritance_column, 'version', 'lock_version', versioned_inheritance_column]
185 self.version_association_options = {
186 :class_name => "#{self.to_s}::#{versioned_class_name}",
187 :foreign_key => "#{versioned_foreign_key}",
188 :order => 'version',
189 :dependent => :delete_all
190 }.merge(options[:association_options] || {})
191
192 if block_given?
193 extension_module_name = "#{versioned_class_name}Extension"
194 silence_warnings do
195 self.const_set(extension_module_name, Module.new(&extension))
196 end
197
198 options[:extend] = self.const_get(extension_module_name)
199 end
200
201 class_eval do
202 has_many :versions, version_association_options
203 before_save :set_new_version
204 after_create :save_version_on_create
205 after_update :save_version
206 after_save :clear_old_versions
207 after_save :clear_changed_attributes
208
209 unless options[:if_changed].nil?
210 self.track_changed_attributes = true
211 options[:if_changed] = [options[:if_changed]] unless options[:if_changed].is_a?(Array)
212 options[:if_changed].each do |attr_name|
213 define_method("#{attr_name}=") do |value|
214 write_changed_attribute attr_name, value
215 end
216 end
217 end
218
219 include options[:extend] if options[:extend].is_a?(Module)
220 end
221
222 # create the dynamic versioned model
223 const_set(versioned_class_name, Class.new(ActiveRecord::Base)).class_eval do
224 def self.reloadable? ; false ; end
225 end
226
227 versioned_class.set_table_name versioned_table_name
228 versioned_class.belongs_to self.to_s.demodulize.underscore.to_sym,
229 :class_name => "::#{self.to_s}",
230 :foreign_key => versioned_foreign_key
231 versioned_class.send :include, options[:extend] if options[:extend].is_a?(Module)
232 versioned_class.set_sequence_name version_sequence_name if version_sequence_name
233 end
234 end
235
236 module ActMethods
237 def self.included(base) # :nodoc:
238 base.extend ClassMethods
239 end
240
241 # Saves a version of the model if applicable
242 def save_version
243 save_version_on_create if save_version?
244 end
245
246 # Saves a version of the model in the versioned table. This is called in the after_save callback by default
247 def save_version_on_create
248 rev = self.class.versioned_class.new
249 self.clone_versioned_model(self, rev)
250 rev.version = send(self.class.version_column)
251 rev.send("#{self.class.versioned_foreign_key}=", self.id)
252 rev.save
253 end
254
255 # Clears old revisions if a limit is set with the :limit option in <tt>acts_as_versioned</tt>.
256 # Override this method to set your own criteria for clearing old versions.
257 def clear_old_versions
258 return if self.class.max_version_limit == 0
259 excess_baggage = send(self.class.version_column).to_i - self.class.max_version_limit
260 if excess_baggage > 0
261 sql = "DELETE FROM #{self.class.versioned_table_name} WHERE version <= #{excess_baggage} AND #{self.class.versioned_foreign_key} = #{self.id}"
262 self.class.versioned_class.connection.execute sql
263 end
264 end
265
266 # Finds a specific version of this model.
267 def find_version(version)
268 return version if version.is_a?(self.class.versioned_class)
269 return nil if version.is_a?(ActiveRecord::Base)
270 find_versions(:conditions => ['version = ?', version], :limit => 1).first
271 end
272
273 # Finds versions of this model. Takes an options hash like <tt>find</tt>
274 def find_versions(options = {})
275 versions.find(:all, options)
276 end
277
278 # Reverts a model to a given version. Takes either a version number or an instance of the versioned model
279 def revert_to(version)
280 if version.is_a?(self.class.versioned_class)
281 return false unless version.send(self.class.versioned_foreign_key) == self.id and !version.new_record?
282 else
283 return false unless version = find_version(version)
284 end
285 self.clone_versioned_model(version, self)
286 self.send("#{self.class.version_column}=", version.version)
287 true
288 end
289
290 # Reverts a model to a given version and saves the model.
291 # Takes either a version number or an instance of the versioned model
292 def revert_to!(version)
293 revert_to(version) ? save_without_revision : false
294 end
295
296 # Temporarily turns off Optimistic Locking while saving. Used when reverting so that a new version is not created.
297 def save_without_revision
298 save_without_revision!
299 true
300 rescue
301 false
302 end
303
304 def save_without_revision!
305 without_locking do
306 without_revision do
307 save!
308 end
309 end
310 end
311
312 # Returns an array of attribute keys that are versioned. See non_versioned_columns
313 def versioned_attributes
314 self.attributes.keys.select { |k| !self.class.non_versioned_columns.include?(k) }
315 end
316
317 # If called with no parameters, gets whether the current model has changed and needs to be versioned.
318 # If called with a single parameter, gets whether the parameter has changed.
319 def changed?(attr_name = nil)
320 attr_name.nil? ?
321 (!self.class.track_changed_attributes || (changed_attributes && changed_attributes.length > 0)) :
322 (changed_attributes && changed_attributes.include?(attr_name.to_s))
323 end
324
325 # keep old dirty? method
326 alias_method :dirty?, :changed?
327
328 # Clones a model. Used when saving a new version or reverting a model's version.
329 def clone_versioned_model(orig_model, new_model)
330 self.versioned_attributes.each do |key|
331 new_model.send("#{key}=", orig_model.attributes[key]) if orig_model.has_attribute?(key)
332 end
333
334 if orig_model.is_a?(self.class.versioned_class)
335 new_model[new_model.class.inheritance_column] = orig_model[self.class.versioned_inheritance_column]
336 elsif new_model.is_a?(self.class.versioned_class)
337 new_model[self.class.versioned_inheritance_column] = orig_model[orig_model.class.inheritance_column]
338 end
339 end
340
341 # Checks whether a new version shall be saved or not. Calls <tt>version_condition_met?</tt> and <tt>changed?</tt>.
342 def save_version?
343 version_condition_met? && changed?
344 end
345
346 # Checks condition set in the :if option to check whether a revision should be created or not. Override this for
347 # custom version condition checking.
348 def version_condition_met?
349 case
350 when version_condition.is_a?(Symbol)
351 send(version_condition)
352 when version_condition.respond_to?(:call) && (version_condition.arity == 1 || version_condition.arity == -1)
353 version_condition.call(self)
354 else
355 version_condition
356 end
357 end
358
359 # Executes the block with the versioning callbacks disabled.
360 #
361 # @foo.without_revision do
362 # @foo.save
363 # end
364 #
365 def without_revision(&block)
366 self.class.without_revision(&block)
367 end
368
369 # Turns off optimistic locking for the duration of the block
370 #
371 # @foo.without_locking do
372 # @foo.save
373 # end
374 #
375 def without_locking(&block)
376 self.class.without_locking(&block)
377 end
378
379 def empty_callback() end #:nodoc:
380
381 protected
382 # sets the new version before saving, unless you're using optimistic locking. In that case, let it take care of the version.
383 def set_new_version
384 self.send("#{self.class.version_column}=", self.next_version) if new_record? || (!locking_enabled? && save_version?)
385 end
386
387 # Gets the next available version for the current record, or 1 for a new record
388 def next_version
389 return 1 if new_record?
390 (versions.calculate(:max, :version) || 0) + 1
391 end
392
393 # clears current changed attributes. Called after save.
394 def clear_changed_attributes
395 self.changed_attributes = []
396 end
397
398 def write_changed_attribute(attr_name, attr_value)
399 # Convert to db type for comparison. Avoids failing Float<=>String comparisons.
400 attr_value_for_db = self.class.columns_hash[attr_name.to_s].type_cast(attr_value)
401 (self.changed_attributes ||= []) << attr_name.to_s unless self.changed?(attr_name) || self.send(attr_name) == attr_value_for_db
402 write_attribute(attr_name, attr_value_for_db)
403 end
404
405 private
406 CALLBACKS.each do |attr_name|
407 alias_method "orig_#{attr_name}".to_sym, attr_name
408 end
409
410 module ClassMethods
411 # Finds a specific version of a specific row of this model
412 def find_version(id, version)
413 find_versions(id,
414 :conditions => ["#{versioned_foreign_key} = ? AND version = ?", id, version],
415 :limit => 1).first
416 end
417
418 # Finds versions of a specific model. Takes an options hash like <tt>find</tt>
419 def find_versions(id, options = {})
420 versioned_class.find :all, {
421 :conditions => ["#{versioned_foreign_key} = ?", id],
422 :order => 'version' }.merge(options)
423 end
424
425 # Returns an array of columns that are versioned. See non_versioned_columns
426 def versioned_columns
427 self.columns.select { |c| !non_versioned_columns.include?(c.name) }
428 end
429
430 # Returns an instance of the dynamic versioned model
431 def versioned_class
432 const_get versioned_class_name
433 end
434
435 # Rake migration task to create the versioned table using options passed to acts_as_versioned
436 def create_versioned_table(create_table_options = {})
437 # create version column in main table if it does not exist
438 if !self.content_columns.find { |c| %w(version lock_version).include? c.name }
439 self.connection.add_column table_name, :version, :integer
440 end
441
442 self.connection.create_table(versioned_table_name, create_table_options) do |t|
443 t.column versioned_foreign_key, :integer
444 t.column :version, :integer
445 end
446
447 updated_col = nil
448 self.versioned_columns.each do |col|
449 updated_col = col if !updated_col && %(updated_at updated_on).include?(col.name)
450 self.connection.add_column versioned_table_name, col.name, col.type,
451 :limit => col.limit,
452 :default => col.default
453 end
454
455 if type_col = self.columns_hash[inheritance_column]
456 self.connection.add_column versioned_table_name, versioned_inheritance_column, type_col.type,
457 :limit => type_col.limit,
458 :default => type_col.default
459 end
460
461 if updated_col.nil?
462 self.connection.add_column versioned_table_name, :updated_at, :timestamp
463 end
464 end
465
466 # Rake migration task to drop the versioned table
467 def drop_versioned_table
468 self.connection.drop_table versioned_table_name
469 end
470
471 # Executes the block with the versioning callbacks disabled.
472 #
473 # Foo.without_revision do
474 # @foo.save
475 # end
476 #
477 def without_revision(&block)
478 class_eval do
479 CALLBACKS.each do |attr_name|
480 alias_method attr_name, :empty_callback
481 end
482 end
483 result = block.call
484 class_eval do
485 CALLBACKS.each do |attr_name|
486 alias_method attr_name, "orig_#{attr_name}".to_sym
487 end
488 end
489 result
490 end
491
492 # Turns off optimistic locking for the duration of the block
493 #
494 # Foo.without_locking do
495 # @foo.save
496 # end
497 #
498 def without_locking(&block)
499 current = ActiveRecord::Base.lock_optimistically
500 ActiveRecord::Base.lock_optimistically = false if current
501 result = block.call
502 ActiveRecord::Base.lock_optimistically = true if current
503 result
504 end
505 end
506 end
507 end
508 end
509 end
510
511 ActiveRecord::Base.send :include, ActiveRecord::Acts::Versioned No newline at end of file
@@ -0,0 +1,40
1 $:.unshift(File.dirname(__FILE__) + '/../lib')
2
3 require 'test/unit'
4 require File.expand_path(File.join(File.dirname(__FILE__), '../../../../config/environment.rb'))
5 require 'active_record/fixtures'
6
7 config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
8 ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
9 ActiveRecord::Base.establish_connection(config[ENV['DB'] || 'sqlite'])
10
11 load(File.dirname(__FILE__) + "/schema.rb")
12
13 # set up custom sequence on widget_versions for DBs that support sequences
14 if ENV['DB'] == 'postgresql'
15 ActiveRecord::Base.connection.execute "DROP SEQUENCE widgets_seq;" rescue nil
16 ActiveRecord::Base.connection.remove_column :widget_versions, :id
17 ActiveRecord::Base.connection.execute "CREATE SEQUENCE widgets_seq START 101;"
18 ActiveRecord::Base.connection.execute "ALTER TABLE widget_versions ADD COLUMN id INTEGER PRIMARY KEY DEFAULT nextval('widgets_seq');"
19 end
20
21 Test::Unit::TestCase.fixture_path = File.dirname(__FILE__) + "/fixtures/"
22 $LOAD_PATH.unshift(Test::Unit::TestCase.fixture_path)
23
24 class Test::Unit::TestCase #:nodoc:
25 def create_fixtures(*table_names)
26 if block_given?
27 Fixtures.create_fixtures(Test::Unit::TestCase.fixture_path, table_names) { yield }
28 else
29 Fixtures.create_fixtures(Test::Unit::TestCase.fixture_path, table_names)
30 end
31 end
32
33 # Turn off transactional fixtures if you're working with MyISAM tables in MySQL
34 self.use_transactional_fixtures = true
35
36 # Instantiated fixtures are slow, but give you @david where you otherwise would need people(:david)
37 self.use_instantiated_fixtures = false
38
39 # Add more helper methods to be used by all tests here...
40 end No newline at end of file
@@ -0,0 +1,18
1 sqlite:
2 :adapter: sqlite
3 :dbfile: acts_as_versioned_plugin.sqlite.db
4 sqlite3:
5 :adapter: sqlite3
6 :dbfile: acts_as_versioned_plugin.sqlite3.db
7 postgresql:
8 :adapter: postgresql
9 :username: postgres
10 :password: postgres
11 :database: acts_as_versioned_plugin_test
12 :min_messages: ERROR
13 mysql:
14 :adapter: mysql
15 :host: localhost
16 :username: rails
17 :password:
18 :database: acts_as_versioned_plugin_test No newline at end of file
@@ -0,0 +1,6
1 caged:
2 id: 1
3 name: caged
4 mly:
5 id: 2
6 name: mly No newline at end of file
@@ -0,0 +1,3
1 class Landmark < ActiveRecord::Base
2 acts_as_versioned :if_changed => [ :name, :longitude, :latitude ]
3 end
@@ -0,0 +1,7
1 washington:
2 id: 1
3 landmark_id: 1
4 version: 1
5 name: Washington, D.C.
6 latitude: 38.895
7 longitude: -77.036667
@@ -0,0 +1,6
1 washington:
2 id: 1
3 name: Washington, D.C.
4 latitude: 38.895
5 longitude: -77.036667
6 version: 1
@@ -0,0 +1,10
1 welcome:
2 id: 1
3 title: Welcome to the weblog
4 lock_version: 24
5 type: LockedPage
6 thinking:
7 id: 2
8 title: So I was thinking
9 lock_version: 24
10 type: SpecialLockedPage
@@ -0,0 +1,27
1 welcome_1:
2 id: 1
3 page_id: 1
4 title: Welcome to the weblg
5 version: 23
6 version_type: LockedPage
7
8 welcome_2:
9 id: 2
10 page_id: 1
11 title: Welcome to the weblog
12 version: 24
13 version_type: LockedPage
14
15 thinking_1:
16 id: 3
17 page_id: 2
18 title: So I was thinking!!!
19 version: 23
20 version_type: SpecialLockedPage
21
22 thinking_2:
23 id: 4
24 page_id: 2
25 title: So I was thinking
26 version: 24
27 version_type: SpecialLockedPage
@@ -0,0 +1,13
1 class AddVersionedTables < ActiveRecord::Migration
2 def self.up
3 create_table("things") do |t|
4 t.column :title, :text
5 end
6 Thing.create_versioned_table
7 end
8
9 def self.down
10 Thing.drop_versioned_table
11 drop_table "things" rescue nil
12 end
13 end No newline at end of file
@@ -0,0 +1,43
1 class Page < ActiveRecord::Base
2 belongs_to :author
3 has_many :authors, :through => :versions, :order => 'name'
4 belongs_to :revisor, :class_name => 'Author'
5 has_many :revisors, :class_name => 'Author', :through => :versions, :order => 'name'
6 acts_as_versioned :if => :feeling_good? do
7 def self.included(base)
8 base.cattr_accessor :feeling_good
9 base.feeling_good = true
10 base.belongs_to :author
11 base.belongs_to :revisor, :class_name => 'Author'
12 end
13
14 def feeling_good?
15 @@feeling_good == true
16 end
17 end
18 end
19
20 module LockedPageExtension
21 def hello_world
22 'hello_world'
23 end
24 end
25
26 class LockedPage < ActiveRecord::Base
27 acts_as_versioned \
28 :inheritance_column => :version_type,
29 :foreign_key => :page_id,
30 :table_name => :locked_pages_revisions,
31 :class_name => 'LockedPageRevision',
32 :version_column => :lock_version,
33 :limit => 2,
34 :if_changed => :title,
35 :extend => LockedPageExtension
36 end
37
38 class SpecialLockedPage < LockedPage
39 end
40
41 class Author < ActiveRecord::Base
42 has_many :pages
43 end No newline at end of file
@@ -0,0 +1,16
1 welcome_2:
2 id: 1
3 page_id: 1
4 title: Welcome to the weblog
5 body: Such a lovely day
6 version: 24
7 author_id: 1
8 revisor_id: 1
9 welcome_1:
10 id: 2
11 page_id: 1
12 title: Welcome to the weblg
13 body: Such a lovely day
14 version: 23
15 author_id: 2
16 revisor_id: 2
@@ -0,0 +1,7
1 welcome:
2 id: 1
3 title: Welcome to the weblog
4 body: Such a lovely day
5 version: 24
6 author_id: 1
7 revisor_id: 1 No newline at end of file
@@ -0,0 +1,6
1 class Widget < ActiveRecord::Base
2 acts_as_versioned :sequence_name => 'widgets_seq', :association_options => {
3 :dependent => nil, :order => 'version desc'
4 }
5 non_versioned_columns << 'foo'
6 end No newline at end of file
@@ -0,0 +1,32
1 require File.join(File.dirname(__FILE__), 'abstract_unit')
2
3 if ActiveRecord::Base.connection.supports_migrations?
4 class Thing < ActiveRecord::Base
5 attr_accessor :version
6 acts_as_versioned
7 end
8
9 class MigrationTest < Test::Unit::TestCase
10 self.use_transactional_fixtures = false
11 def teardown
12 ActiveRecord::Base.connection.initialize_schema_information
13 ActiveRecord::Base.connection.update "UPDATE schema_info SET version = 0"
14
15 Thing.connection.drop_table "things" rescue nil
16 Thing.connection.drop_table "thing_versions" rescue nil
17 Thing.reset_column_information
18 end
19
20 def test_versioned_migration
21 assert_raises(ActiveRecord::StatementInvalid) { Thing.create :title => 'blah blah' }
22 # take 'er up
23 ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/')
24 t = Thing.create :title => 'blah blah'
25 assert_equal 1, t.versions.size
26
27 # now lets take 'er back down
28 ActiveRecord::Migrator.down(File.dirname(__FILE__) + '/fixtures/migrations/')
29 assert_raises(ActiveRecord::StatementInvalid) { Thing.create :title => 'blah blah' }
30 end
31 end
32 end
@@ -0,0 +1,68
1 ActiveRecord::Schema.define(:version => 0) do
2 create_table :pages, :force => true do |t|
3 t.column :version, :integer
4 t.column :title, :string, :limit => 255
5 t.column :body, :text
6 t.column :updated_on, :datetime
7 t.column :author_id, :integer
8 t.column :revisor_id, :integer
9 end
10
11 create_table :page_versions, :force => true do |t|
12 t.column :page_id, :integer
13 t.column :version, :integer
14 t.column :title, :string, :limit => 255
15 t.column :body, :text
16 t.column :updated_on, :datetime
17 t.column :author_id, :integer
18 t.column :revisor_id, :integer
19 end
20
21 create_table :authors, :force => true do |t|
22 t.column :page_id, :integer
23 t.column :name, :string
24 end
25
26 create_table :locked_pages, :force => true do |t|
27 t.column :lock_version, :integer
28 t.column :title, :string, :limit => 255
29 t.column :type, :string, :limit => 255
30 end
31
32 create_table :locked_pages_revisions, :force => true do |t|
33 t.column :page_id, :integer
34 t.column :version, :integer
35 t.column :title, :string, :limit => 255
36 t.column :version_type, :string, :limit => 255
37 t.column :updated_at, :datetime
38 end
39
40 create_table :widgets, :force => true do |t|
41 t.column :name, :string, :limit => 50
42 t.column :foo, :string
43 t.column :version, :integer
44 t.column :updated_at, :datetime
45 end
46
47 create_table :widget_versions, :force => true do |t|
48 t.column :widget_id, :integer
49 t.column :name, :string, :limit => 50
50 t.column :version, :integer
51 t.column :updated_at, :datetime
52 end
53
54 create_table :landmarks, :force => true do |t|
55 t.column :name, :string
56 t.column :latitude, :float
57 t.column :longitude, :float
58 t.column :version, :integer
59 end
60
61 create_table :landmark_versions, :force => true do |t|
62 t.column :landmark_id, :integer
63 t.column :name, :string
64 t.column :latitude, :float
65 t.column :longitude, :float
66 t.column :version, :integer
67 end
68 end
@@ -0,0 +1,313
1 require File.join(File.dirname(__FILE__), 'abstract_unit')
2 require File.join(File.dirname(__FILE__), 'fixtures/page')
3 require File.join(File.dirname(__FILE__), 'fixtures/widget')
4
5 class VersionedTest < Test::Unit::TestCase
6 fixtures :pages, :page_versions, :locked_pages, :locked_pages_revisions, :authors, :landmarks, :landmark_versions
7
8 def test_saves_versioned_copy
9 p = Page.create :title => 'first title', :body => 'first body'
10 assert !p.new_record?
11 assert_equal 1, p.versions.size
12 assert_equal 1, p.version
13 assert_instance_of Page.versioned_class, p.versions.first
14 end
15
16 def test_saves_without_revision
17 p = pages(:welcome)
18 old_versions = p.versions.count
19
20 p.save_without_revision
21
22 p.without_revision do
23 p.update_attributes :title => 'changed'
24 end
25
26 assert_equal old_versions, p.versions.count
27 end
28
29 def test_rollback_with_version_number
30 p = pages(:welcome)
31 assert_equal 24, p.version
32 assert_equal 'Welcome to the weblog', p.title
33
34 assert p.revert_to!(p.versions.first.version), "Couldn't revert to 23"
35 assert_equal 23, p.version
36 assert_equal 'Welcome to the weblg', p.title
37 end
38
39 def test_versioned_class_name
40 assert_equal 'Version', Page.versioned_class_name
41 assert_equal 'LockedPageRevision', LockedPage.versioned_class_name
42 end
43
44 def test_versioned_class
45 assert_equal Page::Version, Page.versioned_class
46 assert_equal LockedPage::LockedPageRevision, LockedPage.versioned_class
47 end
48
49 def test_special_methods
50 assert_nothing_raised { pages(:welcome).feeling_good? }
51 assert_nothing_raised { pages(:welcome).versions.first.feeling_good? }
52 assert_nothing_raised { locked_pages(:welcome).hello_world }
53 assert_nothing_raised { locked_pages(:welcome).versions.first.hello_world }
54 end
55
56 def test_rollback_with_version_class
57 p = pages(:welcome)
58 assert_equal 24, p.version
59 assert_equal 'Welcome to the weblog', p.title
60
61 assert p.revert_to!(p.versions.first), "Couldn't revert to 23"
62 assert_equal 23, p.version
63 assert_equal 'Welcome to the weblg', p.title
64 end
65
66 def test_rollback_fails_with_invalid_revision
67 p = locked_pages(:welcome)
68 assert !p.revert_to!(locked_pages(:thinking))
69 end
70
71 def test_saves_versioned_copy_with_options
72 p = LockedPage.create :title => 'first title'
73 assert !p.new_record?
74 assert_equal 1, p.versions.size
75 assert_instance_of LockedPage.versioned_class, p.versions.first
76 end
77
78 def test_rollback_with_version_number_with_options
79 p = locked_pages(:welcome)
80 assert_equal 'Welcome to the weblog', p.title
81 assert_equal 'LockedPage', p.versions.first.version_type
82
83 assert p.revert_to!(p.versions.first.version), "Couldn't revert to 23"
84 assert_equal 'Welcome to the weblg', p.title
85 assert_equal 'LockedPage', p.versions.first.version_type
86 end
87
88 def test_rollback_with_version_class_with_options
89 p = locked_pages(:welcome)
90 assert_equal 'Welcome to the weblog', p.title
91 assert_equal 'LockedPage', p.versions.first.version_type
92
93 assert p.revert_to!(p.versions.first), "Couldn't revert to 1"
94 assert_equal 'Welcome to the weblg', p.title
95 assert_equal 'LockedPage', p.versions.first.version_type
96 end
97
98 def test_saves_versioned_copy_with_sti
99 p = SpecialLockedPage.create :title => 'first title'
100 assert !p.new_record?
101 assert_equal 1, p.versions.size
102 assert_instance_of LockedPage.versioned_class, p.versions.first
103 assert_equal 'SpecialLockedPage', p.versions.first.version_type
104 end
105
106 def test_rollback_with_version_number_with_sti
107 p = locked_pages(:thinking)
108 assert_equal 'So I was thinking', p.title
109
110 assert p.revert_to!(p.versions.first.version), "Couldn't revert to 1"
111 assert_equal 'So I was thinking!!!', p.title
112 assert_equal 'SpecialLockedPage', p.versions.first.version_type
113 end
114
115 def test_lock_version_works_with_versioning
116 p = locked_pages(:thinking)
117 p2 = LockedPage.find(p.id)
118
119 p.title = 'fresh title'
120 p.save
121 assert_equal 2, p.versions.size # limit!
122
123 assert_raises(ActiveRecord::StaleObjectError) do
124 p2.title = 'stale title'
125 p2.save
126 end
127 end
128
129 def test_version_if_condition
130 p = Page.create :title => "title"
131 assert_equal 1, p.version
132
133 Page.feeling_good = false
134 p.save
135 assert_equal 1, p.version
136 Page.feeling_good = true
137 end
138
139 def test_version_if_condition2
140 # set new if condition
141 Page.class_eval do
142 def new_feeling_good() title[0..0] == 'a'; end
143 alias_method :old_feeling_good, :feeling_good?
144 alias_method :feeling_good?, :new_feeling_good
145 end
146
147 p = Page.create :title => "title"
148 assert_equal 1, p.version # version does not increment
149 assert_equal 1, p.versions(true).size
150
151 p.update_attributes(:title => 'new title')
152 assert_equal 1, p.version # version does not increment
153 assert_equal 1, p.versions(true).size
154
155 p.update_attributes(:title => 'a title')
156 assert_equal 2, p.version
157 assert_equal 2, p.versions(true).size
158
159 # reset original if condition
160 Page.class_eval { alias_method :feeling_good?, :old_feeling_good }
161 end
162
163 def test_version_if_condition_with_block
164 # set new if condition
165 old_condition = Page.version_condition
166 Page.version_condition = Proc.new { |page| page.title[0..0] == 'b' }
167
168 p = Page.create :title => "title"
169 assert_equal 1, p.version # version does not increment
170 assert_equal 1, p.versions(true).size
171
172 p.update_attributes(:title => 'a title')
173 assert_equal 1, p.version # version does not increment
174 assert_equal 1, p.versions(true).size
175
176 p.update_attributes(:title => 'b title')
177 assert_equal 2, p.version
178 assert_equal 2, p.versions(true).size
179
180 # reset original if condition
181 Page.version_condition = old_condition
182 end
183
184 def test_version_no_limit
185 p = Page.create :title => "title", :body => 'first body'
186 p.save
187 p.save
188 5.times do |i|
189 assert_page_title p, i
190 end
191 end
192
193 def test_version_max_limit
194 p = LockedPage.create :title => "title"
195 p.update_attributes(:title => "title1")
196 p.update_attributes(:title => "title2")
197 5.times do |i|
198 assert_page_title p, i, :lock_version
199 assert p.versions(true).size <= 2, "locked version can only store 2 versions"
200 end
201 end
202
203 def test_track_changed_attributes_default_value
204 assert !Page.track_changed_attributes
205 assert LockedPage.track_changed_attributes
206 assert SpecialLockedPage.track_changed_attributes
207 end
208
209 def test_version_order
210 assert_equal 23, pages(:welcome).versions.first.version
211 assert_equal 24, pages(:welcome).versions.last.version
212 assert_equal 23, pages(:welcome).find_versions.first.version
213 assert_equal 24, pages(:welcome).find_versions.last.version
214 end
215
216 def test_track_changed_attributes
217 p = LockedPage.create :title => "title"
218 assert_equal 1, p.lock_version
219 assert_equal 1, p.versions(true).size
220
221 p.title = 'title'
222 assert !p.save_version?
223 p.save
224 assert_equal 2, p.lock_version # still increments version because of optimistic locking
225 assert_equal 1, p.versions(true).size
226
227 p.title = 'updated title'
228 assert p.save_version?
229 p.save
230 assert_equal 3, p.lock_version
231 assert_equal 1, p.versions(true).size # version 1 deleted
232
233 p.title = 'updated title!'
234 assert p.save_version?
235 p.save
236 assert_equal 4, p.lock_version
237 assert_equal 2, p.versions(true).size # version 1 deleted
238 end
239
240 def assert_page_title(p, i, version_field = :version)
241 p.title = "title#{i}"
242 p.save
243 assert_equal "title#{i}", p.title
244 assert_equal (i+4), p.send(version_field)
245 end
246
247 def test_find_versions
248 assert_equal 2, locked_pages(:welcome).versions.size
249 assert_equal 1, locked_pages(:welcome).find_versions(:conditions => ['title LIKE ?', '%weblog%']).length
250 assert_equal 2, locked_pages(:welcome).find_versions(:conditions => ['title LIKE ?', '%web%']).length
251 assert_equal 0, locked_pages(:thinking).find_versions(:conditions => ['title LIKE ?', '%web%']).length
252 assert_equal 2, locked_pages(:welcome).find_versions.length
253 end
254
255 def test_with_sequence
256 assert_equal 'widgets_seq', Widget.versioned_class.sequence_name
257 Widget.create :name => 'new widget'
258 Widget.create :name => 'new widget'
259 Widget.create :name => 'new widget'
260 assert_equal 3, Widget.count
261 assert_equal 3, Widget.versioned_class.count
262 end
263
264 def test_has_many_through
265 assert_equal [authors(:caged), authors(:mly)], pages(:welcome).authors
266 end
267
268 def test_has_many_through_with_custom_association
269 assert_equal [authors(:caged), authors(:mly)], pages(:welcome).revisors
270 end
271
272 def test_referential_integrity
273 pages(:welcome).destroy
274 assert_equal 0, Page.count
275 assert_equal 0, Page::Version.count
276 end
277
278 def test_association_options
279 association = Page.reflect_on_association(:versions)
280 options = association.options
281 assert_equal :delete_all, options[:dependent]
282 assert_equal 'version', options[:order]
283
284 association = Widget.reflect_on_association(:versions)
285 options = association.options
286 assert_nil options[:dependent]
287 assert_equal 'version desc', options[:order]
288 assert_equal 'widget_id', options[:foreign_key]
289
290 widget = Widget.create :name => 'new widget'
291 assert_equal 1, Widget.count
292 assert_equal 1, Widget.versioned_class.count
293 widget.destroy
294 assert_equal 0, Widget.count
295 assert_equal 1, Widget.versioned_class.count
296 end
297
298 def test_versioned_records_should_belong_to_parent
299 page = pages(:welcome)
300 page_version = page.versions.last
301 assert_equal page, page_version.page
302 end
303
304 def test_unchanged_attributes
305 landmarks(:washington).attributes = landmarks(:washington).attributes
306 assert !landmarks(:washington).changed?
307 end
308
309 def test_unchanged_string_attributes
310 landmarks(:washington).attributes = landmarks(:washington).attributes.inject({}) { |params, (key, value)| params.update key => value.to_s }
311 assert !landmarks(:washington).changed?
312 end
313 end
@@ -1,133 +1,147
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class ApplicationController < ActionController::Base
19 19 before_filter :check_if_login_required, :set_localization
20 20 filter_parameter_logging :password
21 21
22 22 def logged_in_user=(user)
23 23 @logged_in_user = user
24 24 session[:user_id] = (user ? user.id : nil)
25 25 end
26 26
27 27 def logged_in_user
28 28 if session[:user_id]
29 29 @logged_in_user ||= User.find(session[:user_id])
30 30 else
31 31 nil
32 32 end
33 33 end
34 34
35 def logged_in_user_membership
36 @user_membership ||= Member.find(:first, :conditions => ["user_id=? and project_id=?", self.logged_in_user.id, @project.id])
37 end
38
35 39 # check if login is globally required to access the application
36 40 def check_if_login_required
37 41 require_login if Setting.login_required?
38 42 end
39 43
40 44 def set_localization
41 45 lang = begin
42 46 if self.logged_in_user and self.logged_in_user.language and !self.logged_in_user.language.empty? and GLoc.valid_languages.include? self.logged_in_user.language.to_sym
43 47 self.logged_in_user.language
44 48 elsif request.env['HTTP_ACCEPT_LANGUAGE']
45 49 accept_lang = parse_qvalues(request.env['HTTP_ACCEPT_LANGUAGE']).first.split('-').first
46 50 if accept_lang and !accept_lang.empty? and GLoc.valid_languages.include? accept_lang.to_sym
47 51 accept_lang
48 52 end
49 53 end
50 54 rescue
51 55 nil
52 56 end || Setting.default_language
53 57 set_language_if_valid(lang)
54 58 end
55 59
56 60 def require_login
57 61 unless self.logged_in_user
58 62 store_location
59 63 redirect_to :controller => "account", :action => "login"
60 64 return false
61 65 end
62 66 true
63 67 end
64 68
65 69 def require_admin
66 70 return unless require_login
67 71 unless self.logged_in_user.admin?
68 72 render :nothing => true, :status => 403
69 73 return false
70 74 end
71 75 true
72 76 end
73 77
74 78 # authorizes the user for the requested action.
75 79 def authorize(ctrl = params[:controller], action = params[:action])
76 80 # check if action is allowed on public projects
77 81 if @project.is_public? and Permission.allowed_to_public "%s/%s" % [ ctrl, action ]
78 82 return true
79 83 end
80 84 # if action is not public, force login
81 85 return unless require_login
82 86 # admin is always authorized
83 87 return true if self.logged_in_user.admin?
84 88 # if not admin, check membership permission
85 89 @user_membership ||= Member.find(:first, :conditions => ["user_id=? and project_id=?", self.logged_in_user.id, @project.id])
86 90 if @user_membership and Permission.allowed_to_role( "%s/%s" % [ ctrl, action ], @user_membership.role_id )
87 91 return true
88 92 end
89 93 render :nothing => true, :status => 403
90 94 false
91 95 end
92 96
97 # make sure that the user is a member of the project (or admin) if project is private
98 # used as a before_filter for actions that do not require any particular permission on the project
99 def check_project_privacy
100 return true if @project.is_public?
101 return false unless logged_in_user
102 return true if logged_in_user.admin? || logged_in_user_membership
103 render :nothing => true, :status => 403
104 false
105 end
106
93 107 # store current uri in session.
94 108 # return to this location by calling redirect_back_or_default
95 109 def store_location
96 110 session[:return_to_params] = params
97 111 end
98 112
99 113 # move to the last store_location call or to the passed default one
100 114 def redirect_back_or_default(default)
101 115 if session[:return_to_params].nil?
102 116 redirect_to default
103 117 else
104 118 redirect_to session[:return_to_params]
105 119 session[:return_to_params] = nil
106 120 end
107 121 end
108 122
109 123 def render_404
110 124 @html_title = "404"
111 125 render :template => "common/404", :layout => true, :status => 404
112 126 return false
113 127 end
114 128
115 129 # qvalues http header parser
116 130 # code taken from webrick
117 131 def parse_qvalues(value)
118 132 tmp = []
119 133 if value
120 134 parts = value.split(/,\s*/)
121 135 parts.each {|part|
122 136 if m = %r{^([^\s,]+?)(?:;\s*q=(\d+(?:\.\d+)?))?$}.match(part)
123 137 val = m[1]
124 138 q = (m[2] or 1).to_f
125 139 tmp.push([val, q])
126 140 end
127 141 }
128 142 tmp = tmp.sort_by{|val, q| -q}
129 143 tmp.collect!{|val, q| val}
130 144 end
131 145 return tmp
132 146 end
133 147 end No newline at end of file
@@ -1,600 +1,613
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require 'csv'
19 19
20 20 class ProjectsController < ApplicationController
21 21 layout 'base'
22 22 before_filter :find_project, :authorize, :except => [ :index, :list, :add ]
23 23 before_filter :require_admin, :only => [ :add, :destroy ]
24 24
25 25 helper :sort
26 26 include SortHelper
27 27 helper :custom_fields
28 28 include CustomFieldsHelper
29 29 helper :ifpdf
30 30 include IfpdfHelper
31 31 helper IssuesHelper
32 32 helper :queries
33 33 include QueriesHelper
34 34
35 35 def index
36 36 list
37 37 render :action => 'list' unless request.xhr?
38 38 end
39 39
40 40 # Lists public projects
41 41 def list
42 42 sort_init 'name', 'asc'
43 43 sort_update
44 44 @project_count = Project.count(:all, :conditions => ["is_public=?", true])
45 45 @project_pages = Paginator.new self, @project_count,
46 46 15,
47 47 params['page']
48 48 @projects = Project.find :all, :order => sort_clause,
49 49 :conditions => ["is_public=?", true],
50 50 :limit => @project_pages.items_per_page,
51 51 :offset => @project_pages.current.offset
52 52
53 53 render :action => "list", :layout => false if request.xhr?
54 54 end
55 55
56 56 # Add a new project
57 57 def add
58 58 @custom_fields = IssueCustomField.find(:all)
59 59 @root_projects = Project.find(:all, :conditions => "parent_id is null")
60 60 @project = Project.new(params[:project])
61 61 if request.get?
62 62 @custom_values = ProjectCustomField.find(:all).collect { |x| CustomValue.new(:custom_field => x, :customized => @project) }
63 63 else
64 64 @project.custom_fields = CustomField.find(params[:custom_field_ids]) if params[:custom_field_ids]
65 65 @custom_values = ProjectCustomField.find(:all).collect { |x| CustomValue.new(:custom_field => x, :customized => @project, :value => params["custom_fields"][x.id.to_s]) }
66 66 @project.custom_values = @custom_values
67 67 if params[:repository_enabled] && params[:repository_enabled] == "1"
68 68 @project.repository = Repository.new
69 69 @project.repository.attributes = params[:repository]
70 70 end
71 if "1" == params[:wiki_enabled]
72 @project.wiki = Wiki.new
73 @project.wiki.attributes = params[:wiki]
74 end
71 75 if @project.save
72 76 flash[:notice] = l(:notice_successful_create)
73 77 redirect_to :controller => 'admin', :action => 'projects'
74 78 end
75 79 end
76 80 end
77 81
78 82 # Show @project
79 83 def show
80 84 @custom_values = @project.custom_values.find(:all, :include => :custom_field)
81 85 @members = @project.members.find(:all, :include => [:user, :role])
82 86 @subprojects = @project.children if @project.children.size > 0
83 87 @news = @project.news.find(:all, :limit => 5, :include => [ :author, :project ], :order => "news.created_on DESC")
84 88 @trackers = Tracker.find(:all, :order => 'position')
85 89 @open_issues_by_tracker = Issue.count(:group => :tracker, :joins => "INNER JOIN issue_statuses ON issue_statuses.id = issues.status_id", :conditions => ["project_id=? and issue_statuses.is_closed=?", @project.id, false])
86 90 @total_issues_by_tracker = Issue.count(:group => :tracker, :conditions => ["project_id=?", @project.id])
87 91 end
88 92
89 93 def settings
90 94 @root_projects = Project::find(:all, :conditions => ["parent_id is null and id <> ?", @project.id])
91 95 @custom_fields = IssueCustomField.find(:all)
92 96 @issue_category ||= IssueCategory.new
93 97 @member ||= @project.members.new
94 98 @roles = Role.find(:all, :order => 'position')
95 99 @users = User.find_active(:all) - @project.users
96 100 @custom_values ||= ProjectCustomField.find(:all).collect { |x| @project.custom_values.find_by_custom_field_id(x.id) || CustomValue.new(:custom_field => x) }
97 101 end
98 102
99 103 # Edit @project
100 104 def edit
101 105 if request.post?
102 106 @project.custom_fields = IssueCustomField.find(params[:custom_field_ids]) if params[:custom_field_ids]
103 107 if params[:custom_fields]
104 108 @custom_values = ProjectCustomField.find(:all).collect { |x| CustomValue.new(:custom_field => x, :customized => @project, :value => params["custom_fields"][x.id.to_s]) }
105 109 @project.custom_values = @custom_values
106 110 end
107 111 if params[:repository_enabled]
108 112 case params[:repository_enabled]
109 113 when "0"
110 114 @project.repository = nil
111 115 when "1"
112 116 @project.repository ||= Repository.new
113 117 @project.repository.update_attributes params[:repository]
114 118 end
115 119 end
120 if params[:wiki_enabled]
121 case params[:wiki_enabled]
122 when "0"
123 @project.wiki.destroy
124 when "1"
125 @project.wiki ||= Wiki.new
126 @project.wiki.update_attributes params[:wiki]
127 end
128 end
116 129 @project.attributes = params[:project]
117 130 if @project.save
118 131 flash[:notice] = l(:notice_successful_update)
119 132 redirect_to :action => 'settings', :id => @project
120 133 else
121 134 settings
122 135 render :action => 'settings'
123 136 end
124 137 end
125 138 end
126 139
127 140 # Delete @project
128 141 def destroy
129 142 if request.post? and params[:confirm]
130 143 @project.destroy
131 144 redirect_to :controller => 'admin', :action => 'projects'
132 145 end
133 146 end
134 147
135 148 # Add a new issue category to @project
136 149 def add_issue_category
137 150 if request.post?
138 151 @issue_category = @project.issue_categories.build(params[:issue_category])
139 152 if @issue_category.save
140 153 flash[:notice] = l(:notice_successful_create)
141 154 redirect_to :action => 'settings', :tab => 'categories', :id => @project
142 155 else
143 156 settings
144 157 render :action => 'settings'
145 158 end
146 159 end
147 160 end
148 161
149 162 # Add a new version to @project
150 163 def add_version
151 164 @version = @project.versions.build(params[:version])
152 165 if request.post? and @version.save
153 166 flash[:notice] = l(:notice_successful_create)
154 167 redirect_to :action => 'settings', :tab => 'versions', :id => @project
155 168 end
156 169 end
157 170
158 171 # Add a new member to @project
159 172 def add_member
160 173 @member = @project.members.build(params[:member])
161 174 if request.post?
162 175 if @member.save
163 176 flash[:notice] = l(:notice_successful_create)
164 177 redirect_to :action => 'settings', :tab => 'members', :id => @project
165 178 else
166 179 settings
167 180 render :action => 'settings'
168 181 end
169 182 end
170 183 end
171 184
172 185 # Show members list of @project
173 186 def list_members
174 187 @members = @project.members.find(:all)
175 188 end
176 189
177 190 # Add a new document to @project
178 191 def add_document
179 192 @categories = Enumeration::get_values('DCAT')
180 193 @document = @project.documents.build(params[:document])
181 194 if request.post? and @document.save
182 195 # Save the attachments
183 196 params[:attachments].each { |a|
184 197 Attachment.create(:container => @document, :file => a, :author => logged_in_user) unless a.size == 0
185 198 } if params[:attachments] and params[:attachments].is_a? Array
186 199 flash[:notice] = l(:notice_successful_create)
187 200 Mailer.deliver_document_add(@document) if Permission.find_by_controller_and_action(params[:controller], params[:action]).mail_enabled?
188 201 redirect_to :action => 'list_documents', :id => @project
189 202 end
190 203 end
191 204
192 205 # Show documents list of @project
193 206 def list_documents
194 207 @documents = @project.documents.find :all, :include => :category
195 208 end
196 209
197 210 # Add a new issue to @project
198 211 def add_issue
199 212 @tracker = Tracker.find(params[:tracker_id])
200 213 @priorities = Enumeration::get_values('IPRI')
201 214 @issue = Issue.new(:project => @project, :tracker => @tracker)
202 215 if request.get?
203 216 @issue.start_date = Date.today
204 217 @custom_values = @project.custom_fields_for_issues(@tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue) }
205 218 else
206 219 @issue.attributes = params[:issue]
207 220 @issue.author_id = self.logged_in_user.id if self.logged_in_user
208 221 # Multiple file upload
209 222 @attachments = []
210 223 params[:attachments].each { |a|
211 224 @attachments << Attachment.new(:container => @issue, :file => a, :author => logged_in_user) unless a.size == 0
212 225 } if params[:attachments] and params[:attachments].is_a? Array
213 226 @custom_values = @project.custom_fields_for_issues(@tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue, :value => params["custom_fields"][x.id.to_s]) }
214 227 @issue.custom_values = @custom_values
215 228 if @issue.save
216 229 @attachments.each(&:save)
217 230 flash[:notice] = l(:notice_successful_create)
218 231 Mailer.deliver_issue_add(@issue) if Permission.find_by_controller_and_action(params[:controller], params[:action]).mail_enabled?
219 232 redirect_to :action => 'list_issues', :id => @project
220 233 end
221 234 end
222 235 end
223 236
224 237 # Show filtered/sorted issues list of @project
225 238 def list_issues
226 239 sort_init 'issues.id', 'desc'
227 240 sort_update
228 241
229 242 retrieve_query
230 243
231 244 @results_per_page_options = [ 15, 25, 50, 100 ]
232 245 if params[:per_page] and @results_per_page_options.include? params[:per_page].to_i
233 246 @results_per_page = params[:per_page].to_i
234 247 session[:results_per_page] = @results_per_page
235 248 else
236 249 @results_per_page = session[:results_per_page] || 25
237 250 end
238 251
239 252 if @query.valid?
240 253 @issue_count = Issue.count(:include => [:status, :project], :conditions => @query.statement)
241 254 @issue_pages = Paginator.new self, @issue_count, @results_per_page, params['page']
242 255 @issues = Issue.find :all, :order => sort_clause,
243 256 :include => [ :author, :status, :tracker, :project, :priority ],
244 257 :conditions => @query.statement,
245 258 :limit => @issue_pages.items_per_page,
246 259 :offset => @issue_pages.current.offset
247 260 end
248 261 @trackers = Tracker.find :all, :order => 'position'
249 262 render :layout => false if request.xhr?
250 263 end
251 264
252 265 # Export filtered/sorted issues list to CSV
253 266 def export_issues_csv
254 267 sort_init 'issues.id', 'desc'
255 268 sort_update
256 269
257 270 retrieve_query
258 271 render :action => 'list_issues' and return unless @query.valid?
259 272
260 273 @issues = Issue.find :all, :order => sort_clause,
261 274 :include => [ :author, :status, :tracker, :priority, {:custom_values => :custom_field} ],
262 275 :conditions => @query.statement,
263 276 :limit => Setting.issues_export_limit
264 277
265 278 ic = Iconv.new(l(:general_csv_encoding), 'UTF-8')
266 279 export = StringIO.new
267 280 CSV::Writer.generate(export, l(:general_csv_separator)) do |csv|
268 281 # csv header fields
269 282 headers = [ "#", l(:field_status),
270 283 l(:field_tracker),
271 284 l(:field_priority),
272 285 l(:field_subject),
273 286 l(:field_author),
274 287 l(:field_start_date),
275 288 l(:field_due_date),
276 289 l(:field_done_ratio),
277 290 l(:field_created_on),
278 291 l(:field_updated_on)
279 292 ]
280 293 for custom_field in @project.all_custom_fields
281 294 headers << custom_field.name
282 295 end
283 296 csv << headers.collect {|c| ic.iconv(c) }
284 297 # csv lines
285 298 @issues.each do |issue|
286 299 fields = [issue.id, issue.status.name,
287 300 issue.tracker.name,
288 301 issue.priority.name,
289 302 issue.subject,
290 303 issue.author.display_name,
291 304 issue.start_date ? l_date(issue.start_date) : nil,
292 305 issue.due_date ? l_date(issue.due_date) : nil,
293 306 issue.done_ratio,
294 307 l_datetime(issue.created_on),
295 308 l_datetime(issue.updated_on)
296 309 ]
297 310 for custom_field in @project.all_custom_fields
298 311 fields << (show_value issue.custom_value_for(custom_field))
299 312 end
300 313 csv << fields.collect {|c| ic.iconv(c.to_s) }
301 314 end
302 315 end
303 316 export.rewind
304 317 send_data(export.read, :type => 'text/csv; header=present', :filename => 'export.csv')
305 318 end
306 319
307 320 # Export filtered/sorted issues to PDF
308 321 def export_issues_pdf
309 322 sort_init 'issues.id', 'desc'
310 323 sort_update
311 324
312 325 retrieve_query
313 326 render :action => 'list_issues' and return unless @query.valid?
314 327
315 328 @issues = Issue.find :all, :order => sort_clause,
316 329 :include => [ :author, :status, :tracker, :priority ],
317 330 :conditions => @query.statement,
318 331 :limit => Setting.issues_export_limit
319 332
320 333 @options_for_rfpdf ||= {}
321 334 @options_for_rfpdf[:file_name] = "export.pdf"
322 335 render :layout => false
323 336 end
324 337
325 338 def move_issues
326 339 @issues = @project.issues.find(params[:issue_ids]) if params[:issue_ids]
327 340 redirect_to :action => 'list_issues', :id => @project and return unless @issues
328 341 @projects = []
329 342 # find projects to which the user is allowed to move the issue
330 343 @logged_in_user.memberships.each {|m| @projects << m.project if Permission.allowed_to_role("projects/move_issues", m.role_id)}
331 344 # issue can be moved to any tracker
332 345 @trackers = Tracker.find(:all)
333 346 if request.post? and params[:new_project_id] and params[:new_tracker_id]
334 347 new_project = Project.find(params[:new_project_id])
335 348 new_tracker = Tracker.find(params[:new_tracker_id])
336 349 @issues.each { |i|
337 350 # project dependent properties
338 351 unless i.project_id == new_project.id
339 352 i.category = nil
340 353 i.fixed_version = nil
341 354 end
342 355 # move the issue
343 356 i.project = new_project
344 357 i.tracker = new_tracker
345 358 i.save
346 359 }
347 360 flash[:notice] = l(:notice_successful_update)
348 361 redirect_to :action => 'list_issues', :id => @project
349 362 end
350 363 end
351 364
352 365 def add_query
353 366 @query = Query.new(params[:query])
354 367 @query.project = @project
355 368 @query.user = logged_in_user
356 369
357 370 params[:fields].each do |field|
358 371 @query.add_filter(field, params[:operators][field], params[:values][field])
359 372 end if params[:fields]
360 373
361 374 if request.post? and @query.save
362 375 flash[:notice] = l(:notice_successful_create)
363 376 redirect_to :controller => 'reports', :action => 'issue_report', :id => @project
364 377 end
365 378 render :layout => false if request.xhr?
366 379 end
367 380
368 381 # Add a news to @project
369 382 def add_news
370 383 @news = News.new(:project => @project)
371 384 if request.post?
372 385 @news.attributes = params[:news]
373 386 @news.author_id = self.logged_in_user.id if self.logged_in_user
374 387 if @news.save
375 388 flash[:notice] = l(:notice_successful_create)
376 389 redirect_to :action => 'list_news', :id => @project
377 390 end
378 391 end
379 392 end
380 393
381 394 # Show news list of @project
382 395 def list_news
383 396 @news_pages, @news = paginate :news, :per_page => 10, :conditions => ["project_id=?", @project.id], :include => :author, :order => "news.created_on DESC"
384 397 render :action => "list_news", :layout => false if request.xhr?
385 398 end
386 399
387 400 def add_file
388 401 if request.post?
389 402 @version = @project.versions.find_by_id(params[:version_id])
390 403 # Save the attachments
391 404 @attachments = []
392 405 params[:attachments].each { |file|
393 406 next unless file.size > 0
394 407 a = Attachment.create(:container => @version, :file => file, :author => logged_in_user)
395 408 @attachments << a unless a.new_record?
396 409 } if params[:attachments] and params[:attachments].is_a? Array
397 410 Mailer.deliver_attachments_add(@attachments) if !@attachments.empty? and Permission.find_by_controller_and_action(params[:controller], params[:action]).mail_enabled?
398 411 redirect_to :controller => 'projects', :action => 'list_files', :id => @project
399 412 end
400 413 @versions = @project.versions
401 414 end
402 415
403 416 def list_files
404 417 @versions = @project.versions
405 418 end
406 419
407 420 # Show changelog for @project
408 421 def changelog
409 422 @trackers = Tracker.find(:all, :conditions => ["is_in_chlog=?", true], :order => 'position')
410 423 if request.get?
411 424 @selected_tracker_ids = @trackers.collect {|t| t.id.to_s }
412 425 else
413 426 @selected_tracker_ids = params[:tracker_ids].collect { |id| id.to_i.to_s } if params[:tracker_ids] and params[:tracker_ids].is_a? Array
414 427 end
415 428 @selected_tracker_ids ||= []
416 429 @fixed_issues = @project.issues.find(:all,
417 430 :include => [ :fixed_version, :status, :tracker ],
418 431 :conditions => [ "issue_statuses.is_closed=? and issues.tracker_id in (#{@selected_tracker_ids.join(',')}) and issues.fixed_version_id is not null", true],
419 432 :order => "versions.effective_date DESC, issues.id DESC"
420 433 ) unless @selected_tracker_ids.empty?
421 434 @fixed_issues ||= []
422 435 end
423 436
424 437 def roadmap
425 438 @trackers = Tracker.find(:all, :conditions => ["is_in_roadmap=?", true], :order => 'position')
426 439 if request.get?
427 440 @selected_tracker_ids = @trackers.collect {|t| t.id.to_s }
428 441 else
429 442 @selected_tracker_ids = params[:tracker_ids].collect { |id| id.to_i.to_s } if params[:tracker_ids] and params[:tracker_ids].is_a? Array
430 443 end
431 444 @selected_tracker_ids ||= []
432 445 @versions = @project.versions.find(:all,
433 446 :conditions => [ "versions.effective_date>?", Date.today],
434 447 :order => "versions.effective_date ASC"
435 448 )
436 449 end
437 450
438 451 def activity
439 452 if params[:year] and params[:year].to_i > 1900
440 453 @year = params[:year].to_i
441 454 if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
442 455 @month = params[:month].to_i
443 456 end
444 457 end
445 458 @year ||= Date.today.year
446 459 @month ||= Date.today.month
447 460
448 461 @date_from = Date.civil(@year, @month, 1)
449 462 @date_to = (@date_from >> 1)-1
450 463
451 464 @events_by_day = {}
452 465
453 466 unless params[:show_issues] == "0"
454 467 @project.issues.find(:all, :include => [:author, :status], :conditions => ["issues.created_on>=? and issues.created_on<=?", @date_from, @date_to] ).each { |i|
455 468 @events_by_day[i.created_on.to_date] ||= []
456 469 @events_by_day[i.created_on.to_date] << i
457 470 }
458 471 @show_issues = 1
459 472 end
460 473
461 474 unless params[:show_news] == "0"
462 475 @project.news.find(:all, :conditions => ["news.created_on>=? and news.created_on<=?", @date_from, @date_to], :include => :author ).each { |i|
463 476 @events_by_day[i.created_on.to_date] ||= []
464 477 @events_by_day[i.created_on.to_date] << i
465 478 }
466 479 @show_news = 1
467 480 end
468 481
469 482 unless params[:show_files] == "0"
470 483 Attachment.find(:all, :select => "attachments.*", :joins => "LEFT JOIN versions ON versions.id = attachments.container_id", :conditions => ["attachments.container_type='Version' and versions.project_id=? and attachments.created_on>=? and attachments.created_on<=?", @project.id, @date_from, @date_to], :include => :author ).each { |i|
471 484 @events_by_day[i.created_on.to_date] ||= []
472 485 @events_by_day[i.created_on.to_date] << i
473 486 }
474 487 @show_files = 1
475 488 end
476 489
477 490 unless params[:show_documents] == "0"
478 491 @project.documents.find(:all, :conditions => ["documents.created_on>=? and documents.created_on<=?", @date_from, @date_to] ).each { |i|
479 492 @events_by_day[i.created_on.to_date] ||= []
480 493 @events_by_day[i.created_on.to_date] << i
481 494 }
482 495 Attachment.find(:all, :select => "attachments.*", :joins => "LEFT JOIN documents ON documents.id = attachments.container_id", :conditions => ["attachments.container_type='Document' and documents.project_id=? and attachments.created_on>=? and attachments.created_on<=?", @project.id, @date_from, @date_to], :include => :author ).each { |i|
483 496 @events_by_day[i.created_on.to_date] ||= []
484 497 @events_by_day[i.created_on.to_date] << i
485 498 }
486 499 @show_documents = 1
487 500 end
488 501
489 502 render :layout => false if request.xhr?
490 503 end
491 504
492 505 def calendar
493 506 if params[:year] and params[:year].to_i > 1900
494 507 @year = params[:year].to_i
495 508 if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
496 509 @month = params[:month].to_i
497 510 end
498 511 end
499 512 @year ||= Date.today.year
500 513 @month ||= Date.today.month
501 514
502 515 @date_from = Date.civil(@year, @month, 1)
503 516 @date_to = (@date_from >> 1)-1
504 517 # start on monday
505 518 @date_from = @date_from - (@date_from.cwday-1)
506 519 # finish on sunday
507 520 @date_to = @date_to + (7-@date_to.cwday)
508 521
509 522 @issues = @project.issues.find(:all, :include => [:tracker, :status, :assigned_to, :priority], :conditions => ["((start_date>=? and start_date<=?) or (due_date>=? and due_date<=?))", @date_from, @date_to, @date_from, @date_to])
510 523 render :layout => false if request.xhr?
511 524 end
512 525
513 526 def gantt
514 527 if params[:year] and params[:year].to_i >0
515 528 @year_from = params[:year].to_i
516 529 if params[:month] and params[:month].to_i >=1 and params[:month].to_i <= 12
517 530 @month_from = params[:month].to_i
518 531 else
519 532 @month_from = 1
520 533 end
521 534 else
522 535 @month_from ||= (Date.today << 1).month
523 536 @year_from ||= (Date.today << 1).year
524 537 end
525 538
526 539 @zoom = (params[:zoom].to_i > 0 and params[:zoom].to_i < 5) ? params[:zoom].to_i : 2
527 540 @months = (params[:months].to_i > 0 and params[:months].to_i < 25) ? params[:months].to_i : 6
528 541
529 542 @date_from = Date.civil(@year_from, @month_from, 1)
530 543 @date_to = (@date_from >> @months) - 1
531 544 @issues = @project.issues.find(:all, :order => "start_date, due_date", :include => [:tracker, :status, :assigned_to, :priority], :conditions => ["(((start_date>=? and start_date<=?) or (due_date>=? and due_date<=?) or (start_date<? and due_date>?)) and start_date is not null and due_date is not null)", @date_from, @date_to, @date_from, @date_to, @date_from, @date_to])
532 545
533 546 if params[:output]=='pdf'
534 547 @options_for_rfpdf ||= {}
535 548 @options_for_rfpdf[:file_name] = "gantt.pdf"
536 549 render :template => "projects/gantt.rfpdf", :layout => false
537 550 else
538 551 render :template => "projects/gantt.rhtml"
539 552 end
540 553 end
541 554
542 555 def search
543 556 @question = params[:q] || ""
544 557 @question.strip!
545 558 @all_words = params[:all_words] || (params[:submit] ? false : true)
546 559 @scope = params[:scope] || (params[:submit] ? [] : %w(issues news documents) )
547 560 if !@question.empty?
548 561 # tokens must be at least 3 character long
549 562 @tokens = @question.split.uniq.select {|w| w.length > 2 }
550 563 # no more than 5 tokens to search for
551 564 @tokens.slice! 5..-1 if @tokens.size > 5
552 565 # strings used in sql like statement
553 566 like_tokens = @tokens.collect {|w| "%#{w}%"}
554 567 operator = @all_words ? " AND " : " OR "
555 568 limit = 10
556 569 @results = []
557 570 @results += @project.issues.find(:all, :limit => limit, :include => :author, :conditions => [ (["(LOWER(issues.subject) like ? OR LOWER(issues.description) like ?)"] * like_tokens.size).join(operator), * (like_tokens * 2).sort] ) if @scope.include? 'issues'
558 571 @results += @project.news.find(:all, :limit => limit, :conditions => [ (["(LOWER(news.title) like ? OR LOWER(news.description) like ?)"] * like_tokens.size).join(operator), * (like_tokens * 2).sort], :include => :author ) if @scope.include? 'news'
559 572 @results += @project.documents.find(:all, :limit => limit, :conditions => [ (["(LOWER(title) like ? OR LOWER(description) like ?)"] * like_tokens.size).join(operator), * (like_tokens * 2).sort] ) if @scope.include? 'documents'
560 573 @question = @tokens.join(" ")
561 574 end
562 575 end
563 576
564 577 private
565 578 # Find project of id params[:id]
566 579 # if not found, redirect to project list
567 580 # Used as a before_filter
568 581 def find_project
569 582 @project = Project.find(params[:id])
570 583 @html_title = @project.name
571 584 rescue ActiveRecord::RecordNotFound
572 585 render_404
573 586 end
574 587
575 588 # Retrieve query from session or build a new query
576 589 def retrieve_query
577 590 if params[:query_id]
578 591 @query = @project.queries.find(params[:query_id])
579 592 session[:query] = @query
580 593 else
581 594 if params[:set_filter] or !session[:query] or session[:query].project_id != @project.id
582 595 # Give it a name, required to be valid
583 596 @query = Query.new(:name => "_")
584 597 @query.project = @project
585 598 if params[:fields] and params[:fields].is_a? Array
586 599 params[:fields].each do |field|
587 600 @query.add_filter(field, params[:operators][field], params[:values][field])
588 601 end
589 602 else
590 603 @query.available_filters.keys.each do |field|
591 604 @query.add_short_filter(field, params[field]) if params[field]
592 605 end
593 606 end
594 607 session[:query] = @query
595 608 else
596 609 @query = session[:query]
597 610 end
598 611 end
599 612 end
600 613 end
@@ -1,197 +1,229
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 module ApplicationHelper
19 19
20 20 # Return current logged in user or nil
21 21 def loggedin?
22 22 @logged_in_user
23 23 end
24 24
25 25 # Return true if user is logged in and is admin, otherwise false
26 26 def admin_loggedin?
27 27 @logged_in_user and @logged_in_user.admin?
28 28 end
29 29
30 30 # Return true if user is authorized for controller/action, otherwise false
31 31 def authorize_for(controller, action)
32 32 # check if action is allowed on public projects
33 33 if @project.is_public? and Permission.allowed_to_public "%s/%s" % [ controller, action ]
34 34 return true
35 35 end
36 36 # check if user is authorized
37 37 if @logged_in_user and (@logged_in_user.admin? or Permission.allowed_to_role( "%s/%s" % [ controller, action ], @logged_in_user.role_for_project(@project.id) ) )
38 38 return true
39 39 end
40 40 return false
41 41 end
42 42
43 43 # Display a link if user is authorized
44 44 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
45 45 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller], options[:action])
46 46 end
47 47
48 48 # Display a link to user's account page
49 49 def link_to_user(user)
50 50 link_to user.display_name, :controller => 'account', :action => 'show', :id => user
51 51 end
52 52
53 53 def image_to_function(name, function, html_options = {})
54 54 html_options.symbolize_keys!
55 55 tag(:input, html_options.merge({
56 56 :type => "image", :src => image_path(name),
57 57 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
58 58 }))
59 59 end
60 60
61 61 def format_date(date)
62 62 l_date(date) if date
63 63 end
64 64
65 65 def format_time(time)
66 l_datetime(time) if time
66 l_datetime((time.is_a? String) ? time.to_time : time) if time
67 67 end
68 68
69 69 def day_name(day)
70 70 l(:general_day_names).split(',')[day-1]
71 71 end
72 72
73 73 def month_name(month)
74 74 l(:actionview_datehelper_select_month_names).split(',')[month-1]
75 75 end
76 76
77 77 def pagination_links_full(paginator, options={}, html_options={})
78 78 html = ''
79 79 html << link_to_remote(('&#171; ' + l(:label_previous)),
80 80 {:update => "content", :url => { :page => paginator.current.previous }},
81 81 {:href => url_for(:action => 'list', :params => params.merge({:page => paginator.current.previous}))}) + ' ' if paginator.current.previous
82 82
83 83 html << (pagination_links_each(paginator, options) do |n|
84 84 link_to_remote(n.to_s,
85 85 {:url => {:action => 'list', :params => params.merge({:page => n})}, :update => 'content'},
86 86 {:href => url_for(:action => 'list', :params => params.merge({:page => n}))})
87 87 end || '')
88 88
89 89 html << ' ' + link_to_remote((l(:label_next) + ' &#187;'),
90 90 {:update => "content", :url => { :page => paginator.current.next }},
91 91 {:href => url_for(:action => 'list', :params => params.merge({:page => paginator.current.next}))}) if paginator.current.next
92 92 html
93 93 end
94 94
95 def textilizable(text)
96 text = (Setting.text_formatting == 'textile') && (ActionView::Helpers::TextHelper.method_defined? "textilize") ? RedCloth.new(h(text)).to_html : simple_format(auto_link(h(text)))
97 # turn "#id" patterns into links to issues
98 text = text.gsub(/#(\d+)([^;\d])/, "<a href='/issues/show/\\1'>#\\1</a>\\2")
95 # textilize text according to system settings and RedCloth availability
96 def textilizable(text, options = {})
97 # different methods for formatting wiki links
98 case options[:wiki_links]
99 when :local
100 # used for local links to html files
101 format_wiki_link = Proc.new {|title| "#{title}.html" }
102 when :anchor
103 # used for single-file wiki export
104 format_wiki_link = Proc.new {|title| "##{title}" }
105 else
106 if @project
107 format_wiki_link = Proc.new {|title| url_for :controller => 'wiki', :action => 'index', :id => @project, :page => title }
108 else
109 format_wiki_link = Proc.new {|title| title }
110 end
111 end
112
113 # turn wiki links into textile links:
114 # example:
115 # [[link]] -> "link":link
116 # [[link|title]] -> "title":link
117 text = text.gsub(/\[\[([^\]\|]+)(\|([^\]\|]+))?\]\]/) {|m| "\"#{$3 || $1}\":" + format_wiki_link.call(Wiki.titleize($1)) }
118
119 # turn issue ids to textile links
120 # example:
121 # #52 -> "#52":/issues/show/52
122 text = text.gsub(/#(\d+)([\s\.\(\)\-,:;])/) {|m| "\"##{$1}\":" + url_for(:controller => 'issues', :action => 'show', :id => $1) + $2 }
123
124 # turn revision ids to textile links (@project needed)
125 # example:
126 # r52 -> "r52":/repositories/revision/6?rev=52 (@project.id is 6)
127 text = text.gsub(/r(\d+)([\s\.\(\)\-,:;])/) {|m| "\"r#{$1}\":" + url_for(:controller => 'repositories', :action => 'revision', :id => @project.id, :rev => $1) + $2 } if @project
128
129 # finally textilize text
130 text = (Setting.text_formatting == 'textile') && (ActionView::Helpers::TextHelper.method_defined? "textilize") ? auto_link(RedCloth.new(text, [:filter_html]).to_html) : simple_format(auto_link(h(text)))
99 131 end
100 132
101 133 def error_messages_for(object_name, options = {})
102 134 options = options.symbolize_keys
103 135 object = instance_variable_get("@#{object_name}")
104 136 if object && !object.errors.empty?
105 137 # build full_messages here with controller current language
106 138 full_messages = []
107 139 object.errors.each do |attr, msg|
108 140 next if msg.nil?
109 141 msg = msg.first if msg.is_a? Array
110 142 if attr == "base"
111 143 full_messages << l(msg)
112 144 else
113 145 full_messages << "&#171; " + (l_has_string?("field_" + attr) ? l("field_" + attr) : object.class.human_attribute_name(attr)) + " &#187; " + l(msg) unless attr == "custom_values"
114 146 end
115 147 end
116 148 # retrieve custom values error messages
117 149 if object.errors[:custom_values]
118 150 object.custom_values.each do |v|
119 151 v.errors.each do |attr, msg|
120 152 next if msg.nil?
121 153 msg = msg.first if msg.is_a? Array
122 154 full_messages << "&#171; " + v.custom_field.name + " &#187; " + l(msg)
123 155 end
124 156 end
125 157 end
126 158 content_tag("div",
127 159 content_tag(
128 160 options[:header_tag] || "h2", lwr(:gui_validation_error, full_messages.length) + " :"
129 161 ) +
130 162 content_tag("ul", full_messages.collect { |msg| content_tag("li", msg) }),
131 163 "id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation"
132 164 )
133 165 else
134 166 ""
135 167 end
136 168 end
137 169
138 170 def lang_options_for_select(blank=true)
139 171 (blank ? [["(auto)", ""]] : []) +
140 172 (GLoc.valid_languages.sort {|x,y| x.to_s <=> y.to_s }).collect {|lang| [ l_lang_name(lang.to_s, lang), lang.to_s]}
141 173 end
142 174
143 175 def label_tag_for(name, option_tags = nil, options = {})
144 176 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
145 177 content_tag("label", label_text)
146 178 end
147 179
148 180 def labelled_tabular_form_for(name, object, options, &proc)
149 181 options[:html] ||= {}
150 182 options[:html].store :class, "tabular"
151 183 form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
152 184 end
153 185
154 186 def check_all_links(form_name)
155 187 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
156 188 " | " +
157 189 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
158 190 end
159 191
160 192 def calendar_for(field_id)
161 193 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
162 194 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
163 195 end
164 196 end
165 197
166 198 class TabularFormBuilder < ActionView::Helpers::FormBuilder
167 199 include GLoc
168 200
169 201 def initialize(object_name, object, template, options, proc)
170 202 set_language_if_valid options.delete(:lang)
171 203 @object_name, @object, @template, @options, @proc = object_name, object, template, options, proc
172 204 end
173 205
174 206 (field_helpers - %w(radio_button hidden_field) + %w(date_select)).each do |selector|
175 207 src = <<-END_SRC
176 208 def #{selector}(field, options = {})
177 209 return super if options.delete :no_label
178 210 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
179 211 label = @template.content_tag("label", label_text,
180 212 :class => (@object && @object.errors[field] ? "error" : nil),
181 213 :for => (@object_name.to_s + "_" + field.to_s))
182 214 label + super
183 215 end
184 216 END_SRC
185 217 class_eval src, __FILE__, __LINE__
186 218 end
187 219
188 220 def select(field, choices, options = {}, html_options = {})
189 221 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
190 222 label = @template.content_tag("label", label_text,
191 223 :class => (@object && @object.errors[field] ? "error" : nil),
192 224 :for => (@object_name.to_s + "_" + field.to_s))
193 225 label + super
194 226 end
195 227
196 228 end
197 229
@@ -1,70 +1,71
1 1 # redMine - project management software
2 2 # Copyright (C) 2006 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class Project < ActiveRecord::Base
19 19 has_many :versions, :dependent => :destroy, :order => "versions.effective_date DESC, versions.name DESC"
20 20 has_many :members, :dependent => :delete_all, :include => :user, :conditions => "users.status=#{User::STATUS_ACTIVE}"
21 21 has_many :users, :through => :members
22 22 has_many :custom_values, :dependent => :delete_all, :as => :customized
23 23 has_many :issues, :dependent => :destroy, :order => "issues.created_on DESC", :include => [:status, :tracker]
24 24 has_many :queries, :dependent => :delete_all
25 25 has_many :documents, :dependent => :destroy
26 26 has_many :news, :dependent => :delete_all, :include => :author
27 27 has_many :issue_categories, :dependent => :delete_all, :order => "issue_categories.name"
28 28 has_one :repository, :dependent => :destroy
29 has_one :wiki, :dependent => :destroy
29 30 has_and_belongs_to_many :custom_fields, :class_name => 'IssueCustomField', :join_table => 'custom_fields_projects', :association_foreign_key => 'custom_field_id'
30 31 acts_as_tree :order => "name", :counter_cache => true
31 32
32 33 validates_presence_of :name, :description
33 34 validates_uniqueness_of :name
34 35 validates_associated :custom_values, :on => :update
35 validates_associated :repository
36 validates_associated :repository, :wiki
36 37 validates_format_of :name, :with => /^[\w\s\'\-]*$/i
37 38
38 39 # returns latest created projects
39 40 # non public projects will be returned only if user is a member of those
40 41 def self.latest(user=nil, count=5)
41 42 find(:all, :limit => count, :conditions => visible_by(user), :order => "projects.created_on DESC")
42 43 end
43 44
44 45 def self.visible_by(user=nil)
45 46 if user && !user.memberships.empty?
46 47 return ["projects.is_public = ? or projects.id IN (#{user.memberships.collect{|m| m.project_id}.join(',')})", true]
47 48 else
48 49 return ["projects.is_public = ?", true]
49 50 end
50 51 end
51 52
52 53 # Returns an array of all custom fields enabled for project issues
53 54 # (explictly associated custom fields and custom fields enabled for all projects)
54 55 def custom_fields_for_issues(tracker)
55 56 tracker.custom_fields.find(:all, :include => :projects,
56 57 :conditions => ["is_for_all=? or project_id=?", true, self.id])
57 58 #(CustomField.for_all + custom_fields).uniq
58 59 end
59 60
60 61 def all_custom_fields
61 62 @all_custom_fields ||= IssueCustomField.find(:all, :include => :projects,
62 63 :conditions => ["is_for_all=? or project_id=?", true, self.id])
63 64 end
64 65
65 66 protected
66 67 def validate
67 68 errors.add(parent_id, " must be a root project") if parent and parent.parent
68 69 errors.add_to_base("A project with subprojects can't be a subproject") if parent and children.size > 0
69 70 end
70 71 end
@@ -1,147 +1,149
1 1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2 2 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
3 3 <head>
4 4 <title><%= Setting.app_title + (@html_title ? ": #{@html_title}" : "") %></title>
5 5 <meta http-equiv="content-type" content="text/html; charset=utf-8" />
6 6 <meta name="description" content="redMine" />
7 7 <meta name="keywords" content="issue,bug,tracker" />
8 8 <!--[if IE]>
9 9 <style type="text/css">
10 10 body {behavior: url(<%= stylesheet_path "csshover.htc" %>);}
11 11 </style>
12 12 <![endif]-->
13 13 <%= stylesheet_link_tag "application" %>
14 14 <%= stylesheet_link_tag "print", :media => "print" %>
15 15 <%= javascript_include_tag :defaults %>
16 16 <%= javascript_include_tag 'menu' %>
17 17 <%= stylesheet_link_tag 'jstoolbar' %>
18 18 <!-- page specific tags --><%= yield :header_tags %>
19 19 </head>
20 20
21 21 <body>
22 22 <div id="container" >
23 23
24 24 <div id="header">
25 25 <div style="float: left;">
26 26 <h1><%= Setting.app_title %></h1>
27 27 <h2><%= Setting.app_subtitle %></h2>
28 28 </div>
29 29 <div style="float: right; padding-right: 1em; padding-top: 0.2em;">
30 30 <% if loggedin? %><small><%=l(:label_logged_as)%> <b><%= @logged_in_user.login %></b></small><% end %>
31 31 </div>
32 32 </div>
33 33
34 34 <div id="navigation">
35 35 <ul>
36 36 <li><%= link_to l(:label_home), { :controller => 'welcome' }, :class => "icon icon-home" %></li>
37 37 <li><%= link_to l(:label_my_page), { :controller => 'my', :action => 'page'}, :class => "icon icon-mypage" %></li>
38 38 <li><%= link_to l(:label_project_plural), { :controller => 'projects' }, :class => "icon icon-projects" %></li>
39 39
40 40 <% unless @project.nil? || @project.id.nil? %>
41 41 <li class="submenu"><%= link_to @project.name, { :controller => 'projects', :action => 'show', :id => @project }, :class => "icon icon-projects", :onmouseover => "buttonMouseover(event, 'menuProject');" %></li>
42 42 <% end %>
43 43
44 44 <% if loggedin? %>
45 45 <li><%= link_to l(:label_my_account), { :controller => 'my', :action => 'account' }, :class => "icon icon-user" %></li>
46 46 <% end %>
47 47
48 48 <% if admin_loggedin? %>
49 49 <li class="submenu"><%= link_to l(:label_administration), { :controller => 'admin' }, :class => "icon icon-admin", :onmouseover => "buttonMouseover(event, 'menuAdmin');" %></li>
50 50 <% end %>
51 51
52 52 <li class="right"><%= link_to l(:label_help), { :controller => 'help', :ctrl => params[:controller], :page => params[:action] }, :onclick => "window.open(this.href); return false;", :class => "icon icon-help" %></li>
53 53
54 54 <% if loggedin? %>
55 55 <li class="right"><%= link_to l(:label_logout), { :controller => 'account', :action => 'logout' }, :class => "icon icon-user" %></li>
56 56 <% else %>
57 57 <li class="right"><%= link_to l(:label_login), { :controller => 'account', :action => 'login' }, :class => "icon icon-user" %></li>
58 58 <% end %>
59 59 </ul>
60 60 </div>
61 61
62 62 <% if admin_loggedin? %>
63 63 <div id="menuAdmin" class="menu" onmouseover="menuMouseover(event)">
64 64 <a class="menuItem" href="<%= url_for :controller => 'admin', :action => 'projects' %>" onmouseover="menuItemMouseover(event,'menuProjects');"><span class="menuItemText"><%=l(:label_project_plural)%></span><span class="menuItemArrow">&#9654;</span></a>
65 65 <a class="menuItem" href="<%= url_for :controller => 'users' %>" onmouseover="menuItemMouseover(event,'menuUsers');"><span class="menuItemText"><%=l(:label_user_plural)%></span><span class="menuItemArrow">&#9654;</span></a>
66 66 <%= link_to l(:label_role_and_permissions), {:controller => 'roles' }, :class => "menuItem" %>
67 67 <a class="menuItem" href="<%= url_for :controller => 'trackers' %>" onmouseover="menuItemMouseover(event,'menuTrackers');"><span class="menuItemText"><%=l(:label_tracker_plural)%></span><span class="menuItemArrow">&#9654;</span></a>
68 68 <%= link_to l(:label_custom_field_plural), {:controller => 'custom_fields' }, :class => "menuItem" %>
69 69 <%= link_to l(:label_enumerations), {:controller => 'enumerations' }, :class => "menuItem" %>
70 70 <%= link_to l(:field_mail_notification), {:controller => 'admin', :action => 'mail_options' }, :class => "menuItem" %>
71 71 <%= link_to l(:label_authentication), {:controller => 'auth_sources' }, :class => "menuItem" %>
72 72 <%= link_to l(:label_settings), {:controller => 'settings' }, :class => "menuItem" %>
73 73 <%= link_to l(:label_information_plural), {:controller => 'admin', :action => 'info' }, :class => "menuItem" %>
74 74 </div>
75 75 <div id="menuTrackers" class="menu">
76 76 <%= link_to l(:label_issue_status_plural), {:controller => 'issue_statuses' }, :class => "menuItem" %>
77 77 <%= link_to l(:label_workflow), {:controller => 'roles', :action => 'workflow' }, :class => "menuItem" %>
78 78 </div>
79 79 <div id="menuProjects" class="menu"><%= link_to l(:label_new), {:controller => 'projects', :action => 'add' }, :class => "menuItem" %></div>
80 80 <div id="menuUsers" class="menu"><%= link_to l(:label_new), {:controller => 'users', :action => 'add' }, :class => "menuItem" %></a></div>
81 81 <% end %>
82 82
83 83 <% unless @project.nil? || @project.id.nil? %>
84 84 <div id="menuProject" class="menu" onmouseover="menuMouseover(event)">
85 85 <%= link_to l(:label_calendar), {:controller => 'projects', :action => 'calendar', :id => @project }, :class => "menuItem" %>
86 86 <%= link_to l(:label_gantt), {:controller => 'projects', :action => 'gantt', :id => @project }, :class => "menuItem" %>
87 87 <%= link_to l(:label_issue_plural), {:controller => 'projects', :action => 'list_issues', :id => @project, :set_filter => 1 }, :class => "menuItem" %>
88 88 <%= link_to l(:label_report_plural), {:controller => 'reports', :action => 'issue_report', :id => @project }, :class => "menuItem" %>
89 89 <%= link_to l(:label_activity), {:controller => 'projects', :action => 'activity', :id => @project }, :class => "menuItem" %>
90 90 <%= link_to l(:label_news_plural), {:controller => 'projects', :action => 'list_news', :id => @project }, :class => "menuItem" %>
91 91 <%= link_to l(:label_change_log), {:controller => 'projects', :action => 'changelog', :id => @project }, :class => "menuItem" %>
92 92 <%= link_to l(:label_roadmap), {:controller => 'projects', :action => 'roadmap', :id => @project }, :class => "menuItem" %>
93 93 <%= link_to l(:label_document_plural), {:controller => 'projects', :action => 'list_documents', :id => @project }, :class => "menuItem" %>
94 <%= link_to l(:label_wiki), {:controller => 'wiki', :id => @project, :page => nil }, :class => "menuItem" if @project.wiki and !@project.wiki.new_record? %>
94 95 <%= link_to l(:label_member_plural), {:controller => 'projects', :action => 'list_members', :id => @project }, :class => "menuItem" %>
95 96 <%= link_to l(:label_attachment_plural), {:controller => 'projects', :action => 'list_files', :id => @project }, :class => "menuItem" %>
96 97 <%= link_to l(:label_search), {:controller => 'projects', :action => 'search', :id => @project }, :class => "menuItem" %>
97 98 <%= link_to l(:label_repository), {:controller => 'repositories', :action => 'show', :id => @project}, :class => "menuItem" if @project.repository and !@project.repository.new_record? %>
98 99 <%= link_to_if_authorized l(:label_settings), {:controller => 'projects', :action => 'settings', :id => @project }, :class => "menuItem" %>
99 100 </div>
100 101 <% end %>
101 102
102 103
103 104 <div id="subcontent">
104 105
105 106 <% unless @project.nil? || @project.id.nil? %>
106 107 <h2><%= @project.name %></h2>
107 108 <ul class="menublock">
108 109 <li><%= link_to l(:label_overview), :controller => 'projects', :action => 'show', :id => @project %></li>
109 110 <li><%= link_to l(:label_calendar), :controller => 'projects', :action => 'calendar', :id => @project %></li>
110 111 <li><%= link_to l(:label_gantt), :controller => 'projects', :action => 'gantt', :id => @project %></li>
111 112 <li><%= link_to l(:label_issue_plural), :controller => 'projects', :action => 'list_issues', :id => @project, :set_filter => 1 %></li>
112 113 <li><%= link_to l(:label_report_plural), :controller => 'reports', :action => 'issue_report', :id => @project %></li>
113 114 <li><%= link_to l(:label_activity), :controller => 'projects', :action => 'activity', :id => @project %></li>
114 115 <li><%= link_to l(:label_news_plural), :controller => 'projects', :action => 'list_news', :id => @project %></li>
115 116 <li><%= link_to l(:label_change_log), :controller => 'projects', :action => 'changelog', :id => @project %></li>
116 117 <li><%= link_to l(:label_roadmap), :controller => 'projects', :action => 'roadmap', :id => @project %></li>
117 118 <li><%= link_to l(:label_document_plural), :controller => 'projects', :action => 'list_documents', :id => @project %></li>
119 <li><%= link_to l(:label_wiki), :controller => 'wiki', :id => @project, :page => nil if @project.wiki and !@project.wiki.new_record? %></li>
118 120 <li><%= link_to l(:label_member_plural), :controller => 'projects', :action => 'list_members', :id => @project %></li>
119 121 <li><%= link_to l(:label_attachment_plural), :controller => 'projects', :action => 'list_files', :id => @project %></li>
120 122 <li><%= link_to l(:label_search), :controller => 'projects', :action => 'search', :id => @project %></li>
121 123 <li><%= link_to l(:label_repository), :controller => 'repositories', :action => 'show', :id => @project if @project.repository and !@project.repository.new_record? %></li>
122 124 <li><%= link_to_if_authorized l(:label_settings), :controller => 'projects', :action => 'settings', :id => @project %></li>
123 125 </ul>
124 126 <% end %>
125 127
126 128 <% if loggedin? and @logged_in_user.memberships.length > 0 %>
127 129 <h2><%=l(:label_my_projects) %></h2>
128 130 <ul class="menublock">
129 131 <% for membership in @logged_in_user.memberships %>
130 132 <li><%= link_to membership.project.name, :controller => 'projects', :action => 'show', :id => membership.project %></li>
131 133 <% end %>
132 134 </ul>
133 135 <% end %>
134 136 </div>
135 137
136 138 <div id="content">
137 139 <% if flash[:notice] %><p style="color: green"><%= flash[:notice] %></p><% end %>
138 140 <%= @content_for_layout %>
139 141 </div>
140 142
141 143 <div id="footer">
142 144 <p><a href="http://redmine.rubyforge.org/">redMine</a> <small><%= Redmine::VERSION %> &copy 2006-2007 Jean-Philippe Lang</small></p>
143 145 </div>
144 146
145 147 </div>
146 148 </body>
147 149 </html> No newline at end of file
@@ -1,46 +1,60
1 1 <%= error_messages_for 'project' %>
2 2
3 3 <div class="box">
4 4 <!--[form:project]-->
5 5 <p><%= f.text_field :name, :required => true %></p>
6 6
7 7 <% if admin_loggedin? and !@root_projects.empty? %>
8 8 <p><%= f.select :parent_id, (@root_projects.collect {|p| [p.name, p.id]}), { :include_blank => true } %></p>
9 9 <% end %>
10 10
11 11 <p><%= f.text_area :description, :required => true, :cols => 60, :rows => 3 %></p>
12 12 <p><%= f.text_field :homepage, :size => 40 %></p>
13 13 <p><%= f.check_box :is_public %></p>
14 14
15 15 <% for @custom_value in @custom_values %>
16 16 <p><%= custom_field_tag_with_label @custom_value %></p>
17 17 <% end %>
18 18
19 19 <% unless @custom_fields.empty? %>
20 20 <p><label><%=l(:label_custom_field_plural)%></label>
21 21 <% for custom_field in @custom_fields %>
22 22 <%= check_box_tag "custom_field_ids[]", custom_field.id, ((@project.custom_fields.include? custom_field) or custom_field.is_for_all?), (custom_field.is_for_all? ? {:disabled => "disabled"} : {}) %>
23 23 <%= custom_field.name %>
24 24 <% end %></p>
25 25 <% end %>
26 26 <!--[eoform:project]-->
27 27 </div>
28 28
29 29 <div class="box"><h3><%= check_box_tag "repository_enabled", 1, !@project.repository.nil?, :onclick => "Element.toggle('repository');" %> <%= l(:label_repository) %></h3>
30 30 <%= hidden_field_tag "repository_enabled", 0 %>
31 31 <div id="repository">
32 32 <% fields_for :repository, @project.repository, { :builder => TabularFormBuilder, :lang => current_language} do |repository| %>
33 33 <p><%= repository.text_field :url, :size => 60, :required => true %><br />(http://, https://, svn://, file:///)</p>
34 34 <p><%= repository.text_field :login, :size => 30 %></p>
35 35 <p><%= repository.password_field :password, :size => 30 %></p>
36 36 <% end %>
37 37 </div>
38 38 <%= javascript_tag "Element.hide('repository');" if @project.repository.nil? %>
39 39 </div>
40 40
41 <div class="box">
42 <h3><%= check_box_tag "wiki_enabled", 1, !@project.wiki.nil?, :onclick => "Element.toggle('wiki');" %> <%= l(:label_wiki) %></h3>
43 <%= hidden_field_tag "wiki_enabled", 0 %>
44 <div id="wiki">
45 <% fields_for :wiki, @project.wiki, { :builder => TabularFormBuilder, :lang => current_language} do |wiki| %>
46 <p><%= wiki.text_field :start_page, :size => 60, :required => true %></p>
47 <% # content_tag("div", "", :id => "wiki_start_page_auto_complete", :class => "auto_complete") +
48 # auto_complete_field("wiki_start_page", { :url => { :controller => 'wiki', :action => 'auto_complete_for_wiki_page', :id => @project } })
49 %>
50 <% end %>
51 </div>
52 <%= javascript_tag "Element.hide('wiki');" if @project.wiki.nil? %>
53 </div>
54
41 55 <% content_for :header_tags do %>
42 56 <%= javascript_include_tag 'calendar/calendar' %>
43 57 <%= javascript_include_tag "calendar/lang/calendar-#{current_language}.js" %>
44 58 <%= javascript_include_tag 'calendar/calendar-setup' %>
45 59 <%= stylesheet_link_tag 'calendar' %>
46 60 <% end %> No newline at end of file
@@ -1,43 +1,46
1 1 <h2><%= l(:label_settings) %></h2>
2 2
3 3 <% form_tag({:action => 'edit'}, :class => "tabular") do %>
4 4 <div class="box">
5 5 <p><label><%= l(:setting_app_title) %></label>
6 6 <%= text_field_tag 'settings[app_title]', Setting.app_title, :size => 30 %></p>
7 7
8 8 <p><label><%= l(:setting_app_subtitle) %></label>
9 9 <%= text_field_tag 'settings[app_subtitle]', Setting.app_subtitle, :size => 60 %></p>
10 10
11 11 <p><label><%= l(:setting_welcome_text) %></label>
12 12 <%= text_area_tag 'settings[welcome_text]', Setting.welcome_text, :cols => 60, :rows => 5 %></p>
13 13
14 14 <p><label><%= l(:setting_default_language) %></label>
15 15 <%= select_tag 'settings[default_language]', options_for_select( lang_options_for_select(false), Setting.default_language) %></p>
16 16
17 17 <p><label><%= l(:setting_login_required) %></label>
18 18 <%= check_box_tag 'settings[login_required]', 1, Setting.login_required? %><%= hidden_field_tag 'settings[login_required]', 0 %></p>
19 19
20 20 <p><label><%= l(:setting_self_registration) %></label>
21 21 <%= check_box_tag 'settings[self_registration]', 1, Setting.self_registration? %><%= hidden_field_tag 'settings[self_registration]', 0 %></p>
22 22
23 23 <p><label><%= l(:label_password_lost) %></label>
24 24 <%= check_box_tag 'settings[lost_password]', 1, Setting.lost_password? %><%= hidden_field_tag 'settings[lost_password]', 0 %></p>
25 25
26 26 <p><label><%= l(:setting_attachment_max_size) %></label>
27 27 <%= text_field_tag 'settings[attachment_max_size]', Setting.attachment_max_size, :size => 6 %> KB</p>
28 28
29 29 <p><label><%= l(:setting_issues_export_limit) %></label>
30 30 <%= text_field_tag 'settings[issues_export_limit]', Setting.issues_export_limit, :size => 6 %></p>
31 31
32 32 <p><label><%= l(:setting_mail_from) %></label>
33 33 <%= text_field_tag 'settings[mail_from]', Setting.mail_from, :size => 60 %></p>
34 34
35 35 <p><label><%= l(:setting_host_name) %></label>
36 36 <%= text_field_tag 'settings[host_name]', Setting.host_name, :size => 60 %></p>
37 37
38 38 <p><label><%= l(:setting_text_formatting) %></label>
39 39 <%= select_tag 'settings[text_formatting]', options_for_select( [[l(:label_none), 0], ["textile", "textile"]], Setting.text_formatting) %></p>
40 40
41 <p><label><%= l(:setting_wiki_compression) %></label>
42 <%= select_tag 'settings[wiki_compression]', options_for_select( [[l(:label_none), 0], ["gzip", "gzip"]], Setting.wiki_compression) %></p>
43
41 44 </div>
42 45 <%= submit_tag l(:button_save) %>
43 46 <% end %> No newline at end of file
@@ -1,24 +1,25
1 1 ActionController::Routing::Routes.draw do |map|
2 2 # Add your own custom routes here.
3 3 # The priority is based upon order of creation: first created -> highest priority.
4 4
5 5 # Here's a sample route:
6 6 # map.connect 'products/:id', :controller => 'catalog', :action => 'view'
7 7 # Keep in mind you can assign values other than :controller and :action
8 8
9 9 # You can have the root of your site routed by hooking up ''
10 10 # -- just remember to delete public/index.html.
11 11 map.connect '', :controller => "welcome"
12 12
13 map.connect 'wiki/:id/:page/:action', :controller => 'wiki', :page => nil
13 14 map.connect 'roles/workflow/:id/:role_id/:tracker_id', :controller => 'roles', :action => 'workflow'
14 15 map.connect 'help/:ctrl/:page', :controller => 'help'
15 16 #map.connect ':controller/:action/:id/:sort_key/:sort_order'
16 17
17 18 # Allow downloading Web Service WSDL as a file with an extension
18 19 # instead of a file named 'wsdl'
19 20 map.connect ':controller/service.wsdl', :action => 'wsdl'
20 21
21 22
22 23 # Install the default route as the lowest priority.
23 24 map.connect ':controller/:action/:id'
24 25 end
@@ -1,47 +1,49
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18
19 19 # DO NOT MODIFY THIS FILE !!!
20 20 # Settings can be defined through the application in Admin -> Settings
21 21
22 22 app_title:
23 23 default: redMine
24 24 app_subtitle:
25 25 default: Project management
26 26 welcome_text:
27 27 default:
28 28 login_required:
29 29 default: 0
30 30 self_registration:
31 31 default: 1
32 32 lost_password:
33 33 default: 1
34 34 attachment_max_size:
35 35 format: int
36 36 default: 5120
37 37 issues_export_limit:
38 38 format: int
39 39 default: 500
40 40 mail_from:
41 41 default: redmine@somenet.foo
42 42 text_formatting:
43 43 default: textile
44 wiki_compression:
45 default: ""
44 46 default_language:
45 47 default: en
46 48 host_name:
47 49 default: localhost:3000 No newline at end of file
@@ -1,384 +1,390
1 1 _gloc_rule_default: '|n| n==1 ? "" : "_plural" '
2 2
3 3 actionview_datehelper_select_day_prefix:
4 4 actionview_datehelper_select_month_names: January,February,March,April,May,June,July,August,September,October,November,December
5 5 actionview_datehelper_select_month_names_abbr: Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec
6 6 actionview_datehelper_select_month_prefix:
7 7 actionview_datehelper_select_year_prefix:
8 8 actionview_datehelper_time_in_words_day: 1 day
9 9 actionview_datehelper_time_in_words_day_plural: %d days
10 10 actionview_datehelper_time_in_words_hour_about: about an hour
11 11 actionview_datehelper_time_in_words_hour_about_plural: about %d hours
12 12 actionview_datehelper_time_in_words_hour_about_single: about an hour
13 13 actionview_datehelper_time_in_words_minute: 1 minute
14 14 actionview_datehelper_time_in_words_minute_half: half a minute
15 15 actionview_datehelper_time_in_words_minute_less_than: less than a minute
16 16 actionview_datehelper_time_in_words_minute_plural: %d minutes
17 17 actionview_datehelper_time_in_words_minute_single: 1 minute
18 18 actionview_datehelper_time_in_words_second_less_than: less than a second
19 19 actionview_datehelper_time_in_words_second_less_than_plural: less than %d seconds
20 20 actionview_instancetag_blank_option: Bitte auserwählt
21 21
22 22 activerecord_error_inclusion: ist nicht in der Liste eingeschlossen
23 23 activerecord_error_exclusion: ist reserviert
24 24 activerecord_error_invalid: ist unzulässig
25 25 activerecord_error_confirmation: bringt nicht Bestätigung zusammen
26 26 activerecord_error_accepted: muß angenommen werden
27 27 activerecord_error_empty: kann nicht leer sein
28 28 activerecord_error_blank: kann nicht leer sein
29 29 activerecord_error_too_long: ist zu lang
30 30 activerecord_error_too_short: ist zu kurz
31 31 activerecord_error_wrong_length: ist die falsche Länge
32 32 activerecord_error_taken: ist bereits genommen worden
33 33 activerecord_error_not_a_number: ist nicht eine Zahl
34 34 activerecord_error_not_a_date: ist nicht ein gültiges Datum
35 35 activerecord_error_greater_than_start_date: muß als grösser sein beginnen Datum
36 36
37 37 general_fmt_age: %d yr
38 38 general_fmt_age_plural: %d yrs
39 39 general_fmt_date: %%b %%d, %%Y (%%a)
40 40 general_fmt_datetime: %%b %%d, %%Y (%%a), %%I:%%M %%p
41 41 general_fmt_datetime_short: %%b %%d, %%I:%%M %%p
42 42 general_fmt_time: %%I:%%M %%p
43 43 general_text_No: 'Nein'
44 44 general_text_Yes: 'Ja'
45 45 general_text_no: 'nein'
46 46 general_text_yes: 'ja'
47 47 general_lang_de: 'Deutsch'
48 48 general_csv_separator: ';'
49 49 general_csv_encoding: ISO-8859-1
50 50 general_pdf_encoding: ISO-8859-1
51 51 general_day_names: Montag,Dienstag,Mittwoch,Donnerstag,Freitag,Samstag,Sonntag
52 52
53 53 notice_account_updated: Konto wurde erfolgreich aktualisiert.
54 54 notice_account_invalid_creditentials: Unzulässiger Benutzer oder Passwort
55 55 notice_account_password_updated: Passwort wurde erfolgreich aktualisiert.
56 56 notice_account_wrong_password: Falsches Passwort
57 57 notice_account_register_done: Konto wurde erfolgreich verursacht.
58 58 notice_account_unknown_email: Unbekannter Benutzer.
59 59 notice_can_t_change_password: Dieses Konto verwendet eine externe Authentisierung Quelle. Unmöglich, das Kennwort zu ändern.
60 60 notice_account_lost_email_sent: Ein email mit Anweisungen, ein neues Kennwort zu wählen ist dir geschickt worden.
61 61 notice_account_activated: Dein Konto ist aktiviert worden. Du kannst jetzt einloggen.
62 62 notice_successful_create: Erfolgreiche Kreation.
63 63 notice_successful_update: Erfolgreiches Update.
64 64 notice_successful_delete: Erfolgreiche Auslassung.
65 65 notice_successful_connection: Erfolgreicher Anschluß.
66 66 notice_file_not_found: Erbetene Akte besteht nicht oder ist gelöscht worden.
67 67 notice_locking_conflict: Data have been updated by another user.
68 68 notice_scm_error: Eintragung und/oder Neuausgabe besteht nicht im Behälter.
69 69
70 70 mail_subject_lost_password: Dein redMine Kennwort
71 71 mail_subject_register: redMine Kontoaktivierung
72 72
73 73 gui_validation_error: 1 Störung
74 74 gui_validation_error_plural: %d Störungen
75 75
76 76 field_name: Name
77 77 field_description: Beschreibung
78 78 field_summary: Zusammenfassung
79 79 field_is_required: Erforderlich
80 80 field_firstname: Vorname
81 81 field_lastname: Nachname
82 82 field_mail: Email
83 83 field_filename: Datei
84 84 field_filesize: Grootte
85 85 field_downloads: Downloads
86 86 field_author: Autor
87 87 field_created_on: Angelegt
88 88 field_updated_on: aktualisiert
89 89 field_field_format: Format
90 90 field_is_for_all: Für alle Projekte
91 91 field_possible_values: Mögliche Werte
92 92 field_regexp: Regulärer Ausdruck
93 93 field_min_length: Minimale Länge
94 94 field_max_length: Maximale Länge
95 95 field_value: Wert
96 96 field_category: Kategorie
97 97 field_title: Títel
98 98 field_project: Projekt
99 99 field_issue: Antrag
100 100 field_status: Status
101 101 field_notes: Anmerkungen
102 102 field_is_closed: Problem erledigt
103 103 field_is_default: Rückstellung status
104 104 field_html_color: Farbe
105 105 field_tracker: Tracker
106 106 field_subject: Thema
107 107 field_due_date: Abgabedatum
108 108 field_assigned_to: Zugewiesen an
109 109 field_priority: Priorität
110 110 field_fixed_version: Erledigt in Version
111 111 field_user: Benutzer
112 112 field_role: Rolle
113 113 field_homepage: Startseite
114 114 field_is_public: Öffentlich
115 115 field_parent: Subprojekt von
116 116 field_is_in_chlog: Ansicht der Issues in der Historie
117 117 field_is_in_roadmap: Ansicht der Issues in der Roadmap
118 118 field_login: Mitgliedsname
119 119 field_mail_notification: Mailbenachrichtigung
120 120 field_admin: Administrator
121 121 field_locked: Gesperrt
122 122 field_last_login_on: Letzte Anmeldung
123 123 field_language: Sprache
124 124 field_effective_date: Datum
125 125 field_password: Passwort
126 126 field_new_password: Neues Passwort
127 127 field_password_confirmation: Bestätigung
128 128 field_version: Version
129 129 field_type: Typ
130 130 field_host: Host
131 131 field_port: Port
132 132 field_account: Konto
133 133 field_base_dn: Base DN
134 134 field_attr_login: Mitgliedsnameattribut
135 135 field_attr_firstname: Vornamensattribut
136 136 field_attr_lastname: Namenattribut
137 137 field_attr_mail: Emailattribut
138 138 field_onthefly: On-the-fly Benutzerkreation
139 139 field_start_date: Beginn
140 140 field_done_ratio: %% Getan
141 141 field_auth_source: Authentisierung Modus
142 142 field_hide_mail: Mein email address verstecken
143 143 field_comment: Anmerkung
144 144 field_url: URL
145 field_start_page: Hauptseite
145 146
146 147 setting_app_title: Applikation Titel
147 148 setting_app_subtitle: Applikation Untertitel
148 149 setting_welcome_text: Willkommener Text
149 150 setting_default_language: Rückstellung Sprache
150 151 setting_login_required: Authent. erfordert
151 152 setting_self_registration: Selbstausrichtung ermöglicht
152 153 setting_attachment_max_size: Dateimaximumgröße
153 154 setting_issues_export_limit: Issues export limit
154 155 setting_mail_from: Emission address
155 156 setting_host_name: Host Name
156 157 setting_text_formatting: Textformatierung
158 setting_wiki_compression: Wiki Geschichte Kompression
157 159
158 160 label_user: Benutzer
159 161 label_user_plural: Benutzer
160 162 label_user_new: Neuer Benutzer
161 163 label_project: Projekt
162 164 label_project_new: Neues Projekt
163 165 label_project_plural: Projekte
164 166 label_project_latest: Neueste Projekte
165 167 label_issue: Antrag
166 168 label_issue_new: Neue Antrag
167 169 label_issue_plural: Anträge
168 170 label_issue_view_all: Alle Anträge ansehen
169 171 label_document: Dokument
170 172 label_document_new: Neues Dokument
171 173 label_document_plural: Dokumente
172 174 label_role: Rolle
173 175 label_role_plural: Rollen
174 176 label_role_new: Neue Rolle
175 177 label_role_and_permissions: Rollen und Rechte
176 178 label_member: Mitglied
177 179 label_member_new: Neues Mitglied
178 180 label_member_plural: Mitglieder
179 181 label_tracker: Tracker
180 182 label_tracker_plural: Tracker
181 183 label_tracker_new: Neuer Tracker
182 184 label_workflow: Workflow
183 185 label_issue_status: Antrag Status
184 186 label_issue_status_plural: Antrag Stati
185 187 label_issue_status_new: Neuer Status
186 188 label_issue_category: Antrag Kategorie
187 189 label_issue_category_plural: Antrag Kategorien
188 190 label_issue_category_new: Neue Kategorie
189 191 label_custom_field: Benutzerdefiniertes Feld
190 192 label_custom_field_plural: Benutzerdefinierte Felder
191 193 label_custom_field_new: Neues Feld
192 194 label_enumerations: Enumerationen
193 195 label_enumeration_new: Neuer Wert
194 196 label_information: Information
195 197 label_information_plural: Informationen
196 198 label_please_login: Anmelden
197 199 label_register: Anmelden
198 200 label_password_lost: Passwort vergessen
199 201 label_home: Hauptseite
200 202 label_my_page: Meine Seite
201 203 label_my_account: Mein Konto
202 204 label_my_projects: Meine Projekte
203 205 label_administration: Administration
204 206 label_login: Einloggen
205 207 label_logout: Abmelden
206 208 label_help: Hilfe
207 209 label_reported_issues: Gemeldete Issues
208 210 label_assigned_to_me_issues: Mir zugewiesen
209 211 label_last_login: Letzte Anmeldung
210 212 label_last_updates: Letztes aktualisiertes
211 213 label_last_updates_plural: %d Letztes aktualisiertes
212 214 label_registered_on: Angemeldet am
213 215 label_activity: Aktivität
214 216 label_new: Neue
215 217 label_logged_as: Angemeldet als
216 218 label_environment: Environment
217 219 label_authentication: Authentisierung
218 220 label_auth_source: Authentisierung Modus
219 221 label_auth_source_new: Neuer Authentisierung Modus
220 222 label_auth_source_plural: Authentisierung Modi
221 223 label_subproject: Vorprojekt von
222 224 label_subproject_plural: Vorprojekte
223 225 label_min_max_length: Min - Max Länge
224 226 label_list: Liste
225 227 label_date: Date
226 228 label_integer: Zahl
227 229 label_boolean: Boolesch
228 230 label_string: Text
229 231 label_text: Langer Text
230 232 label_attribute: Attribut
231 233 label_attribute_plural: Attribute
232 234 label_download: %d Herunterlade
233 235 label_download_plural: %d Herunterlade
234 236 label_no_data: Nichts anzuzeigen
235 237 label_change_status: Statuswechsel
236 238 label_history: Historie
237 239 label_attachment: Datei
238 240 label_attachment_new: Neue Datei
239 241 label_attachment_delete: Löschungakten
240 242 label_attachment_plural: Dateien
241 243 label_report: Bericht
242 244 label_report_plural: Berichte
243 245 label_news: Neuigkeit
244 246 label_news_new: Neuigkeite addieren
245 247 label_news_plural: Neuigkeiten
246 248 label_news_latest: Letzte Neuigkeiten
247 249 label_news_view_all: Alle Neuigkeiten anzeigen
248 250 label_change_log: Change log
249 251 label_settings: Konfiguration
250 252 label_overview: Übersicht
251 253 label_version: Version
252 254 label_version_new: Neue Version
253 255 label_version_plural: Versionen
254 256 label_confirmation: Bestätigung
255 257 label_export_to: Export zu
256 258 label_read: Lesen...
257 259 label_public_projects: Öffentliche Projekte
258 260 label_open_issues: geöffnet
259 261 label_open_issues_plural: geöffnet
260 262 label_closed_issues: geschlossen
261 263 label_closed_issues_plural: geschlossen
262 264 label_total: Gesamtzahl
263 265 label_permissions: Berechtigungen
264 266 label_current_status: Gegenwärtiger Status
265 267 label_new_statuses_allowed: Neue Status gewährten
266 268 label_all: alle
267 269 label_none: kein
268 270 label_next: Weiter
269 271 label_previous: Zurück
270 272 label_used_by: Benutzt von
271 273 label_details: Details...
272 274 label_add_note: Eine Anmerkung addieren
273 275 label_per_page: Pro Seite
274 276 label_calendar: Kalender
275 277 label_months_from: Monate von
276 278 label_gantt: Gantt
277 279 label_internal: Intern
278 280 label_last_changes: %d änderungen des Letzten
279 281 label_change_view_all: Alle änderungen ansehen
280 282 label_personalize_page: Diese Seite personifizieren
281 283 label_comment: Anmerkung
282 284 label_comment_plural: Anmerkungen
283 285 label_comment_add: Anmerkung addieren
284 286 label_comment_added: Anmerkung fügte hinzu
285 287 label_comment_delete: Anmerkungen löschen
286 288 label_query: Benutzerdefiniertes Frage
287 289 label_query_plural: Benutzerdefinierte Fragen
288 290 label_query_new: Neue Frage
289 291 label_filter_add: Filter addieren
290 292 label_filter_plural: Filter
291 293 label_equals: ist
292 294 label_not_equals: ist nicht
293 295 label_in_less_than: an weniger als
294 296 label_in_more_than: an mehr als
295 297 label_in: an
296 298 label_today: heute
297 299 label_less_than_ago: vor weniger als
298 300 label_more_than_ago: vor mehr als
299 301 label_ago: vor
300 302 label_contains: enthält
301 303 label_not_contains: enthält nicht
302 304 label_day_plural: Tage
303 305 label_repository: SVN Behälter
304 306 label_browse: Grasen
305 307 label_modification: %d änderung
306 308 label_modification_plural: %d änderungen
307 309 label_revision: Neuausgabe
308 310 label_revision_plural: Neuausgaben
309 311 label_added: hinzugefügt
310 312 label_modified: geändert
311 313 label_deleted: gelöscht
312 314 label_latest_revision: Neueste Neuausgabe
313 315 label_view_revisions: Die Neuausgaben ansehen
314 316 label_max_size: Maximale Größe
315 317 label_on: auf
316 318 label_sort_highest: Erste
317 319 label_sort_higher: Aufzurichten
318 320 label_sort_lower: Herabzusteigen
319 321 label_sort_lowest: Letzter
320 322 label_roadmap: Roadmap
321 323 label_search: Suche
322 324 label_result: %d Resultat
323 325 label_result_plural: %d Resultate
324 326 label_all_words: Alle Wörter
327 label_wiki: Wiki
328 label_page_index: Index
329 label_current_version: Gegenwärtige Version
330 label_preview: Vorbetrachtung
325 331
326 332 button_login: Einloggen
327 333 button_submit: Einreichen
328 334 button_save: Speichern
329 335 button_check_all: Alles auswählen
330 336 button_uncheck_all: Alles abwählen
331 337 button_delete: Löschen
332 338 button_create: Anlegen
333 339 button_test: Testen
334 340 button_edit: Bearbeiten
335 341 button_add: Hinzufügen
336 342 button_change: Wechseln
337 343 button_apply: Anwenden
338 344 button_clear: Zurücksetzen
339 345 button_lock: Verriegeln
340 346 button_unlock: Entriegeln
341 347 button_download: Fernzuladen
342 348 button_list: Aufzulisten
343 349 button_view: Siehe
344 350 button_move: Bewegen
345 351 button_back: Rückkehr
346 352 button_cancel: Annullieren
347 353 button_activate: Aktivieren
348 354 button_sort: Sortieren
349 355
350 356 text_select_mail_notifications: Aktionen für die Mailbenachrichtigung aktiviert werden soll.
351 357 text_regexp_info: eg. ^[A-Z0-9]+$
352 358 text_min_max_length_info: 0 heisst keine Beschränkung
353 359 text_project_destroy_confirmation: Sind sie sicher, daß sie das Projekt löschen wollen ?
354 360 text_workflow_edit: Auswahl Workflow zum Bearbeiten
355 361 text_are_you_sure: Sind sie sicher ?
356 362 text_journal_changed: geändert von %s zu %s
357 363 text_journal_set_to: gestellt zu %s
358 364 text_journal_deleted: gelöscht
359 365 text_tip_task_begin_day: Aufgabe, die an diesem Tag beginnt
360 366 text_tip_task_end_day: Aufgabe, die an diesem Tag beendet
361 367 text_tip_task_begin_end_day: Aufgabe, die an diesem Tag beginnt und beendet
362 368
363 369 default_role_manager: Manager
364 370 default_role_developper: Developer
365 371 default_role_reporter: Reporter
366 372 default_tracker_bug: Fehler
367 373 default_tracker_feature: Feature
368 374 default_tracker_support: Support
369 375 default_issue_status_new: Neu
370 376 default_issue_status_assigned: Zugewiesen
371 377 default_issue_status_resolved: Gelöst
372 378 default_issue_status_feedback: Feedback
373 379 default_issue_status_closed: Erledigt
374 380 default_issue_status_rejected: Abgewiesen
375 381 default_doc_category_user: Benutzerdokumentation
376 382 default_doc_category_tech: Technische Dokumentation
377 383 default_priority_low: Niedrig
378 384 default_priority_normal: Normal
379 385 default_priority_high: Hoch
380 386 default_priority_urgent: Dringend
381 387 default_priority_immediate: Sofort
382 388
383 389 enumeration_issue_priorities: Issue-Prioritäten
384 390 enumeration_doc_categories: Dokumentenkategorien
@@ -1,384 +1,390
1 1 _gloc_rule_default: '|n| n==1 ? "" : "_plural" '
2 2
3 3 actionview_datehelper_select_day_prefix:
4 4 actionview_datehelper_select_month_names: January,February,March,April,May,June,July,August,September,October,November,December
5 5 actionview_datehelper_select_month_names_abbr: Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec
6 6 actionview_datehelper_select_month_prefix:
7 7 actionview_datehelper_select_year_prefix:
8 8 actionview_datehelper_time_in_words_day: 1 day
9 9 actionview_datehelper_time_in_words_day_plural: %d days
10 10 actionview_datehelper_time_in_words_hour_about: about an hour
11 11 actionview_datehelper_time_in_words_hour_about_plural: about %d hours
12 12 actionview_datehelper_time_in_words_hour_about_single: about an hour
13 13 actionview_datehelper_time_in_words_minute: 1 minute
14 14 actionview_datehelper_time_in_words_minute_half: half a minute
15 15 actionview_datehelper_time_in_words_minute_less_than: less than a minute
16 16 actionview_datehelper_time_in_words_minute_plural: %d minutes
17 17 actionview_datehelper_time_in_words_minute_single: 1 minute
18 18 actionview_datehelper_time_in_words_second_less_than: less than a second
19 19 actionview_datehelper_time_in_words_second_less_than_plural: less than %d seconds
20 20 actionview_instancetag_blank_option: Please select
21 21
22 22 activerecord_error_inclusion: is not included in the list
23 23 activerecord_error_exclusion: is reserved
24 24 activerecord_error_invalid: is invalid
25 25 activerecord_error_confirmation: doesn't match confirmation
26 26 activerecord_error_accepted: must be accepted
27 27 activerecord_error_empty: can't be empty
28 28 activerecord_error_blank: can't be blank
29 29 activerecord_error_too_long: is too long
30 30 activerecord_error_too_short: is too short
31 31 activerecord_error_wrong_length: is the wrong length
32 32 activerecord_error_taken: has already been taken
33 33 activerecord_error_not_a_number: is not a number
34 34 activerecord_error_not_a_date: is not a valid date
35 35 activerecord_error_greater_than_start_date: must be greater than start date
36 36
37 37 general_fmt_age: %d yr
38 38 general_fmt_age_plural: %d yrs
39 39 general_fmt_date: %%m/%%d/%%Y
40 40 general_fmt_datetime: %%m/%%d/%%Y %%I:%%M %%p
41 41 general_fmt_datetime_short: %%b %%d, %%I:%%M %%p
42 42 general_fmt_time: %%I:%%M %%p
43 43 general_text_No: 'No'
44 44 general_text_Yes: 'Yes'
45 45 general_text_no: 'no'
46 46 general_text_yes: 'yes'
47 47 general_lang_en: 'English'
48 48 general_csv_separator: ','
49 49 general_csv_encoding: ISO-8859-1
50 50 general_pdf_encoding: ISO-8859-1
51 51 general_day_names: Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday
52 52
53 53 notice_account_updated: Account was successfully updated.
54 54 notice_account_invalid_creditentials: Invalid user or password
55 55 notice_account_password_updated: Password was successfully updated.
56 56 notice_account_wrong_password: Wrong password
57 57 notice_account_register_done: Account was successfully created.
58 58 notice_account_unknown_email: Unknown user.
59 59 notice_can_t_change_password: This account uses an external authentication source. Impossible to change the password.
60 60 notice_account_lost_email_sent: An email with instructions to choose a new password has been sent to you.
61 61 notice_account_activated: Your account has been activated. You can now log in.
62 62 notice_successful_create: Successful creation.
63 63 notice_successful_update: Successful update.
64 64 notice_successful_delete: Successful deletion.
65 65 notice_successful_connection: Successful connection.
66 66 notice_file_not_found: The page you were trying to access doesn't exist or has been removed.
67 67 notice_locking_conflict: Data have been updated by another user.
68 68 notice_scm_error: Entry and/or revision doesn't exist in the repository.
69 69
70 70 mail_subject_lost_password: Your redMine password
71 71 mail_subject_register: redMine account activation
72 72
73 73 gui_validation_error: 1 error
74 74 gui_validation_error_plural: %d errors
75 75
76 76 field_name: Name
77 77 field_description: Description
78 78 field_summary: Summary
79 79 field_is_required: Required
80 80 field_firstname: Firstname
81 81 field_lastname: Lastname
82 82 field_mail: Email
83 83 field_filename: File
84 84 field_filesize: Size
85 85 field_downloads: Downloads
86 86 field_author: Author
87 87 field_created_on: Created
88 88 field_updated_on: Updated
89 89 field_field_format: Format
90 90 field_is_for_all: For all projects
91 91 field_possible_values: Possible values
92 92 field_regexp: Regular expression
93 93 field_min_length: Minimum length
94 94 field_max_length: Maximum length
95 95 field_value: Value
96 96 field_category: Category
97 97 field_title: Title
98 98 field_project: Project
99 99 field_issue: Issue
100 100 field_status: Status
101 101 field_notes: Notes
102 102 field_is_closed: Issue closed
103 103 field_is_default: Default status
104 104 field_html_color: Color
105 105 field_tracker: Tracker
106 106 field_subject: Subject
107 107 field_due_date: Due date
108 108 field_assigned_to: Assigned to
109 109 field_priority: Priority
110 110 field_fixed_version: Fixed version
111 111 field_user: User
112 112 field_role: Role
113 113 field_homepage: Homepage
114 114 field_is_public: Public
115 115 field_parent: Subproject of
116 116 field_is_in_chlog: Issues displayed in changelog
117 117 field_is_in_roadmap: Issues displayed in roadmap
118 118 field_login: Login
119 119 field_mail_notification: Mail notifications
120 120 field_admin: Administrator
121 121 field_locked: Locked
122 122 field_last_login_on: Last connection
123 123 field_language: Language
124 124 field_effective_date: Date
125 125 field_password: Password
126 126 field_new_password: New password
127 127 field_password_confirmation: Confirmation
128 128 field_version: Version
129 129 field_type: Type
130 130 field_host: Host
131 131 field_port: Port
132 132 field_account: Account
133 133 field_base_dn: Base DN
134 134 field_attr_login: Login attribute
135 135 field_attr_firstname: Firstname attribute
136 136 field_attr_lastname: Lastname attribute
137 137 field_attr_mail: Email attribute
138 138 field_onthefly: On-the-fly user creation
139 139 field_start_date: Start
140 140 field_done_ratio: %% Done
141 141 field_auth_source: Authentication mode
142 142 field_hide_mail: Hide my email address
143 143 field_comment: Comment
144 144 field_url: URL
145 field_start_page: Start page
145 146
146 147 setting_app_title: Application title
147 148 setting_app_subtitle: Application subtitle
148 149 setting_welcome_text: Welcome text
149 150 setting_default_language: Default language
150 151 setting_login_required: Authent. required
151 152 setting_self_registration: Self-registration enabled
152 153 setting_attachment_max_size: Attachment max. size
153 154 setting_issues_export_limit: Issues export limit
154 155 setting_mail_from: Emission mail address
155 156 setting_host_name: Host name
156 157 setting_text_formatting: Text formatting
158 setting_wiki_compression: Wiki history compression
157 159
158 160 label_user: User
159 161 label_user_plural: Users
160 162 label_user_new: New user
161 163 label_project: Project
162 164 label_project_new: New project
163 165 label_project_plural: Projects
164 166 label_project_latest: Latest projects
165 167 label_issue: Issue
166 168 label_issue_new: New issue
167 169 label_issue_plural: Issues
168 170 label_issue_view_all: View all issues
169 171 label_document: Document
170 172 label_document_new: New document
171 173 label_document_plural: Documents
172 174 label_role: Role
173 175 label_role_plural: Roles
174 176 label_role_new: New role
175 177 label_role_and_permissions: Roles and permissions
176 178 label_member: Member
177 179 label_member_new: New member
178 180 label_member_plural: Members
179 181 label_tracker: Tracker
180 182 label_tracker_plural: Trackers
181 183 label_tracker_new: New tracker
182 184 label_workflow: Workflow
183 185 label_issue_status: Issue status
184 186 label_issue_status_plural: Issue statuses
185 187 label_issue_status_new: New status
186 188 label_issue_category: Issue category
187 189 label_issue_category_plural: Issue categories
188 190 label_issue_category_new: New category
189 191 label_custom_field: Custom field
190 192 label_custom_field_plural: Custom fields
191 193 label_custom_field_new: New custom field
192 194 label_enumerations: Enumerations
193 195 label_enumeration_new: New value
194 196 label_information: Information
195 197 label_information_plural: Information
196 198 label_please_login: Please login
197 199 label_register: Register
198 200 label_password_lost: Lost password
199 201 label_home: Home
200 202 label_my_page: My page
201 203 label_my_account: My account
202 204 label_my_projects: My projects
203 205 label_administration: Administration
204 206 label_login: Login
205 207 label_logout: Logout
206 208 label_help: Help
207 209 label_reported_issues: Reported issues
208 210 label_assigned_to_me_issues: Issues assigned to me
209 211 label_last_login: Last connection
210 212 label_last_updates: Last updated
211 213 label_last_updates_plural: %d last updated
212 214 label_registered_on: Registered on
213 215 label_activity: Activity
214 216 label_new: New
215 217 label_logged_as: Logged as
216 218 label_environment: Environment
217 219 label_authentication: Authentication
218 220 label_auth_source: Authentication mode
219 221 label_auth_source_new: New authentication mode
220 222 label_auth_source_plural: Authentication modes
221 223 label_subproject: Subproject
222 224 label_subproject_plural: Subprojects
223 225 label_min_max_length: Min - Max length
224 226 label_list: List
225 227 label_date: Date
226 228 label_integer: Integer
227 229 label_boolean: Boolean
228 230 label_string: Text
229 231 label_text: Long text
230 232 label_attribute: Attribute
231 233 label_attribute_plural: Attributes
232 234 label_download: %d Download
233 235 label_download_plural: %d Downloads
234 236 label_no_data: No data to display
235 237 label_change_status: Change status
236 238 label_history: History
237 239 label_attachment: File
238 240 label_attachment_new: New file
239 241 label_attachment_delete: Delete file
240 242 label_attachment_plural: Files
241 243 label_report: Report
242 244 label_report_plural: Reports
243 245 label_news: News
244 246 label_news_new: Add news
245 247 label_news_plural: News
246 248 label_news_latest: Latest news
247 249 label_news_view_all: View all news
248 250 label_change_log: Change log
249 251 label_settings: Settings
250 252 label_overview: Overview
251 253 label_version: Version
252 254 label_version_new: New version
253 255 label_version_plural: Versions
254 256 label_confirmation: Confirmation
255 257 label_export_to: Export to
256 258 label_read: Read...
257 259 label_public_projects: Public projects
258 260 label_open_issues: open
259 261 label_open_issues_plural: open
260 262 label_closed_issues: closed
261 263 label_closed_issues_plural: closed
262 264 label_total: Total
263 265 label_permissions: Permissions
264 266 label_current_status: Current status
265 267 label_new_statuses_allowed: New statuses allowed
266 268 label_all: all
267 269 label_none: none
268 270 label_next: Next
269 271 label_previous: Previous
270 272 label_used_by: Used by
271 273 label_details: Details...
272 274 label_add_note: Add a note
273 275 label_per_page: Per page
274 276 label_calendar: Calendar
275 277 label_months_from: months from
276 278 label_gantt: Gantt
277 279 label_internal: Internal
278 280 label_last_changes: last %d changes
279 281 label_change_view_all: View all changes
280 282 label_personalize_page: Personalize this page
281 283 label_comment: Comment
282 284 label_comment_plural: Comments
283 285 label_comment_add: Add a comment
284 286 label_comment_added: Comment added
285 287 label_comment_delete: Delete comments
286 288 label_query: Custom query
287 289 label_query_plural: Custom queries
288 290 label_query_new: New query
289 291 label_filter_add: Add filter
290 292 label_filter_plural: Filters
291 293 label_equals: is
292 294 label_not_equals: is not
293 295 label_in_less_than: in less than
294 296 label_in_more_than: in more than
295 297 label_in: in
296 298 label_today: today
297 299 label_less_than_ago: less than days ago
298 300 label_more_than_ago: more than days ago
299 301 label_ago: days ago
300 302 label_contains: contains
301 303 label_not_contains: doesn't contain
302 304 label_day_plural: days
303 305 label_repository: SVN Repository
304 306 label_browse: Browse
305 307 label_modification: %d change
306 308 label_modification_plural: %d changes
307 309 label_revision: Revision
308 310 label_revision_plural: Revisions
309 311 label_added: added
310 312 label_modified: modified
311 313 label_deleted: deleted
312 314 label_latest_revision: Latest revision
313 315 label_view_revisions: View revisions
314 316 label_max_size: Maximum size
315 317 label_on: 'on'
316 318 label_sort_highest: Move to top
317 319 label_sort_higher: Move up
318 320 label_sort_lower: Move down
319 321 label_sort_lowest: Move to bottom
320 322 label_roadmap: Roadmap
321 323 label_search: Search
322 324 label_result: %d result
323 325 label_result_plural: %d results
324 326 label_all_words: All words
327 label_wiki: Wiki
328 label_page_index: Index
329 label_current_version: Current version
330 label_preview: Preview
325 331
326 332 button_login: Login
327 333 button_submit: Submit
328 334 button_save: Save
329 335 button_check_all: Check all
330 336 button_uncheck_all: Uncheck all
331 337 button_delete: Delete
332 338 button_create: Create
333 339 button_test: Test
334 340 button_edit: Edit
335 341 button_add: Add
336 342 button_change: Change
337 343 button_apply: Apply
338 344 button_clear: Clear
339 345 button_lock: Lock
340 346 button_unlock: Unlock
341 347 button_download: Download
342 348 button_list: List
343 349 button_view: View
344 350 button_move: Move
345 351 button_back: Back
346 352 button_cancel: Cancel
347 353 button_activate: Activate
348 354 button_sort: Sort
349 355
350 356 text_select_mail_notifications: Select actions for which mail notifications should be sent.
351 357 text_regexp_info: eg. ^[A-Z0-9]+$
352 358 text_min_max_length_info: 0 means no restriction
353 359 text_project_destroy_confirmation: Are you sure you want to delete this project and all related data ?
354 360 text_workflow_edit: Select a role and a tracker to edit the workflow
355 361 text_are_you_sure: Are you sure ?
356 362 text_journal_changed: changed from %s to %s
357 363 text_journal_set_to: set to %s
358 364 text_journal_deleted: deleted
359 365 text_tip_task_begin_day: task beginning this day
360 366 text_tip_task_end_day: task ending this day
361 367 text_tip_task_begin_end_day: task beginning and ending this day
362 368
363 369 default_role_manager: Manager
364 370 default_role_developper: Developer
365 371 default_role_reporter: Reporter
366 372 default_tracker_bug: Bug
367 373 default_tracker_feature: Feature
368 374 default_tracker_support: Support
369 375 default_issue_status_new: New
370 376 default_issue_status_assigned: Assigned
371 377 default_issue_status_resolved: Resolved
372 378 default_issue_status_feedback: Feedback
373 379 default_issue_status_closed: Closed
374 380 default_issue_status_rejected: Rejected
375 381 default_doc_category_user: User documentation
376 382 default_doc_category_tech: Technical documentation
377 383 default_priority_low: Low
378 384 default_priority_normal: Normal
379 385 default_priority_high: High
380 386 default_priority_urgent: Urgent
381 387 default_priority_immediate: Immediate
382 388
383 389 enumeration_issue_priorities: Issue priorities
384 390 enumeration_doc_categories: Document categories
@@ -1,384 +1,390
1 1 _gloc_rule_default: '|n| n==1 ? "" : "_plural" '
2 2
3 3 actionview_datehelper_select_day_prefix:
4 4 actionview_datehelper_select_month_names: Enero,Febrero,Marzo,Abril,Mayo,Junio,Julio,Agosto,Septiembre,Octubre,Noviembre,Diciembre
5 5 actionview_datehelper_select_month_names_abbr: Ene,Feb,Mar,Abr,Mayo,Jun,Jul,Ago,Sep,Oct,Nov,Dic
6 6 actionview_datehelper_select_month_prefix:
7 7 actionview_datehelper_select_year_prefix:
8 8 actionview_datehelper_time_in_words_day: 1 day
9 9 actionview_datehelper_time_in_words_day_plural: %d days
10 10 actionview_datehelper_time_in_words_hour_about: about an hour
11 11 actionview_datehelper_time_in_words_hour_about_plural: about %d hours
12 12 actionview_datehelper_time_in_words_hour_about_single: about an hour
13 13 actionview_datehelper_time_in_words_minute: 1 minute
14 14 actionview_datehelper_time_in_words_minute_half: half a minute
15 15 actionview_datehelper_time_in_words_minute_less_than: less than a minute
16 16 actionview_datehelper_time_in_words_minute_plural: %d minutes
17 17 actionview_datehelper_time_in_words_minute_single: 1 minute
18 18 actionview_datehelper_time_in_words_second_less_than: less than a second
19 19 actionview_datehelper_time_in_words_second_less_than_plural: less than %d seconds
20 20 actionview_instancetag_blank_option: Please select
21 21
22 22 activerecord_error_inclusion: is not included in the list
23 23 activerecord_error_exclusion: is reserved
24 24 activerecord_error_invalid: is invalid
25 25 activerecord_error_confirmation: doesn't match confirmation
26 26 activerecord_error_accepted: must be accepted
27 27 activerecord_error_empty: can't be empty
28 28 activerecord_error_blank: can't be blank
29 29 activerecord_error_too_long: is too long
30 30 activerecord_error_too_short: is too short
31 31 activerecord_error_wrong_length: is the wrong length
32 32 activerecord_error_taken: has already been taken
33 33 activerecord_error_not_a_number: is not a number
34 34 activerecord_error_not_a_date: no es una fecha válida
35 35 activerecord_error_greater_than_start_date: debe ser la fecha mayor que del comienzo
36 36
37 37 general_fmt_age: %d año
38 38 general_fmt_age_plural: %d años
39 39 general_fmt_date: %%d/%%m/%%Y
40 40 general_fmt_datetime: %%d/%%m/%%Y %%H:%%M
41 41 general_fmt_datetime_short: %%d/%%m %%H:%%M
42 42 general_fmt_time: %%H:%%M
43 43 general_text_No: 'No'
44 44 general_text_Yes: 'Sí'
45 45 general_text_no: 'no'
46 46 general_text_yes: 'sí'
47 47 general_lang_es: 'Español'
48 48 general_csv_separator: ';'
49 49 general_csv_encoding: ISO-8859-1
50 50 general_pdf_encoding: ISO-8859-1
51 51 general_day_names: Lunes,Martes,Miércoles,Jueves,Viernes,Sábado,Domingo
52 52
53 53 notice_account_updated: Account was successfully updated.
54 54 notice_account_invalid_creditentials: Invalid user or password
55 55 notice_account_password_updated: Password was successfully updated.
56 56 notice_account_wrong_password: Wrong password
57 57 notice_account_register_done: Account was successfully created.
58 58 notice_account_unknown_email: Unknown user.
59 59 notice_can_t_change_password: This account uses an external authentication source. Impossible to change the password.
60 60 notice_account_lost_email_sent: An email with instructions to choose a new password has been sent to you.
61 61 notice_account_activated: Your account has been activated. You can now log in.
62 62 notice_successful_create: Successful creation.
63 63 notice_successful_update: Successful update.
64 64 notice_successful_delete: Successful deletion.
65 65 notice_successful_connection: Successful connection.
66 66 notice_file_not_found: La página que intentabas tener acceso no existe ni se ha quitado.
67 67 notice_locking_conflict: Data have been updated by another user.
68 68 notice_scm_error: La entrada y/o la revisión no existe en el depósito.
69 69
70 70 mail_subject_lost_password: Tu contraseña del redMine
71 71 mail_subject_register: Activación de la cuenta del redMine
72 72
73 73 gui_validation_error: 1 error
74 74 gui_validation_error_plural: %d errores
75 75
76 76 field_name: Nombre
77 77 field_description: Descripción
78 78 field_summary: Resumen
79 79 field_is_required: Obligatorio
80 80 field_firstname: Nombre
81 81 field_lastname: Apellido
82 82 field_mail: Email
83 83 field_filename: Fichero
84 84 field_filesize: Tamaño
85 85 field_downloads: Telecargas
86 86 field_author: Autor
87 87 field_created_on: Creado
88 88 field_updated_on: Actualizado
89 89 field_field_format: Formato
90 90 field_is_for_all: Para todos los proyectos
91 91 field_possible_values: Valores posibles
92 92 field_regexp: Expresión regular
93 93 field_min_length: Longitud mínima
94 94 field_max_length: Longitud máxima
95 95 field_value: Valor
96 96 field_category: Categoría
97 97 field_title: Título
98 98 field_project: Proyecto
99 99 field_issue: Petición
100 100 field_status: Estatuto
101 101 field_notes: Notas
102 102 field_is_closed: Petición resuelta
103 103 field_is_default: Estatuto por defecto
104 104 field_html_color: Color
105 105 field_tracker: Tracker
106 106 field_subject: Tema
107 107 field_due_date: Fecha debida
108 108 field_assigned_to: Asignado a
109 109 field_priority: Prioridad
110 110 field_fixed_version: Versión corregida
111 111 field_user: Usuario
112 112 field_role: Papel
113 113 field_homepage: Sitio web
114 114 field_is_public: Público
115 115 field_parent: Proyecto secundario de
116 116 field_is_in_chlog: Consultar las peticiones en el histórico
117 117 field_is_in_roadmap: Consultar las peticiones en el roadmap
118 118 field_login: Identificador
119 119 field_mail_notification: Notificación por mail
120 120 field_admin: Administrador
121 121 field_locked: Cerrado
122 122 field_last_login_on: Última conexión
123 123 field_language: Lengua
124 124 field_effective_date: Fecha
125 125 field_password: Contraseña
126 126 field_new_password: Nueva contraseña
127 127 field_password_confirmation: Confirmación
128 128 field_version: Versión
129 129 field_type: Tipo
130 130 field_host: Anfitrión
131 131 field_port: Puerto
132 132 field_account: Cuenta
133 133 field_base_dn: Base DN
134 134 field_attr_login: Cualidad del identificador
135 135 field_attr_firstname: Cualidad del nombre
136 136 field_attr_lastname: Cualidad del apellido
137 137 field_attr_mail: Cualidad del Email
138 138 field_onthefly: Creación del usuario On-the-fly
139 139 field_start_date: Comienzo
140 140 field_done_ratio: %% Realizado
141 141 field_auth_source: Modo de la autentificación
142 142 field_hide_mail: Ocultar mi email address
143 143 field_comment: Comentario
144 144 field_url: URL
145 field_start_page: Página principal
145 146
146 147 setting_app_title: Título del aplicación
147 148 setting_app_subtitle: Subtítulo del aplicación
148 149 setting_welcome_text: Texto acogida
149 150 setting_default_language: Lengua del defecto
150 151 setting_login_required: Autentif. requerida
151 152 setting_self_registration: Registro permitido
152 153 setting_attachment_max_size: Tamaño máximo del fichero
153 154 setting_issues_export_limit: Issues export limit
154 155 setting_mail_from: Email de la emisión
155 156 setting_host_name: Nombre de anfitrión
156 157 setting_text_formatting: Formato de texto
158 setting_wiki_compression: Compresión de la historia de Wiki
157 159
158 160 label_user: Usuario
159 161 label_user_plural: Usuarios
160 162 label_user_new: Nuevo usuario
161 163 label_project: Proyecto
162 164 label_project_new: Nuevo proyecto
163 165 label_project_plural: Proyectos
164 166 label_project_latest: Los proyectos más últimos
165 167 label_issue: Petición
166 168 label_issue_new: Nueva petición
167 169 label_issue_plural: Peticiones
168 170 label_issue_view_all: Ver todas las peticiones
169 171 label_document: Documento
170 172 label_document_new: Nuevo documento
171 173 label_document_plural: Documentos
172 174 label_role: Papel
173 175 label_role_plural: Papeles
174 176 label_role_new: Nuevo papel
175 177 label_role_and_permissions: Papeles y permisos
176 178 label_member: Miembro
177 179 label_member_new: Nuevo miembro
178 180 label_member_plural: Miembros
179 181 label_tracker: Tracker
180 182 label_tracker_plural: Trackers
181 183 label_tracker_new: Nuevo tracker
182 184 label_workflow: Workflow
183 185 label_issue_status: Estatuto de petición
184 186 label_issue_status_plural: Estatutos de las peticiones
185 187 label_issue_status_new: Nuevo estatuto
186 188 label_issue_category: Categoría de las peticiones
187 189 label_issue_category_plural: Categorías de las peticiones
188 190 label_issue_category_new: Nueva categoría
189 191 label_custom_field: Campo personalizado
190 192 label_custom_field_plural: Campos personalizados
191 193 label_custom_field_new: Nuevo campo personalizado
192 194 label_enumerations: Listas de valores
193 195 label_enumeration_new: Nuevo valor
194 196 label_information: Informacion
195 197 label_information_plural: Informaciones
196 198 label_please_login: Conexión
197 199 label_register: Registrar
198 200 label_password_lost: ¿Olvidaste la contraseña?
199 201 label_home: Acogida
200 202 label_my_page: Mi página
201 203 label_my_account: Mi cuenta
202 204 label_my_projects: Mis proyectos
203 205 label_administration: Administración
204 206 label_login: Conexión
205 207 label_logout: Desconexión
206 208 label_help: Ayuda
207 209 label_reported_issues: Peticiones registradas
208 210 label_assigned_to_me_issues: Peticiones que me están asignadas
209 211 label_last_login: Última conexión
210 212 label_last_updates: Actualizado
211 213 label_last_updates_plural: %d Actualizados
212 214 label_registered_on: Inscrito el
213 215 label_activity: Actividad
214 216 label_new: Nuevo
215 217 label_logged_as: Conectado como
216 218 label_environment: Environment
217 219 label_authentication: Autentificación
218 220 label_auth_source: Modo de la autentificación
219 221 label_auth_source_new: Nuevo modo de la autentificación
220 222 label_auth_source_plural: Modos de la autentificación
221 223 label_subproject: Proyecto secundario
222 224 label_subproject_plural: Proyectos secundarios
223 225 label_min_max_length: Longitud mín - máx
224 226 label_list: Lista
225 227 label_date: Fecha
226 228 label_integer: Número
227 229 label_boolean: Boleano
228 230 label_string: Texto
229 231 label_text: Texto largo
230 232 label_attribute: Cualidad
231 233 label_attribute_plural: Cualidades
232 234 label_download: %d Telecarga
233 235 label_download_plural: %d Telecargas
234 236 label_no_data: Ningunos datos a exhibir
235 237 label_change_status: Cambiar el estatuto
236 238 label_history: Histórico
237 239 label_attachment: Fichero
238 240 label_attachment_new: Nuevo fichero
239 241 label_attachment_delete: Suprimir el fichero
240 242 label_attachment_plural: Ficheros
241 243 label_report: Informe
242 244 label_report_plural: Informes
243 245 label_news: Noticia
244 246 label_news_new: Nueva noticia
245 247 label_news_plural: Noticias
246 248 label_news_latest: Últimas noticias
247 249 label_news_view_all: Ver todas las noticias
248 250 label_change_log: Cambios
249 251 label_settings: Configuración
250 252 label_overview: Vistazo
251 253 label_version: Versión
252 254 label_version_new: Nueva versión
253 255 label_version_plural: Versiónes
254 256 label_confirmation: Confirmación
255 257 label_export_to: Exportar a
256 258 label_read: Leer...
257 259 label_public_projects: Proyectos publicos
258 260 label_open_issues: abierta
259 261 label_open_issues_plural: abiertas
260 262 label_closed_issues: cerrada
261 263 label_closed_issues_plural: cerradas
262 264 label_total: Total
263 265 label_permissions: Permisos
264 266 label_current_status: Estado actual
265 267 label_new_statuses_allowed: Nuevos estatutos autorizados
266 268 label_all: todos
267 269 label_none: ninguno
268 270 label_next: Próximo
269 271 label_previous: Precedente
270 272 label_used_by: Utilizado por
271 273 label_details: Detalles...
272 274 label_add_note: Agregar una nota
273 275 label_per_page: Por la página
274 276 label_calendar: Calendario
275 277 label_months_from: meses de
276 278 label_gantt: Gantt
277 279 label_internal: Interno
278 280 label_last_changes: %d cambios del último
279 281 label_change_view_all: Ver todos los cambios
280 282 label_personalize_page: Personalizar esta página
281 283 label_comment: Comentario
282 284 label_comment_plural: Comentarios
283 285 label_comment_add: Agregar un comentario
284 286 label_comment_added: Comentario agregó
285 287 label_comment_delete: Suprimir comentarios
286 288 label_query: Pregunta personalizada
287 289 label_query_plural: Preguntas personalizadas
288 290 label_query_new: Nueva preguntas
289 291 label_filter_add: Agregar el filtro
290 292 label_filter_plural: Filtros
291 293 label_equals: igual
292 294 label_not_equals: no igual
293 295 label_in_less_than: en menos que
294 296 label_in_more_than: en más que
295 297 label_in: en
296 298 label_today: hoy
297 299 label_less_than_ago: hace menos de
298 300 label_more_than_ago: hace más de
299 301 label_ago: hace
300 302 label_contains: contiene
301 303 label_not_contains: no contiene
302 304 label_day_plural: días
303 305 label_repository: Depósito SVN
304 306 label_browse: Hojear
305 307 label_modification: %d modificación
306 308 label_modification_plural: %d modificaciones
307 309 label_revision: Revisión
308 310 label_revision_plural: Revisiones
309 311 label_added: agregado
310 312 label_modified: modificado
311 313 label_deleted: suprimido
312 314 label_latest_revision: La revisión más última
313 315 label_view_revisions: Ver las revisiones
314 316 label_max_size: Tamaño máximo
315 317 label_on: en
316 318 label_sort_highest: Primero
317 319 label_sort_higher: Subir
318 320 label_sort_lower: Bajar
319 321 label_sort_lowest: Último
320 322 label_roadmap: Roadmap
321 323 label_search: Búsqueda
322 324 label_result: %d resultado
323 325 label_result_plural: %d resultados
324 326 label_all_words: Todas las palabras
327 label_wiki: Wiki
328 label_page_index: Índice
329 label_current_version: Versión actual
330 label_preview: Previo
325 331
326 332 button_login: Conexión
327 333 button_submit: Someter
328 334 button_save: Validar
329 335 button_check_all: Seleccionar todo
330 336 button_uncheck_all: No seleccionar nada
331 337 button_delete: Suprimir
332 338 button_create: Crear
333 339 button_test: Testar
334 340 button_edit: Modificar
335 341 button_add: Añadir
336 342 button_change: Cambiar
337 343 button_apply: Aplicar
338 344 button_clear: Anular
339 345 button_lock: Bloquear
340 346 button_unlock: Desbloquear
341 347 button_download: Telecargar
342 348 button_list: Listar
343 349 button_view: Ver
344 350 button_move: Mover
345 351 button_back: Atrás
346 352 button_cancel: Cancelar
347 353 button_activate: Activar
348 354 button_sort: Clasificar
349 355
350 356 text_select_mail_notifications: Seleccionar las actividades que necesitan la activación de la notificación por mail.
351 357 text_regexp_info: eg. ^[A-Z0-9]+$
352 358 text_min_max_length_info: 0 para ninguna restricción
353 359 text_project_destroy_confirmation: ¿ Estás seguro de querer eliminar el proyecto ?
354 360 text_workflow_edit: Seleccionar un workflow para actualizar
355 361 text_are_you_sure: ¿ Estás seguro ?
356 362 text_journal_changed: cambiado de %s a %s
357 363 text_journal_set_to: fijado a %s
358 364 text_journal_deleted: suprimido
359 365 text_tip_task_begin_day: tarea que comienza este día
360 366 text_tip_task_end_day: tarea que termina este día
361 367 text_tip_task_begin_end_day: tarea que comienza y termina este día
362 368
363 369 default_role_manager: Manager
364 370 default_role_developper: Desarrollador
365 371 default_role_reporter: Informador
366 372 default_tracker_bug: Anomalía
367 373 default_tracker_feature: Evolución
368 374 default_tracker_support: Asistencia
369 375 default_issue_status_new: Nuevo
370 376 default_issue_status_assigned: Asignada
371 377 default_issue_status_resolved: Resuelta
372 378 default_issue_status_feedback: Comentario
373 379 default_issue_status_closed: Cerrada
374 380 default_issue_status_rejected: Rechazada
375 381 default_doc_category_user: Documentación del usuario
376 382 default_doc_category_tech: Documentación tecnica
377 383 default_priority_low: Bajo
378 384 default_priority_normal: Normal
379 385 default_priority_high: Alto
380 386 default_priority_urgent: Urgente
381 387 default_priority_immediate: Ahora
382 388
383 389 enumeration_issue_priorities: Prioridad de las peticiones
384 390 enumeration_doc_categories: Categorías del documento
@@ -1,384 +1,390
1 1 _gloc_rule_default: '|n| n<=1 ? "" : "_plural" '
2 2
3 3 actionview_datehelper_select_day_prefix:
4 4 actionview_datehelper_select_month_names: Janvier,Février,Mars,Avril,Mai,Juin,Juillet,Août,Septembre,Octobre,Novembre,Décembre
5 5 actionview_datehelper_select_month_names_abbr: Jan,Fév,Mars,Avril,Mai,Juin,Juil,Août,Sept,Oct,Nov,Déc
6 6 actionview_datehelper_select_month_prefix:
7 7 actionview_datehelper_select_year_prefix:
8 8 actionview_datehelper_time_in_words_day: 1 jour
9 9 actionview_datehelper_time_in_words_day_plural: %d jours
10 10 actionview_datehelper_time_in_words_hour_about: about an hour
11 11 actionview_datehelper_time_in_words_hour_about_plural: about %d hours
12 12 actionview_datehelper_time_in_words_hour_about_single: about an hour
13 13 actionview_datehelper_time_in_words_minute: 1 minute
14 14 actionview_datehelper_time_in_words_minute_half: 30 secondes
15 15 actionview_datehelper_time_in_words_minute_less_than: moins d'une minute
16 16 actionview_datehelper_time_in_words_minute_plural: %d minutes
17 17 actionview_datehelper_time_in_words_minute_single: 1 minute
18 18 actionview_datehelper_time_in_words_second_less_than: moins d'une seconde
19 19 actionview_datehelper_time_in_words_second_less_than_plural: moins de %d secondes
20 20 actionview_instancetag_blank_option: Choisir
21 21
22 22 activerecord_error_inclusion: n'est pas inclus dans la liste
23 23 activerecord_error_exclusion: est reservé
24 24 activerecord_error_invalid: est invalide
25 25 activerecord_error_confirmation: ne correspond pas à la confirmation
26 26 activerecord_error_accepted: doit être accepté
27 27 activerecord_error_empty: doit être renseigné
28 28 activerecord_error_blank: doit être renseigné
29 29 activerecord_error_too_long: est trop long
30 30 activerecord_error_too_short: est trop court
31 31 activerecord_error_wrong_length: n'est pas de la bonne longueur
32 32 activerecord_error_taken: est déjà utilisé
33 33 activerecord_error_not_a_number: n'est pas un nombre
34 34 activerecord_error_not_a_date: n'est pas une date valide
35 35 activerecord_error_greater_than_start_date: doit être postérieur à la date de début
36 36
37 37 general_fmt_age: %d an
38 38 general_fmt_age_plural: %d ans
39 39 general_fmt_date: %%d/%%m/%%Y
40 40 general_fmt_datetime: %%d/%%m/%%Y %%H:%%M
41 41 general_fmt_datetime_short: %%d/%%m %%H:%%M
42 42 general_fmt_time: %%H:%%M
43 43 general_text_No: 'Non'
44 44 general_text_Yes: 'Oui'
45 45 general_text_no: 'non'
46 46 general_text_yes: 'oui'
47 47 general_lang_fr: 'Français'
48 48 general_csv_separator: ';'
49 49 general_csv_encoding: ISO-8859-1
50 50 general_pdf_encoding: ISO-8859-1
51 51 general_day_names: Lundi,Mardi,Mercredi,Jeudi,Vendredi,Samedi,Dimanche
52 52
53 53 notice_account_updated: Le compte a été mis à jour avec succès.
54 54 notice_account_invalid_creditentials: Identifiant ou mot de passe invalide.
55 55 notice_account_password_updated: Mot de passe mis à jour avec succès.
56 56 notice_account_wrong_password: Mot de passe incorrect
57 57 notice_account_register_done: Un message contenant les instructions pour activer votre compte vous a été envoyé.
58 58 notice_account_unknown_email: Aucun compte ne correspond à cette adresse.
59 59 notice_can_t_change_password: Ce compte utilise une authentification externe. Impossible de changer le mot de passe.
60 60 notice_account_lost_email_sent: Un message contenant les instructions pour choisir un nouveau mot de passe vous a été envoyé.
61 61 notice_account_activated: Votre compte a été activé. Vous pouvez à présent vous connecter.
62 62 notice_successful_create: Création effectuée avec succès.
63 63 notice_successful_update: Mise à jour effectuée avec succès.
64 64 notice_successful_delete: Suppression effectuée avec succès.
65 65 notice_successful_connection: Connection réussie.
66 66 notice_file_not_found: La page à laquelle vous souhaitez accéder n'existe pas ou a été supprimée.
67 67 notice_locking_conflict: Les données ont été mises à jour par un autre utilisateur. Mise à jour impossible.
68 68 notice_scm_error: L'entrée et/ou la révision demandée n'existe pas dans le dépôt.
69 69
70 70 mail_subject_lost_password: Votre mot de passe redMine
71 71 mail_subject_register: Activation de votre compte redMine
72 72
73 73 gui_validation_error: 1 erreur
74 74 gui_validation_error_plural: %d erreurs
75 75
76 76 field_name: Nom
77 77 field_description: Description
78 78 field_summary: Résumé
79 79 field_is_required: Obligatoire
80 80 field_firstname: Prénom
81 81 field_lastname: Nom
82 82 field_mail: Email
83 83 field_filename: Fichier
84 84 field_filesize: Taille
85 85 field_downloads: Téléchargements
86 86 field_author: Auteur
87 87 field_created_on: Créé
88 88 field_updated_on: Mis à jour
89 89 field_field_format: Format
90 90 field_is_for_all: Pour tous les projets
91 91 field_possible_values: Valeurs possibles
92 92 field_regexp: Expression régulière
93 93 field_min_length: Longueur minimum
94 94 field_max_length: Longueur maximum
95 95 field_value: Valeur
96 96 field_category: Catégorie
97 97 field_title: Titre
98 98 field_project: Projet
99 99 field_issue: Demande
100 100 field_status: Statut
101 101 field_notes: Notes
102 102 field_is_closed: Demande fermée
103 103 field_is_default: Statut par défaut
104 104 field_html_color: Couleur
105 105 field_tracker: Tracker
106 106 field_subject: Sujet
107 107 field_due_date: Date d'échéance
108 108 field_assigned_to: Assigné à
109 109 field_priority: Priorité
110 110 field_fixed_version: Version corrigée
111 111 field_user: Utilisateur
112 112 field_role: Rôle
113 113 field_homepage: Site web
114 114 field_is_public: Public
115 115 field_parent: Sous-projet de
116 116 field_is_in_chlog: Demandes affichées dans l'historique
117 117 field_is_in_roadmap: Demandes affichées dans la roadmap
118 118 field_login: Identifiant
119 119 field_mail_notification: Notifications par mail
120 120 field_admin: Administrateur
121 121 field_locked: Verrouillé
122 122 field_last_login_on: Dernière connexion
123 123 field_language: Langue
124 124 field_effective_date: Date
125 125 field_password: Mot de passe
126 126 field_new_password: Nouveau mot de passe
127 127 field_password_confirmation: Confirmation
128 128 field_version: Version
129 129 field_type: Type
130 130 field_host: Hôte
131 131 field_port: Port
132 132 field_account: Compte
133 133 field_base_dn: Base DN
134 134 field_attr_login: Attribut Identifiant
135 135 field_attr_firstname: Attribut Prénom
136 136 field_attr_lastname: Attribut Nom
137 137 field_attr_mail: Attribut Email
138 138 field_onthefly: Création des utilisateurs à la volée
139 139 field_start_date: Début
140 140 field_done_ratio: %% Réalisé
141 141 field_auth_source: Mode d'authentification
142 142 field_hide_mail: Cacher mon adresse mail
143 143 field_comment: Commentaire
144 144 field_url: URL
145 field_start_page: Page de démarrage
145 146
146 147 setting_app_title: Titre de l'application
147 148 setting_app_subtitle: Sous-titre de l'application
148 149 setting_welcome_text: Texte d'accueil
149 150 setting_default_language: Langue par défaut
150 151 setting_login_required: Authentif. obligatoire
151 152 setting_self_registration: Enregistrement autorisé
152 153 setting_attachment_max_size: Taille max des fichiers
153 154 setting_issues_export_limit: Limite export demandes
154 155 setting_mail_from: Adresse d'émission
155 156 setting_host_name: Nom d'hôte
156 157 setting_text_formatting: Formatage du texte
158 setting_wiki_compression: Compression historique wiki
157 159
158 160 label_user: Utilisateur
159 161 label_user_plural: Utilisateurs
160 162 label_user_new: Nouvel utilisateur
161 163 label_project: Projet
162 164 label_project_new: Nouveau projet
163 165 label_project_plural: Projets
164 166 label_project_latest: Derniers projets
165 167 label_issue: Demande
166 168 label_issue_new: Nouvelle demande
167 169 label_issue_plural: Demandes
168 170 label_issue_view_all: Voir toutes les demandes
169 171 label_document: Document
170 172 label_document_new: Nouveau document
171 173 label_document_plural: Documents
172 174 label_role: Rôle
173 175 label_role_plural: Rôles
174 176 label_role_new: Nouveau rôle
175 177 label_role_and_permissions: Rôles et permissions
176 178 label_member: Membre
177 179 label_member_new: Nouveau membre
178 180 label_member_plural: Membres
179 181 label_tracker: Tracker
180 182 label_tracker_plural: Trackers
181 183 label_tracker_new: Nouveau tracker
182 184 label_workflow: Workflow
183 185 label_issue_status: Statut de demandes
184 186 label_issue_status_plural: Statuts de demandes
185 187 label_issue_status_new: Nouveau statut
186 188 label_issue_category: Catégorie de demandes
187 189 label_issue_category_plural: Catégories de demandes
188 190 label_issue_category_new: Nouvelle catégorie
189 191 label_custom_field: Champ personnalisé
190 192 label_custom_field_plural: Champs personnalisés
191 193 label_custom_field_new: Nouveau champ personnalisé
192 194 label_enumerations: Listes de valeurs
193 195 label_enumeration_new: Nouvelle valeur
194 196 label_information: Information
195 197 label_information_plural: Informations
196 198 label_please_login: Identification
197 199 label_register: S'enregistrer
198 200 label_password_lost: Mot de passe perdu
199 201 label_home: Accueil
200 202 label_my_page: Ma page
201 203 label_my_account: Mon compte
202 204 label_my_projects: Mes projets
203 205 label_administration: Administration
204 206 label_login: Connexion
205 207 label_logout: Déconnexion
206 208 label_help: Aide
207 209 label_reported_issues: Demandes soumises
208 210 label_assigned_to_me_issues: Demandes qui me sont assignées
209 211 label_last_login: Dernière connexion
210 212 label_last_updates: Dernière mise à jour
211 213 label_last_updates_plural: %d dernières mises à jour
212 214 label_registered_on: Inscrit le
213 215 label_activity: Activité
214 216 label_new: Nouveau
215 217 label_logged_as: Connecté en tant que
216 218 label_environment: Environnement
217 219 label_authentication: Authentification
218 220 label_auth_source: Mode d'authentification
219 221 label_auth_source_new: Nouveau mode d'authentification
220 222 label_auth_source_plural: Modes d'authentification
221 223 label_subproject: Sous-projet
222 224 label_subproject_plural: Sous-projets
223 225 label_min_max_length: Longueurs mini - maxi
224 226 label_list: Liste
225 227 label_date: Date
226 228 label_integer: Entier
227 229 label_boolean: Booléen
228 230 label_string: Texte
229 231 label_text: Texte long
230 232 label_attribute: Attribut
231 233 label_attribute_plural: Attributs
232 234 label_download: %d Téléchargement
233 235 label_download_plural: %d Téléchargements
234 236 label_no_data: Aucune donnée à afficher
235 237 label_change_status: Changer le statut
236 238 label_history: Historique
237 239 label_attachment: Fichier
238 240 label_attachment_new: Nouveau fichier
239 241 label_attachment_delete: Supprimer le fichier
240 242 label_attachment_plural: Fichiers
241 243 label_report: Rapport
242 244 label_report_plural: Rapports
243 245 label_news: Annonce
244 246 label_news_new: Nouvelle annonce
245 247 label_news_plural: Annonces
246 248 label_news_latest: Dernières annonces
247 249 label_news_view_all: Voir toutes les annonces
248 250 label_change_log: Historique
249 251 label_settings: Configuration
250 252 label_overview: Aperçu
251 253 label_version: Version
252 254 label_version_new: Nouvelle version
253 255 label_version_plural: Versions
254 256 label_confirmation: Confirmation
255 257 label_export_to: Exporter en
256 258 label_read: Lire...
257 259 label_public_projects: Projets publics
258 260 label_open_issues: ouvert
259 261 label_open_issues_plural: ouverts
260 262 label_closed_issues: fermé
261 263 label_closed_issues_plural: fermés
262 264 label_total: Total
263 265 label_permissions: Permissions
264 266 label_current_status: Statut actuel
265 267 label_new_statuses_allowed: Nouveaux statuts autorisés
266 268 label_all: tous
267 269 label_none: aucun
268 270 label_next: Suivant
269 271 label_previous: Précédent
270 272 label_used_by: Utilisé par
271 273 label_details: Détails...
272 274 label_add_note: Ajouter une note
273 275 label_per_page: Par page
274 276 label_calendar: Calendrier
275 277 label_months_from: mois depuis
276 278 label_gantt: Gantt
277 279 label_internal: Interne
278 280 label_last_changes: %d derniers changements
279 281 label_change_view_all: Voir tous les changements
280 282 label_personalize_page: Personnaliser cette page
281 283 label_comment: Commentaire
282 284 label_comment_plural: Commentaires
283 285 label_comment_add: Ajouter un commentaire
284 286 label_comment_added: Commentaire ajouté
285 287 label_comment_delete: Supprimer les commentaires
286 288 label_query: Rapport personnalisé
287 289 label_query_plural: Rapports personnalisés
288 290 label_query_new: Nouveau rapport
289 291 label_filter_add: Ajouter le filtre
290 292 label_filter_plural: Filtres
291 293 label_equals: égal
292 294 label_not_equals: différent
293 295 label_in_less_than: dans moins de
294 296 label_in_more_than: dans plus de
295 297 label_in: dans
296 298 label_today: aujourd'hui
297 299 label_less_than_ago: il y a moins de
298 300 label_more_than_ago: il y a plus de
299 301 label_ago: il y a
300 302 label_contains: contient
301 303 label_not_contains: ne contient pas
302 304 label_day_plural: jours
303 305 label_repository: Dépôt SVN
304 306 label_browse: Parcourir
305 307 label_modification: %d modification
306 308 label_modification_plural: %d modifications
307 309 label_revision: Révision
308 310 label_revision_plural: Révisions
309 311 label_added: ajouté
310 312 label_modified: modifié
311 313 label_deleted: supprimé
312 314 label_latest_revision: Dernière révision
313 315 label_view_revisions: Voir les révisions
314 316 label_max_size: Taille maximale
315 317 label_on: sur
316 318 label_sort_highest: Remonter en premier
317 319 label_sort_higher: Remonter
318 320 label_sort_lower: Descendre
319 321 label_sort_lowest: Descendre en dernier
320 322 label_roadmap: Roadmap
321 323 label_search: Recherche
322 324 label_result: %d résultat
323 325 label_result_plural: %d résultats
324 326 label_all_words: Tous les mots
327 label_wiki: Wiki
328 label_page_index: Index
329 label_current_version: Version actuelle
330 label_preview: Prévisualisation
325 331
326 332 button_login: Connexion
327 333 button_submit: Soumettre
328 334 button_save: Sauvegarder
329 335 button_check_all: Tout cocher
330 336 button_uncheck_all: Tout décocher
331 337 button_delete: Supprimer
332 338 button_create: Créer
333 339 button_test: Tester
334 340 button_edit: Modifier
335 341 button_add: Ajouter
336 342 button_change: Changer
337 343 button_apply: Appliquer
338 344 button_clear: Effacer
339 345 button_lock: Verrouiller
340 346 button_unlock: Déverrouiller
341 347 button_download: Télécharger
342 348 button_list: Lister
343 349 button_view: Voir
344 350 button_move: Déplacer
345 351 button_back: Retour
346 352 button_cancel: Annuler
347 353 button_activate: Activer
348 354 button_sort: Trier
349 355
350 356 text_select_mail_notifications: Sélectionner les actions pour lesquelles la notification par mail doit être activée.
351 357 text_regexp_info: ex. ^[A-Z0-9]+$
352 358 text_min_max_length_info: 0 pour aucune restriction
353 359 text_project_destroy_confirmation: Etes-vous sûr de vouloir supprimer ce projet et tout ce qui lui est rattaché ?
354 360 text_workflow_edit: Sélectionner un tracker et un rôle pour éditer le workflow
355 361 text_are_you_sure: Etes-vous sûr ?
356 362 text_journal_changed: changé de %s à %s
357 363 text_journal_set_to: mis à %s
358 364 text_journal_deleted: supprimé
359 365 text_tip_task_begin_day: tâche commençant ce jour
360 366 text_tip_task_end_day: tâche finissant ce jour
361 367 text_tip_task_begin_end_day: tâche commençant et finissant ce jour
362 368
363 369 default_role_manager: Manager
364 370 default_role_developper: Développeur
365 371 default_role_reporter: Rapporteur
366 372 default_tracker_bug: Anomalie
367 373 default_tracker_feature: Evolution
368 374 default_tracker_support: Assistance
369 375 default_issue_status_new: Nouveau
370 376 default_issue_status_assigned: Assigné
371 377 default_issue_status_resolved: Résolu
372 378 default_issue_status_feedback: Commentaire
373 379 default_issue_status_closed: Fermé
374 380 default_issue_status_rejected: Rejeté
375 381 default_doc_category_user: Documentation utilisateur
376 382 default_doc_category_tech: Documentation technique
377 383 default_priority_low: Bas
378 384 default_priority_normal: Normal
379 385 default_priority_high: Haut
380 386 default_priority_urgent: Urgent
381 387 default_priority_immediate: Immédiat
382 388
383 389 enumeration_issue_priorities: Priorités des demandes
384 390 enumeration_doc_categories: Catégories des documents
@@ -1,385 +1,391
1 1 _gloc_rule_default: '|n| n==1 ? "" : "_plural" '
2 2
3 3 actionview_datehelper_select_day_prefix:
4 4 actionview_datehelper_select_month_names: 1月, 2月, 3月, 4月, 5月, 6月, 7月, 8月, 9月, 10月, 11月, 12月
5 5 actionview_datehelper_select_month_names_abbr: 1月, 2月, 3月, 4月, 5月, 6月, 7月, 8月, 9月, 10月, 11月, 12月
6 6 actionview_datehelper_select_month_prefix:
7 7 actionview_datehelper_select_year_prefix:
8 8 actionview_datehelper_select_year_suffix:
9 9 actionview_datehelper_time_in_words_day: 1日
10 10 actionview_datehelper_time_in_words_day_plural: %d日間
11 11 actionview_datehelper_time_in_words_hour_about: 約1時間
12 12 actionview_datehelper_time_in_words_hour_about_plural: 約%d時間
13 13 actionview_datehelper_time_in_words_hour_about_single: 約1時間
14 14 actionview_datehelper_time_in_words_minute: 1分
15 15 actionview_datehelper_time_in_words_minute_half: 約30秒
16 16 actionview_datehelper_time_in_words_minute_less_than: 1分以内
17 17 actionview_datehelper_time_in_words_minute_plural: %d分
18 18 actionview_datehelper_time_in_words_minute_single: 1分
19 19 actionview_datehelper_time_in_words_second_less_than: 1秒以内
20 20 actionview_datehelper_time_in_words_second_less_than_plural: %d秒以内
21 21 actionview_instancetag_blank_option: 選んでください
22 22
23 23 activerecord_error_inclusion: がリストに含まれていません
24 24 activerecord_error_exclusion: が予約されています
25 25 activerecord_error_invalid: が無効です
26 26 activerecord_error_confirmation: 確認のパスワードと合っていません
27 27 activerecord_error_accepted: must be accepted
28 28 activerecord_error_empty: が空です
29 29 activerecord_error_blank: が空白です
30 30 activerecord_error_too_long: が長すぎます
31 31 activerecord_error_too_short: が短かすぎます
32 32 activerecord_error_wrong_length: の長さが間違っています
33 33 activerecord_error_taken: has already been taken
34 34 activerecord_error_not_a_number: が数字ではありません
35 35 activerecord_error_not_a_date: の日付が間違っています
36 36 activerecord_error_greater_than_start_date: を開始日より後にしてください
37 37
38 38 general_fmt_age: %d歳
39 39 general_fmt_age_plural: %d歳
40 40 general_fmt_date: %%Y年%%m月%%d日
41 41 general_fmt_datetime: %%Y年%%m月%%d日 %%H:%%M %%p
42 42 general_fmt_datetime_short: %%b %%d, %%H:%%M %%p
43 43 general_fmt_time: %%H:%%M %%p
44 44 general_text_No: 'いいえ'
45 45 general_text_Yes: 'はい'
46 46 general_text_no: 'いいえ'
47 47 general_text_yes: 'はい'
48 48 general_lang_ja: 'Japanese (日本語)'
49 49 general_csv_separator: ','
50 50 general_csv_encoding: SJIS
51 51 general_pdf_encoding: SJIS
52 52 general_day_names: 日曜日, 月曜日, 火曜日, 水曜日, 木曜日, 金曜日, 土曜日
53 53
54 54 notice_account_updated: アカウントが更新されました。
55 55 notice_account_invalid_creditentials: ユーザ名もしくはパスワードが無効
56 56 notice_account_password_updated: パスワードが更新されました。
57 57 notice_account_wrong_password: パスワードが違います
58 58 notice_account_register_done: アカウントが作成されました。
59 59 notice_account_unknown_email: ユーザが存在しません。
60 60 notice_can_t_change_password: このアカウントでは外部認証を使っています。パスワードは変更できません。
61 61 notice_account_lost_email_sent: 新しいパスワードのメールを送信しました。
62 62 notice_account_activated: アカウントが有効になりました。ログインできます。
63 63 notice_successful_create: 作成しました。
64 64 notice_successful_update: 更新しました。
65 65 notice_successful_delete: 削除しました。
66 66 notice_successful_connection: 接続しました。
67 67 notice_file_not_found: アクセスしようとしたページは存在しないか削除されています。
68 68 notice_locking_conflict: 別のユーザがデータを更新しています。
69 69 notice_scm_error: リポジトリに、エントリ/リビジョンが存在しません。
70 70
71 71 mail_subject_lost_password: redMine パスワード
72 72 mail_subject_register: redMine アカウントが有効になりました
73 73
74 74 gui_validation_error: 1 件のエラー
75 75 gui_validation_error_plural: %d 件のエラー
76 76
77 77 field_name: 名前
78 78 field_description: 説明
79 79 field_summary: サマリ
80 80 field_is_required: 必須
81 81 field_firstname: 名前
82 82 field_lastname: 苗字
83 83 field_mail: メールアドレス
84 84 field_filename: ファイル
85 85 field_filesize: サイズ
86 86 field_downloads: ダウンロード
87 87 field_author: 起票者
88 88 field_created_on: 作成日
89 89 field_updated_on: 更新日
90 90 field_field_format: 書式
91 91 field_is_for_all: 全プロジェクト向け
92 92 field_possible_values: 選択肢
93 93 field_regexp: 正規表現
94 94 field_min_length: 最小値
95 95 field_max_length: 最大値
96 96 field_value:
97 97 field_category: カテゴリ
98 98 field_title: タイトル
99 99 field_project: プロジェクト
100 100 field_issue: 問題
101 101 field_status: ステータス
102 102 field_notes: 注記
103 103 field_is_closed: 終了した問題
104 104 field_is_default: デフォルトのステータス
105 105 field_html_color:
106 106 field_tracker: トラッカー
107 107 field_subject: 題名
108 108 field_due_date: 期限日
109 109 field_assigned_to: 担当者
110 110 field_priority: 優先度
111 111 field_fixed_version: 修正されたバージョン
112 112 field_user: ユーザ
113 113 field_role: 役割
114 114 field_homepage: ホームページ
115 115 field_is_public: 公開
116 116 field_parent: 親プロジェクト名
117 117 field_is_in_chlog: 変更記録に表示されている問題
118 118 field_is_in_roadmap: Issues displayed in roadmap
119 119 field_login: ログイン
120 120 field_mail_notification: メール通知
121 121 field_admin: 管理者
122 122 field_locked: ロック済
123 123 field_last_login_on: 最終接続日
124 124 field_language: 言語
125 125 field_effective_date: 日付
126 126 field_password: パスワード
127 127 field_new_password: 新しいパスワード
128 128 field_password_confirmation: パスワードの確認
129 129 field_version: バージョン
130 130 field_type: タイプ
131 131 field_host: ホスト
132 132 field_port: ポート
133 133 field_account: アカウント
134 134 field_base_dn: Base DN
135 135 field_attr_login: ログイン名属性
136 136 field_attr_firstname: 名前属性
137 137 field_attr_lastname: 苗字属性
138 138 field_attr_mail: メール属性
139 139 field_onthefly: あわせてユーザを作成
140 140 field_start_date: 開始日
141 141 field_done_ratio: 進捗 %%
142 142 field_auth_source: 認証モード
143 143 field_hide_mail: Emailアドレスを隠す
144 144 field_comment: コメント
145 145 field_url: URL
146 field_start_page: メインページ
146 147
147 148 setting_app_title: アプリケーションのタイトル
148 149 setting_app_subtitle: アプリケーションのサブタイトル
149 150 setting_welcome_text: ウェルカムメッセージ
150 151 setting_default_language: 既定の言語
151 152 setting_login_required: 認証が必要
152 153 setting_self_registration: ユーザは自分で登録できる
153 154 setting_attachment_max_size: 添付の最大サイズ
154 155 setting_issues_export_limit: 出力する問題数の上限
155 156 setting_mail_from: Emission メールアドレス
156 157 setting_host_name: ホスト名
157 158 setting_text_formatting: テキストの書式
159 setting_wiki_compression: Wiki history compression
158 160
159 161 label_user: ユーザ
160 162 label_user_plural: ユーザ
161 163 label_user_new: 新しいユーザ
162 164 label_project: プロジェクト
163 165 label_project_new: 新しいプロジェクト
164 166 label_project_plural: プロジェクト
165 167 label_project_latest: 最近のプロジェクト
166 168 label_issue: 問題
167 169 label_issue_new: 新しい問題
168 170 label_issue_plural: 問題
169 171 label_issue_view_all: 問題を全て見る
170 172 label_document: 文書
171 173 label_document_new: 新しい文書
172 174 label_document_plural: 文書
173 175 label_role: ロール
174 176 label_role_plural: ロール
175 177 label_role_new: 新しいロール
176 178 label_role_and_permissions: ロールと権限
177 179 label_member: メンバー
178 180 label_member_new: 新しいメンバー
179 181 label_member_plural: メンバー
180 182 label_tracker: トラッカー
181 183 label_tracker_plural: トラッカー
182 184 label_tracker_new: 新しいトラッカーを作成
183 185 label_workflow: ワークフロー
184 186 label_issue_status: 問題の状態
185 187 label_issue_status_plural: 問題の状態
186 188 label_issue_status_new: 新しい状態
187 189 label_issue_category: 問題のカテゴリ
188 190 label_issue_category_plural: 問題のカテゴリ
189 191 label_issue_category_new: 新しいカテゴリ
190 192 label_custom_field: カスタムフィールド
191 193 label_custom_field_plural: カスタムフィールド
192 194 label_custom_field_new: 新しいカスタムフィールドを作成
193 195 label_enumerations: 列挙項目
194 196 label_enumeration_new: 新しい値
195 197 label_information: 情報
196 198 label_information_plural: 情報
197 199 label_please_login: ログインしてください
198 200 label_register: 登録する
199 201 label_password_lost: パスワードの再発行
200 202 label_home: ホーム
201 203 label_my_page: マイページ
202 204 label_my_account: マイアカウント
203 205 label_my_projects: マイプロジェクト
204 206 label_administration: 管理
205 207 label_login: ログイン
206 208 label_logout: ログアウト
207 209 label_help: ヘルプ
208 210 label_reported_issues: 報告されている問題
209 211 label_assigned_to_me_issues: 担当している問題
210 212 label_last_login: 最近の接続
211 213 label_last_updates: 最近の更新 1 件
212 214 label_last_updates_plural: 最近の更新 %d 件
213 215 label_registered_on: 登録日
214 216 label_activity: 活動
215 217 label_new: 新しく作成
216 218 label_logged_as: ログイン中:
217 219 label_environment: 環境
218 220 label_authentication: 認証
219 221 label_auth_source: 認証モード
220 222 label_auth_source_new: 新しい認証モード
221 223 label_auth_source_plural: 認証モード
222 224 label_subproject: サブプロジェクト
223 225 label_subproject_plural: サブプロジェクト
224 226 label_min_max_length: 最小値 - 最大値の長さ
225 227 label_list: リストから選択
226 228 label_date: 日付
227 229 label_integer: 整数
228 230 label_boolean: 真偽値
229 231 label_string: テキスト
230 232 label_text: 長いテキスト
231 233 label_attribute: 属性
232 234 label_attribute_plural: 属性
233 235 label_download: %d ダウンロード
234 236 label_download_plural: %d ダウンロード
235 237 label_no_data: 表示するデータがありません
236 238 label_change_status: 変更の状況
237 239 label_history: 履歴
238 240 label_attachment: ファイル
239 241 label_attachment_new: 新しいファイル
240 242 label_attachment_delete: ファイルを削除
241 243 label_attachment_plural: ファイル
242 244 label_report: レポート
243 245 label_report_plural: レポート
244 246 label_news: ニュース
245 247 label_news_new: ニュースを追加
246 248 label_news_plural: ニュース
247 249 label_news_latest: 最新ニュース
248 250 label_news_view_all: 全てのニュースを見る
249 251 label_change_log: 変更記録
250 252 label_settings: 設定
251 253 label_overview: 概要
252 254 label_version: バージョン
253 255 label_version_new: 新しいバージョン
254 256 label_version_plural: バージョン
255 257 label_confirmation: 確認
256 258 label_export_to: 他の形式に出力
257 259 label_read: 読む...
258 260 label_public_projects: 公開プロジェクト
259 261 label_open_issues: 未着手
260 262 label_open_issues_plural: 未着手
261 263 label_closed_issues: 終了
262 264 label_closed_issues_plural: 終了
263 265 label_total: 合計
264 266 label_permissions: 権限
265 267 label_current_status: 現在の状態
266 268 label_new_statuses_allowed: 状態の移行先
267 269 label_all: 全て
268 270 label_none: なし
269 271 label_next:
270 272 label_previous:
271 273 label_used_by: 使用中
272 274 label_details: 詳細...
273 275 label_add_note: 注記を追加
274 276 label_per_page: ページ毎
275 277 label_calendar: カレンダー
276 278 label_months_from: ヶ月 from
277 279 label_gantt: ガントチャート
278 280 label_internal: Internal
279 281 label_last_changes: 最新の変更 %d 件
280 282 label_change_view_all: 全ての変更を見る
281 283 label_personalize_page: このページをパーソナライズする
282 284 label_comment: コメント
283 285 label_comment_plural: コメント
284 286 label_comment_add: コメント追加
285 287 label_comment_added: 追加されたコメント
286 288 label_comment_delete: コメント削除
287 289 label_query: カスタムクエリ
288 290 label_query_plural: カスタムクエリ
289 291 label_query_new: 新しいクエリ
290 292 label_filter_add: フィルタ追加
291 293 label_filter_plural: フィルタ
292 294 label_equals: 等しい
293 295 label_not_equals: 等しくない
294 296 label_in_less_than: 残日数がこれより多い
295 297 label_in_more_than: 残日数がこれより少ない
296 298 label_in: 残日数
297 299 label_today: 今日
298 300 label_less_than_ago: 経過日数がこれより少ない
299 301 label_more_than_ago: 経過日数がこれより多い
300 302 label_ago: 日前
301 303 label_contains: 含む
302 304 label_not_contains: 含まない
303 305 label_day_plural:
304 306 label_repository: SVNリポジトリ
305 307 label_browse: ブラウズ
306 308 label_modification: %d 点の変更
307 309 label_modification_plural: %d 点の変更
308 310 label_revision: リビジョン
309 311 label_revision_plural: リビジョン
310 312 label_added: 追加された
311 313 label_modified: 変更された
312 314 label_deleted: 削除された
313 315 label_latest_revision: 最新リビジョン
314 316 label_view_revisions: リビジョンを見る
315 317 label_max_size: 最大サイズ
316 318 label_on:
317 319 label_sort_highest: 一番上へ
318 320 label_sort_higher: 上へ
319 321 label_sort_lower: 下へ
320 322 label_sort_lowest: 一番下へ
321 323 label_roadmap: ロードマップ
322 324 label_search: 検索
323 325 label_result: %d 件の結果
324 326 label_result_plural: %d 件の結果
325 327 label_all_words: すべての単語
328 label_wiki: Wiki
329 label_page_index: 索引
330 label_current_version: 最近版
331 label_preview: 下検分
326 332
327 333 button_login: ログイン
328 334 button_submit: 変更
329 335 button_save: 保存
330 336 button_check_all: チェックを全部つける
331 337 button_uncheck_all: チェックを全部外す
332 338 button_delete: 削除
333 339 button_create: 作成
334 340 button_test: テスト
335 341 button_edit: 編集
336 342 button_add: 追加
337 343 button_change: 変更
338 344 button_apply: 適用
339 345 button_clear: クリア
340 346 button_lock: ロック
341 347 button_unlock: アンロック
342 348 button_download: ダウンロード
343 349 button_list: 一覧
344 350 button_view: 見る
345 351 button_move: 移動
346 352 button_back: 戻る
347 353 button_cancel: キャンセル
348 354 button_activate: 有効にする
349 355 button_sort: ソート
350 356
351 357 text_select_mail_notifications: どのメール通知を送信するか、アクションを選択してください。
352 358 text_regexp_info: 例) ^[A-Z0-9]+$
353 359 text_min_max_length_info: 0だと無制限になります
354 360 text_project_destroy_confirmation: 本当にこのプロジェクトと関連データを削除したいのですか?
355 361 text_workflow_edit: ワークフローを編集するロールとトラッカーを選んでください
356 362 text_are_you_sure: 本当に?
357 363 text_journal_changed: %s から %s への変更
358 364 text_journal_set_to: %s にセット
359 365 text_journal_deleted: 削除
360 366 text_tip_task_begin_day: この日に開始するタスク
361 367 text_tip_task_end_day: この日に終了するタスク
362 368 text_tip_task_begin_end_day: この日のうちに開始して終了するタスク
363 369
364 370 default_role_manager: 管理者
365 371 default_role_developper: 開発者
366 372 default_role_reporter: 報告者
367 373 default_tracker_bug: バグ
368 374 default_tracker_feature: 機能
369 375 default_tracker_support: サポート
370 376 default_issue_status_new: 新規
371 377 default_issue_status_assigned: 分担
372 378 default_issue_status_resolved: 解決
373 379 default_issue_status_feedback: フィードバック
374 380 default_issue_status_closed: 終了
375 381 default_issue_status_rejected: 却下
376 382 default_doc_category_user: ユーザ文書
377 383 default_doc_category_tech: 技術文書
378 384 default_priority_low: 低め
379 385 default_priority_normal: 通常
380 386 default_priority_high: 高め
381 387 default_priority_urgent: 急いで
382 388 default_priority_immediate: 今すぐ
383 389
384 390 enumeration_issue_priorities: 問題の優先度
385 391 enumeration_doc_categories: 文書カテゴリ
@@ -1,579 +1,602
1 1 /* andreas08 - an open source xhtml/css website layout by Andreas Viklund - http://andreasviklund.com . Free to use in any way and for any purpose as long as the proper credits are given to the original designer. Version: 1.0, November 28, 2005 */
2 2 /* Edited by Jean-Philippe Lang *>
3 3 /**************** Body and tag styles ****************/
4 4
5 5 #header * {margin:0; padding:0;}
6 6 p, ul, ol, li {margin:0; padding:0;}
7 7
8 8 body{
9 9 font:76% Verdana,Tahoma,Arial,sans-serif;
10 10 line-height:1.4em;
11 11 text-align:center;
12 12 color:#303030;
13 13 background:#e8eaec;
14 14 margin:0;
15 15 }
16 16
17 17 a{color:#467aa7;font-weight:bold;text-decoration:none;background-color:inherit;}
18 18 a:hover{color:#2a5a8a; text-decoration:none; background-color:inherit;}
19 19 a img{border:none;}
20 20
21 21 p{margin:0 0 1em 0;}
22 22 p form{margin-top:0; margin-bottom:20px;}
23 23
24 24 img.left,img.center,img.right{padding:4px; border:1px solid #a0a0a0;}
25 25 img.left{float:left; margin:0 12px 5px 0;}
26 26 img.center{display:block; margin:0 auto 5px auto;}
27 27 img.right{float:right; margin:0 0 5px 12px;}
28 28
29 29 /**************** Header and navigation styles ****************/
30 30
31 31 #container{
32 32 width:100%;
33 33 min-width: 800px;
34 34 margin:0;
35 35 padding:0;
36 36 text-align:left;
37 37 background:#ffffff;
38 38 color:#303030;
39 39 }
40 40
41 41 #header{
42 42 height:4.5em;
43 43 margin:0;
44 44 background:#467aa7;
45 45 color:#ffffff;
46 46 margin-bottom:1px;
47 47 }
48 48
49 49 #header h1{
50 50 padding:10px 0 0 20px;
51 51 font-size:2em;
52 52 background-color:inherit;
53 53 color:#fff;
54 54 letter-spacing:-1px;
55 55 font-weight:bold;
56 56 font-family: Trebuchet MS,Georgia,"Times New Roman",serif;
57 57 }
58 58
59 59 #header h2{
60 60 margin:3px 0 0 40px;
61 61 font-size:1.5em;
62 62 background-color:inherit;
63 63 color:#f0f2f4;
64 64 letter-spacing:-1px;
65 65 font-weight:normal;
66 66 font-family: Trebuchet MS,Georgia,"Times New Roman",serif;
67 67 }
68 68
69 69 #navigation{
70 70 height:2.2em;
71 71 line-height:2.2em;
72 72 margin:0;
73 73 background:#578bb8;
74 74 color:#ffffff;
75 75 }
76 76
77 77 #navigation li{
78 78 float:left;
79 79 list-style-type:none;
80 80 border-right:1px solid #ffffff;
81 81 white-space:nowrap;
82 82 }
83 83
84 84 #navigation li.right {
85 85 float:right;
86 86 list-style-type:none;
87 87 border-right:0;
88 88 border-left:1px solid #ffffff;
89 89 white-space:nowrap;
90 90 }
91 91
92 92 #navigation li a{
93 93 display:block;
94 94 padding:0px 10px 0px 22px;
95 95 font-size:0.8em;
96 96 font-weight:normal;
97 97 text-decoration:none;
98 98 background-color:inherit;
99 99 color: #ffffff;
100 100 }
101 101
102 102 #navigation li.submenu {background:url(../images/arrow_down.png) 96% 80% no-repeat;}
103 103 #navigation li.submenu a {padding:0px 16px 0px 22px;}
104 104 * html #navigation a {width:1%;}
105 105
106 106 #navigation .selected,#navigation a:hover{
107 107 color:#ffffff;
108 108 text-decoration:none;
109 109 background-color: #80b0da;
110 110 }
111 111
112 112 /**************** Icons *******************/
113 113 .icon {
114 114 background-position: 0% 40%;
115 115 background-repeat: no-repeat;
116 116 padding-left: 20px;
117 117 padding-top: 2px;
118 118 padding-bottom: 3px;
119 119 vertical-align: middle;
120 120 }
121 121
122 122 #navigation .icon {
123 123 background-position: 4px 50%;
124 124 }
125 125
126 126 .icon22 {
127 127 background-position: 0% 40%;
128 128 background-repeat: no-repeat;
129 129 padding-left: 26px;
130 130 line-height: 22px;
131 131 vertical-align: middle;
132 132 }
133 133
134 134 .icon-add { background-image: url(../images/add.png); }
135 135 .icon-edit { background-image: url(../images/edit.png); }
136 136 .icon-del { background-image: url(../images/delete.png); }
137 137 .icon-move { background-image: url(../images/move.png); }
138 138 .icon-save { background-image: url(../images/save.png); }
139 139 .icon-cancel { background-image: url(../images/cancel.png); }
140 140 .icon-pdf { background-image: url(../images/pdf.png); }
141 141 .icon-csv { background-image: url(../images/csv.png); }
142 .icon-html { background-image: url(../images/html.png); }
143 .icon-txt { background-image: url(../images/txt.png); }
142 144 .icon-file { background-image: url(../images/file.png); }
143 145 .icon-folder { background-image: url(../images/folder.png); }
144 146 .icon-package { background-image: url(../images/package.png); }
145 147 .icon-home { background-image: url(../images/home.png); }
146 148 .icon-user { background-image: url(../images/user.png); }
147 149 .icon-mypage { background-image: url(../images/user_page.png); }
148 150 .icon-admin { background-image: url(../images/admin.png); }
149 151 .icon-projects { background-image: url(../images/projects.png); }
150 152 .icon-logout { background-image: url(../images/logout.png); }
151 153 .icon-help { background-image: url(../images/help.png); }
152 154 .icon-attachment { background-image: url(../images/attachment.png); }
155 .icon-index { background-image: url(../images/index.png); }
156 .icon-history { background-image: url(../images/history.png); }
153 157
154 158 .icon22-projects { background-image: url(../images/22x22/projects.png); }
155 159 .icon22-users { background-image: url(../images/22x22/users.png); }
156 160 .icon22-tracker { background-image: url(../images/22x22/tracker.png); }
157 161 .icon22-role { background-image: url(../images/22x22/role.png); }
158 162 .icon22-workflow { background-image: url(../images/22x22/workflow.png); }
159 163 .icon22-options { background-image: url(../images/22x22/options.png); }
160 164 .icon22-notifications { background-image: url(../images/22x22/notifications.png); }
161 165 .icon22-authent { background-image: url(../images/22x22/authent.png); }
162 166 .icon22-info { background-image: url(../images/22x22/info.png); }
163 167 .icon22-comment { background-image: url(../images/22x22/comment.png); }
164 168 .icon22-package { background-image: url(../images/22x22/package.png); }
165 169 .icon22-settings { background-image: url(../images/22x22/settings.png); }
166 170
167 171 /**************** Content styles ****************/
168 172
169 173 html>body #content {
170 174 height: auto;
171 175 min-height: 500px;
172 176 }
173 177
174 178 #content{
175 179 width: auto;
176 180 height:500px;
177 181 font-size:0.9em;
178 182 padding:20px 10px 10px 20px;
179 183 margin-left: 120px;
180 184 border-left: 1px dashed #c0c0c0;
181 185
182 186 }
183 187
184 #content h2{
188 #content h2, #content div.wiki h1 {
185 189 display:block;
186 190 margin:0 0 16px 0;
187 191 font-size:1.7em;
188 192 font-weight:normal;
189 193 letter-spacing:-1px;
190 194 color:#606060;
191 195 background-color:inherit;
192 196 font-family: Trebuchet MS,Georgia,"Times New Roman",serif;
193 197 }
194 198
195 199 #content h2 a{font-weight:normal;}
196 200 #content h3{margin:0 0 12px 0; font-size:1.4em;color:#707070;font-family: Trebuchet MS,Georgia,"Times New Roman",serif;}
197 201 #content h4{font-size: 1em; margin-bottom: 12px; margin-top: 20px; font-weight: normal; border-bottom: dotted 1px #c0c0c0;}
198 202 #content a:hover,#subcontent a:hover{text-decoration:underline;}
199 203 #content ul,#content ol{margin:0 5px 16px 35px;}
200 204 #content dl{margin:0 5px 10px 25px;}
201 205 #content dt{font-weight:bold; margin-bottom:5px;}
202 206 #content dd{margin:0 0 10px 15px;}
203 207
204 208 #content .tabs{height: 2.6em;}
205 209 #content .tabs ul{margin:0;}
206 210 #content .tabs ul li{
207 211 float:left;
208 212 list-style-type:none;
209 213 white-space:nowrap;
210 214 margin-right:8px;
211 215 background:#fff;
212 216 }
213 217 #content .tabs ul li a{
214 218 display:block;
215 219 font-size: 0.9em;
216 220 text-decoration:none;
217 221 line-height:1em;
218 222 padding:4px;
219 223 border: 1px solid #c0c0c0;
220 224 }
221 225
222 226 #content .tabs ul li a.selected, #content .tabs ul li a:hover{
223 227 background-color: #80b0da;
224 228 border: 1px solid #80b0da;
225 229 color: #fff;
226 230 text-decoration:none;
227 231 }
228 232
229 233 /***********************************************/
230 234
231 235 form {display: inline;}
232 236 blockquote {padding-left: 6px; border-left: 2px solid #ccc;}
233 237 input, select {vertical-align: middle; margin-bottom: 4px;}
234 238
235 239 input.button-small {font-size: 0.8em;}
236 240 .select-small {font-size: 0.8em;}
237 241 label {font-weight: bold; font-size: 1em; color: #505050;}
238 242 fieldset {border:1px solid #c0c0c0; padding: 6px;}
239 243 legend {color: #505050;}
240 244 .required {color: #bb0000;}
241 245 .odd {background-color:#f6f7f8;}
242 246 .even {background-color: #fff;}
243 247 hr { border:0; border-top: dotted 1px #fff; border-bottom: dotted 1px #c0c0c0; }
244 248 table p {margin:0; padding:0;}
245 249
246 250 .highlight { background-color: #FCFD8D;}
247 251
248 252 div.square {
249 253 border: 1px solid #999;
250 254 float: left;
251 255 margin: .4em .5em 0 0;
252 256 overflow: hidden;
253 257 width: .6em; height: .6em;
254 258 }
255 259
256 260 ul.documents {
257 261 list-style-type: none;
258 262 padding: 0;
259 263 margin: 0;
260 264 }
261 265
262 266 ul.documents li {
263 267 background-image: url(../images/32x32/file.png);
264 268 background-repeat: no-repeat;
265 269 background-position: 0 1px;
266 270 padding-left: 36px;
267 271 margin-bottom: 10px;
268 272 margin-left: -37px;
269 273 }
270 274
271 275 /********** Table used to display lists of things ***********/
272 276
273 277 table.list {
274 278 width:100%;
275 279 border-collapse: collapse;
276 280 border: 1px dotted #d0d0d0;
277 281 margin-bottom: 6px;
278 282 }
279 283
280 284 table.with-cells td {
281 285 border: 1px solid #d7d7d7;
282 286 }
283 287
284 288 table.list td {
285 289 padding:2px;
286 290 }
287 291
288 292 table.list thead th {
289 293 text-align: center;
290 294 background: #eee;
291 295 border: 1px solid #d7d7d7;
292 296 color: #777;
293 297 }
294 298
295 299 table.list tbody th {
296 300 font-weight: normal;
297 301 background: #eed;
298 302 border: 1px solid #d7d7d7;
299 303 }
300 304
301 305 /********** Validation error messages *************/
302 306 #errorExplanation {
303 307 width: 400px;
304 308 border: 0;
305 309 padding: 7px;
306 310 padding-bottom: 3px;
307 311 margin-bottom: 0px;
308 312 }
309 313
310 314 #errorExplanation h2 {
311 315 text-align: left;
312 316 font-weight: bold;
313 317 padding: 5px 5px 10px 26px;
314 318 font-size: 1em;
315 319 margin: -7px;
316 320 background: url(../images/alert.png) no-repeat 6px 6px;
317 321 }
318 322
319 323 #errorExplanation p {
320 324 color: #333;
321 325 margin-bottom: 0;
322 326 padding: 5px;
323 327 }
324 328
325 329 #errorExplanation ul li {
326 330 font-size: 1em;
327 331 list-style: none;
328 332 margin-left: -16px;
329 333 }
330 334
331 335 /*========== Drop down menu ==============*/
332 336 div.menu {
333 337 background-color: #FFFFFF;
334 338 border-style: solid;
335 339 border-width: 1px;
336 340 border-color: #7F9DB9;
337 341 position: absolute;
338 342 top: 0px;
339 343 left: 0px;
340 344 padding: 0;
341 345 visibility: hidden;
342 346 z-index: 101;
343 347 }
344 348
345 349 div.menu a.menuItem {
346 350 font-size: 10px;
347 351 font-weight: normal;
348 352 line-height: 2em;
349 353 color: #000000;
350 354 background-color: #FFFFFF;
351 355 cursor: default;
352 356 display: block;
353 357 padding: 0 1em;
354 358 margin: 0;
355 359 border: 0;
356 360 text-decoration: none;
357 361 white-space: nowrap;
358 362 }
359 363
360 364 div.menu a.menuItem:hover, div.menu a.menuItemHighlight {
361 365 background-color: #80b0da;
362 366 color: #ffffff;
363 367 }
364 368
365 369 div.menu a.menuItem span.menuItemText {}
366 370
367 371 div.menu a.menuItem span.menuItemArrow {
368 372 margin-right: -.75em;
369 373 }
370 374
371 375 /**************** Sidebar styles ****************/
372 376
373 377 #subcontent{
374 378 position: absolute;
375 379 left: 0px;
376 380 width:110px;
377 381 padding:20px 20px 10px 5px;
378 382 }
379 383
380 384 #subcontent h2{
381 385 display:block;
382 386 margin:0 0 5px 0;
383 387 font-size:1.0em;
384 388 font-weight:bold;
385 389 text-align:left;
386 390 color:#606060;
387 391 background-color:inherit;
388 392 font-family: Trebuchet MS,Georgia,"Times New Roman",serif;
389 393 }
390 394
391 395 #subcontent p{margin:0 0 16px 0; font-size:0.9em;}
392 396
393 397 /**************** Menublock styles ****************/
394 398
395 399 .menublock{margin:0 0 20px 8px; font-size:0.8em;}
396 400 .menublock li{list-style:none; display:block; padding:1px; margin-bottom:0px;}
397 401 .menublock li a{font-weight:bold; text-decoration:none;}
398 402 .menublock li a:hover{text-decoration:none;}
399 403 .menublock li ul{margin:0; font-size:1em; font-weight:normal;}
400 404 .menublock li ul li{margin-bottom:0;}
401 405 .menublock li ul a{font-weight:normal;}
402 406
403 407 /**************** Footer styles ****************/
404 408
405 409 #footer{
406 410 clear:both;
407 411 padding:5px 0;
408 412 margin:0;
409 413 font-size:0.9em;
410 414 color:#f0f0f0;
411 415 background:#467aa7;
412 416 }
413 417
414 418 #footer p{padding:0; margin:0; text-align:center;}
415 419 #footer a{color:#f0f0f0; background-color:inherit; font-weight:bold;}
416 420 #footer a:hover{color:#ffffff; background-color:inherit; text-decoration: underline;}
417 421
418 422 /**************** Misc classes and styles ****************/
419 423
420 424 .splitcontentleft{float:left; width:49%;}
421 425 .splitcontentright{float:right; width:49%;}
422 426 .clear{clear:both;}
423 427 .small{font-size:0.8em;line-height:1.4em;padding:0 0 0 0;}
424 428 .hide{display:none;}
425 429 .textcenter{text-align:center;}
426 430 .textright{text-align:right;}
427 431 .important{color:#f02025; background-color:inherit; font-weight:bold;}
428 432
429 433 .box{
430 434 margin:0 0 20px 0;
431 435 padding:10px;
432 436 border:1px solid #c0c0c0;
433 437 background-color:#fafbfc;
434 438 color:#505050;
435 439 line-height:1.5em;
436 440 }
437 441
438 442 a.close-icon {
439 443 display:block;
440 444 margin-top:3px;
441 445 overflow:hidden;
442 446 width:12px;
443 447 height:12px;
444 448 background-repeat: no-repeat;
445 449 cursor:pointer;
446 450 background-image:url('../images/close.png');
447 451 }
448 452
449 453 a.close-icon:hover {
450 454 background-image:url('../images/close_hl.png');
451 455 }
452 456
453 457 .rightbox{
454 458 background: #fafbfc;
455 459 border: 1px solid #c0c0c0;
456 460 float: right;
457 461 padding: 8px;
458 462 position: relative;
459 463 margin: 0 5px 5px;
460 464 }
461 465
462 466 .layout-active {
463 467 background: #ECF3E1;
464 468 }
465 469
466 470 .block-receiver {
467 471 border:1px dashed #c0c0c0;
468 472 margin-bottom: 20px;
469 473 padding: 15px 0 15px 0;
470 474 }
471 475
472 476 .mypage-box {
473 477 margin:0 0 20px 0;
474 478 color:#505050;
475 479 line-height:1.5em;
476 480 }
477 481
478 482 .handle {
479 483 cursor: move;
480 484 }
481 485
482 486 .login {
483 487 width: 50%;
484 488 text-align: left;
485 489 }
486 490
487 491 img.calendar-trigger {
488 492 cursor: pointer;
489 493 vertical-align: middle;
490 494 margin-left: 4px;
491 495 }
492 496
493 497 #history p {
494 498 margin-left: 34px;
495 499 }
496 500
497 501 /***** Contextual links div *****/
498 502 .contextual {
499 503 float: right;
500 504 font-size: 0.8em;
501 505 line-height: 16px;
502 506 padding: 2px;
503 507 }
504 508
505 509 .contextual select, .contextual input {
506 510 font-size: 1em;
507 511 }
508 512
509 513 /***** Gantt chart *****/
510 514 .gantt_hdr {
511 515 position:absolute;
512 516 top:0;
513 517 height:16px;
514 518 border-top: 1px solid #c0c0c0;
515 519 border-bottom: 1px solid #c0c0c0;
516 520 border-right: 1px solid #c0c0c0;
517 521 text-align: center;
518 522 overflow: hidden;
519 523 }
520 524
521 525 .task {
522 526 position: absolute;
523 527 height:8px;
524 528 font-size:0.8em;
525 529 color:#888;
526 530 padding:0;
527 531 margin:0;
528 532 line-height:0.8em;
529 533 }
530 534
531 535 .task_late { background:#f66 url(../images/task_late.png); border: 1px solid #f66; }
532 536 .task_done { background:#66f url(../images/task_done.png); border: 1px solid #66f; }
533 537 .task_todo { background:#aaa url(../images/task_todo.png); border: 1px solid #aaa; }
534 538
535 539 /***** Tooltips ******/
536 540 .tooltip{position:relative;z-index:24;}
537 541 .tooltip:hover{z-index:25;color:#000;}
538 542 .tooltip span.tip{display: none}
539 543
540 544 div.tooltip:hover span.tip{
541 545 display:block;
542 546 position:absolute;
543 547 top:12px; left:24px; width:270px;
544 548 border:1px solid #555;
545 549 background-color:#fff;
546 550 padding: 4px;
547 551 font-size: 0.8em;
548 552 color:#505050;
549 553 }
550 554
551 555 /***** CSS FORM ******/
552 556 .tabular p{
553 557 margin: 0;
554 558 padding: 5px 0 8px 0;
555 559 padding-left: 180px; /*width of left column containing the label elements*/
556 560 height: 1%;
557 561 }
558 562
559 563 .tabular label{
560 564 font-weight: bold;
561 565 float: left;
562 566 margin-left: -180px; /*width of left column*/
563 567 width: 175px; /*width of labels. Should be smaller than left column to create some right
564 568 margin*/
565 569 }
566 570
567 571 .error {
568 572 color: #cc0000;
569 573 }
570 574
571 575
572 576 /*.threepxfix class below:
573 577 Targets IE6- ONLY. Adds 3 pixel indent for multi-line form contents.
574 578 to account for 3 pixel bug: http://www.positioniseverything.net/explorer/threepxtest.html
575 579 */
576 580
577 581 * html .threepxfix{
578 582 margin-left: 3px;
579 583 }
584
585 /***** Wiki sections ****/
586 #content div.wiki { font-size: 110%}
587
588 #content div.wiki h2, div.wiki h3 { font-family: Trebuchet MS,Georgia,"Times New Roman",serif; color:#606060; }
589 #content div.wiki h2 { font-size: 1.4em;}
590 #content div.wiki h3 { font-size: 1.2em;}
591
592 div.wiki table {
593 border: 1px solid #505050;
594 border-collapse: collapse;
595 }
596
597 div.wiki table, div.wiki td {
598 border: 1px solid #bbb;
599 padding: 4px;
600 }
601
602 #preview .preview { background: #fafbfc url(../images/draft.png); }
@@ -1,541 +1,550
1 1 ---
2 2 permissions_052:
3 3 action: destroy_comment
4 4 id: 52
5 5 description: label_comment_delete
6 6 controller: news
7 7 mail_enabled: false
8 8 mail_option: false
9 9 sort: 1133
10 10 is_public: false
11 11 permissions_041:
12 12 action: add_file
13 13 id: 41
14 14 description: button_add
15 15 controller: projects
16 16 mail_enabled: false
17 17 mail_option: true
18 18 sort: 1320
19 19 is_public: false
20 20 permissions_030:
21 21 action: destroy
22 22 id: 30
23 23 description: button_delete
24 24 controller: news
25 25 mail_enabled: false
26 26 mail_option: false
27 27 sort: 1122
28 28 is_public: false
29 29 permissions_019:
30 30 action: download
31 31 id: 19
32 32 description: button_download
33 33 controller: issues
34 34 mail_enabled: false
35 35 mail_option: false
36 36 sort: 1010
37 37 is_public: true
38 38 permissions_008:
39 39 action: edit
40 40 id: 8
41 41 description: button_edit
42 42 controller: members
43 43 mail_enabled: false
44 44 mail_option: false
45 45 sort: 221
46 46 is_public: false
47 47 permissions_053:
48 48 action: add_query
49 49 id: 53
50 50 description: button_create
51 51 controller: projects
52 52 mail_enabled: false
53 53 mail_option: false
54 54 sort: 600
55 55 is_public: false
56 56 permissions_042:
57 57 action: destroy_file
58 58 id: 42
59 59 description: button_delete
60 60 controller: versions
61 61 mail_enabled: false
62 62 mail_option: false
63 63 sort: 1322
64 64 is_public: false
65 65 permissions_031:
66 66 action: list_documents
67 67 id: 31
68 68 description: button_list
69 69 controller: projects
70 70 mail_enabled: false
71 71 mail_option: false
72 72 sort: 1200
73 73 is_public: true
74 74 permissions_020:
75 75 action: add_issue
76 76 id: 20
77 77 description: button_add
78 78 controller: projects
79 79 mail_enabled: true
80 80 mail_option: true
81 81 sort: 1050
82 82 is_public: false
83 83 permissions_009:
84 84 action: destroy
85 85 id: 9
86 86 description: button_delete
87 87 controller: members
88 88 mail_enabled: false
89 89 mail_option: false
90 90 sort: 222
91 91 is_public: false
92 92 permissions_054:
93 93 action: show
94 94 id: 54
95 95 description: button_view
96 96 controller: repositories
97 97 mail_enabled: false
98 98 mail_option: false
99 99 sort: 1450
100 100 is_public: true
101 101 permissions_043:
102 102 action: move_issues
103 103 id: 43
104 104 description: button_move
105 105 controller: projects
106 106 mail_enabled: false
107 107 mail_option: false
108 108 sort: 1061
109 109 is_public: false
110 110 permissions_032:
111 111 action: show
112 112 id: 32
113 113 description: button_view
114 114 controller: documents
115 115 mail_enabled: false
116 116 mail_option: false
117 117 sort: 1201
118 118 is_public: true
119 119 permissions_021:
120 120 action: edit
121 121 id: 21
122 122 description: button_edit
123 123 controller: issues
124 124 mail_enabled: false
125 125 mail_option: false
126 126 sort: 1055
127 127 is_public: false
128 128 permissions_010:
129 129 action: add_version
130 130 id: 10
131 131 description: button_add
132 132 controller: projects
133 133 mail_enabled: false
134 134 mail_option: false
135 135 sort: 320
136 136 is_public: false
137 137 permissions_055:
138 138 action: browse
139 139 id: 55
140 140 description: label_browse
141 141 controller: repositories
142 142 mail_enabled: false
143 143 mail_option: false
144 144 sort: 1460
145 145 is_public: true
146 146 permissions_044:
147 147 action: add_note
148 148 id: 44
149 149 description: label_add_note
150 150 controller: issues
151 151 mail_enabled: false
152 152 mail_option: true
153 153 sort: 1057
154 154 is_public: false
155 155 permissions_033:
156 156 action: download
157 157 id: 33
158 158 description: button_download
159 159 controller: documents
160 160 mail_enabled: false
161 161 mail_option: false
162 162 sort: 1202
163 163 is_public: true
164 164 permissions_022:
165 165 action: change_status
166 166 id: 22
167 167 description: label_change_status
168 168 controller: issues
169 169 mail_enabled: true
170 170 mail_option: true
171 171 sort: 1060
172 172 is_public: false
173 173 permissions_011:
174 174 action: edit
175 175 id: 11
176 176 description: button_edit
177 177 controller: versions
178 178 mail_enabled: false
179 179 mail_option: false
180 180 sort: 321
181 181 is_public: false
182 182 permissions_056:
183 183 action: entry
184 184 id: 56
185 185 description: entry
186 186 controller: repositories
187 187 mail_enabled: false
188 188 mail_option: false
189 189 sort: 1462
190 190 is_public: true
191 191 permissions_045:
192 192 action: export_issues_pdf
193 193 id: 45
194 194 description: label_export_pdf
195 195 controller: projects
196 196 mail_enabled: false
197 197 mail_option: false
198 198 sort: 1002
199 199 is_public: true
200 200 permissions_034:
201 201 action: add_document
202 202 id: 34
203 203 description: button_add
204 204 controller: projects
205 205 mail_enabled: false
206 206 mail_option: true
207 207 sort: 1220
208 208 is_public: false
209 209 permissions_023:
210 210 action: destroy
211 211 id: 23
212 212 description: button_delete
213 213 controller: issues
214 214 mail_enabled: false
215 215 mail_option: false
216 216 sort: 1065
217 217 is_public: false
218 218 permissions_012:
219 219 action: destroy
220 220 id: 12
221 221 description: button_delete
222 222 controller: versions
223 223 mail_enabled: false
224 224 mail_option: false
225 225 sort: 322
226 226 is_public: false
227 227 permissions_001:
228 228 action: show
229 229 id: 1
230 230 description: label_overview
231 231 controller: projects
232 232 mail_enabled: false
233 233 mail_option: false
234 234 sort: 100
235 235 is_public: true
236 236 permissions_057:
237 237 action: revisions
238 238 id: 57
239 239 description: label_view_revisions
240 240 controller: repositories
241 241 mail_enabled: false
242 242 mail_option: false
243 243 sort: 1470
244 244 is_public: true
245 245 permissions_046:
246 246 action: export_pdf
247 247 id: 46
248 248 description: label_export_pdf
249 249 controller: issues
250 250 mail_enabled: false
251 251 mail_option: false
252 252 sort: 1015
253 253 is_public: true
254 254 permissions_035:
255 255 action: edit
256 256 id: 35
257 257 description: button_edit
258 258 controller: documents
259 259 mail_enabled: false
260 260 mail_option: false
261 261 sort: 1221
262 262 is_public: false
263 263 permissions_024:
264 264 action: add_attachment
265 265 id: 24
266 266 description: label_attachment_new
267 267 controller: issues
268 268 mail_enabled: false
269 269 mail_option: true
270 270 sort: 1070
271 271 is_public: false
272 272 permissions_013:
273 273 action: add_issue_category
274 274 id: 13
275 275 description: button_add
276 276 controller: projects
277 277 mail_enabled: false
278 278 mail_option: false
279 279 sort: 420
280 280 is_public: false
281 281 permissions_002:
282 282 action: changelog
283 283 id: 2
284 284 description: label_change_log
285 285 controller: projects
286 286 mail_enabled: false
287 287 mail_option: false
288 288 sort: 105
289 289 is_public: true
290 290 permissions_058:
291 291 action: revision
292 292 id: 58
293 293 description: label_view_revisions
294 294 controller: repositories
295 295 mail_enabled: false
296 296 mail_option: false
297 297 sort: 1472
298 298 is_public: true
299 299 permissions_047:
300 300 action: activity
301 301 id: 47
302 302 description: label_activity
303 303 controller: projects
304 304 mail_enabled: false
305 305 mail_option: false
306 306 sort: 160
307 307 is_public: true
308 308 permissions_036:
309 309 action: destroy
310 310 id: 36
311 311 description: button_delete
312 312 controller: documents
313 313 mail_enabled: false
314 314 mail_option: false
315 315 sort: 1222
316 316 is_public: false
317 317 permissions_025:
318 318 action: destroy_attachment
319 319 id: 25
320 320 description: label_attachment_delete
321 321 controller: issues
322 322 mail_enabled: false
323 323 mail_option: false
324 324 sort: 1075
325 325 is_public: false
326 326 permissions_014:
327 327 action: edit
328 328 id: 14
329 329 description: button_edit
330 330 controller: issue_categories
331 331 mail_enabled: false
332 332 mail_option: false
333 333 sort: 421
334 334 is_public: false
335 335 permissions_003:
336 336 action: issue_report
337 337 id: 3
338 338 description: label_report_plural
339 339 controller: reports
340 340 mail_enabled: false
341 341 mail_option: false
342 342 sort: 110
343 343 is_public: true
344 344 permissions_059:
345 345 action: diff
346 346 id: 59
347 347 description: diff
348 348 controller: repositories
349 349 mail_enabled: false
350 350 mail_option: false
351 351 sort: 1480
352 352 is_public: true
353 353 permissions_048:
354 354 action: calendar
355 355 id: 48
356 356 description: label_calendar
357 357 controller: projects
358 358 mail_enabled: false
359 359 mail_option: false
360 360 sort: 165
361 361 is_public: true
362 362 permissions_037:
363 363 action: add_attachment
364 364 id: 37
365 365 description: label_attachment_new
366 366 controller: documents
367 367 mail_enabled: false
368 368 mail_option: true
369 369 sort: 1223
370 370 is_public: false
371 371 permissions_026:
372 372 action: list_news
373 373 id: 26
374 374 description: button_list
375 375 controller: projects
376 376 mail_enabled: false
377 377 mail_option: false
378 378 sort: 1100
379 379 is_public: true
380 380 permissions_015:
381 381 action: destroy
382 382 id: 15
383 383 description: button_delete
384 384 controller: issue_categories
385 385 mail_enabled: false
386 386 mail_option: false
387 387 sort: 422
388 388 is_public: false
389 389 permissions_004:
390 390 action: settings
391 391 id: 4
392 392 description: label_settings
393 393 controller: projects
394 394 mail_enabled: false
395 395 mail_option: false
396 396 sort: 150
397 397 is_public: false
398 398 permissions_060:
399 399 action: search
400 400 id: 61
401 401 description: label_search
402 402 controller: projects
403 403 mail_enabled: false
404 404 mail_option: false
405 405 sort: 130
406 406 is_public: true
407 407 permissions_049:
408 408 action: gantt
409 409 id: 49
410 410 description: label_gantt
411 411 controller: projects
412 412 mail_enabled: false
413 413 mail_option: false
414 414 sort: 166
415 415 is_public: true
416 416 permissions_038:
417 417 action: destroy_attachment
418 418 id: 38
419 419 description: label_attachment_delete
420 420 controller: documents
421 421 mail_enabled: false
422 422 mail_option: false
423 423 sort: 1224
424 424 is_public: false
425 425 permissions_027:
426 426 action: show
427 427 id: 27
428 428 description: button_view
429 429 controller: news
430 430 mail_enabled: false
431 431 mail_option: false
432 432 sort: 1101
433 433 is_public: true
434 434 permissions_016:
435 435 action: list_issues
436 436 id: 16
437 437 description: button_list
438 438 controller: projects
439 439 mail_enabled: false
440 440 mail_option: false
441 441 sort: 1000
442 442 is_public: true
443 443 permissions_005:
444 444 action: edit
445 445 id: 5
446 446 description: button_edit
447 447 controller: projects
448 448 mail_enabled: false
449 449 mail_option: false
450 450 sort: 151
451 451 is_public: false
452 permissions_061:
453 action: search
454 id: 62
455 description: label_search
456 controller: projects
457 mail_enabled: false
458 mail_option: false
459 sort: 130
460 is_public: true
452 461 permissions_050:
453 462 action: history
454 463 id: 50
455 464 description: label_history
456 465 controller: issues
457 466 mail_enabled: false
458 467 mail_option: false
459 468 sort: 1006
460 469 is_public: true
461 470 permissions_039:
462 471 action: list_files
463 472 id: 39
464 473 description: button_list
465 474 controller: projects
466 475 mail_enabled: false
467 476 mail_option: false
468 477 sort: 1300
469 478 is_public: true
470 479 permissions_028:
471 480 action: add_news
472 481 id: 28
473 482 description: button_add
474 483 controller: projects
475 484 mail_enabled: false
476 485 mail_option: false
477 486 sort: 1120
478 487 is_public: false
479 488 permissions_017:
480 489 action: export_issues_csv
481 490 id: 17
482 491 description: label_export_csv
483 492 controller: projects
484 493 mail_enabled: false
485 494 mail_option: false
486 495 sort: 1001
487 496 is_public: true
488 497 permissions_006:
489 498 action: list_members
490 499 id: 6
491 500 description: button_list
492 501 controller: projects
493 502 mail_enabled: false
494 503 mail_option: false
495 504 sort: 200
496 505 is_public: true
497 506 permissions_051:
498 507 action: add_comment
499 508 id: 51
500 509 description: label_comment_add
501 510 controller: news
502 511 mail_enabled: false
503 512 mail_option: false
504 513 sort: 1130
505 514 is_public: false
506 515 permissions_040:
507 516 action: download
508 517 id: 40
509 518 description: button_download
510 519 controller: versions
511 520 mail_enabled: false
512 521 mail_option: false
513 522 sort: 1301
514 523 is_public: true
515 524 permissions_029:
516 525 action: edit
517 526 id: 29
518 527 description: button_edit
519 528 controller: news
520 529 mail_enabled: false
521 530 mail_option: false
522 531 sort: 1121
523 532 is_public: false
524 533 permissions_018:
525 534 action: show
526 535 id: 18
527 536 description: button_view
528 537 controller: issues
529 538 mail_enabled: false
530 539 mail_option: false
531 540 sort: 1005
532 541 is_public: true
533 542 permissions_007:
534 543 action: add_member
535 544 id: 7
536 545 description: button_add
537 546 controller: projects
538 547 mail_enabled: false
539 548 mail_option: false
540 549 sort: 220
541 550 is_public: false
@@ -1,379 +1,163
1 1 ---
2 permissions_roles_075:
3 role_id: 3
4 permission_id: 34
5 permissions_roles_047:
6 role_id: 1
7 permission_id: 15
8 permissions_roles_102:
9 role_id: 2
10 permission_id: 4
11 permissions_roles_019:
2 permissions_roles_054:
12 3 role_id: 3
13 permission_id: 30
14 permissions_roles_048:
15 role_id: 2
16 permission_id: 24
17 permissions_roles_103:
18 role_id: 2
19 permission_id: 27
20 permissions_roles_076:
4 permission_id: 44
5 permissions_roles_043:
21 6 role_id: 2
22 permission_id: 41
23 permissions_roles_049:
7 permission_id: 25
8 permissions_roles_032:
24 9 role_id: 1
25 permission_id: 3
26 permissions_roles_104:
27 role_id: 2
28 permission_id: 36
29 permissions_roles_077:
30 role_id: 2
31 permission_id: 7
32 permissions_roles_105:
33 role_id: 2
34 permission_id: 32
35 permissions_roles_078:
36 role_id: 3
37 permission_id: 38
38 permissions_roles_106:
39 role_id: 2
40 permission_id: 14
41 permissions_roles_020:
42 role_id: 2
43 permission_id: 9
44 permissions_roles_079:
45 role_id: 2
46 permission_id: 18
47 permissions_roles_107:
48 role_id: 3
49 permission_id: 40
10 permission_id: 42
50 11 permissions_roles_021:
51 12 role_id: 1
52 permission_id: 13
53 permissions_roles_108:
13 permission_id: 22
14 permissions_roles_010:
54 15 role_id: 1
55 permission_id: 29
56 permissions_roles_050:
57 role_id: 2
58 permission_id: 29
59 permissions_roles_022:
60 role_id: 3
61 16 permission_id: 4
62 permissions_roles_109:
17 permissions_roles_044:
63 18 role_id: 3
64 19 permission_id: 22
65 permissions_roles_051:
66 role_id: 3
67 permission_id: 37
68 permissions_roles_023:
69 role_id: 1
70 permission_id: 23
71 permissions_roles_052:
72 role_id: 2
73 permission_id: 33
74 permissions_roles_024:
75 role_id: 1
76 permission_id: 1
77 permissions_roles_080:
78 role_id: 2
79 permission_id: 13
80 permissions_roles_053:
81 role_id: 2
82 permission_id: 1
83 permissions_roles_025:
84 role_id: 2
85 permission_id: 10
86 permissions_roles_081:
87 role_id: 3
88 permission_id: 20
89 permissions_roles_054:
20 permissions_roles_033:
90 21 role_id: 2
91 permission_id: 12
92 permissions_roles_026:
93 role_id: 1
94 permission_id: 36
95 permissions_roles_082:
96 role_id: 1
97 permission_id: 39
98 permissions_roles_110:
99 role_id: 3
100 permission_id: 6
101 permissions_roles_027:
102 role_id: 3
103 permission_id: 31
104 permissions_roles_083:
105 role_id: 1
106 permission_id: 33
107 permissions_roles_055:
22 permission_id: 22
23 permissions_roles_022:
108 24 role_id: 1
109 25 permission_id: 38
110 permissions_roles_111:
111 role_id: 3
112 permission_id: 1
113 permissions_roles_028:
114 role_id: 1
115 permission_id: 24
116 permissions_roles_084:
117 role_id: 3
118 permission_id: 16
119 permissions_roles_056:
120 role_id: 2
121 permission_id: 5
122 permissions_roles_029:
123 role_id: 1
124 permission_id: 9
125 permissions_roles_085:
126 role_id: 3
127 permission_id: 27
128 permissions_roles_057:
129 role_id: 1
130 permission_id: 16
131 permissions_roles_112:
26 permissions_roles_011:
132 27 role_id: 1
133 28 permission_id: 20
134 permissions_roles_086:
135 role_id: 3
136 permission_id: 12
137 permissions_roles_058:
138 role_id: 1
139 permission_id: 26
140 permissions_roles_113:
141 role_id: 2
142 permission_id: 37
143 permissions_roles_087:
29 permissions_roles_045:
144 30 role_id: 1
145 permission_id: 5
146 permissions_roles_059:
147 role_id: 3
148 permission_id: 18
149 permissions_roles_114:
31 permission_id: 12
32 permissions_roles_034:
150 33 role_id: 2
151 permission_id: 20
152 permissions_roles_115:
34 permission_id: 44
35 permissions_roles_023:
153 36 role_id: 2
154 37 permission_id: 15
155 permissions_roles_088:
156 role_id: 2
157 permission_id: 3
38 permissions_roles_012:
39 role_id: 1
40 permission_id: 36
158 41 permissions_roles_001:
159 role_id: 2
160 permission_id: 21
161 permissions_roles_116:
162 role_id: 3
163 permission_id: 23
164 permissions_roles_030:
165 42 role_id: 1
166 permission_id: 30
167 permissions_roles_089:
43 permission_id: 14
44 permissions_roles_046:
168 45 role_id: 1
169 permission_id: 28
170 permissions_roles_002:
171 role_id: 3
172 46 permission_id: 29
173 permissions_roles_117:
174 role_id: 3
175 permission_id: 28
176 permissions_roles_031:
177 role_id: 2
178 permission_id: 38
179 permissions_roles_003:
180 role_id: 3
181 permission_id: 41
182 permissions_roles_118:
47 permissions_roles_035:
183 48 role_id: 1
184 permission_id: 34
185 permissions_roles_032:
186 role_id: 3
187 permission_id: 9
188 permissions_roles_004:
49 permission_id: 10
50 permissions_roles_024:
189 51 role_id: 2
190 permission_id: 8
191 permissions_roles_060:
52 permission_id: 42
53 permissions_roles_013:
192 54 role_id: 2
193 permission_id: 2
194 permissions_roles_119:
55 permission_id: 13
56 permissions_roles_002:
195 57 role_id: 1
196 permission_id: 21
197 permissions_roles_033:
198 role_id: 2
199 permission_id: 28
200 permissions_roles_005:
201 role_id: 3
202 permission_id: 3
203 permissions_roles_061:
204 role_id: 2
205 permission_id: 40
206 permissions_roles_006:
207 role_id: 3
208 permission_id: 14
209 permissions_roles_090:
58 permission_id: 34
59 permissions_roles_047:
210 60 role_id: 2
211 permission_id: 26
212 permissions_roles_062:
61 permission_id: 4
62 permissions_roles_036:
213 63 role_id: 1
214 permission_id: 19
215 permissions_roles_034:
216 role_id: 2
217 permission_id: 11
218 permissions_roles_007:
64 permission_id: 25
65 permissions_roles_025:
219 66 role_id: 1
220 permission_id: 35
221 permissions_roles_091:
222 role_id: 3
223 permission_id: 35
224 permissions_roles_063:
225 role_id: 2
226 permission_id: 30
227 permissions_roles_035:
228 role_id: 2
229 permission_id: 23
230 permissions_roles_008:
67 permission_id: 8
68 permissions_roles_014:
231 69 role_id: 2
232 permission_id: 17
233 permissions_roles_092:
70 permission_id: 38
71 permissions_roles_003:
234 72 role_id: 2
235 permission_id: 31
236 permissions_roles_064:
237 role_id: 3
238 permission_id: 33
239 permissions_roles_036:
240 role_id: 3
241 permission_id: 5
242 permissions_roles_120:
243 role_id: 3
244 permission_id: 13
245 permissions_roles_009:
246 role_id: 1
247 permission_id: 12
248 permissions_roles_093:
73 permission_id: 11
74 permissions_roles_048:
249 75 role_id: 2
250 permission_id: 42
251 permissions_roles_065:
252 role_id: 3
253 permission_id: 26
76 permission_id: 34
254 77 permissions_roles_037:
255 78 role_id: 1
256 permission_id: 42
257 permissions_roles_121:
258 role_id: 3
259 permission_id: 2
260 permissions_roles_094:
261 role_id: 3
262 permission_id: 39
263 permissions_roles_066:
264 role_id: 2
265 permission_id: 6
266 permissions_roles_038:
79 permission_id: 43
80 permissions_roles_026:
267 81 role_id: 1
268 permission_id: 25
269 permissions_roles_122:
82 permission_id: 23
83 permissions_roles_015:
270 84 role_id: 1
271 permission_id: 7
272 permissions_roles_095:
85 permission_id: 5
86 permissions_roles_004:
273 87 role_id: 2
274 permission_id: 19
275 permissions_roles_067:
276 role_id: 1
277 permission_id: 17
278 permissions_roles_039:
279 role_id: 3
280 88 permission_id: 36
281 permissions_roles_123:
89 permissions_roles_049:
282 90 role_id: 3
283 91 permission_id: 24
284 permissions_roles_096:
285 role_id: 1
286 permission_id: 18
287 permissions_roles_068:
288 role_id: 1
289 permission_id: 32
290 permissions_roles_124:
92 permissions_roles_038:
93 role_id: 2
94 permission_id: 24
95 permissions_roles_027:
291 96 role_id: 1
292 permission_id: 11
293 permissions_roles_010:
97 permission_id: 41
98 permissions_roles_016:
294 99 role_id: 1
295 permission_id: 8
296 permissions_roles_069:
297 role_id: 3
298 permission_id: 19
299 permissions_roles_097:
300 role_id: 2
301 permission_id: 35
302 permissions_roles_125:
303 role_id: 2
304 permission_id: 16
305 permissions_roles_011:
306 role_id: 3
307 permission_id: 42
308 permissions_roles_098:
100 permission_id: 21
101 permissions_roles_005:
309 102 role_id: 1
310 permission_id: 6
311 permissions_roles_126:
312 role_id: 3
313 permission_id: 7
314 permissions_roles_012:
315 role_id: 3
316 permission_id: 8
317 permissions_roles_040:
103 permission_id: 53
104 permissions_roles_050:
318 105 role_id: 1
319 permission_id: 2
320 permissions_roles_099:
106 permission_id: 13
107 permissions_roles_039:
321 108 role_id: 3
322 permission_id: 17
323 permissions_roles_041:
109 permission_id: 20
110 permissions_roles_028:
324 111 role_id: 2
325 permission_id: 39
326 permissions_roles_013:
327 role_id: 1
328 permission_id: 40
329 permissions_roles_070:
330 role_id: 3
331 permission_id: 11
332 permissions_roles_042:
112 permission_id: 20
113 permissions_roles_017:
333 114 role_id: 1
334 115 permission_id: 37
335 permissions_roles_014:
336 role_id: 1
337 permission_id: 22
338 permissions_roles_071:
116 permissions_roles_006:
339 117 role_id: 1
340 permission_id: 4
341 permissions_roles_043:
342 role_id: 3
343 permission_id: 32
344 permissions_roles_015:
345 role_id: 2
346 permission_id: 22
347 permissions_roles_072:
118 permission_id: 15
119 permissions_roles_051:
348 120 role_id: 1
349 permission_id: 27
350 permissions_roles_044:
121 permission_id: 30
122 permissions_roles_040:
351 123 role_id: 1
124 permission_id: 11
125 permissions_roles_029:
126 role_id: 2
127 permission_id: 43
128 permissions_roles_018:
129 role_id: 2
352 130 permission_id: 14
353 permissions_roles_016:
354 role_id: 3
355 permission_id: 15
356 permissions_roles_073:
131 permissions_roles_007:
132 role_id: 1
133 permission_id: 35
134 permissions_roles_052:
357 135 role_id: 2
358 permission_id: 34
359 permissions_roles_045:
360 role_id: 3
361 136 permission_id: 10
362 permissions_roles_100:
137 permissions_roles_041:
363 138 role_id: 1
364 permission_id: 10
365 permissions_roles_017:
366 role_id: 3
367 permission_id: 25
368 permissions_roles_074:
139 permission_id: 28
140 permissions_roles_030:
141 role_id: 1
142 permission_id: 9
143 permissions_roles_019:
369 144 role_id: 2
370 permission_id: 25
371 permissions_roles_046:
145 permission_id: 41
146 permissions_roles_008:
147 role_id: 2
148 permission_id: 12
149 permissions_roles_053:
150 role_id: 2
151 permission_id: 35
152 permissions_roles_042:
372 153 role_id: 1
373 permission_id: 31
374 permissions_roles_101:
375 role_id: 3
376 permission_id: 21
377 permissions_roles_018:
154 permission_id: 44
155 permissions_roles_031:
378 156 role_id: 1
379 permission_id: 41
157 permission_id: 24
158 permissions_roles_020:
159 role_id: 1
160 permission_id: 7
161 permissions_roles_009:
162 role_id: 2
163 permission_id: 37
@@ -1,63 +1,63
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.dirname(__FILE__) + '/../test_helper'
19 19
20 20 class MailerTest < Test::Unit::TestCase
21 21 fixtures :projects, :issues, :users, :members, :documents, :attachments, :tokens, :journals, :journal_details, :trackers, :issue_statuses, :enumerations
22 22
23 23 # test mailer methods for each language
24 24 def test_issue_add
25 25 issue = Issue.find(1)
26 26 GLoc.valid_languages.each do |lang|
27 Setting.default_language = lang
27 Setting.default_language = lang.to_s
28 28 assert Mailer.deliver_issue_add(issue)
29 29 end
30 30 end
31 31
32 32 def test_issue_edit
33 33 journal = Journal.find(1)
34 34 GLoc.valid_languages.each do |lang|
35 Setting.default_language = lang
35 Setting.default_language = lang.to_s
36 36 assert Mailer.deliver_issue_edit(journal)
37 37 end
38 38 end
39 39
40 40 def test_document_add
41 41 document = Document.find(1)
42 42 GLoc.valid_languages.each do |lang|
43 Setting.default_language = lang
43 Setting.default_language = lang.to_s
44 44 assert Mailer.deliver_document_add(document)
45 45 end
46 46 end
47 47
48 48 def test_lost_password
49 49 token = Token.find(2)
50 50 GLoc.valid_languages.each do |lang|
51 token.user.update_attribute :language, lang
51 token.user.update_attribute :language, lang.to_s
52 52 assert Mailer.deliver_lost_password(token)
53 53 end
54 54 end
55 55
56 56 def test_register
57 57 token = Token.find(1)
58 58 GLoc.valid_languages.each do |lang|
59 token.user.update_attribute :language, lang
59 token.user.update_attribute :language, lang.to_s
60 60 assert Mailer.deliver_register(token)
61 61 end
62 62 end
63 63 end No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now