irwi 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +21 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +53 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/app/views/base_wiki_pages/_wiki_page_actions.html.erb +4 -0
- data/app/views/base_wiki_pages/_wiki_page_history.html.erb +31 -0
- data/app/views/base_wiki_pages/_wiki_page_info.html.erb +3 -0
- data/app/views/base_wiki_pages/_wiki_page_style.html.erb +69 -0
- data/app/views/base_wiki_pages/compare.html.erb +13 -0
- data/app/views/base_wiki_pages/edit.html.erb +14 -0
- data/app/views/base_wiki_pages/history.html.erb +7 -0
- data/app/views/base_wiki_pages/no.html.erb +1 -0
- data/app/views/base_wiki_pages/not_allowed.html.erb +1 -0
- data/app/views/base_wiki_pages/show.html.erb +10 -0
- data/generators/irwi_wiki/irwi_wiki_generator.rb +22 -0
- data/generators/irwi_wiki/templates/controllers/wiki_pages_controller.rb +5 -0
- data/generators/irwi_wiki/templates/helpers/wiki_pages_helper.rb +3 -0
- data/generators/irwi_wiki/templates/migrate/create_wiki_pages.rb +44 -0
- data/generators/irwi_wiki/templates/models/wiki_page.rb +5 -0
- data/generators/irwi_wiki/templates/models/wiki_page_version.rb +5 -0
- data/irwi.gemspec +99 -0
- data/lib/irwi.rb +7 -0
- data/lib/irwi/comparators/base.rb +17 -0
- data/lib/irwi/comparators/diff_lcs.rb +52 -0
- data/lib/irwi/comparators/spans/changed_span.rb +18 -0
- data/lib/irwi/comparators/spans/not_changed_span.rb +20 -0
- data/lib/irwi/config.rb +18 -0
- data/lib/irwi/extensions/controllers/wiki_pages.rb +123 -0
- data/lib/irwi/extensions/models/wiki_page.rb +46 -0
- data/lib/irwi/extensions/models/wiki_page_version.rb +40 -0
- data/lib/irwi/formatters/blue_cloth.rb +11 -0
- data/lib/irwi/formatters/red_cloth.rb +11 -0
- data/lib/irwi/helpers/wiki_pages_helper.rb +83 -0
- data/lib/irwi/support/route_mapper.rb +17 -0
- data/lib/irwi/support/template_finder.rb +11 -0
- data/rails/init.rb +31 -0
- data/spec/comparators/diff_lcs_spec.rb +37 -0
- data/spec/config_spec.rb +58 -0
- data/spec/extensions/controllers/wiki_pages_spec.rb +53 -0
- data/spec/helpers/wiki_pages_helper_spec.rb +74 -0
- data/spec/rcov.opts +2 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/support/route_mapper_spec.rb +20 -0
- data/spec/support/template_finder_spec.rb +33 -0
- data/tasks/riwiki_tasks.rake +4 -0
- metadata +127 -0
data/lib/irwi.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
class Irwi::Comparators::Base
|
2
|
+
|
3
|
+
def render_changes( old_text, new_text )
|
4
|
+
build_changes( old_text, new_text ).join('')
|
5
|
+
end
|
6
|
+
|
7
|
+
protected
|
8
|
+
|
9
|
+
def new_not_changed( value )
|
10
|
+
Irwi::Comparators::Spans::NotChangedSpan.new( value )
|
11
|
+
end
|
12
|
+
|
13
|
+
def new_changed( action, old_value, new_value )
|
14
|
+
Irwi::Comparators::Spans::ChangedSpan.new( action, old_value, new_value )
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
class Irwi::Comparators::DiffLcs < Irwi::Comparators::Base
|
2
|
+
|
3
|
+
def initialize
|
4
|
+
super
|
5
|
+
|
6
|
+
require 'diff/lcs'
|
7
|
+
end
|
8
|
+
|
9
|
+
def build_changes( old_text, new_text )
|
10
|
+
diffs = Diff::LCS.sdiff( old_text || '', new_text || '' ) # Building symmetric diff sequence
|
11
|
+
changes = [] # Array for our result changes
|
12
|
+
|
13
|
+
diffs.each do |change|
|
14
|
+
case change.action
|
15
|
+
when '=' then
|
16
|
+
if !changes.empty? && changes.last.action == '=' # Append to last not changed span, if exists
|
17
|
+
changes.last.value << change.old_element
|
18
|
+
else
|
19
|
+
changes << new_not_changed( change.old_element )
|
20
|
+
end
|
21
|
+
|
22
|
+
when '+' then
|
23
|
+
if !changes.empty? && changes.last.action == '+' # Append to last addition, if exists
|
24
|
+
changes.last.new_value << change.new_element
|
25
|
+
elsif !changes.empty? && changes.last.action == '!' # Append to last replace, if exists (it's necessary when replacing short string with a new long)
|
26
|
+
changes.last.new_value << change.new_element
|
27
|
+
else
|
28
|
+
changes << new_changed( '+', nil, change.new_element )
|
29
|
+
end
|
30
|
+
|
31
|
+
when '-' then
|
32
|
+
if !changes.empty? && changes.last.action == '-' # Append to last deletion, if exists
|
33
|
+
changes.last.old_value << change.old_element
|
34
|
+
else
|
35
|
+
changes << new_changed( '-', change.old_element, nil )
|
36
|
+
end
|
37
|
+
|
38
|
+
when '!' then
|
39
|
+
if !changes.empty? && changes.last.action == '!' # Append to last replace, if exists
|
40
|
+
changes.last.old_value << change.old_element
|
41
|
+
changes.last.new_value << change.new_element
|
42
|
+
else
|
43
|
+
changes << new_changed( '!', change.old_element, change.new_element )
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
changes
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class Irwi::Comparators::Spans::ChangedSpan
|
2
|
+
|
3
|
+
attr_accessor :action, :old_value, :new_value
|
4
|
+
|
5
|
+
def initialize( act, ov, nv )
|
6
|
+
@action = act
|
7
|
+
@old_value = ov
|
8
|
+
@new_value = nv
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
s = ''
|
13
|
+
s << "<span class=\"removed\">#{@old_value}</span>" if @old_value
|
14
|
+
s << "<span class=\"added\">#{@new_value}</span>" if @new_value
|
15
|
+
s
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class Irwi::Comparators::Spans::NotChangedSpan
|
2
|
+
|
3
|
+
attr_accessor :value
|
4
|
+
|
5
|
+
def initialize( v )
|
6
|
+
@value = v
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_s
|
10
|
+
@value
|
11
|
+
end
|
12
|
+
|
13
|
+
def action
|
14
|
+
'='
|
15
|
+
end
|
16
|
+
|
17
|
+
alias_method :old_value, :value
|
18
|
+
alias_method :new_value, :value
|
19
|
+
|
20
|
+
end
|
data/lib/irwi/config.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
class Irwi::Config
|
2
|
+
|
3
|
+
attr_accessor_with_default :user_class_name, 'User'
|
4
|
+
|
5
|
+
attr_accessor_with_default :page_class_name, 'WikiPage'
|
6
|
+
attr_accessor_with_default :page_version_class_name, 'WikiPageVersion'
|
7
|
+
|
8
|
+
attr_accessor_with_default :page_version_foreign_key, 'page_id'
|
9
|
+
|
10
|
+
attr_accessor_with_default :formatter do
|
11
|
+
Irwi::Formatters::RedCloth.new
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_accessor_with_default :comparator do
|
15
|
+
Irwi::Comparators::DiffLcs.new
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
module Irwi::Extensions::Controllers::WikiPages
|
2
|
+
|
3
|
+
module ClassMethods
|
4
|
+
|
5
|
+
def page_class
|
6
|
+
@page_class ||= Irwi.config.page_class_name.constantize
|
7
|
+
end
|
8
|
+
|
9
|
+
def set_page_class(arg)
|
10
|
+
@page_class = arg
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
module InstanceMethods
|
16
|
+
|
17
|
+
include Irwi::Support::TemplateFinder
|
18
|
+
|
19
|
+
def show
|
20
|
+
return not_allowed unless show_allowed?
|
21
|
+
|
22
|
+
render_template( @page.new_record? ? 'no' : 'show' )
|
23
|
+
end
|
24
|
+
|
25
|
+
def history
|
26
|
+
return not_allowed unless show_allowed? && history_allowed?
|
27
|
+
|
28
|
+
render_template( @page.new_record? ? 'no' : 'history' )
|
29
|
+
end
|
30
|
+
|
31
|
+
def compare
|
32
|
+
return not_allowed unless show_allowed? && history_allowed?
|
33
|
+
|
34
|
+
if @page.new_record?
|
35
|
+
render_template 'no'
|
36
|
+
else
|
37
|
+
@versions = @page.versions.between( params[:old] || 1, params[:new] || @page.last_version_number ).all # Loading all versions between first and last
|
38
|
+
|
39
|
+
@new_version = @versions.last # Loading next version
|
40
|
+
@old_version = @versions.first # Loading previous version
|
41
|
+
|
42
|
+
render_template 'compare'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def edit
|
47
|
+
return not_allowed unless show_allowed? && edit_allowed?
|
48
|
+
|
49
|
+
render_template 'edit'
|
50
|
+
end
|
51
|
+
|
52
|
+
def update
|
53
|
+
return not_allowed unless @page.new_record? || ( show_allowed? && edit_allowed? ) # Check for rights (but not for new record, for it we will use second check only)
|
54
|
+
|
55
|
+
@page.attributes = params[:page] # Assign new attributes
|
56
|
+
|
57
|
+
return not_allowed unless show_allowed? && edit_allowed? # Double check: used beacause action may become invalid after attributes update
|
58
|
+
|
59
|
+
@page.updator = @current_user # Assing user, which updated page
|
60
|
+
@page.creator = @current_user if @page.new_record? # Assign it's creator if it's new page
|
61
|
+
|
62
|
+
if @page.save
|
63
|
+
redirect_to url_for( :action => :show, :path => @page.path.split('/') ) # redirect to new page's path (if it changed)
|
64
|
+
else
|
65
|
+
render_template 'edit'
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
protected
|
70
|
+
|
71
|
+
# Retrieves wiki_page_class for this controller
|
72
|
+
def page_class
|
73
|
+
self.class.page_class
|
74
|
+
end
|
75
|
+
|
76
|
+
# Renders user-specified or default template
|
77
|
+
def render_template( template )
|
78
|
+
render "#{template_dir template}/#{template}"
|
79
|
+
end
|
80
|
+
|
81
|
+
# Initialize @current_user instance variable
|
82
|
+
def setup_current_user
|
83
|
+
@current_user = respond_to?( :current_user ) ? current_user : nil # Find user by current_user method or return nil
|
84
|
+
end
|
85
|
+
|
86
|
+
# Initialize @page instance variable
|
87
|
+
def setup_page
|
88
|
+
@page = page_class.find_by_path_or_new( params[:path].join('/') ) # Find existing page by path or create new
|
89
|
+
end
|
90
|
+
|
91
|
+
# Method, which called when user tries to visit
|
92
|
+
def not_allowed
|
93
|
+
render_template 'not_allowed'
|
94
|
+
end
|
95
|
+
|
96
|
+
# Check is it allowed for current user to see current page. Designed to be redefined by application programmer
|
97
|
+
def show_allowed?
|
98
|
+
true
|
99
|
+
end
|
100
|
+
|
101
|
+
# Check is it allowed for current user see current page history. Designed to be redefined by application programmer
|
102
|
+
def history_allowed?
|
103
|
+
true
|
104
|
+
end
|
105
|
+
|
106
|
+
# Check is it allowed for current user edit current page. Designed to be redefined by application programmer
|
107
|
+
def edit_allowed?
|
108
|
+
true
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.included( base )
|
114
|
+
base.send :extend, Irwi::Extensions::Controllers::WikiPages::ClassMethods
|
115
|
+
base.send :include, Irwi::Extensions::Controllers::WikiPages::InstanceMethods
|
116
|
+
|
117
|
+
base.before_filter :setup_current_user # Setup @current_user instance variable before each action
|
118
|
+
base.before_filter :setup_page # Setup @page instance variable before each action
|
119
|
+
|
120
|
+
base.helper_method :show_allowed?, :edit_allowed?, :history_allowed? # Access control methods are avaliable in views
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Irwi::Extensions::Models::WikiPage
|
2
|
+
|
3
|
+
module ClassMethods
|
4
|
+
|
5
|
+
def find_by_path_or_new( path )
|
6
|
+
self.find_by_path( path ) || self.new( :path => path )
|
7
|
+
end
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
module InstanceMethods
|
12
|
+
|
13
|
+
# Retrieve number of last version
|
14
|
+
def last_version_number
|
15
|
+
last = versions.last
|
16
|
+
last ? last.number : 0
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
|
21
|
+
def create_new_version
|
22
|
+
n = last_version_number
|
23
|
+
|
24
|
+
v = versions.build
|
25
|
+
v.attributes = attributes.slice( *v.attribute_names )
|
26
|
+
v.number = n + 1
|
27
|
+
v.save!
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.included( base )
|
33
|
+
base.send :extend, Irwi::Extensions::Models::WikiPage::ClassMethods
|
34
|
+
base.send :include, Irwi::Extensions::Models::WikiPage::InstanceMethods
|
35
|
+
|
36
|
+
base.send :attr_accessor, :comment, :previous_version_number
|
37
|
+
|
38
|
+
base.belongs_to :creator, :class_name => Irwi.config.user_class_name
|
39
|
+
base.belongs_to :updator, :class_name => Irwi.config.user_class_name
|
40
|
+
|
41
|
+
base.has_many :versions, :class_name => Irwi.config.page_version_class_name, :foreign_key => Irwi.config.page_version_foreign_key, :order => 'id ASC'
|
42
|
+
|
43
|
+
base.after_save :create_new_version
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Irwi::Extensions::Models::WikiPageVersion
|
2
|
+
|
3
|
+
module ClassMethods
|
4
|
+
|
5
|
+
end
|
6
|
+
|
7
|
+
module InstanceMethods
|
8
|
+
|
9
|
+
def next
|
10
|
+
self.class.first :conditions => ["id > ? AND page_id = ?", id, page_id], :order => 'id ASC'
|
11
|
+
end
|
12
|
+
|
13
|
+
def previous
|
14
|
+
self.class.first :conditions => ["id < ? AND page_id = ?", id, page_id], :order => 'id DESC'
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def raise_on_update
|
20
|
+
raise ActiveRecordError.new "Cann't modify existent version"
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.included( base )
|
26
|
+
base.send :extend, Irwi::Extensions::Models::WikiPageVersion::ClassMethods
|
27
|
+
base.send :include, Irwi::Extensions::Models::WikiPageVersion::InstanceMethods
|
28
|
+
|
29
|
+
base.belongs_to :page, :class_name => Irwi.config.page_class_name
|
30
|
+
base.belongs_to :updator, :class_name => Irwi.config.user_class_name
|
31
|
+
|
32
|
+
base.before_update :raise_on_update
|
33
|
+
|
34
|
+
base.named_scope :between, lambda { | first, last |
|
35
|
+
first, last = last, first if last < first # Reordering if neeeded
|
36
|
+
{ :conditions => [ 'number >= ? AND number <= ?', first, last ] }
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Irwi::Helpers::WikiPagesHelper
|
2
|
+
|
3
|
+
include Irwi::Support::TemplateFinder
|
4
|
+
|
5
|
+
# Edit form for wiki page model
|
6
|
+
def wiki_page_form( config = {}, &block )
|
7
|
+
form_for( :page, @page, { :url => url_for( :action => :update ), :html=> { :class => 'wiki_form' } }.merge!( config ), &block )
|
8
|
+
end
|
9
|
+
|
10
|
+
def wiki_page_edit_path
|
11
|
+
url_for( :action => :edit )
|
12
|
+
end
|
13
|
+
|
14
|
+
def wiki_page_history_path
|
15
|
+
url_for( :action => :history )
|
16
|
+
end
|
17
|
+
|
18
|
+
def wiki_page_compare_path
|
19
|
+
url_for( :action => :compare )
|
20
|
+
end
|
21
|
+
|
22
|
+
def wiki_page_path
|
23
|
+
url_for( :action => :show )
|
24
|
+
end
|
25
|
+
|
26
|
+
def wiki_content( text )
|
27
|
+
sanitize( Irwi.config.formatter.format( text ) )
|
28
|
+
end
|
29
|
+
|
30
|
+
def wiki_diff( old_text, new_text )
|
31
|
+
Irwi.config.comparator.render_changes( old_text, new_text )
|
32
|
+
end
|
33
|
+
|
34
|
+
def wiki_user( user )
|
35
|
+
return '<Unknown>' if user.nil?
|
36
|
+
|
37
|
+
"User##{user.id}"
|
38
|
+
end
|
39
|
+
|
40
|
+
##
|
41
|
+
# Instead of having to translate strings and defining a default value:
|
42
|
+
#
|
43
|
+
# t("Hello World!", :default => 'Hello World!')
|
44
|
+
#
|
45
|
+
# We define this method to define the value only once:
|
46
|
+
#
|
47
|
+
# wt("Hello World!")
|
48
|
+
#
|
49
|
+
# Note that interpolation still works ...
|
50
|
+
#
|
51
|
+
# wt("Hello {{world}}!", :world => @world)
|
52
|
+
#
|
53
|
+
def wt(msg, *args)
|
54
|
+
config = args.extract_options!
|
55
|
+
config[:default] = msg
|
56
|
+
config[:scope] = 'wiki'
|
57
|
+
I18n.t(msg, config)
|
58
|
+
end
|
59
|
+
|
60
|
+
def wiki_page_style
|
61
|
+
render :partial => "#{template_dir '_wiki_page_style'}/wiki_page_style"
|
62
|
+
end
|
63
|
+
|
64
|
+
def wiki_page_info(page = nil)
|
65
|
+
page ||= @page # By default take page from instance variable
|
66
|
+
|
67
|
+
render :partial => "#{template_dir '_wiki_page_info'}/wiki_page_info", :locals => { :page => page }
|
68
|
+
end
|
69
|
+
|
70
|
+
def wiki_page_actions(page = nil)
|
71
|
+
page ||= @page # By default take page from instance variable
|
72
|
+
|
73
|
+
render :partial => "#{template_dir '_wiki_page_actions'}/wiki_page_actions", :locals => { :page => page }
|
74
|
+
end
|
75
|
+
|
76
|
+
def wiki_page_history(page = nil,versions = nil)
|
77
|
+
page ||= @page # By default take page from instance variable
|
78
|
+
versions ||= page.versions
|
79
|
+
|
80
|
+
render :partial => "#{template_dir '_wiki_page_history'}/wiki_page_history", :locals => { :page => page, :versions => versions, :with_form => (versions.size > 1) }
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|