typo 3.99.1 → 3.99.2
Sign up to get free protection for your applications and to get access to all the features.
- data/app/controllers/articles_controller.rb +2 -1
- data/app/models/blog.rb +3 -3
- data/app/models/comment.rb +2 -3
- data/app/models/content_observer.rb +1 -1
- data/bin/typo +36 -1
- data/components/plugins/textfilters/flickr_controller.rb +1 -1
- data/components/plugins/textfilters/textile_and_markdown_controller.rb +8 -0
- data/components/plugins/textfilters/textile_controller.rb +1 -1
- data/doc/Installer.txt +9 -0
- data/installer/apache13.conf.example.template +33 -0
- data/installer/apache20.conf.example.template +40 -0
- data/installer/lighttpd.conf.example.template +6 -0
- data/installer/rails-installer.rb +37 -6
- data/lib/sanitize.rb +107 -0
- data/lib/tasks/release.rake +1 -1
- data/lib/typo_version.rb +1 -1
- data/test/functional/articles_controller_test.rb +6 -4
- data/test/unit/trigger_test.rb +2 -2
- metadata +7 -26
- data/cache/META/DATA/ACTION_PARAM/localhost.3000/articles/index/.cache +0 -540
- data/cache/META/DATA/ACTION_PARAM/localhost.3000/articles/permalink/day=09&month=09&title=i-need-a-new-keyboard-and-mouse&year=2005.cache +0 -414
- data/cache/META/META/ACTION_PARAM/localhost.3000/articles/index/.cache +0 -3
- data/cache/META/META/ACTION_PARAM/localhost.3000/articles/permalink/day=09&month=09&title=i-need-a-new-keyboard-and-mouse&year=2005.cache +0 -3
- data/installer/typo-installer.rb +0 -35
@@ -22,7 +22,7 @@ class ArticlesController < ContentController
|
|
22
22
|
:conditions =>
|
23
23
|
['published = ? AND contents.created_at < ? AND blog_id = ?',
|
24
24
|
true, Time.now, this_blog.id],
|
25
|
-
:order_by => "contents.
|
25
|
+
:order_by => "contents.published_at DESC",
|
26
26
|
:include => [:categories, :tags])
|
27
27
|
end
|
28
28
|
|
@@ -200,6 +200,7 @@ class ArticlesController < ContentController
|
|
200
200
|
def render_grouping(klass)
|
201
201
|
return list_groupings(klass) unless params[:id]
|
202
202
|
|
203
|
+
@page_title = "#{this_blog.blog_name} - #{klass.to_s.underscore} #{params[:id]}"
|
203
204
|
@articles = klass.find_by_permalink(params[:id]).articles.find_already_published rescue []
|
204
205
|
auto_discovery_feed :type => klass.to_s.underscore, :id => params[:id]
|
205
206
|
render_paginated_index("Can't find posts with #{klass.to_prefix} '#{h(params[:id])}'")
|
data/app/models/blog.rb
CHANGED
@@ -138,9 +138,9 @@ class Blog < ActiveRecord::Base
|
|
138
138
|
end
|
139
139
|
|
140
140
|
def article_url(article, only_path = true, anchor = nil)
|
141
|
-
|
142
|
-
:month => sprintf("%.2d", article.
|
143
|
-
:day => sprintf("%.2d", article.
|
141
|
+
url_for(:year => article.published_at.year,
|
142
|
+
:month => sprintf("%.2d", article.published_at.month),
|
143
|
+
:day => sprintf("%.2d", article.published_at.day),
|
144
144
|
:title => article.permalink, :anchor => anchor,
|
145
145
|
:only_path => only_path)
|
146
146
|
end
|
data/app/models/comment.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require_dependency 'spam_protection'
|
2
|
+
require 'sanitize'
|
2
3
|
|
3
4
|
class Comment < Content
|
4
5
|
include TypoGuid
|
@@ -48,10 +49,8 @@ class Comment < Content
|
|
48
49
|
end
|
49
50
|
end
|
50
51
|
|
51
|
-
|
52
|
-
|
53
52
|
def body_html_postprocess(value, controller)
|
54
|
-
|
53
|
+
sanitize(controller.send(:auto_link, value),'a href, b, br, i, p, em, strong, pre, code')
|
55
54
|
end
|
56
55
|
|
57
56
|
def default_text_filter_config_key
|
data/bin/typo
CHANGED
@@ -1,6 +1,41 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require 'installer/
|
3
|
+
require 'installer/rails-installer'
|
4
|
+
|
5
|
+
class TypoInstaller < RailsInstaller
|
6
|
+
application_name 'typo'
|
7
|
+
support_location 'the Typo mailing list'
|
8
|
+
rails_version '1.1.4'
|
9
|
+
|
10
|
+
def install_sequence
|
11
|
+
stop
|
12
|
+
|
13
|
+
backup_database
|
14
|
+
pre_migrate_database
|
15
|
+
copy_files
|
16
|
+
freeze_rails
|
17
|
+
create_default_config_files
|
18
|
+
create_directories
|
19
|
+
create_initial_database
|
20
|
+
set_initial_port_number
|
21
|
+
expand_template_files
|
22
|
+
|
23
|
+
migrate
|
24
|
+
sweep_cache
|
25
|
+
save
|
26
|
+
|
27
|
+
run_rails_tests
|
28
|
+
|
29
|
+
start
|
30
|
+
end
|
31
|
+
|
32
|
+
# Sweep the cache
|
33
|
+
def sweep_cache
|
34
|
+
Dir.chdir(install_directory)
|
35
|
+
message "Cleaning out #{@@app_name.capitalize}'s cache"
|
36
|
+
status = system("rake -s sweep_cache > /dev/null 2> /dev/null")
|
37
|
+
end
|
38
|
+
end
|
4
39
|
|
5
40
|
# Installer program
|
6
41
|
directory = ARGV[1]
|
@@ -55,7 +55,7 @@ This macro takes a number of parameters:
|
|
55
55
|
imageurl = details['source']
|
56
56
|
imagelink = flickrimage.url
|
57
57
|
|
58
|
-
caption ||= flickrimage.description
|
58
|
+
caption ||= sanitize(CGI.unescapeHTML(flickrimage.description))
|
59
59
|
title ||= flickrimage.title
|
60
60
|
alt ||= title
|
61
61
|
|
@@ -0,0 +1,8 @@
|
|
1
|
+
class Plugins::Textfilters::TextileAndMarkdownController < TextFilterPlugin::Markup
|
2
|
+
plugin_display_name "Textile with Markdown"
|
3
|
+
plugin_description 'Textile and Markdown markup languages'
|
4
|
+
|
5
|
+
def self.filtertext(controller,content,text,params)
|
6
|
+
RedCloth.new(text).to_html
|
7
|
+
end
|
8
|
+
end
|
data/doc/Installer.txt
CHANGED
@@ -24,6 +24,7 @@ You'll need the following software installed on your system:
|
|
24
24
|
|
25
25
|
Most modern Unix systems should have all four of these easily available in pre-built form.
|
26
26
|
|
27
|
+
|
27
28
|
Installing Typo
|
28
29
|
---------------
|
29
30
|
|
@@ -46,6 +47,14 @@ Installing Typo
|
|
46
47
|
installer/apache.conf.example to run your Typo installation under Apache.
|
47
48
|
|
48
49
|
|
50
|
+
Starting and Stopping Typo
|
51
|
+
--------------------------
|
52
|
+
|
53
|
+
To stop Typo from running, run `typo stop /path/to/typo`. To restart it, run
|
54
|
+
`typo start /path/to/typo`. Since this Typo install uses Mongrel, not FastCGI,
|
55
|
+
you can't depend on your web server restarting it automatically on reboot. You'll need to either create an init script or cron reboot entry to restart it, depending on your host and/or privilege level.
|
56
|
+
|
57
|
+
|
49
58
|
TODO
|
50
59
|
----
|
51
60
|
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# Apache 1.3 HTTP proxy example for Typo with Mongrel
|
2
|
+
#
|
3
|
+
# Before any of this will work, you need to make sure that the mod_proxy
|
4
|
+
# modules are loaded. For shared hosting, your provider will have do this
|
5
|
+
# globally. The line required looks like this, although the exact path may
|
6
|
+
# vary:
|
7
|
+
#
|
8
|
+
# LoadModule proxy_module /usr/lib/apache/modules/mod_proxy.so
|
9
|
+
#
|
10
|
+
# Then you'll want a VirtualHost section that looks about like this:
|
11
|
+
|
12
|
+
<VirtualHost blog.example.com>
|
13
|
+
ServerName blog.example.com
|
14
|
+
ServerAlias www.blog.example.com
|
15
|
+
|
16
|
+
# Change this to your email address
|
17
|
+
ServerAdmin webmaster@localhost
|
18
|
+
|
19
|
+
# Change these to be valid paths for your host. The DocumentRoot path
|
20
|
+
# isn't very important because we don't actually use it for anything.
|
21
|
+
# For security's sake, it's best that it points to an empty directory,
|
22
|
+
# but that's not critical.
|
23
|
+
DocumentRoot /var/www/blog
|
24
|
+
ErrorLog /var/log/apache2/blog_error.log
|
25
|
+
CustomLog /var/log/apache2/blog_access.log combined
|
26
|
+
|
27
|
+
ServerSignature On
|
28
|
+
|
29
|
+
# This is the important part--it sets up proxying.
|
30
|
+
ProxyRequests Off
|
31
|
+
ProxyPass / $RAILS_URL
|
32
|
+
ProxyPassReverse / $RAILS_URL
|
33
|
+
</VirtualHost>
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# Apache 2.0/2.2 HTTP proxy example for Typo with Mongrel
|
2
|
+
#
|
3
|
+
# Before any of this will work, you need to make sure that the mod_proxy and
|
4
|
+
# mod_proxy_http modules are loaded. For shared hosting, your provider will
|
5
|
+
# have do this globally. The two lines required look like this, although the
|
6
|
+
# exact path may vary:
|
7
|
+
#
|
8
|
+
# LoadModule proxy_module /usr/lib/apache2/modules/mod_proxy.so
|
9
|
+
# LoadModule proxy_http_module /usr/lib/apache2/modules/mod_proxy_http.so
|
10
|
+
#
|
11
|
+
# Then you'll want a VirtualHost section that looks about like this:
|
12
|
+
|
13
|
+
<VirtualHost blog.example.com>
|
14
|
+
ServerName blog.example.com
|
15
|
+
ServerAlias www.blog.example.com
|
16
|
+
|
17
|
+
# Change this to your email address
|
18
|
+
ServerAdmin webmaster@localhost
|
19
|
+
|
20
|
+
# Change these to be valid paths for your host. The DocumentRoot path
|
21
|
+
# isn't very important because we don't actually use it for anything.
|
22
|
+
# For security's sake, it's best that it points to an empty directory,
|
23
|
+
# but that's not critical.
|
24
|
+
DocumentRoot /var/www/blog
|
25
|
+
ErrorLog /var/log/apache2/blog_error.log
|
26
|
+
CustomLog /var/log/apache2/blog_access.log combined
|
27
|
+
|
28
|
+
ServerSignature On
|
29
|
+
|
30
|
+
# This is the important part--it sets up proxying.
|
31
|
+
ProxyRequests Off
|
32
|
+
<Proxy *>
|
33
|
+
Order deny,allow
|
34
|
+
Allow from all
|
35
|
+
</Proxy>
|
36
|
+
|
37
|
+
ProxyPass / $RAILS_URL
|
38
|
+
ProxyPassReverse / $RAILS_URL
|
39
|
+
ProxyPreserveHost On
|
40
|
+
</VirtualHost>
|
@@ -26,7 +26,8 @@ class RailsInstaller
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def initialize(install_directory)
|
29
|
-
|
29
|
+
# use an absolute path, not a relative path.
|
30
|
+
@install_directory = File.expand_path(install_directory)
|
30
31
|
|
31
32
|
@config = read_yml(config_file) rescue nil
|
32
33
|
@config ||= Hash.new
|
@@ -54,7 +55,7 @@ class RailsInstaller
|
|
54
55
|
message ''
|
55
56
|
message "#{@@app_name.capitalize} is now running on http://#{`hostname`.chomp}:#{config['port-number']}"
|
56
57
|
message "Use '#{@@app_name} start #{install_directory}' to restart after boot."
|
57
|
-
message "Look in installer
|
58
|
+
message "Look in installer/*.conf.example to see how to integrate with your web server."
|
58
59
|
end
|
59
60
|
|
60
61
|
# The default install sequence. Override this if you need to add extra
|
@@ -70,6 +71,7 @@ class RailsInstaller
|
|
70
71
|
create_directories
|
71
72
|
create_initial_database
|
72
73
|
set_initial_port_number
|
74
|
+
expand_template_files
|
73
75
|
|
74
76
|
migrate
|
75
77
|
save
|
@@ -124,6 +126,8 @@ class RailsInstaller
|
|
124
126
|
# Backup the database
|
125
127
|
def backup_database
|
126
128
|
return unless File.exists? db_file
|
129
|
+
Dir.chdir(install_directory)
|
130
|
+
|
127
131
|
return unless config['database'] == 'sqlite'
|
128
132
|
new_db_file = db_file+"-#{Time.now.strftime('%Y%m%d-%H%M')}.sql"
|
129
133
|
|
@@ -139,6 +143,7 @@ class RailsInstaller
|
|
139
143
|
|
140
144
|
message "Reading files from #{source_directory}"
|
141
145
|
new_files = sha1_hash_directory_tree(source_directory)
|
146
|
+
new_files.delete('/config/database.yml') # Never copy this.
|
142
147
|
|
143
148
|
# Next, we compare the original install hash to the current hash. For each
|
144
149
|
# entry:
|
@@ -179,6 +184,7 @@ class RailsInstaller
|
|
179
184
|
end
|
180
185
|
|
181
186
|
write_yml(files_yml,new_files)
|
187
|
+
Dir.chdir(install_directory)
|
182
188
|
end
|
183
189
|
|
184
190
|
# Copy one file from source_directory to install_directory, creating directories as needed.
|
@@ -343,7 +349,6 @@ class RailsInstaller
|
|
343
349
|
|
344
350
|
return unless old_schema_version > 0
|
345
351
|
|
346
|
-
Dir.chdir(install_directory)
|
347
352
|
|
348
353
|
# Are we downgrading?
|
349
354
|
if old_schema_version > new_schema_version
|
@@ -358,7 +363,6 @@ class RailsInstaller
|
|
358
363
|
|
359
364
|
# Migrate the database
|
360
365
|
def migrate
|
361
|
-
Dir.chdir(install_directory)
|
362
366
|
message "Migrating #{@@app_name.capitalize}'s database to newest release"
|
363
367
|
status = system("rake -s migrate")
|
364
368
|
|
@@ -369,9 +373,8 @@ class RailsInstaller
|
|
369
373
|
|
370
374
|
# Sweep the cache
|
371
375
|
def run_rails_tests
|
372
|
-
Dir.chdir(install_directory)
|
373
376
|
message "Running tests. This may take a minute or two"
|
374
|
-
status =
|
377
|
+
status = system_silently("rake -s test")
|
375
378
|
|
376
379
|
if status
|
377
380
|
message "All tests pass. Congratulations."
|
@@ -433,6 +436,34 @@ class RailsInstaller
|
|
433
436
|
specs.last.full_gem_path
|
434
437
|
end
|
435
438
|
|
439
|
+
def system_silently(command)
|
440
|
+
if RUBY_PLATFORM =~ /mswin32/
|
441
|
+
null = 'NUL:'
|
442
|
+
else
|
443
|
+
null = '/dev/null'
|
444
|
+
end
|
445
|
+
|
446
|
+
system("#{command} > #{null} 2> #{null}")
|
447
|
+
end
|
448
|
+
|
449
|
+
def expand_template_files
|
450
|
+
rails_host = config['bind-address'] || `hostname`.chomp
|
451
|
+
rails_port = config['port-number'].to_s
|
452
|
+
rails_url = "http://#{rails_host}:#{rails_port}"
|
453
|
+
Dir[File.join(install_directory,'installer','*.template')].each do |template_file|
|
454
|
+
output_file = template_file.gsub(/\.template/,'')
|
455
|
+
next if File.exists?(output_file) # don't overwrite files
|
456
|
+
|
457
|
+
message "expanding #{File.basename(output_file)} template"
|
458
|
+
|
459
|
+
text = File.read(template_file).gsub(/\$RAILS_URL/,rails_url).gsub(/\$RAILS_HOST/,rails_host).gsub(/\$RAILS_PORT/,rails_port)
|
460
|
+
|
461
|
+
File.open(output_file,'w') do |f|
|
462
|
+
f.write text
|
463
|
+
end
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
436
467
|
# Execute a command-line command
|
437
468
|
def execute_command(*args)
|
438
469
|
if args.size < 2
|
data/lib/sanitize.rb
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
#
|
2
|
+
# $Id: sanitize.rb 3 2005-04-05 12:51:14Z dwight $
|
3
|
+
#
|
4
|
+
# Copyright (c) 2005 Dwight Shih
|
5
|
+
# A derived work of the Perl version:
|
6
|
+
# Copyright (c) 2002 Brad Choate, bradchoate.com
|
7
|
+
#
|
8
|
+
# Permission is hereby granted, free of charge, to
|
9
|
+
# any person obtaining a copy of this software and
|
10
|
+
# associated documentation files (the "Software"), to
|
11
|
+
# deal in the Software without restriction, including
|
12
|
+
# without limitation the rights to use, copy, modify,
|
13
|
+
# merge, publish, distribute, sublicense, and/or sell
|
14
|
+
# copies of the Software, and to permit persons to
|
15
|
+
# whom the Software is furnished to do so, subject to
|
16
|
+
# the following conditions:
|
17
|
+
#
|
18
|
+
# The above copyright notice and this permission
|
19
|
+
# notice shall be included in all copies or
|
20
|
+
# substantial portions of the Software.
|
21
|
+
#
|
22
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY
|
23
|
+
# OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
24
|
+
# LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
25
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND
|
26
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
27
|
+
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
28
|
+
# OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
29
|
+
# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
|
30
|
+
# OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
31
|
+
# OTHER DEALINGS IN THE SOFTWARE.
|
32
|
+
#
|
33
|
+
|
34
|
+
def sanitize( html, okTags='a href, b, br, i, p' )
|
35
|
+
# no closing tag necessary for these
|
36
|
+
soloTags = ["br","hr"]
|
37
|
+
|
38
|
+
# Build hash of allowed tags with allowed attributes
|
39
|
+
tags = okTags.downcase().split(',').collect!{ |s| s.split(' ') }
|
40
|
+
allowed = Hash.new
|
41
|
+
tags.each do |s|
|
42
|
+
key = s.shift
|
43
|
+
allowed[key] = s
|
44
|
+
end
|
45
|
+
|
46
|
+
# Analyze all <> elements
|
47
|
+
stack = Array.new
|
48
|
+
result = html.gsub( /(<.*?>)/m ) do | element |
|
49
|
+
if element =~ /\A<\/(\w+)/ then
|
50
|
+
# </tag>
|
51
|
+
tag = $1.downcase
|
52
|
+
if allowed.include?(tag) && stack.include?(tag) then
|
53
|
+
# If allowed and on the stack
|
54
|
+
# Then pop down the stack
|
55
|
+
top = stack.pop
|
56
|
+
out = "</#{top}>"
|
57
|
+
until top == tag do
|
58
|
+
top = stack.pop
|
59
|
+
out << "</#{top}>"
|
60
|
+
end
|
61
|
+
out
|
62
|
+
end
|
63
|
+
elsif element =~ /\A<(\w+)\s*\/>/
|
64
|
+
# <tag />
|
65
|
+
tag = $1.downcase
|
66
|
+
if allowed.include?(tag) then
|
67
|
+
"<#{tag} />"
|
68
|
+
end
|
69
|
+
elsif element =~ /\A<(\w+)/ then
|
70
|
+
# <tag ...>
|
71
|
+
tag = $1.downcase
|
72
|
+
if allowed.include?(tag) then
|
73
|
+
if ! soloTags.include?(tag) then
|
74
|
+
stack.push(tag)
|
75
|
+
end
|
76
|
+
if allowed[tag].length == 0 then
|
77
|
+
# no allowed attributes
|
78
|
+
"<#{tag}>"
|
79
|
+
else
|
80
|
+
# allowed attributes?
|
81
|
+
out = "<#{tag}"
|
82
|
+
while ( $' =~ /(\w+)=("[^"]+")/ )
|
83
|
+
attr = $1.downcase
|
84
|
+
valu = $2
|
85
|
+
if allowed[tag].include?(attr) then
|
86
|
+
out << " #{attr}=#{valu}"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
out << ">"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# eat up unmatched leading >
|
96
|
+
while result.sub!(/\A([^<]*)>/m) { $1 } do end
|
97
|
+
|
98
|
+
# eat up unmatched trailing <
|
99
|
+
while result.sub!(/<([^>]*)\Z/m) { $1 } do end
|
100
|
+
|
101
|
+
# clean up the stack
|
102
|
+
if stack.length > 0 then
|
103
|
+
result << "</#{stack.reverse.join('></')}>"
|
104
|
+
end
|
105
|
+
|
106
|
+
result
|
107
|
+
end
|