radiant-taggable-extension 1.2.5 → 2.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +82 -18
- data/Rakefile +0 -15
- data/app/helpers/taggable_helper.rb +18 -0
- data/app/models/library_page.rb +60 -0
- data/app/models/tag.rb +19 -2
- data/app/views/admin/assets/_edit_metadata.html.haml +20 -0
- data/app/views/admin/assets/_edit_title.html.haml +14 -0
- data/app/views/admin/tags/_show_assets.html.haml +22 -0
- data/lib/link_renderer.rb +22 -0
- data/lib/radiant-taggable-extension.rb +8 -0
- data/lib/radius/asset_tags.rb +151 -0
- data/lib/radius/library_tags.rb +387 -0
- data/lib/radius/taggable_tags.rb +655 -0
- data/lib/taggable/admin_pages_controller.rb +16 -0
- data/lib/taggable/admin_ui.rb +43 -0
- data/lib/taggable/asset.rb +37 -0
- data/lib/taggable/model.rb +144 -0
- data/lib/taggable/page.rb +54 -0
- data/lib/taggable/site_controller.rb +33 -0
- data/radiant-taggable-extension.gemspec +23 -85
- data/spec/controllers/site_controller_spec.rb +96 -0
- data/spec/datasets/tags_dataset.rb +2 -0
- data/spec/lib/taggable_page_spec.rb +1 -1
- data/spec/models/library_page_spec.rb +47 -0
- data/taggable_extension.rb +17 -11
- metadata +49 -49
- data/.gitignore +0 -1
- data/VERSION +0 -1
- data/lib/taggable_admin_page_controller.rb +0 -18
- data/lib/taggable_admin_ui.rb +0 -41
- data/lib/taggable_model.rb +0 -139
- data/lib/taggable_page.rb +0 -51
- data/lib/taggable_tags.rb +0 -529
data/README.md
CHANGED
@@ -1,34 +1,35 @@
|
|
1
1
|
# Taggable
|
2
2
|
|
3
|
-
This is another way to apply tags to objects in your radiant site and retrieve objects by tag. If you're looking at this you will also want to look at the [tags](http://github.com/jomz/radiant-tags-extension/tree) extension, which does a good job of tag-clouding and may be all you need
|
3
|
+
This is another way to apply tags to objects in your radiant site and retrieve objects by tag. If you're looking at this you will also want to look at the [tags](http://github.com/jomz/radiant-tags-extension/tree) extension, which does a good job of tag-clouding and may be all you need.
|
4
|
+
|
5
|
+
Taggable now includes what was previously the `library` extension so it handles radiant assets as well as pages and provides radius tags to support a faceted search.
|
4
6
|
|
5
7
|
## Why?
|
6
8
|
|
7
9
|
This extension differs from `tags` in a few ways that matter to me but may not to you:
|
8
10
|
|
9
11
|
* We're not so focused on tag clouds - though you can still make them - but more on archival and linking functions.
|
10
|
-
*
|
12
|
+
* It provides faceted search of any tagged class.
|
13
|
+
* We subvert the keywords mechanism on pages rather than adding another one. I may change this soon to play more nicely with page fields.
|
11
14
|
* The tag-choosing and tag-removal interface is (about to be) quite nice.
|
12
15
|
* It's editorially versatile: tags can be used as page pointers and their visibility is controllable
|
13
|
-
* Anything can be tagged. By default we only do pages but other extensions can participate with a single line in a model class. See the [taggable_events](https://github.com/spanner/radiant-taggable_events-extension) extension for a minimal example or just put `
|
16
|
+
* Anything can be tagged. By default we only do pages but other extensions can participate with a single line in a model class. See the [taggable_events](https://github.com/spanner/radiant-taggable_events-extension) extension for a minimal example or just put `has_tags` at the top of a model class.
|
14
17
|
* We don't use `has_many_polymorphs` (it burns!)
|
15
18
|
* Or any of the tagging libraries: it only takes a few scopes
|
16
|
-
* it's multi-site compatible: if our fork is installed then you get site-scoped tags and taggings.
|
17
19
|
|
18
20
|
When you first install the extension you shouldn't see much difference: all we do out of the box is to take over (and make more prominent) the keywords field in the page-edit view.
|
19
21
|
|
20
22
|
## New
|
21
23
|
|
22
|
-
|
23
|
-
the text box, but we're getting there. Migration is required for the metaphone support.
|
24
|
+
* API change: `has_tags` instead of `is_taggable`.
|
24
25
|
|
25
|
-
|
26
|
+
* The library extension has been reincorporated into taggable, since radiant 1 has assets. Out of the box you get tags, clouds and faceting of both pages and assets. It should work with 0.9.1 too but you will need paperclipped.
|
26
27
|
|
27
|
-
|
28
|
+
* The long-promised tag-suggester is there in a useable though slightly basic form.
|
28
29
|
|
29
30
|
## Status
|
30
31
|
|
31
|
-
|
32
|
+
Apart from any crumpling caused by the recent reincorporation of Library this is all mature code that has been in use for years.
|
32
33
|
|
33
34
|
## Efficiency
|
34
35
|
|
@@ -44,23 +45,32 @@ is handled in a single pass.
|
|
44
45
|
|
45
46
|
The exception is the `r:tag_cloud` tag: there we have to gather a list of descendant pages first. It's done in a fairly frugal way (by generation rather than individual) but still likely to involve several preparatory queries as well as the cloud computation.
|
46
47
|
|
48
|
+
## Library pages
|
49
|
+
|
50
|
+
The **LibraryPage** page type is a handy cache-friendly way of catching tag parameters and displaying lists of related items: any path following the address of the page is taken as a slash-separated list of tags, so with a tag page at /archive you can call addresses like:
|
51
|
+
|
52
|
+
/archive/lasagne/chips/pudding
|
53
|
+
|
54
|
+
and the right tags will be retrieved, if they exist.
|
55
|
+
|
47
56
|
## Radius tags
|
48
57
|
|
49
|
-
This extension creates
|
58
|
+
This extension creates a great many radius tags. There are several kinds:
|
50
59
|
|
51
|
-
###
|
60
|
+
### Tag information
|
52
61
|
|
53
62
|
are used in the usual to display the properties and associations of a given tag (which can be supplied to a library as a query parameter or just specified in the radius tag)
|
54
63
|
|
55
64
|
<r:tag:title />
|
56
65
|
<r:tag:description />
|
57
66
|
<r:tag:pages:each>...</r:tag:pages:each>
|
67
|
+
<r:tags:each >...</r:tags:each>
|
58
68
|
|
59
69
|
currently only available in a tag cloud (or a `top_tags` list):
|
60
70
|
|
61
71
|
<r:tag:use_count />
|
62
72
|
|
63
|
-
###
|
73
|
+
### Page tags and tag pages
|
64
74
|
|
65
75
|
These display the tag-associations of a given page.
|
66
76
|
|
@@ -71,7 +81,46 @@ These display the tag-associations of a given page.
|
|
71
81
|
<r:tag_cloud [url=""] />
|
72
82
|
|
73
83
|
The library extension adds a lot more ways to retrieve lists of tags and tagged objects, and to work with assets in the same way as we do here with pages.
|
84
|
+
|
85
|
+
### Tag assets and asset tags
|
86
|
+
|
87
|
+
All the page tags have asset equivalents:
|
88
|
+
|
89
|
+
<r:tags:assets:each tags="foo, bar">...</r:tags:assets:each>
|
90
|
+
<r:related_assets:each>...</r:related_assets:each>
|
91
|
+
|
92
|
+
and for any `*asset*` tag you can substitute an asset type, so this also works:
|
93
|
+
|
94
|
+
<r:related_images:each>...</r:related_images:each>
|
95
|
+
|
96
|
+
Within the each loops you can use all the usual page and asset tags.
|
97
|
+
|
98
|
+
### Library page tags
|
99
|
+
|
100
|
+
The library tags focus on two tasks: choosing a set of tags and displaying a set of matching objects.
|
101
|
+
|
102
|
+
<r:library:tags />
|
103
|
+
<r:library:tags:each>...</r:library:tags:each>
|
104
|
+
|
105
|
+
Displays a list of the tags available. If any tags have been requested, this will show the list of coincident tags (that can be used to limit the result set further). If not it shows all the available tags. If a `for` attribute is set:
|
106
|
+
|
107
|
+
<r:library:tags for="images" />
|
108
|
+
<r:library:tags for="pages" />
|
109
|
+
|
110
|
+
Then we show only the set of tags attached to any object of that kind.
|
111
|
+
|
112
|
+
<r:library:requested_tags />
|
113
|
+
<r:library:requested_tags:each>...</r:library:requested_tags:each>
|
74
114
|
|
115
|
+
Displays the currently-limiting set of tags.
|
116
|
+
|
117
|
+
<r:library:pages:each>...</r:library:pages:each>
|
118
|
+
<r:library:assets:each>...</r:library:assets:each>
|
119
|
+
<r:library:images:each>...</r:library:images:each>
|
120
|
+
<r:library:videos:each>...</r:library:videos:each>
|
121
|
+
|
122
|
+
Display the list of (that kind of) objects associated with the current tag set.
|
123
|
+
|
75
124
|
## Note about tag cloud prominence
|
76
125
|
|
77
126
|
The calculation of prominence here applies a logarithmic curve to create a more even distribution of weight. It's continuous rather than banded, and sets the font size and opacity for each tag in a style attribute.
|
@@ -100,16 +149,31 @@ Include the sample tagcloud.css in your styles and put this somewhere in the pag
|
|
100
149
|
<r:tag_cloud />
|
101
150
|
|
102
151
|
Seek venture capital immediately.
|
152
|
+
|
153
|
+
### To display a faceted image browser on a library page:
|
154
|
+
|
155
|
+
<r:library:if_requested_tags>
|
156
|
+
<p>Displaying pictures tagged with <r:library:requested_tags /></p>
|
157
|
+
</r:library:if_requested_tags>
|
158
|
+
|
159
|
+
<r:library:images:each paginated="true" per_page="20">
|
160
|
+
<r:assets:link size="full"><r:assets:image size="small" /></r:assets:link>
|
161
|
+
</r:library:images:each>
|
162
|
+
|
163
|
+
<r:library:tags for="images" />
|
103
164
|
|
104
|
-
|
165
|
+
### To automate the illustration of a page based on tag-overlap:
|
105
166
|
|
106
|
-
|
167
|
+
<r:related_images:each limit="3">
|
168
|
+
<r:assets:image size="standard" />
|
169
|
+
<p class="caption"><r:assets:caption /></p>
|
170
|
+
</r:related_images:each>
|
107
171
|
|
108
172
|
## Requirements
|
109
173
|
|
110
|
-
* Radiant 0.
|
174
|
+
* Radiant 1 or radiant 0.9.x with the paperclipped extension.
|
111
175
|
|
112
|
-
|
176
|
+
Radiant 1 is strongly recommended.
|
113
177
|
|
114
178
|
## Installation
|
115
179
|
|
@@ -123,10 +187,10 @@ The update task will bring over a couple of CSS files for styling tags but you'l
|
|
123
187
|
|
124
188
|
## Bugs
|
125
189
|
|
126
|
-
|
190
|
+
Quite likely. [Github issues](http://github.com/spanner/radiant-taggable-extension/issues), please, or for little things an email or github message is fine.
|
127
191
|
|
128
192
|
## Author and copyright
|
129
193
|
|
130
194
|
* William Ross, for spanner. will at spanner.org
|
131
|
-
* Copyright 2008-
|
195
|
+
* Copyright 2008-2011 spanner ltd
|
132
196
|
* released under the same terms as Rails and/or Radiant
|
data/Rakefile
CHANGED
@@ -1,18 +1,3 @@
|
|
1
|
-
begin
|
2
|
-
require 'jeweler'
|
3
|
-
Jeweler::Tasks.new do |gem|
|
4
|
-
gem.name = "radiant-taggable-extension"
|
5
|
-
gem.summary = %Q{Taggable Extension for Radiant CMS}
|
6
|
-
gem.description = %Q{General purpose tagging extension: more versatile but less focused than the tags extension}
|
7
|
-
gem.email = "will@spanner.org"
|
8
|
-
gem.homepage = "http://github.com/spanner/radiant-taggable-extension"
|
9
|
-
gem.authors = ["spanner"]
|
10
|
-
gem.add_dependency "radiant", ">= 0.9.0"
|
11
|
-
end
|
12
|
-
rescue LoadError
|
13
|
-
puts "Jeweler (or a dependency) not available. This is only required if you plan to package taggable as a gem."
|
14
|
-
end
|
15
|
-
|
16
1
|
# In rails 1.2, plugins aren't available in the path until they're loaded.
|
17
2
|
# Check to see if the rspec plugin is installed first and require
|
18
3
|
# it if it is. If not, use the gem version.
|
@@ -1,5 +1,23 @@
|
|
1
1
|
module TaggableHelper
|
2
2
|
|
3
|
+
def clean_html(text)
|
4
|
+
Sanitize.clean(text, Sanitize::Config::RELAXED)
|
5
|
+
end
|
6
|
+
|
7
|
+
def strip_html(text)
|
8
|
+
Sanitize.clean(text)
|
9
|
+
end
|
10
|
+
|
11
|
+
def truncate_words(text='', options={})
|
12
|
+
return '' if text.blank?
|
13
|
+
ellipsis = options[:ellipsis] || '…'
|
14
|
+
limit = (options[:limit] || 64).to_i
|
15
|
+
text = strip_html(text) if options[:strip]
|
16
|
+
words = text.split
|
17
|
+
ellipsis = '' unless words.size > limit
|
18
|
+
words[0..(limit-1)].join(" ") + ellipsis
|
19
|
+
end
|
20
|
+
|
3
21
|
def available_pointer_pages()
|
4
22
|
root = Page.respond_to?(:homepage) ? Page.homepage : Page.find_by_parent_id(nil)
|
5
23
|
options = pointer_option_branch(root)
|
@@ -0,0 +1,60 @@
|
|
1
|
+
class LibraryPage < Page
|
2
|
+
include WillPaginate::ViewHelpers
|
3
|
+
|
4
|
+
class RedirectRequired < StandardError
|
5
|
+
def initialize(message = nil); super end
|
6
|
+
end
|
7
|
+
|
8
|
+
description %{ Takes tag names in child position or as paramaters so that tagged items can be listed. }
|
9
|
+
|
10
|
+
attr_accessor :requested_tags, :strict_match
|
11
|
+
|
12
|
+
def cache?
|
13
|
+
true
|
14
|
+
end
|
15
|
+
|
16
|
+
def find_by_url(url, live = true, clean = false)
|
17
|
+
url = clean_url(url) if clean
|
18
|
+
my_url = self.url
|
19
|
+
return false unless url =~ /^#{Regexp.quote(my_url)}(.*)/
|
20
|
+
tags = $1.split('/')
|
21
|
+
if slug_child = children.find_by_slug(tags[0])
|
22
|
+
found = slug_child.find_by_url(url, live, clean)
|
23
|
+
return found if found
|
24
|
+
end
|
25
|
+
remove_tags, add_tags = tags.partition{|t| t.first == '-'}
|
26
|
+
add_request_tags(add_tags)
|
27
|
+
remove_request_tags(remove_tags)
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
def add_request_tags(tags=[])
|
32
|
+
if tags.any?
|
33
|
+
tags.collect! { |tag| Tag.find_by_title(Rack::Utils::unescape(tag)) }
|
34
|
+
self.requested_tags = (self.requested_tags + tags.select{|t| !t.nil?}).uniq
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def remove_request_tags(tags=[])
|
39
|
+
if tags.any?
|
40
|
+
tags.collect! { |tag|
|
41
|
+
tag.slice!(0) if tag.first == '-'
|
42
|
+
Tag.find_by_title(Rack::Utils::unescape(tag))
|
43
|
+
}
|
44
|
+
self.requested_tags = (self.requested_tags - tags.select{|t| !t.nil?}).uniq
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def requested_tags
|
49
|
+
@requested_tags ||= []
|
50
|
+
end
|
51
|
+
|
52
|
+
# this isn't very pleasing but it's the best way to let the controller know
|
53
|
+
# of our real address once tags have been added and removed.
|
54
|
+
|
55
|
+
def url_with_tags(tags = requested_tags)
|
56
|
+
clean_url( url_without_tags + '/' + tags.uniq.map(&:clean_title).to_param )
|
57
|
+
end
|
58
|
+
alias_method_chain :url, :tags
|
59
|
+
|
60
|
+
end
|
data/app/models/tag.rb
CHANGED
@@ -129,12 +129,26 @@ class Tag < ActiveRecord::Base
|
|
129
129
|
taggings.map {|t| t.tagged}
|
130
130
|
end
|
131
131
|
|
132
|
-
# Returns a list of all the
|
132
|
+
# Returns a list of all the pages tagged with this tag.
|
133
133
|
|
134
134
|
def pages
|
135
135
|
Page.from_tags([self])
|
136
136
|
end
|
137
137
|
|
138
|
+
# Returns a list of all the assets tagged with this tag.
|
139
|
+
|
140
|
+
def assets
|
141
|
+
Asset.from_tags([self])
|
142
|
+
end
|
143
|
+
|
144
|
+
# Returns a list of all the assets of a particular type tagged with this tag.
|
145
|
+
|
146
|
+
Asset.known_types.each do |type|
|
147
|
+
define_method type.to_s.pluralize.intern do
|
148
|
+
Asset.send("#{type.to_s.pluralize}".intern).from_tags([self])
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
138
152
|
# Returns a list of all the tags that have been applied alongside this one.
|
139
153
|
|
140
154
|
def coincident_tags
|
@@ -244,10 +258,13 @@ class Tag < ActiveRecord::Base
|
|
244
258
|
# adds retrieval methods for a taggable class to this class and to Tagging.
|
245
259
|
|
246
260
|
def self.define_retrieval_methods(classname)
|
261
|
+
define_method "#{classname.downcase}_taggings".to_sym do
|
262
|
+
self.taggings.of_a(classname)
|
263
|
+
end
|
247
264
|
define_method classname.downcase.pluralize.to_sym do
|
248
265
|
classname.constantize.send :from_tag, self
|
249
266
|
end
|
250
|
-
end
|
267
|
+
end
|
251
268
|
|
252
269
|
protected
|
253
270
|
|
@@ -0,0 +1,20 @@
|
|
1
|
+
- fields_for :asset, @asset do |fields|
|
2
|
+
#extended-metadata{ :class => "row", :style => "display: none" }
|
3
|
+
%table.fieldset{ :cellpadding => "0", :cellspacing => "0", :border => "0" }
|
4
|
+
%tr
|
5
|
+
%td.label
|
6
|
+
= fields.label :caption
|
7
|
+
%td.field
|
8
|
+
= fields.text_field :caption, :class => 'textbox', :maxlength => 255
|
9
|
+
-if @asset.new_record? || @asset.image?
|
10
|
+
%tr
|
11
|
+
%td.label
|
12
|
+
Display
|
13
|
+
%td.field{:style => 'text-align: left;'}
|
14
|
+
= fields.check_box :furniture
|
15
|
+
= fields.label :furniture, "Site furniture? (not shown in lists and galleries)"
|
16
|
+
= render_region :extended_metadata
|
17
|
+
%p
|
18
|
+
%small
|
19
|
+
%a{ :id => "more-extended-metadata", :href => "#", :onclick => "#{toggle_javascript_for('extended-metadata')}; return false;" } More
|
20
|
+
%a{ :id => "less-extended-metadata", :href => "#", :onclick => "#{toggle_javascript_for('extended-metadata')}; return false;", :style => "display: none" } Less
|
@@ -0,0 +1,14 @@
|
|
1
|
+
- include_stylesheet 'admin/taggable'
|
2
|
+
- include_javascript 'autocomplete'
|
3
|
+
- include_javascript 'admin/taggable'
|
4
|
+
|
5
|
+
- fields_for :asset, @asset do |fields|
|
6
|
+
%p.keywords
|
7
|
+
%label{:for=>"asset_keywords"}
|
8
|
+
Tags
|
9
|
+
= fields.text_field :keywords, :class => 'textbox tagger'
|
10
|
+
|
11
|
+
%p.title
|
12
|
+
%label{:for=>"asset_title"}
|
13
|
+
Title
|
14
|
+
= fields.text_field :title, :class => 'textbox', :maxlength => 100
|
@@ -0,0 +1,22 @@
|
|
1
|
+
- include_stylesheet 'admin/assets'
|
2
|
+
|
3
|
+
- asset_taggings = @tag.taggings.of_a :asset
|
4
|
+
- if asset_taggings.any?
|
5
|
+
%h2
|
6
|
+
Tagged assets
|
7
|
+
%table.index
|
8
|
+
- asset_taggings.each do |tagging|
|
9
|
+
- asset = tagging.tagged
|
10
|
+
- if asset
|
11
|
+
- dom_id = "tagging_#{tagging.id}"
|
12
|
+
%tr{:id => dom_id}
|
13
|
+
%td.asset{:style => 'width: 48px'}
|
14
|
+
= link_to image_tag(asset.thumbnail(:icon)), edit_admin_asset_path(asset), :class => 'icon'
|
15
|
+
%td.asset-title
|
16
|
+
= link_to asset.title, edit_admin_asset_path(asset)
|
17
|
+
%td.actions
|
18
|
+
= link_to_remote image('minus') + ' detach', |
|
19
|
+
:html => { :class => "action", :title => "Detach tag from asset" }, |
|
20
|
+
:url => admin_tagging_url(tagging), :method => :delete, |
|
21
|
+
:after => "Effect.Fade('#{dom_id}', { duration: 0.5 })", |
|
22
|
+
:complete => "Element.remove('#{dom_id}');"
|
@@ -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,8 @@
|
|
1
|
+
module RadiantTaggableExtension
|
2
|
+
VERSION = '2.0.0.rc1'
|
3
|
+
SUMMARY = %q{Tagging, clouding and faceting extension for Radiant CMS}
|
4
|
+
DESCRIPTION = %q{General purpose tagging and retrieval extension: more versatile but less focused than the tags extension. A good way to support faceted search.}
|
5
|
+
URL = "http://spanner.org/radiant/taggable"
|
6
|
+
AUTHORS = ["William Ross"]
|
7
|
+
EMAIL = ["radiant@spanner.org"]
|
8
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
module Radius
|
2
|
+
module AssetTags
|
3
|
+
include Radiant::Taggable
|
4
|
+
|
5
|
+
class TagError < StandardError; end
|
6
|
+
|
7
|
+
%w{tags tags:each copyright if_copyright unless_copyright minigallery illustration}.each do |name|
|
8
|
+
deprecated_tag "assets:#{name}", :substitute => "asset:#{name}", :deadline => '2.0'
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
############### assets: tags for displaying tags and relatives when we have an asset
|
13
|
+
# similar tags already exist for pages
|
14
|
+
|
15
|
+
desc %{
|
16
|
+
Cycles through all tags attached to present asset.
|
17
|
+
|
18
|
+
*Usage:*
|
19
|
+
<pre><code><r:assets:tags><r:tag:title /></r:assets:tags></code></pre>
|
20
|
+
}
|
21
|
+
tag 'asset:tags' do |tag|
|
22
|
+
raise TagError, "asset must be defined for asset:tags tag" unless tag.locals.asset
|
23
|
+
tag.locals.tags = tag.locals.asset.tags
|
24
|
+
tag.expand
|
25
|
+
end
|
26
|
+
tag 'asset:tags:each' do |tag|
|
27
|
+
tag.render('tags:each', tag.attr.dup, &tag.block)
|
28
|
+
end
|
29
|
+
|
30
|
+
desc %{
|
31
|
+
Lists all the assets similar to this asset (based on its tagging), in descending order of relatedness.
|
32
|
+
|
33
|
+
*Usage:*
|
34
|
+
<pre><code><r:related_assets:each>...</r:related_assets:each></code></pre>
|
35
|
+
}
|
36
|
+
tag 'related_assets' do |tag|
|
37
|
+
raise TagError, "asset must be defined for related_assets tag" unless tag.locals.asset
|
38
|
+
tag.locals.assets = tag.locals.asset.related_assets
|
39
|
+
tag.expand
|
40
|
+
end
|
41
|
+
tag 'related_assets:each' do |tag|
|
42
|
+
tag.render('assets:each', tag.attr.dup, &tag.block)
|
43
|
+
end
|
44
|
+
|
45
|
+
############### extra asset fields
|
46
|
+
|
47
|
+
desc %{
|
48
|
+
Expands if the asset has a copyright notice.
|
49
|
+
}
|
50
|
+
tag "asset:if_copyright" do |tag|
|
51
|
+
options = tag.attr.dup
|
52
|
+
asset = find_asset(tag, options)
|
53
|
+
tag.expand if asset.respond_to?(method) && asset.send(method)
|
54
|
+
end
|
55
|
+
|
56
|
+
desc %{
|
57
|
+
Expands unless the asset has a copyright notice.
|
58
|
+
}
|
59
|
+
tag "asset:unless_copyright" do |tag|
|
60
|
+
options = tag.attr.dup
|
61
|
+
asset = find_asset(tag, options)
|
62
|
+
tag.expand unless asset.respond_to?(method) && asset.send(method)
|
63
|
+
end
|
64
|
+
|
65
|
+
desc %{
|
66
|
+
Renders the 'copyright' attribute of the asset.
|
67
|
+
}
|
68
|
+
tag "asset:copyright" do |tag|
|
69
|
+
options = tag.attr.dup
|
70
|
+
asset = find_asset(tag, options)
|
71
|
+
asset.send(method) rescue nil
|
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 "asset: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 'asset: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
|
+
|