radiant-library-extension 2.0.0
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.
- data/README.md +112 -0
- data/Rakefile +137 -0
- data/app/models/library_page.rb +61 -0
- data/app/views/admin/assets/_edit_metadata.html.haml +20 -0
- data/app/views/admin/assets/_edit_title.html.haml +31 -0
- data/app/views/admin/tags/_show_assets.html.haml +22 -0
- data/cucumber.yml +1 -0
- data/db/migrate/20091030115120_furniture.rb +9 -0
- data/features/support/env.rb +16 -0
- data/features/support/paths.rb +14 -0
- data/lib/library/library_tag.rb +15 -0
- data/lib/library/library_tags.rb +276 -0
- data/lib/library/link_renderer.rb +22 -0
- data/lib/library/more_asset_tags.rb +151 -0
- data/lib/library/more_tag_tags.rb +179 -0
- data/lib/library/site_controller.rb +33 -0
- data/lib/library/tagged_asset.rb +42 -0
- data/lib/tasks/library_extension_tasks.rake +28 -0
- data/library_extension.rb +24 -0
- data/spec/controllers/library_site_controller_spec.rb +97 -0
- data/spec/datasets/library_pages_dataset.rb +10 -0
- data/spec/datasets/library_sites_dataset.rb +9 -0
- data/spec/datasets/library_tags_dataset.rb +43 -0
- data/spec/models/library_page_spec.rb +47 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +36 -0
- metadata +128 -0
@@ -0,0 +1,276 @@
|
|
1
|
+
module Library
|
2
|
+
module LibraryTags
|
3
|
+
include Radiant::Taggable
|
4
|
+
|
5
|
+
class TagError < StandardError; end
|
6
|
+
|
7
|
+
############### tags for use on library pages
|
8
|
+
# usually to build a faceted browser
|
9
|
+
|
10
|
+
tag "library" do |tag|
|
11
|
+
raise TagError, "library:* tags can only be used on a LibraryPage" unless tag.locals.page && tag.locals.page.is_a?(LibraryPage)
|
12
|
+
tag.expand
|
13
|
+
end
|
14
|
+
|
15
|
+
desc %{
|
16
|
+
Displays a list of the tags that can be used to narrow the set of results displayed. This begins as a list of
|
17
|
+
all available tags, and as they are chosen it shrinks to show only the coincident tags that will further reduce the set.
|
18
|
+
|
19
|
+
This is normally used to display a list or cloud of facets that can be added to a search. The default is to show a cloud.
|
20
|
+
|
21
|
+
<pre><code>
|
22
|
+
<r:library:tags:each><li><r:tag:link /></li></r:library:tags:each>
|
23
|
+
<r:library:tags:list />
|
24
|
+
<r:library:tags:cloud />
|
25
|
+
<r:library:tags />
|
26
|
+
</code></pre>
|
27
|
+
|
28
|
+
To show only those tags attached to a particular kind of object, supply a 'for' parameter.
|
29
|
+
The parameter can be 'pages', 'assets' or the plural of any asset type. If you're displaying an image gallery,
|
30
|
+
you may want to start with a cloud of all the tags that have been applied to images:
|
31
|
+
|
32
|
+
<pre><code>
|
33
|
+
<r:library:tags for="images" />
|
34
|
+
</code></pre>
|
35
|
+
|
36
|
+
You can still display pages associated with those tags, but the list will not include tags that only have pages.
|
37
|
+
}
|
38
|
+
tag "library:tags" do |tag|
|
39
|
+
tag.locals.tags = _get_coincident_tags(tag)
|
40
|
+
if tag.double?
|
41
|
+
tag.expand
|
42
|
+
else
|
43
|
+
tag.render('tags:cloud', tag.attr.dup)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
tag "library:tags:each" do |tag|
|
47
|
+
tag.render('tags:each', tag.attr.dup, &tag.block)
|
48
|
+
end
|
49
|
+
tag "library:tags:list" do |tag|
|
50
|
+
tag.render('tags:list', tag.attr.dup)
|
51
|
+
end
|
52
|
+
tag "library:tags:cloud" do |tag|
|
53
|
+
tag.render('tags:cloud', tag.attr.dup)
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
desc %{
|
58
|
+
Expands if there are is more than one tag to show.
|
59
|
+
(If there is only one tag coincident with the present set then it offers no reduction).
|
60
|
+
|
61
|
+
*Usage:*
|
62
|
+
<pre><code>
|
63
|
+
<r:library:if_tags>
|
64
|
+
Displaying items tagged with all of <r:requested_tags />
|
65
|
+
<r:library:if_requested_tags>
|
66
|
+
</code></pre>
|
67
|
+
}
|
68
|
+
tag "library:unless_tags" do |tag|
|
69
|
+
tag.locals.tags = _get_coincident_tags(tag)
|
70
|
+
tag.expand if tag.locals.tags.length > 1
|
71
|
+
end
|
72
|
+
desc %{
|
73
|
+
Expands if there are is more than one tag to show.
|
74
|
+
(If there is only one tag coincident with the present set then it offers no reduction, so we don't show it).
|
75
|
+
|
76
|
+
*Usage:*
|
77
|
+
<pre><code>
|
78
|
+
<r:library:if_tags><r:library:tags /></r:library:if_tags>
|
79
|
+
</code></pre>
|
80
|
+
}
|
81
|
+
tag "library:if_tags" do |tag|
|
82
|
+
tag.locals.tags = _get_coincident_tags(tag)
|
83
|
+
tag.expand if tag.locals.tags.length > 1
|
84
|
+
end
|
85
|
+
|
86
|
+
desc %{
|
87
|
+
Expands if there are is one or no coincident tags to show.
|
88
|
+
}
|
89
|
+
tag "library:unless_tags" do |tag|
|
90
|
+
tag.expand unless _get_coincident_tags(tag).length > 1
|
91
|
+
end
|
92
|
+
|
93
|
+
desc %{
|
94
|
+
Displays a list of the tags requested by the user.
|
95
|
+
To offer links that remove the tag from the current set, these will both work:
|
96
|
+
|
97
|
+
*Usage:*
|
98
|
+
<pre><code>
|
99
|
+
<r:library:requested_tags:each><li><r:tag:unlink /></li></r:library:requested_tags:each>
|
100
|
+
<r:library:requested_tags />
|
101
|
+
</code></pre>
|
102
|
+
}
|
103
|
+
tag "library:requested_tags" do |tag|
|
104
|
+
tag.locals.tags = _get_requested_tags(tag)
|
105
|
+
if tag.double?
|
106
|
+
tag.expand
|
107
|
+
else
|
108
|
+
tag.render('tags:unlink_list', tag.attr.dup)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
tag "library:requested_tags:each" do |tag|
|
112
|
+
tag.render('tags:each', tag.attr.dup, &tag.block)
|
113
|
+
end
|
114
|
+
|
115
|
+
desc %{
|
116
|
+
Expands if any tags have been specified:
|
117
|
+
|
118
|
+
*Usage:*
|
119
|
+
<pre><code>
|
120
|
+
<r:library:if_requested_tags>
|
121
|
+
Displaying items tagged with all of <r:requested_tags />
|
122
|
+
<r:library:if_requested_tags>
|
123
|
+
</code></pre>
|
124
|
+
}
|
125
|
+
tag "library:if_requested_tags" do |tag|
|
126
|
+
tag.locals.tags = _get_requested_tags(tag)
|
127
|
+
tag.expand if tag.locals.tags.any?
|
128
|
+
end
|
129
|
+
|
130
|
+
desc %{
|
131
|
+
Expands if no tags have been specified:
|
132
|
+
|
133
|
+
*Usage:*
|
134
|
+
<pre><code>
|
135
|
+
<r:library:unless_requested_tags>
|
136
|
+
Showing everything. Choose a tag to start narrowing down the list.
|
137
|
+
<r:library:unless_requested_tags>
|
138
|
+
</code></pre>
|
139
|
+
}
|
140
|
+
tag "library:unless_requested_tags" do |tag|
|
141
|
+
tag.expand unless _get_requested_tags(tag).any?
|
142
|
+
end
|
143
|
+
|
144
|
+
desc %{
|
145
|
+
Displays a list of the pages associated with the current tag set. If no tags are specified, this will show all pages.
|
146
|
+
You can use all the usual r:page tags within the list.
|
147
|
+
|
148
|
+
*Usage:*
|
149
|
+
<pre><code>
|
150
|
+
<r:library:pages:each><li><r:link /><br /><r:content part="description" /></li></r:library:pages:each>
|
151
|
+
</code></pre>
|
152
|
+
}
|
153
|
+
tag "library:pages" do |tag|
|
154
|
+
tag.locals.pages = _get_pages(tag)
|
155
|
+
tag.expand
|
156
|
+
end
|
157
|
+
tag "library:pages:each" do |tag|
|
158
|
+
tag.render('page_list', tag.attr.dup, &tag.block) # r:page_list is defined in taggable
|
159
|
+
end
|
160
|
+
|
161
|
+
desc %{
|
162
|
+
Expands if there are any pages associated with all of the current tag set.
|
163
|
+
|
164
|
+
*Usage:*
|
165
|
+
<pre><code>
|
166
|
+
<r:library:if_pages><h2>Pages</h2>...</r:library:if_pages>
|
167
|
+
</code></pre>
|
168
|
+
}
|
169
|
+
tag "library:if_pages" do |tag|
|
170
|
+
tag.expand if _get_pages(tag).any?
|
171
|
+
end
|
172
|
+
|
173
|
+
desc %{
|
174
|
+
Displays a list of the assets associated with the current tag set. If no tags are specified, this will show all assets.
|
175
|
+
You can use all the usual r:assets tags within the list.
|
176
|
+
|
177
|
+
*Usage:*
|
178
|
+
<pre><code>
|
179
|
+
<r:library:assets:each><li><r:assets:thumbnail /></li></r:library:assets:each>
|
180
|
+
</code></pre>
|
181
|
+
}
|
182
|
+
tag "library:assets" do |tag|
|
183
|
+
tag.locals.assets = _get_assets(tag)
|
184
|
+
tag.expand
|
185
|
+
end
|
186
|
+
tag "library:assets:each" do |tag|
|
187
|
+
tag.render('asset_list', tag.attr.dup, &tag.block)
|
188
|
+
end
|
189
|
+
|
190
|
+
desc %{
|
191
|
+
Expands if there are any assets associated with all of the current tag set.
|
192
|
+
|
193
|
+
*Usage:*
|
194
|
+
<pre><code>
|
195
|
+
<r:library:if_assets><h2>Assets</h2>...</r:library:if_assets>
|
196
|
+
</code></pre>
|
197
|
+
}
|
198
|
+
tag "library:if_assets" do |tag|
|
199
|
+
tag.expand if _get_assets(tag).any?
|
200
|
+
end
|
201
|
+
|
202
|
+
Asset.known_types.each do |type|
|
203
|
+
these = type.to_s.pluralize
|
204
|
+
|
205
|
+
desc %{
|
206
|
+
Displays a list of the all the #{these} associated with the current tag set. If no tags are specified, this will show all such assets.
|
207
|
+
You can use all the usual r:assets tags within the list.
|
208
|
+
|
209
|
+
*Usage:*
|
210
|
+
<pre><code>
|
211
|
+
<r:library:#{these}:each><li><r:assets:link /></li></r:library:#{these}:each>
|
212
|
+
</code></pre>
|
213
|
+
}
|
214
|
+
tag "library:#{these}" do |tag|
|
215
|
+
tag.locals.assets = _get_assets(tag).send(these.intern)
|
216
|
+
tag.expand
|
217
|
+
end
|
218
|
+
tag "library:#{these}:each" do |tag|
|
219
|
+
tag.render('asset_list', tag.attr.dup, &tag.block)
|
220
|
+
end
|
221
|
+
|
222
|
+
desc %{
|
223
|
+
Expands if there are any #{these} associated with all of the current tag set.
|
224
|
+
|
225
|
+
*Usage:*
|
226
|
+
<pre><code>
|
227
|
+
<r:library:if_#{these}><h2>#{these.titlecase}</h2>...</r:library:if_#{these}>
|
228
|
+
</code></pre>
|
229
|
+
}
|
230
|
+
tag "library:if_#{these}" do |tag|
|
231
|
+
tag.locals.assets = _get_assets(tag).send(these.intern)
|
232
|
+
tag.expand if tag.locals.assets.any?
|
233
|
+
end
|
234
|
+
|
235
|
+
private
|
236
|
+
|
237
|
+
def _get_requested_tags(tag)
|
238
|
+
tag.locals.page.requested_tags
|
239
|
+
end
|
240
|
+
|
241
|
+
def _get_coincident_tags(tag)
|
242
|
+
requested = _get_requested_tags(tag)
|
243
|
+
limit = tag.attr['limit'] || 50
|
244
|
+
if requested.any?
|
245
|
+
Tag.coincident_with(requested)
|
246
|
+
else
|
247
|
+
Tag.most_popular(limit)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
# in the absence of any requested tags we default to all, not none
|
253
|
+
|
254
|
+
def _get_pages(tag)
|
255
|
+
requested = _get_requested_tags(tag)
|
256
|
+
if requested.any?
|
257
|
+
Page.tagged_with(requested)
|
258
|
+
else
|
259
|
+
Page.scoped({}) # there must be a better way to return a new scope
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
def _get_assets(tag)
|
264
|
+
requested = _get_requested_tags(tag)
|
265
|
+
if requested.any?
|
266
|
+
Asset.tagged_with(requested)
|
267
|
+
else
|
268
|
+
Asset.scoped({})
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
end
|
273
|
+
|
274
|
+
end
|
275
|
+
|
276
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# This handy simplification is adapted from SphinxSearch (thanks)
|
2
|
+
# and originally came from Ultrasphinx
|
3
|
+
# it saves us a lot of including and bodging to make will_paginate's template calls work in the Page model
|
4
|
+
|
5
|
+
module Library
|
6
|
+
class LinkRenderer < WillPaginate::LinkRenderer
|
7
|
+
def initialize(tag)
|
8
|
+
@tag = tag
|
9
|
+
end
|
10
|
+
|
11
|
+
def page_link(page, text, attributes = {})
|
12
|
+
linkclass = %{ class="#{attributes[:class]}"} if attributes[:class]
|
13
|
+
linkrel = %{ rel="#{attributes[:rel]}"} if attributes[:rel]
|
14
|
+
%Q{<a href="#{@tag.locals.page.url}?page=#{page}"#{linkrel}#{linkclass}>#{text}</a>}
|
15
|
+
end
|
16
|
+
|
17
|
+
def page_span(page, text, attributes = {})
|
18
|
+
spanclass = attributes[:class]
|
19
|
+
%{<span class="#{attributes[:class]}">#{text}</span>}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
module Library
|
2
|
+
module MoreAssetTags
|
3
|
+
include Radiant::Taggable
|
4
|
+
|
5
|
+
class TagError < StandardError; end
|
6
|
+
|
7
|
+
############### assets: tags for displaying tags and relatives when we have an asset
|
8
|
+
# similar tags already exist for pages
|
9
|
+
|
10
|
+
desc %{
|
11
|
+
Cycles through all tags attached to present asset.
|
12
|
+
|
13
|
+
*Usage:*
|
14
|
+
<pre><code><r:assets:tags><r:tag:title /></r:assets:tags></code></pre>
|
15
|
+
}
|
16
|
+
tag 'assets:tags' do |tag|
|
17
|
+
raise TagError, "asset must be defined for asset:tags tag" unless tag.locals.asset
|
18
|
+
tag.locals.tags = tag.locals.asset.tags
|
19
|
+
tag.expand
|
20
|
+
end
|
21
|
+
tag 'assets:tags:each' do |tag|
|
22
|
+
tag.render('tags:each', tag.attr.dup, &tag.block)
|
23
|
+
end
|
24
|
+
|
25
|
+
desc %{
|
26
|
+
Lists all the assets similar to this asset (based on its tagging), in descending order of relatedness.
|
27
|
+
|
28
|
+
*Usage:*
|
29
|
+
<pre><code><r:related_assets:each>...</r:related_assets:each></code></pre>
|
30
|
+
}
|
31
|
+
tag 'related_assets' do |tag|
|
32
|
+
raise TagError, "asset must be defined for related_assets tag" unless tag.locals.asset
|
33
|
+
tag.locals.assets = tag.locals.asset.related_assets
|
34
|
+
tag.expand
|
35
|
+
end
|
36
|
+
tag 'related_assets:each' do |tag|
|
37
|
+
tag.render('assets:each', tag.attr.dup, &tag.block)
|
38
|
+
end
|
39
|
+
|
40
|
+
############### extra asset fields
|
41
|
+
|
42
|
+
[:copyright].each do |method|
|
43
|
+
desc %{
|
44
|
+
Expands if the asset has a #{method} notice.
|
45
|
+
The 'title' attribute is required on this tag or the parent tag.
|
46
|
+
}
|
47
|
+
tag "assets:if_#{method.to_s}" do |tag|
|
48
|
+
options = tag.attr.dup
|
49
|
+
asset = find_asset(tag, options)
|
50
|
+
tag.expand if asset.respond_to?(method) && asset.send(method)
|
51
|
+
end
|
52
|
+
|
53
|
+
desc %{
|
54
|
+
Expands unless the asset has a #{method} notice.
|
55
|
+
The 'title' attribute is required on this tag or the parent tag.
|
56
|
+
}
|
57
|
+
tag "assets:unless_#{method.to_s}" do |tag|
|
58
|
+
options = tag.attr.dup
|
59
|
+
asset = find_asset(tag, options)
|
60
|
+
tag.expand unless asset.respond_to?(method) && asset.send(method)
|
61
|
+
end
|
62
|
+
|
63
|
+
desc %{
|
64
|
+
Renders the `#{method.to_s}' attribute of the asset.
|
65
|
+
The 'title' attribute is required on this tag or the parent tag.
|
66
|
+
}
|
67
|
+
tag "assets:#{method.to_s}" do |tag|
|
68
|
+
options = tag.attr.dup
|
69
|
+
asset = find_asset(tag, options)
|
70
|
+
asset.send(method) rescue nil
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
############### useful shorthands
|
75
|
+
|
76
|
+
desc %{
|
77
|
+
Renders an illustration block for the asset, with image and caption.
|
78
|
+
|
79
|
+
*Usage:*
|
80
|
+
<pre><code><r:assets:image [title="asset_title"] [size="icon|thumbnail"]></code></pre>
|
81
|
+
}
|
82
|
+
tag "assets:illustration" do |tag|
|
83
|
+
options = tag.attr.dup
|
84
|
+
options['size'] ||= 'illustration'
|
85
|
+
tag.locals.asset = find_asset(tag, options)
|
86
|
+
if tag.locals.asset.image?
|
87
|
+
result = %{<div class="illustration">}
|
88
|
+
result << tag.render('assets:image', options)
|
89
|
+
result << %{<p class="caption">#{tag.render('assets:caption', options)}</p>}
|
90
|
+
result << "</div>"
|
91
|
+
result
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
desc %{
|
96
|
+
Presents a standard marginal gallery block suitable for turning unobtrusively into a rollover or lightbox gallery.
|
97
|
+
We need to be able to work out a collection of assets: that can be defined already (eg by assets:all) or come from the current page.
|
98
|
+
Default preview size is 'large' and thumbnail size 'thumbnail' but you can specify any of your asset sizes.
|
99
|
+
|
100
|
+
*Usage:*
|
101
|
+
<pre><code>
|
102
|
+
<r:assets:images>
|
103
|
+
<r:assets:minigallery [size="..."] [thumbnail_size="..."] [tags="one,or,more,tags"] />
|
104
|
+
</r:assets:images>
|
105
|
+
</code></pre>
|
106
|
+
|
107
|
+
}
|
108
|
+
tag 'assets:minigallery' do |tag|
|
109
|
+
options = tag.attr.dup.symbolize_keys
|
110
|
+
raise TagError, "asset collection must be available for assets:minigallery tag" unless tag.locals.assets or tag.locals.page or tag.attr[:tags]
|
111
|
+
if options[:tags] && tags = Tag.from_list(options[:tags])
|
112
|
+
tag.locals.assets = Asset.images.from_all_tags(tags)
|
113
|
+
else
|
114
|
+
tag.locals.assets = tag.locals.page.assets
|
115
|
+
end
|
116
|
+
tag.locals.assets.images.to_a # because we can't let empty? trigger a call to count
|
117
|
+
|
118
|
+
unless tag.locals.assets.empty?
|
119
|
+
size = tag.attr['size'] || 'illustration'
|
120
|
+
thumbsize = tag.attr['thumbnail_size'] || 'icon'
|
121
|
+
result = ""
|
122
|
+
result << %{
|
123
|
+
<div class="minigallery">}
|
124
|
+
tag.locals.asset = tag.locals.assets.first
|
125
|
+
result << tag.render('assets:image', {'size' => size})
|
126
|
+
result << %{
|
127
|
+
<p class="caption">#{tag.render('assets:caption')}</p>
|
128
|
+
<ul class="thumbnails">}
|
129
|
+
if tag.locals.assets.size > 1
|
130
|
+
tag.locals.assets.each do |asset|
|
131
|
+
tag.locals.asset = asset
|
132
|
+
result << %{
|
133
|
+
<li class="thumbnail">
|
134
|
+
<a href="#{tag.render('assets:url', 'size' => 'illustration')}" title="#{asset.caption}" id="thumbnail_#{asset.id}">
|
135
|
+
}
|
136
|
+
result << tag.render('assets:image', {'size' => thumbsize, 'alt' => asset.title})
|
137
|
+
result << %{
|
138
|
+
</a>
|
139
|
+
</li>}
|
140
|
+
end
|
141
|
+
end
|
142
|
+
result << %{
|
143
|
+
</ul>
|
144
|
+
</div>}
|
145
|
+
result
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|