@@ -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(('« ' + 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) + ' »'), :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,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 |
@@ -32,6 +32,10 class ApplicationController < ActionController::Base | |||
|
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? |
@@ -89,6 +93,16 class ApplicationController < ActionController::Base | |||
|
89 | 93 | render :nothing => true, :status => 403 |
|
90 | 94 | false |
|
91 | 95 | end |
|
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 | |
|
92 | 106 | |
|
93 | 107 | # store current uri in session. |
|
94 | 108 | # return to this location by calling redirect_back_or_default |
@@ -68,6 +68,10 class ProjectsController < ApplicationController | |||
|
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' |
@@ -113,6 +117,15 class ProjectsController < ApplicationController | |||
|
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) |
@@ -63,7 +63,7 module ApplicationHelper | |||
|
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) |
@@ -92,10 +92,42 module ApplicationHelper | |||
|
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 = {}) |
@@ -26,13 +26,14 class Project < ActiveRecord::Base | |||
|
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 |
@@ -91,6 +91,7 | |||
|
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" %> |
@@ -115,6 +116,7 | |||
|
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> |
@@ -38,6 +38,20 | |||
|
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" %> |
@@ -38,6 +38,9 | |||
|
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 |
@@ -9,7 +9,8 ActionController::Routing::Routes.draw do |map| | |||
|
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' |
@@ -41,6 +41,8 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: |
@@ -142,6 +142,7 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 |
@@ -154,6 +155,7 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 |
@@ -322,6 +324,10 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 |
@@ -142,6 +142,7 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 |
@@ -154,6 +155,7 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 |
@@ -322,6 +324,10 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 |
@@ -142,6 +142,7 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 |
@@ -154,6 +155,7 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 |
@@ -322,6 +324,10 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 |
@@ -142,6 +142,7 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 |
@@ -154,6 +155,7 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 |
@@ -322,6 +324,10 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 |
@@ -143,6 +143,7 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: アプリケーションのサブタイトル |
@@ -155,6 +156,7 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: ユーザ |
@@ -323,6 +325,10 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: 変更 |
@@ -139,6 +139,8 vertical-align: middle; | |||
|
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); } |
@@ -150,6 +152,8 vertical-align: middle; | |||
|
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); } |
@@ -181,7 +185,7 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; |
@@ -576,4 +580,23 to account for 3 pixel bug: http://www.positioniseverything.net/explorer/threepx | |||
|
576 | 580 | |
|
577 | 581 | * html .threepxfix{ |
|
578 | 582 | margin-left: 3px; |
|
579 | } No newline at end of file | |
|
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); } |
@@ -449,6 +449,15 permissions_005: | |||
|
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 |
@@ -1,379 +1,163 | |||
|
1 | 1 | --- |
|
2 |
permissions_roles_0 |
|
|
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: |
|
|
14 |
permissions_roles_04 |
|
|
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: |
|
|
23 |
permissions_roles_0 |
|
|
7 | permission_id: 25 | |
|
8 | permissions_roles_032: | |
|
24 | 9 | role_id: 1 |
|
25 |
permission_id: |
|
|
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: |
|
|
53 |
permissions_roles_10 |
|
|
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_ |
|
|
17 | permissions_roles_044: | |
|
63 | 18 | role_id: 3 |
|
64 | 19 | permission_id: 22 |
|
65 |
permissions_roles_0 |
|
|
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: |
|
|
92 |
permissions_roles_02 |
|
|
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 | 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_0 |
|
|
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: |
|
|
146 |
permissions_roles_0 |
|
|
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: |
|
|
152 |
permissions_roles_ |
|
|
34 | permission_id: 44 | |
|
35 | permissions_roles_023: | |
|
153 | 36 | role_id: 2 |
|
154 | 37 | permission_id: 15 |
|
155 |
permissions_roles_0 |
|
|
156 |
role_id: |
|
|
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: |
|
|
167 |
permissions_roles_0 |
|
|
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_ |
|
|
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: |
|
|
185 |
permissions_roles_0 |
|
|
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: |
|
|
191 |
permissions_roles_0 |
|
|
52 | permission_id: 42 | |
|
53 | permissions_roles_013: | |
|
192 | 54 | role_id: 2 |
|
193 |
permission_id: |
|
|
194 |
permissions_roles_ |
|
|
55 | permission_id: 13 | |
|
56 | permissions_roles_002: | |
|
195 | 57 | role_id: 1 |
|
196 |
permission_id: |
|
|
197 |
permissions_roles_0 |
|
|
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: |
|
|
212 |
permissions_roles_06 |
|
|
61 | permission_id: 4 | |
|
62 | permissions_roles_036: | |
|
213 | 63 | role_id: 1 |
|
214 |
permission_id: |
|
|
215 |
permissions_roles_0 |
|
|
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: |
|
|
221 |
permissions_roles_0 |
|
|
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: |
|
|
233 |
permissions_roles_0 |
|
|
70 | permission_id: 38 | |
|
71 | permissions_roles_003: | |
|
234 | 72 | role_id: 2 |
|
235 |
permission_id: |
|
|
236 |
permissions_roles_0 |
|
|
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: 4 |
|
|
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: 4 |
|
|
257 |
permissions_roles_ |
|
|
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: 2 |
|
|
269 |
permissions_roles_ |
|
|
82 | permission_id: 23 | |
|
83 | permissions_roles_015: | |
|
270 | 84 | role_id: 1 |
|
271 |
permission_id: |
|
|
272 |
permissions_roles_0 |
|
|
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_ |
|
|
89 | permissions_roles_049: | |
|
282 | 90 | role_id: 3 |
|
283 | 91 | permission_id: 24 |
|
284 |
permissions_roles_0 |
|
|
285 |
role_id: |
|
|
286 |
permission_id: |
|
|
287 |
permissions_roles_0 |
|
|
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: |
|
|
293 |
permissions_roles_01 |
|
|
97 | permission_id: 41 | |
|
98 | permissions_roles_016: | |
|
294 | 99 | role_id: 1 |
|
295 |
permission_id: |
|
|
296 |
permissions_roles_0 |
|
|
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: |
|
|
311 |
permissions_roles_ |
|
|
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: |
|
|
320 |
permissions_roles_0 |
|
|
106 | permission_id: 13 | |
|
107 | permissions_roles_039: | |
|
321 | 108 | role_id: 3 |
|
322 |
permission_id: |
|
|
323 |
permissions_roles_0 |
|
|
109 | permission_id: 20 | |
|
110 | permissions_roles_028: | |
|
324 | 111 | role_id: 2 |
|
325 |
permission_id: |
|
|
326 |
permissions_roles_01 |
|
|
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_0 |
|
|
336 | role_id: 1 | |
|
337 | permission_id: 22 | |
|
338 | permissions_roles_071: | |
|
116 | permissions_roles_006: | |
|
339 | 117 | role_id: 1 |
|
340 |
permission_id: |
|
|
341 |
permissions_roles_0 |
|
|
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: |
|
|
350 |
permissions_roles_04 |
|
|
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_0 |
|
|
354 |
role_id: |
|
|
355 |
permission_id: |
|
|
356 |
permissions_roles_0 |
|
|
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_ |
|
|
137 | permissions_roles_041: | |
|
363 | 138 | role_id: 1 |
|
364 |
permission_id: |
|
|
365 |
permissions_roles_0 |
|
|
366 |
role_id: |
|
|
367 |
permission_id: |
|
|
368 |
permissions_roles_0 |
|
|
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: |
|
|
371 |
permissions_roles_0 |
|
|
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: |
|
|
374 |
permissions_roles_ |
|
|
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: 4 |
|
|
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 |
@@ -24,7 +24,7 class MailerTest < Test::Unit::TestCase | |||
|
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 |
@@ -32,7 +32,7 class MailerTest < Test::Unit::TestCase | |||
|
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 |
@@ -40,7 +40,7 class MailerTest < Test::Unit::TestCase | |||
|
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 |
@@ -48,7 +48,7 class MailerTest < Test::Unit::TestCase | |||
|
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 |
@@ -56,7 +56,7 class MailerTest < Test::Unit::TestCase | |||
|
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 |
General Comments 0
You need to be logged in to leave comments.
Login now