junebug-wiki 0.0.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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