radiant-downloads-extension 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/README.markdown +81 -0
- data/Rakefile +137 -0
- data/app/controllers/admin/downloads_controller.rb +3 -0
- data/app/controllers/downloads_controller.rb +21 -0
- data/app/models/download.rb +33 -0
- data/app/views/admin/downloads/_form.html.haml +47 -0
- data/app/views/admin/downloads/edit.html.haml +7 -0
- data/app/views/admin/downloads/index.html.haml +32 -0
- data/app/views/admin/downloads/new.html.haml +7 -0
- data/db/migrate/001_create_downloads.rb +28 -0
- data/downloads_extension.rb +39 -0
- data/lib/download_group.rb +7 -0
- data/lib/download_tags.rb +165 -0
- data/lib/download_ui.rb +38 -0
- data/lib/tasks/downloads_extension_tasks.rake +28 -0
- data/public/stylesheets/admin/downloads.css +25 -0
- data/spec/controllers/downloads_controller_spec.rb +73 -0
- data/spec/datasets/download_groups_dataset.rb +42 -0
- data/spec/datasets/download_readers_dataset.rb +49 -0
- data/spec/datasets/download_sites_dataset.rb +9 -0
- data/spec/datasets/downloads_dataset.rb +28 -0
- data/spec/files/test.pdf +681 -0
- data/spec/files/test.txt +1 -0
- data/spec/models/download_spec.rb +41 -0
- data/spec/models/group_spec.rb +20 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +36 -0
- metadata +130 -0
@@ -0,0 +1,165 @@
|
|
1
|
+
module DownloadTags
|
2
|
+
include Radiant::Taggable
|
3
|
+
|
4
|
+
class TagError < StandardError; end
|
5
|
+
|
6
|
+
# the root group tag is defined in reader_group and should only expand if there is a page group or message group
|
7
|
+
|
8
|
+
desc %{
|
9
|
+
Expands if this group has any downloads.
|
10
|
+
|
11
|
+
<pre><code><r:group:if_downloads>...</r:group:if_downloads /></code></pre>
|
12
|
+
}
|
13
|
+
tag "group:if_downloads" do |tag|
|
14
|
+
tag.expand if tag.locals.group.downloads.any?
|
15
|
+
end
|
16
|
+
|
17
|
+
desc %{
|
18
|
+
Expands if this group does not have any downloads.
|
19
|
+
|
20
|
+
<pre><code><r:group:unless_downloads>...</r:group:unless_downloads /></code></pre>
|
21
|
+
}
|
22
|
+
tag "group:unless_downloads" do |tag|
|
23
|
+
tag.expand unless tag.locals.group.downloads.any?
|
24
|
+
end
|
25
|
+
|
26
|
+
desc %{
|
27
|
+
Cycles through all downloads for the current group.
|
28
|
+
(which will only be defined if this is the home page for a group)
|
29
|
+
|
30
|
+
*Usage:*
|
31
|
+
<pre><code><r:group:downloads:each>...</r:group:downloads:each></code></pre>
|
32
|
+
}
|
33
|
+
tag 'group:downloads' do |tag|
|
34
|
+
tag.expand if tag.locals.group
|
35
|
+
end
|
36
|
+
tag 'group:downloads:each' do |tag|
|
37
|
+
result = []
|
38
|
+
tag.locals.group.downloads.each do |download|
|
39
|
+
tag.locals.download = download
|
40
|
+
result << tag.expand
|
41
|
+
end
|
42
|
+
result
|
43
|
+
end
|
44
|
+
|
45
|
+
desc %{
|
46
|
+
Expands if the current reader has any downloads.
|
47
|
+
|
48
|
+
<pre><code><r:reader:if_downloads>...</r:reader:if_downloads /></code></pre>
|
49
|
+
}
|
50
|
+
tag "reader:if_downloads" do |tag|
|
51
|
+
tag.expand if tag.locals.reader.downloads.any?
|
52
|
+
end
|
53
|
+
|
54
|
+
desc %{
|
55
|
+
Expands if the current reader does not have any downloads.
|
56
|
+
|
57
|
+
<pre><code><r:reader:unless_downloads>...</r:reader:unless_downloads /></code></pre>
|
58
|
+
}
|
59
|
+
tag "reader:unless_downloads" do |tag|
|
60
|
+
tag.expand unless tag.locals.reader.downloads.any?
|
61
|
+
end
|
62
|
+
|
63
|
+
desc %{
|
64
|
+
Cycles through all downloads for the current reader.
|
65
|
+
|
66
|
+
*Usage:*
|
67
|
+
<pre><code><r:reader:downloads:each>...</r:reader:downloads:each></code></pre>
|
68
|
+
}
|
69
|
+
tag 'reader:downloads' do |tag|
|
70
|
+
tag.locals.reader ||= current_reader
|
71
|
+
tag.expand if tag.locals.reader
|
72
|
+
end
|
73
|
+
tag 'reader:downloads:each' do |tag|
|
74
|
+
result = []
|
75
|
+
tag.locals.reader.downloads.each do |download|
|
76
|
+
tag.locals.download = download
|
77
|
+
result << tag.expand
|
78
|
+
end
|
79
|
+
result
|
80
|
+
end
|
81
|
+
|
82
|
+
desc %{
|
83
|
+
The root 'download' tag is not meant to be called directly.
|
84
|
+
All it does is summon a download object so that its fields can be displayed with eg.
|
85
|
+
<pre><code><r:download:url /></code></pre>
|
86
|
+
}
|
87
|
+
|
88
|
+
tag 'download' do |tag|
|
89
|
+
tag.expand
|
90
|
+
# tag.locals.download ||= _get_download(tag)
|
91
|
+
# if tag.locals.download
|
92
|
+
# tag.expand
|
93
|
+
# else
|
94
|
+
# %{No download found with id '#{tag.attr['id']}' or title '#{tag.attr['title']}'}
|
95
|
+
# end
|
96
|
+
end
|
97
|
+
|
98
|
+
desc %{
|
99
|
+
Returns title of current download.
|
100
|
+
|
101
|
+
*Usage:*
|
102
|
+
<pre><code><r:download:title /></code></pre>
|
103
|
+
}
|
104
|
+
tag 'download:title' do |tag|
|
105
|
+
if download = _get_download(tag)
|
106
|
+
download.name
|
107
|
+
end
|
108
|
+
end
|
109
|
+
tag 'download:name' do |tag|
|
110
|
+
if download = _get_download(tag)
|
111
|
+
download.name
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
desc %{
|
116
|
+
Returns description of current download.
|
117
|
+
|
118
|
+
*Usage:*
|
119
|
+
<pre><code><r:download:description /></code></pre>
|
120
|
+
}
|
121
|
+
tag 'download:description' do |tag|
|
122
|
+
if download = _get_download(tag)
|
123
|
+
download.description
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
desc %{
|
128
|
+
Returns the secure url of the current download.
|
129
|
+
|
130
|
+
*Usage:*
|
131
|
+
<pre><code><a href="<r:download:url id="4" />">...</a></code></pre>
|
132
|
+
}
|
133
|
+
tag 'download:url' do |tag|
|
134
|
+
if download = _get_download(tag)
|
135
|
+
download_path(download)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
desc %{
|
140
|
+
Returns a link to the current download.
|
141
|
+
Attributes and enclosed link text are passed through in the usual way.
|
142
|
+
|
143
|
+
*Usage:*
|
144
|
+
<pre><code><r:download:link /></code></pre>
|
145
|
+
}
|
146
|
+
tag 'download:link' do |tag|
|
147
|
+
tag.locals.download = _get_download(tag)
|
148
|
+
options = tag.attr.dup
|
149
|
+
attributes = options.inject('') { |s, (k, v)| s << %{#{k.downcase}="#{v}" } }.strip
|
150
|
+
attributes = " #{attributes}" unless attributes.empty?
|
151
|
+
text = tag.double? ? tag.expand : tag.render('download:title')
|
152
|
+
%{<a href="#{tag.render('download:url')}"#{attributes}>#{text}</a>}
|
153
|
+
end
|
154
|
+
|
155
|
+
|
156
|
+
private
|
157
|
+
|
158
|
+
def _get_download(tag)
|
159
|
+
download = tag.locals.download
|
160
|
+
download ||= Download.find_by_id(tag.attr.delete('id')) if tag.attr['id']
|
161
|
+
download ||= Download.find_by_title(tag.attr.delete('title')) if tag.attr['title']
|
162
|
+
download
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
data/lib/download_ui.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
module DownloadUI
|
2
|
+
|
3
|
+
def self.included(base)
|
4
|
+
base.class_eval do
|
5
|
+
|
6
|
+
attr_accessor :download
|
7
|
+
alias_method :downloads, :download
|
8
|
+
|
9
|
+
def load_default_regions_with_download
|
10
|
+
load_default_regions_without_download
|
11
|
+
@download = load_default_download_regions
|
12
|
+
end
|
13
|
+
|
14
|
+
alias_method_chain :load_default_regions, :download
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
def load_default_download_regions
|
19
|
+
returning OpenStruct.new do |download|
|
20
|
+
download.edit = Radiant::AdminUI::RegionSet.new do |edit|
|
21
|
+
edit.main.concat %w{edit_header edit_form}
|
22
|
+
edit.form.concat %w{edit_title edit_description edit_document edit_access}
|
23
|
+
edit.form_bottom.concat %w{edit_timestamp edit_buttons}
|
24
|
+
end
|
25
|
+
download.index = Radiant::AdminUI::RegionSet.new do |index|
|
26
|
+
index.thead.concat %w{name_header document_header access_header modify_header}
|
27
|
+
index.tbody.concat %w{name_cell document_cell access_cell modify_cell}
|
28
|
+
index.bottom.concat %w{new_button}
|
29
|
+
end
|
30
|
+
download.remove = download.index
|
31
|
+
download.new = download.edit
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
namespace :radiant do
|
2
|
+
namespace :extensions do
|
3
|
+
namespace :downloads do
|
4
|
+
|
5
|
+
desc "Runs the migration of the Downloads extension"
|
6
|
+
task :migrate => :environment do
|
7
|
+
require 'radiant/extension_migrator'
|
8
|
+
if ENV["VERSION"]
|
9
|
+
DownloadsExtension.migrator.migrate(ENV["VERSION"].to_i)
|
10
|
+
else
|
11
|
+
DownloadsExtension.migrator.migrate
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "Copies public assets of the Downloads to the instance public/ directory."
|
16
|
+
task :update => :environment do
|
17
|
+
is_svn_or_dir = proc {|path| path =~ /\.svn/ || File.directory?(path) }
|
18
|
+
puts "Copying assets from DownloadsExtension"
|
19
|
+
Dir[DownloadsExtension.root + "/public/**/*"].reject(&is_svn_or_dir).each do |file|
|
20
|
+
path = file.sub(DownloadsExtension.root, '')
|
21
|
+
directory = File.dirname(path)
|
22
|
+
mkdir_p RAILS_ROOT + directory
|
23
|
+
cp file, RAILS_ROOT + path
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
/* submenu admin */
|
2
|
+
|
3
|
+
#content table.index .node .download {
|
4
|
+
font-size:115%;
|
5
|
+
font-weight:bold;
|
6
|
+
}
|
7
|
+
|
8
|
+
#content table.index .node .download a {
|
9
|
+
color: black;
|
10
|
+
}
|
11
|
+
|
12
|
+
#content .form-area .user select {
|
13
|
+
font-family:Georgia,Palatino,"Times New Roman",Times,serif;
|
14
|
+
font-size:150%;
|
15
|
+
width:60%;
|
16
|
+
}
|
17
|
+
|
18
|
+
#content .form-area p.description {
|
19
|
+
margin-top: 10px;
|
20
|
+
}
|
21
|
+
|
22
|
+
#content .form-area span.formnote {
|
23
|
+
color: #7d796c;
|
24
|
+
font-size: 85%;
|
25
|
+
}
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe DownloadsController do
|
4
|
+
dataset :download_groups
|
5
|
+
|
6
|
+
before do
|
7
|
+
controller.stub!(:request).and_return(request)
|
8
|
+
Page.current_site = sites(:test) if defined? Site
|
9
|
+
request.env["HTTP_REFERER"] = 'http://test.host/referer!'
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "with a protected download" do
|
13
|
+
describe "and no reader" do
|
14
|
+
before do
|
15
|
+
logout_reader
|
16
|
+
get :show, :id => download_id(:grouped)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should redirect to login" do
|
20
|
+
response.should be_redirect
|
21
|
+
response.should redirect_to(reader_login_url)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should set the right return_to url" do
|
25
|
+
session[:return_to].should == request.request_uri
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "and a logged-in reader" do
|
30
|
+
describe "who is not in a permitted group" do
|
31
|
+
before do
|
32
|
+
login_as_reader(:ungrouped)
|
33
|
+
get :show, :id => download_id(:grouped)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should redirect to the permission denied page" do
|
37
|
+
response.should be_success
|
38
|
+
response.should render_template('site/not_allowed')
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "who is in a permitted group" do
|
43
|
+
before do
|
44
|
+
login_as_reader(:normal)
|
45
|
+
get :show, :id => download_id(:grouped)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should return an internal redirect" do
|
49
|
+
dl = downloads(:grouped)
|
50
|
+
response.should be_success
|
51
|
+
response.headers.should include('X-Accel-Redirect');
|
52
|
+
response.headers['X-Accel-Redirect'].should == dl.document.path
|
53
|
+
response.headers['Content-Disposition'].should =~ /^attachment/
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "with an ungrouped download" do
|
60
|
+
describe "and no reader" do
|
61
|
+
before do
|
62
|
+
logout_reader
|
63
|
+
get :show, :id => download_id(:grouped)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should refuse access" do
|
67
|
+
response.should be_redirect
|
68
|
+
response.should redirect_to(reader_login_url)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'digest/sha1'
|
2
|
+
class DownloadGroupsDataset < Dataset::Base
|
3
|
+
datasets = [:downloads, :download_readers]
|
4
|
+
datasets << :download_sites if defined? Site
|
5
|
+
uses *datasets
|
6
|
+
|
7
|
+
def load
|
8
|
+
create_group "Normal"
|
9
|
+
create_group "Busy"
|
10
|
+
create_group "Idle"
|
11
|
+
add_downloads_to_group :normal, [:grouped]
|
12
|
+
add_downloads_to_group :busy, [:grouped, :alsogrouped]
|
13
|
+
add_readers_to_group :busy, [:normal, :another]
|
14
|
+
end
|
15
|
+
|
16
|
+
helpers do
|
17
|
+
def create_group(name, att={})
|
18
|
+
group = create_record Group, name.symbolize, group_attributes(att.update(:name => name))
|
19
|
+
end
|
20
|
+
|
21
|
+
def group_attributes(att={})
|
22
|
+
name = att[:name] || "A group"
|
23
|
+
attributes = {
|
24
|
+
:name => name,
|
25
|
+
:description => "Test group"
|
26
|
+
}.merge(att)
|
27
|
+
attributes[:site_id] ||= site_id(:test) if defined? Site
|
28
|
+
attributes
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def add_readers_to_group(g, rr)
|
33
|
+
g = g.is_a?(Group) ? g : groups(g)
|
34
|
+
g.readers << rr.map{|r| readers(r)}
|
35
|
+
end
|
36
|
+
|
37
|
+
def add_downloads_to_group(g, dd)
|
38
|
+
g = g.is_a?(Group) ? g : groups(g)
|
39
|
+
g.downloads << dd.map{|d| downloads(d)}
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require "authlogic/test_case"
|
2
|
+
|
3
|
+
class DownloadReadersDataset < Dataset::Base
|
4
|
+
uses :download_sites if defined? Site
|
5
|
+
|
6
|
+
def load
|
7
|
+
create_reader "Normal"
|
8
|
+
create_reader "Another"
|
9
|
+
create_reader "Ungrouped"
|
10
|
+
end
|
11
|
+
|
12
|
+
helpers do
|
13
|
+
def create_reader(name, attributes={})
|
14
|
+
attributes = reader_attributes(attributes.update(:name => name))
|
15
|
+
reader = create_model Reader, name.symbolize, attributes
|
16
|
+
end
|
17
|
+
|
18
|
+
def reader_attributes(attributes={})
|
19
|
+
name = attributes[:name] || "John Doe"
|
20
|
+
symbol = name.symbolize
|
21
|
+
attributes = {
|
22
|
+
:name => name,
|
23
|
+
:email => "#{symbol}@spanner.org",
|
24
|
+
:login => "#{symbol}@spanner.org",
|
25
|
+
:activated_at => Time.now - 1.week,
|
26
|
+
:password_salt => "golly",
|
27
|
+
:password => 'password',
|
28
|
+
:password_confirmation => 'password'
|
29
|
+
}.merge(attributes)
|
30
|
+
attributes[:site] = sites(:test) if defined? Site
|
31
|
+
attributes
|
32
|
+
end
|
33
|
+
|
34
|
+
def login_as_reader(reader)
|
35
|
+
activate_authlogic
|
36
|
+
login_reader = reader.is_a?(Reader) ? reader : readers(reader)
|
37
|
+
ReaderSession.create(login_reader)
|
38
|
+
login_reader
|
39
|
+
end
|
40
|
+
|
41
|
+
def logout_reader
|
42
|
+
activate_authlogic
|
43
|
+
if session = ReaderSession.find
|
44
|
+
session.destroy
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
class DownloadSitesDataset < Dataset::Base
|
2
|
+
uses :pages
|
3
|
+
|
4
|
+
def load
|
5
|
+
create_record Site, :test, :name => 'Test Site', :domain => 'test', :base_domain => 'test.host', :position => 1, :mail_from_name => 'test sender', :mail_from_address => 'sender@spanner.org', :homepage_id => page_id(:home)
|
6
|
+
Page.current_site = sites(:test)
|
7
|
+
end
|
8
|
+
|
9
|
+
end
|