junebug-wiki 0.0.19

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,65 @@
1
+ require 'fileutils'
2
+
3
+ require 'junebug'
4
+ require 'junebug/models'
5
+
6
+ module Junebug
7
+ module Generator
8
+ extend self
9
+
10
+ def generate(args)
11
+ if args.empty? || (args[0] == '-h') || (args[0] == '--help')
12
+ puts <<END
13
+ Usage: junebug [options|wikiname]
14
+
15
+ Options:
16
+ -v, --version Display version
17
+ -h, --help Display this page
18
+
19
+ END
20
+ return
21
+ end
22
+
23
+ if (args[0] == '-v') || (args[0] == '--version')
24
+ puts "Junebug v#{Junebug::VERSION}"
25
+ return
26
+ end
27
+
28
+ src_root = File.dirname(__FILE__) + '/../../deploy'
29
+ app = ARGV.first
30
+ FileUtils.cp_r(src_root, app)
31
+ FileUtils.chmod(0755, app+'/wiki')
32
+ FileUtils.cd app
33
+ Junebug.connect
34
+ Junebug.create
35
+
36
+ user = Junebug::Models::User.find(1)
37
+
38
+ puts <<EOS
39
+
40
+ ***********************************
41
+
42
+ Welcome to Junebug!
43
+
44
+ To start your new wiki, do the following:
45
+
46
+ > cd #{app}
47
+ > ruby wiki run
48
+
49
+ Open your browser to http://#{Junebug.config['host']}:#{Junebug.config['port']}#{Junebug.config['sitepath']}
50
+
51
+ Your admin account is:
52
+
53
+ username: #{user.username}
54
+ password: #{user.password}
55
+
56
+ For more information about running and configuring Junebug,
57
+ go to http://www.junebugwiki.com
58
+
59
+ Submit bug reports to tim.myrtle@gmail.com
60
+
61
+ EOS
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,48 @@
1
+
2
+ module Junebug::Helpers
3
+
4
+ def logged_in?
5
+ !@state.user.blank?
6
+ end
7
+
8
+ def is_admin?
9
+ @state.user && @state.user.role == Junebug::Models::User::ROLE_ADMIN
10
+ end
11
+
12
+ def last_updated(page)
13
+ from = page.updated_at.to_i
14
+ to = Time.now.to_i
15
+ from = from.to_time if from.respond_to?(:to_time)
16
+ to = to.to_time if to.respond_to?(:to_time)
17
+ distance = (((to - from).abs)/60).round
18
+ case distance
19
+ when 0..1 : return (distance==0) ? 'less than a minute' : '1 minute'
20
+ when 2..45 : "#{distance} minutes"
21
+ when 46..90 : 'about 1 hour'
22
+ when 90..1440 : "about #{(distance.to_f / 60.0).round} hours"
23
+ when 1441..2880: '1 day'
24
+ else "#{(distance / 1440).round} days"
25
+ end
26
+ end
27
+
28
+ def diff_link(page, version=nil)
29
+ version = page if version.nil?
30
+ a 'diff', :href => R(Junebug::Controllers::Diff,page.title,version.version-1,version.version)
31
+ end
32
+
33
+ def auto_link_urls(text)
34
+ extra_options = ""
35
+ text.gsub(/(<\w+.*?>|[^=!:'"\/]|^)((?:http[s]?:\/\/)|(?:www\.))([^\s<]+\/?)([[:punct:]]|\s|<|$)/) do
36
+ all, a, b, c, d = $&, $1, $2, $3, $4
37
+ if a =~ /<a\s/i # don't replace URL's that are already linked
38
+ all
39
+ else
40
+ text = b + c
41
+ text = yield(text) if block_given?
42
+ %(#{a}<a href="#{b=="www."?"http://www.":b}#{c}"#{extra_options}>#{text}</a>#{d})
43
+ end
44
+ end
45
+ end
46
+
47
+
48
+ end
@@ -0,0 +1,98 @@
1
+ require 'active_record'
2
+ require 'junebug/ext/acts_as_versioned'
3
+
4
+ module Junebug::Models
5
+
6
+ class User < Base
7
+
8
+ ROLE_USER = 0
9
+ ROLE_ADMIN = 10
10
+ validates_uniqueness_of :username
11
+ validates_format_of :username, :with => /^([\w]*)$/
12
+ validates_length_of :username, :within=>3..30
13
+ validates_format_of :password, :with => /^([\w]*)$/
14
+ validates_length_of :password, :within=>5..30
15
+ has_many :pages
16
+
17
+ def username=(text)
18
+ write_attribute(:username, (text ? text.strip.downcase : text) )
19
+ end
20
+
21
+ def password=(text)
22
+ write_attribute(:password, (text ? text.strip : text) )
23
+ end
24
+
25
+ end
26
+
27
+ class Page < Base
28
+ belongs_to :user
29
+ PAGE_LINK = /\[\[([^\]|]*)[|]?([^\]]*)\]\]/
30
+ #before_save { |r| r.title = r.title.underscore }
31
+ #PAGE_LINK = /([A-Z][a-z]+[A-Z]\w+)/
32
+ validates_uniqueness_of :title
33
+ validates_format_of :title, :with => /^[\w ]+$/
34
+ validates_presence_of :title
35
+ acts_as_versioned
36
+ non_versioned_fields.push 'title'
37
+
38
+ def title=(text)
39
+ write_attribute(:title, text ? text.strip.squeeze(' ') : text)
40
+ end
41
+ end
42
+
43
+ class Page::Version < Base
44
+ belongs_to :user
45
+ end
46
+
47
+ class CreateJunebug < V 1.0
48
+ def self.up
49
+ create_table :junebug_users do |t|
50
+ t.column :id, :integer, :null => false
51
+ t.column :username, :string, :null => false
52
+ t.column :password, :string, :null => false
53
+ t.column :role, :integer, :default => 0
54
+ end
55
+ create_table :junebug_pages do |t|
56
+ t.column :title, :string, :limit => 255
57
+ t.column :body, :text
58
+ t.column :user_id, :integer, :null => false
59
+ t.column :readonly, :boolean, :default => false
60
+ t.column :created_at, :datetime
61
+ t.column :updated_at, :datetime
62
+ end
63
+ Page.create_versioned_table
64
+ Page.reset_column_information
65
+
66
+ # Create admin account
67
+ admin = User.new(:role => User::ROLE_ADMIN)
68
+ if ENV['INTERACTIVE']
69
+ loop {
70
+ print "Create an initial user account\n"
71
+ print "\nEnter your username (3-30 chars, no spaces or punctuation): "
72
+ admin.username = STDIN.gets.strip
73
+ print "\nEnter your password (5-30 chars, no spaces or punctuation): "
74
+ admin.password = STDIN.gets.strip
75
+ break if admin.valid?
76
+ puts "\nThe following errors were encountered:"
77
+ admin.errors.full_messages().each {|msg| puts " #{msg}"}
78
+ puts
79
+ }
80
+ else
81
+ admin.username = 'admin'
82
+ admin.password = 'password'
83
+ end
84
+ admin.save
85
+
86
+ # Install some default pages
87
+ pages_file = File.dirname(__FILE__) + "/../../dump/junebug_pages.yml"
88
+ puts pages_file
89
+ YAML.load_file(pages_file).each {|page_data|Page.create(page_data) } if File.exist?(pages_file)
90
+ end
91
+ def self.down
92
+ drop_table :junebug_pages
93
+ drop_table :junebug_users
94
+ Page.drop_versioned_table
95
+ end
96
+ end
97
+
98
+ end
@@ -0,0 +1,4 @@
1
+ $VERBOSE = nil
2
+
3
+ # Load rakefile extensions
4
+ Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].each { |ext| load ext }
@@ -0,0 +1,34 @@
1
+ require 'junebug'
2
+ require 'junebug/models'
3
+ require 'active_record'
4
+
5
+ namespace :dump do
6
+
7
+ desc 'Dump wiki pages'
8
+ task :pages do
9
+ fixtures_dir = File.join('.', 'dump')
10
+
11
+ # clean out fixtures dir
12
+ puts "fixtures_dir: #{fixtures_dir}"
13
+ Dir[File.join(fixtures_dir, '*')].each { |f| rm_f(f, :verbose => false) }
14
+
15
+ # open db connection
16
+ ActiveRecord::Base.establish_connection( :adapter => "sqlite3", :database => "./junebug.db")
17
+
18
+ # open fixtures file
19
+ File.open(File.join(fixtures_dir, "junebug_pages.yml"), 'w') do |file|
20
+
21
+ # grab all pages
22
+ pages = Junebug::Models::Page.find(:all)
23
+ page_data = []
24
+ pages.each do |page|
25
+ page_data << page.attributes
26
+ end
27
+
28
+ file.write page_data.to_yaml
29
+ end
30
+
31
+ puts "Got pages and put them in #{fixtures_dir}."
32
+ end
33
+
34
+ end
@@ -0,0 +1,27 @@
1
+ require 'junebug/config'
2
+
3
+ namespace :update do
4
+
5
+ desc "Update deploy directory"
6
+ task :deploydir do
7
+ mv 'static', 'public', :force => true
8
+ end
9
+
10
+ desc "Update stylesheets"
11
+ task :stylesheets do
12
+ junebug_root = Junebug::Config.rootdir
13
+ cp_r File.join(junebug_root, 'deploy', 'public'), '.'
14
+ end
15
+
16
+ desc "Update rakefile"
17
+ task :rakefile do
18
+ junebug_root = Junebug::Config.rootdir
19
+ cp File.join(junebug_root, 'deploy', 'Rakefile'), '.'
20
+ end
21
+
22
+ desc "Update everything"
23
+ task :everything => [:deploydir, :stylesheets, :rakefile] do
24
+
25
+ end
26
+
27
+ end
@@ -0,0 +1,9 @@
1
+ module Junebug #:nodoc:
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 0
5
+ TINY = 19
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
@@ -0,0 +1,307 @@
1
+ require 'redcloth'
2
+
3
+ module Junebug::Views
4
+ def layout
5
+ html {
6
+ head {
7
+ title @page_title ? @page_title : @page.title
8
+ link :href=>'/style/yui/reset.css', :type=>'text/css', :rel=>'stylesheet'
9
+ link :href=>'/style/yui/fonts.css', :type=>'text/css', :rel=>'stylesheet'
10
+ link :href=>'/style/yui/grids.css', :type=>'text/css', :rel=>'stylesheet'
11
+ link :href=>'/style/base.css', :type=>'text/css', :rel=>'stylesheet'
12
+ link :href=>Junebug.config['feed'], :rel => "alternate", :title => "Recently Updated Pages", :type => "application/atom+xml"
13
+
14
+ }
15
+ body {
16
+ div :id=>'doc', :class=>'yui-t7' do
17
+ self << yield
18
+ end
19
+ }
20
+ }
21
+ end
22
+
23
+ def show
24
+ _header (@version.version == @page.version ? :backlinks : :show), @page.title
25
+ _body {
26
+ _button 'edit', R(Edit, @page.title, @version.version), {:style=>'float: right; margin: 0 0 5px 5px;'} if logged_in? && (@version.version == @page.version && (! @page.readonly || is_admin?))
27
+ _markup @version.body
28
+ _button 'edit', R(Edit, @page.title, @version.version), {:style=>'float: right; margin: 5px 0 0 5px;'} if logged_in? && (@version.version == @page.version && (! @page.readonly || is_admin?)) && (@version.body && @version.body.size > 200)
29
+ br :clear=>'all'
30
+ }
31
+ _footer {
32
+ text "Last edited by <b>#{@version.user.username}</b> on #{@page.updated_at.strftime('%B %d, %Y %I:%M %p')}"
33
+ text " (#{diff_link(@page, @version)}) " if @version.version > 1
34
+ br
35
+ text '<b>[readonly]</b> ' if @page.readonly
36
+ span.actions {
37
+ text "Version #{@version.version} "
38
+ text "(current) " if @version.version == @page.version
39
+ #text 'Other versions: '
40
+ a '«older', :href => R(Show, @page.title, @version.version-1) unless @version.version == 1
41
+ a 'newer»', :href => R(Show, @page.title, @version.version+1) unless @version.version == @page.version
42
+ a 'current', :href => R(Show, @page.title) unless @version.version == @page.version
43
+ a 'versions', :href => R(Versions, @page.title)
44
+ }
45
+ }
46
+ if is_admin?
47
+ div.admin {
48
+ _button 'delete', R(Delete, @page.title), {:onclick=>"return confirm('Sure you want to delete?')"} if @version.version == @page.version
49
+ _button 'revert to', R(Revert, @page.title, @version.version), {:onclick=>"return confirm('Sure you want to revert?')"} if @version.version != @page.version
50
+ }
51
+ end
52
+ end
53
+
54
+ def edit
55
+ _header :show, @page.title
56
+ _body {
57
+ div.formbox {
58
+ form :method => 'post', :action => R(Edit, @page.title) do
59
+ p {
60
+ label 'Page Title'
61
+ br
62
+ input :value => @page.title, :name => 'post_title', :size => 30,
63
+ :type => 'text'
64
+ small " word characters [0-9A-Za-z_] and spaces only"
65
+ }
66
+ p {
67
+ label 'Page Content'
68
+ br
69
+ textarea @page.body, :name => 'post_body', :rows => 17, :cols => 80
70
+ }
71
+ if is_admin?
72
+ opts = { :type => 'checkbox', :value=>'1', :name => 'post_readonly' }
73
+ opts[:checked] = 1 if @page.readonly
74
+ input opts
75
+ text " Readonly "
76
+ end
77
+ if @page.user_id == @state.user.id
78
+ input :type=>'checkbox', :value=>'1', :name=>'quicksave'
79
+ text " Quicksave "
80
+ end
81
+ br
82
+ input :type => 'submit', :name=>'submit', :value=>'cancel', :class=>'button', :style=>'float: right;'
83
+ input :type => 'submit', :name=>'submit', :value=>'save', :class=>'button', :style=>'float: right;'
84
+ end
85
+ a 'syntax help', :href => 'http://hobix.com/textile/', :target=>'_blank'
86
+ br :clear=>'all'
87
+ }
88
+ }
89
+ _footer { '' }
90
+ end
91
+
92
+ def versions
93
+ _header :show, @page.title
94
+ _body {
95
+ h1 @page_title
96
+ ul {
97
+ @versions.each_with_index do |page,i|
98
+ li {
99
+ a "version #{page.version}", :href => R(Show, @page.title, page.version)
100
+ text " (#{diff_link(@page, page)}) " if page.version > 1
101
+ text' - created '
102
+ text last_updated(page)
103
+ text ' ago by '
104
+ strong page.user.username
105
+ text ' (current)' if @page.version == page.version
106
+ }
107
+ end
108
+ }
109
+ }
110
+ _footer { '' }
111
+ end
112
+
113
+ def backlinks
114
+ _header :show, @page.title
115
+ _body {
116
+ h1 "Backlinks to #{@page.title}"
117
+ ul {
118
+ @pages.each { |p| li{ a p.title, :href => R(Show, p.title) } }
119
+ }
120
+ }
121
+ _footer { '' }
122
+ end
123
+
124
+ def list
125
+ _header :static, @page_title
126
+ _body {
127
+ h1 "All Wiki Pages"
128
+ ul {
129
+ @pages.each { |p| li{ a p.title, :href => R(Show, p.title) } }
130
+ }
131
+ }
132
+ _footer { '' }
133
+ end
134
+
135
+
136
+ def recent
137
+ _header :static, @page_title
138
+ _body {
139
+ h1 "Updates in the last 30 days"
140
+ page = @pages.shift
141
+ while page
142
+ yday = page.updated_at.yday
143
+ h2 page.updated_at.strftime('%B %d, %Y')
144
+ ul {
145
+ loop do
146
+ li {
147
+ a page.title, :href => R(Show, page.title)
148
+ text ' ('
149
+ a 'versions', :href => R(Versions, page.title)
150
+ text ",#{diff_link(page)}" if page.version > 1
151
+ text ') '
152
+ span page.updated_at.strftime('%I:%M %p')
153
+ }
154
+ page = @pages.shift
155
+ break unless page && (page.updated_at.yday == yday)
156
+ end
157
+ }
158
+ end
159
+ }
160
+ _footer { '' }
161
+ end
162
+
163
+ def diff
164
+ _header :show, @page.title
165
+ _body {
166
+ text 'Comparing '
167
+ span "version #{@v2.version}", :style=>"background-color: #cfc; padding: 1px 4px;"
168
+ text ' and '
169
+ span "version #{@v1.version}", :style=>"background-color: #ddd; padding: 1px 4px;"
170
+ text ' '
171
+ a "back", :href => R(Show, @page.title)
172
+ br
173
+ br
174
+ pre.diff {
175
+ text @difftext
176
+ }
177
+ }
178
+ _footer { '' }
179
+ end
180
+
181
+ def login
182
+ div.login {
183
+ h1 @page_title
184
+ p.notice { @notice } if @notice
185
+ form :action => R(Login), :method => 'post' do
186
+ label 'Username', :for => 'username'; br
187
+ input :name => 'username', :type => 'text', :value=>( @user ? @user.username : '') ; br
188
+
189
+ label 'Password', :for => 'password'; br
190
+ input :name => 'password', :type => 'password'; br
191
+
192
+ input :name => 'return_to', :type => 'hidden', :value=>@return_to
193
+
194
+ input :type => 'submit', :name => 'login', :value => 'Login'
195
+ end
196
+ }
197
+ end
198
+
199
+ def _button(text, href, options={})
200
+ form :method=>:get, :action=>href do
201
+ opts = {:type=>'submit', :name=>'submit', :value=>text}.merge(options)
202
+ input.button opts
203
+ end
204
+ end
205
+
206
+ def _markup txt
207
+ return '' if txt.blank?
208
+ titles = Junebug::Models::Page.find(:all, :select => 'title').collect { |p| p.title }
209
+ txt.gsub!(Junebug::Models::Page::PAGE_LINK) do
210
+ page = title = $1
211
+ title = $2 unless $2.empty?
212
+ #page = page.gsub /\W/, '_'
213
+ if titles.include?(page)
214
+ %Q{<a href="#{self/R(Show, page)}">#{title}</a>}
215
+ else
216
+ %Q{<span>#{title}<a href="#{self/R(Edit, page, 1)}">?</a></span>}
217
+ end
218
+ end
219
+ text RedCloth.new(auto_link_urls(txt), [ ]).to_html
220
+ end
221
+
222
+ def _header type, page_title
223
+ div :id=>'hd' do
224
+ span :id=>'userlinks', :style=>'float: right;' do
225
+ logged_in? ? (text "Welcome, #{@state.user.username} - " ; a('sign out', :href=>"#{R(Logout)}?return_to=#{@env['REQUEST_URI']}")) : a('sign in', :href=> "#{R(Login)}?return_to=#{@env['REQUEST_URI']}")
226
+ end
227
+ if type == :static
228
+ h1 page_title
229
+ elsif type == :backlinks
230
+ h1 { a page_title, :href => R(Backlinks, page_title) }
231
+ else
232
+ h1 { a page_title, :href => R(Show, page_title) }
233
+ end
234
+ span {
235
+ a 'Home', :href => R(Show, Junebug.config['startpage'])
236
+ text ' | '
237
+ a 'RecentChanges', :href => R(Recent)
238
+ text ' | '
239
+ a 'All Pages', :href => R(List)
240
+ text ' | '
241
+ a 'Help', :href => R(Show, "JunebugHelp")
242
+ }
243
+ end
244
+ end
245
+
246
+ def _body
247
+ div :id=>'bd' do
248
+ div :id=>'yui-main' do
249
+ div :class=>'yui-b' do
250
+ div.content do
251
+ yield
252
+ end
253
+ end
254
+ end
255
+ end
256
+ end
257
+
258
+ def _footer
259
+ div :id=>'ft' do
260
+ span :style=>'float: right;' do
261
+ text 'Powered by '
262
+ a 'JunebugWiki', :href => 'http://www.junebugwiki.com/'
263
+ text " <small>v#{Junebug::VERSION::STRING}</small> "
264
+ a :href => Junebug.config['feed'] do
265
+ img :src => '/images/feed-icon-14x14.png'
266
+ end
267
+ end
268
+ yield
269
+ br :clear=>'all'
270
+ end
271
+ end
272
+
273
+ def self.feed
274
+ xml = Builder::XmlMarkup.new(:indent => 2)
275
+
276
+ xml.instruct!
277
+ xml.feed "xmlns"=>"http://www.w3.org/2005/Atom" do
278
+
279
+ xml.title Junebug.config['feedtitle'] || "Wiki Updates"
280
+ xml.id Junebug.config['url'] + '/'
281
+ xml.link "rel" => "self", "href" => Junebug.config['feed']
282
+
283
+ pages = Junebug::Models::Page.find(:all, :order => 'updated_at DESC', :limit => 20)
284
+ xml.updated pages.first.updated_at.xmlschema
285
+
286
+ pages.each do |page|
287
+ url = Junebug.config['url'] + '/' + page.title
288
+ xml.entry do
289
+ xml.id url
290
+ xml.title page.title
291
+ xml.updated page.updated_at.xmlschema
292
+
293
+ xml.author { xml.name page.user.username }
294
+ xml.link "rel" => "alternate", "href" => url
295
+ xml.summary :type=>'html' do
296
+ #xml.text! CGI::escapeHTML( %|<a href="#{url}">#{page.title}</a> updated by #{page.user.username} (<a href="#{url}/#{page.version-1}/#{page.version}/diff">diff</a>)| )+"\n"
297
+ xml.text! %|<a href="#{url}">#{page.title}</a> updated by #{page.user.username} (<a href="#{url}/#{page.version-1}/#{page.version}/diff">diff</a>)| +"\n"
298
+ end
299
+ # xml.content do
300
+ # xml.text! CGI::escapeHTML(page.body)+"\n"
301
+ # end
302
+ end
303
+ end
304
+ end
305
+ end
306
+
307
+ end