radiant-tags-extension 1.5.1 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +11 -3
- data/app/models/meta_tag.rb +23 -3
- data/app/models/radius_tags.rb +41 -13
- data/app/models/tag_search_page.rb +23 -2
- data/app/views/admin/pages/_tag_field.html.haml +8 -2
- data/app/views/admin/pages/_tag_field_javascript.html.erb +17 -0
- data/config/locales/de.yml +7 -0
- data/config/locales/en.yml +5 -0
- data/config/locales/nl.yml +5 -0
- data/config/routes.rb +20 -0
- data/lib/radiant-tags-extension.rb +3 -0
- data/lib/tagging_methods.rb +1 -3
- data/public/images/admin/tagclose.gif +0 -0
- data/public/javascripts/admin/protomultiselect.js +527 -0
- data/public/stylesheets/admin/tags.css +21 -0
- data/radiant-tags-extension.gemspec +28 -0
- data/tags_extension.rb +0 -21
- metadata +16 -6
data/README
CHANGED
@@ -3,7 +3,6 @@
|
|
3
3
|
Created by: Keith Bingman - keithbingman.com
|
4
4
|
Revived by: Benny Degezelle - gorilla-webdesign.be
|
5
5
|
New features by: Jim Gay - saturnflyer.com
|
6
|
-
Version: 1.5
|
7
6
|
|
8
7
|
This extension enhances the page model with tagging capabilities, tagging as in "2.0" and tagclouds.
|
9
8
|
|
@@ -15,7 +14,16 @@ You can change the load order of extensions in config/environment.rb (see http:/
|
|
15
14
|
|
16
15
|
== Installation
|
17
16
|
|
18
|
-
|
17
|
+
=== Using RubyGems
|
18
|
+
|
19
|
+
`gem install radiant-tags-extension`
|
20
|
+
|
21
|
+
add the following line to your environment.rb
|
22
|
+
config.gem 'radiant-tags-extension'
|
23
|
+
|
24
|
+
=== Classic style
|
25
|
+
|
26
|
+
1. Copy the extension to your vendor/extensions directory as you would any other extension or use `script/extension install tags`
|
19
27
|
2. Run 'rake radiant:extensions:tags:install'
|
20
28
|
3. Make a page to sit in /search/by-tag, and give it the "Tag Search" pagetype.
|
21
29
|
If you want to change this location, it's in Radiant::Config['tags.results_page_url'].
|
@@ -35,4 +43,4 @@ Here's a sample results page to get you started;
|
|
35
43
|
<li><r:link/> - <r:author/> - <r:date/></li>
|
36
44
|
</r:search:results:each>
|
37
45
|
</ul>
|
38
|
-
</r:search:results>
|
46
|
+
</r:search:results>
|
data/app/models/meta_tag.rb
CHANGED
@@ -36,18 +36,38 @@ class MetaTag < ActiveRecord::Base
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def cloud(args = {})
|
39
|
+
args = {:by => 'popularity', :order => 'desc', :limit => 5}.merge(args)
|
40
|
+
|
39
41
|
find(:all, :select => 'meta_tags.*, count(*) as popularity',
|
40
|
-
:limit => args[:limit]
|
42
|
+
:limit => args[:limit],
|
41
43
|
:joins => "JOIN taggings ON taggings.meta_tag_id = meta_tags.id",
|
42
44
|
:conditions => args[:conditions],
|
43
45
|
:group => "meta_tags.id, meta_tags.name",
|
44
|
-
:order =>
|
46
|
+
:order => order_string(args) )
|
47
|
+
end
|
48
|
+
|
49
|
+
def order_string(attr)
|
50
|
+
by = (attr[:by]).strip
|
51
|
+
order = (attr[:order]).strip
|
52
|
+
order_string = ''
|
53
|
+
if self.new.attributes.keys.dup.push("popularity").include?(by)
|
54
|
+
order_string << by
|
55
|
+
else
|
56
|
+
raise TagError.new("`by' attribute of `each' tag must be set to a valid field name")
|
57
|
+
end
|
58
|
+
if order =~ /^(asc|desc)$/i
|
59
|
+
order_string << " #{$1.upcase}"
|
60
|
+
else
|
61
|
+
raise TagError.new(%{`order' attribute of `each' tag must be set to either "asc" or "desc"})
|
62
|
+
end
|
63
|
+
|
45
64
|
end
|
65
|
+
|
46
66
|
end
|
47
67
|
|
48
68
|
def <=>(other)
|
49
69
|
# To be able to sort an array of tags
|
50
70
|
name <=> other.name
|
51
71
|
end
|
52
|
-
|
72
|
+
|
53
73
|
end
|
data/app/models/radius_tags.rb
CHANGED
@@ -85,7 +85,7 @@ module RadiusTags
|
|
85
85
|
The results_page attribute will default to #{Radiant::Config['tags.results_page_url']}
|
86
86
|
|
87
87
|
*Usage:*
|
88
|
-
<pre><code><r:
|
88
|
+
<pre><code><r:tag_cloud [limit="number"] [results_page="/some/url"] [scope="/some/url"]/></code></pre>
|
89
89
|
}
|
90
90
|
tag "tag_cloud" do |tag|
|
91
91
|
tag_cloud = MetaTag.cloud(:limit => tag.attr['limit'] || 5).sort
|
@@ -98,10 +98,33 @@ module RadiusTags
|
|
98
98
|
output += "<li class=\"#{cloud_class}\"><span>#{pluralize(amount, 'page is', 'pages are')} tagged with </span><a href=\"#{results_page}/#{tag}\" class=\"tag\">#{tag}</a></li>"
|
99
99
|
end
|
100
100
|
else
|
101
|
-
return
|
101
|
+
return I18n.t('tags_extension.no_tags_found')
|
102
102
|
end
|
103
103
|
output += "</ol>"
|
104
104
|
end
|
105
|
+
|
106
|
+
desc %{
|
107
|
+
Render a Tag cloud with div-tags
|
108
|
+
The results_page attribute will default to #{Radiant::Config['tags.results_page_url']}
|
109
|
+
|
110
|
+
*Usage:*
|
111
|
+
<pre><code><r:tag_cloud_div [limit="number"] [results_page="/some/url"] [scope="/some/url"]/></code></pre>
|
112
|
+
}
|
113
|
+
tag "tag_cloud_div" do |tag|
|
114
|
+
tag_cloud = MetaTag.cloud(:limit => tag.attr['limit'] || 10).sort
|
115
|
+
tag_cloud = filter_tags_to_url_scope(tag_cloud, tag.attr['scope']) unless tag.attr['scope'].nil?
|
116
|
+
|
117
|
+
results_page = tag.attr['results_page'] || Radiant::Config['tags.results_page_url']
|
118
|
+
output = "<div class=\"tag_cloud\">"
|
119
|
+
if tag_cloud.length > 0
|
120
|
+
build_tag_cloud(tag_cloud, %w(size1 size2 size3 size4 size5 size6 size7 size8 size9)) do |tag, cloud_class, amount|
|
121
|
+
output += "<div class=\"#{cloud_class}\"><a href=\"#{results_page}/#{tag}\" class=\"tag\">#{tag}</a></div>\n"
|
122
|
+
end
|
123
|
+
else
|
124
|
+
return I18n.t('tags_extension.no_tags_found')
|
125
|
+
end
|
126
|
+
output += "</div>"
|
127
|
+
end
|
105
128
|
|
106
129
|
desc %{
|
107
130
|
Render a Tag list, more for 'categories'-ish usage, i.e.: Cats (2) Logs (1) ...
|
@@ -121,7 +144,7 @@ module RadiusTags
|
|
121
144
|
output += "<li class=\"#{cloud_class}\"><a href=\"#{results_page}/#{tag}\" class=\"tag\">#{tag} (#{amount})</a></li>"
|
122
145
|
end
|
123
146
|
else
|
124
|
-
return
|
147
|
+
return I18n.t('tags_extension.no_tags_found')
|
125
148
|
end
|
126
149
|
output += "</ul>"
|
127
150
|
end
|
@@ -174,18 +197,14 @@ module RadiusTags
|
|
174
197
|
Iterates through each tag and allows you to specify the order: by popularity or by name.
|
175
198
|
The default is by name. You may also limit the search; the default is 5 results.
|
176
199
|
|
177
|
-
Usage: <pre><code><r:all_tags:each order="popularity" limit="5">...</r:all_tags:each></code></pre>
|
200
|
+
Usage: <pre><code><r:all_tags:each [order="asc|desc"] [by="name|popularity"] limit="5">...</r:all_tags:each></code></pre>
|
178
201
|
}
|
179
202
|
tag "all_tags:each" do |tag|
|
180
|
-
|
203
|
+
by = tag.attr['by'] || 'name'
|
204
|
+
order = tag.attr['order'] || 'asc'
|
181
205
|
limit = tag.attr['limit'] || '5'
|
182
206
|
result = []
|
183
|
-
|
184
|
-
when 'name'
|
185
|
-
all_tags = MetaTag.find(:all, :limit => limit)
|
186
|
-
else
|
187
|
-
all_tags = MetaTag.cloud(:limit => limit)
|
188
|
-
end
|
207
|
+
all_tags = MetaTag.cloud(:limit => limit, :order => order, :by => by)
|
189
208
|
all_tags.each do |t|
|
190
209
|
next if t.pages.empty? # skip unused tags
|
191
210
|
tag.locals.meta_tag = t
|
@@ -202,9 +221,18 @@ module RadiusTags
|
|
202
221
|
tag "all_tags:each:link" do |tag|
|
203
222
|
results_page = tag.attr['results_page'] || Radiant::Config['tags.results_page_url']
|
204
223
|
name = tag.locals.meta_tag.name
|
205
|
-
|
224
|
+
"<a href=\"#{results_page}/#{name}\" class=\"tag\">#{name}</a>"
|
225
|
+
end
|
226
|
+
|
227
|
+
tag "all_tags:each:popularity" do |tag|
|
228
|
+
(tag.locals.meta_tag.respond_to?(:popularity)) ? tag.locals.meta_tag.popularity : ""
|
229
|
+
end
|
230
|
+
|
231
|
+
tag "all_tags:each:url" do |tag|
|
232
|
+
results_page = tag.attr['results_page'] || Radiant::Config['tags.results_page_url']
|
233
|
+
name = tag.locals.meta_tag.name
|
234
|
+
"#{results_page}/#{name}"
|
206
235
|
end
|
207
|
-
|
208
236
|
|
209
237
|
desc "Set the scope for the tag's pages"
|
210
238
|
tag "all_tags:each:pages" do |tag|
|
@@ -1,7 +1,28 @@
|
|
1
1
|
class TagSearchPage < Page
|
2
2
|
|
3
3
|
attr_accessor :requested_tag
|
4
|
+
|
4
5
|
#### Tags ####
|
6
|
+
|
7
|
+
desc %{ The namespace for all tagsearch tags.}
|
8
|
+
tag 'tagsearch' do |tag|
|
9
|
+
tag.expand
|
10
|
+
end
|
11
|
+
|
12
|
+
desc %{ to show a index page, if the page was not able to find a tag, it is considered to be a index page}
|
13
|
+
tag 'tagsearch:if_index' do |tag|
|
14
|
+
if requested_tag.nil? || (requested_tag && requested_tag.blank?)
|
15
|
+
tag.expand
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
desc %{ resultpage, if the page got a tag, it is considered to be a result page}
|
20
|
+
tag 'tagsearch:unless_index' do |tag|
|
21
|
+
if requested_tag && ! requested_tag.blank?
|
22
|
+
tag.expand
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
5
26
|
desc %{ The namespace for all search tags.}
|
6
27
|
tag 'search' do |tag|
|
7
28
|
tag.expand
|
@@ -68,8 +89,8 @@ class TagSearchPage < Page
|
|
68
89
|
end
|
69
90
|
|
70
91
|
def render
|
71
|
-
self.requested_tag = @request.parameters[:tag]
|
72
|
-
self.title = "
|
92
|
+
self.requested_tag = @request.parameters[:tag] if @request.parameters[:tag]
|
93
|
+
self.title = "#{self.title} #{requested_tag}" if requested_tag
|
73
94
|
|
74
95
|
super
|
75
96
|
end
|
@@ -1,8 +1,14 @@
|
|
1
|
-
|
1
|
+
= render 'tag_field_javascript' if Radiant::Config['tags.complex_strings'] == 'true'
|
2
|
+
|
3
|
+
%tr.tags
|
2
4
|
%th.label
|
3
5
|
%label{:for => "page_meta_tags"}
|
4
|
-
|
6
|
+
= t('tags_extension.tags')
|
5
7
|
%td.field
|
6
8
|
= f.text_field :meta_tags, :value => @page.tag_list, :class => 'textbox'
|
9
|
+
#tag_container{:style => "display: none"}
|
10
|
+
.default
|
11
|
+
= t('tags_extension.type_a_tag')
|
12
|
+
%ul
|
7
13
|
- unless model.errors.on_base.nil?
|
8
14
|
%span.error= model.errors.on_base
|
@@ -0,0 +1,17 @@
|
|
1
|
+
<%
|
2
|
+
include_javascript 'admin/protomultiselect.js'
|
3
|
+
include_stylesheet 'admin/tags.css'
|
4
|
+
%>
|
5
|
+
|
6
|
+
<% content_for 'page_scripts' do %>
|
7
|
+
document.observe('dom:loaded', function() {
|
8
|
+
var taglist = new FacebookList('page_meta_tags', 'tag_container', {
|
9
|
+
newValues: true,
|
10
|
+
separator: ';',
|
11
|
+
separatorKeyCode: 186
|
12
|
+
})
|
13
|
+
|
14
|
+
var tags = <%= MetaTag.all.map {|tag| {:caption => tag.name, :value => tag.name} }.to_json %>
|
15
|
+
tags.each(function(t) { taglist.autoFeed(t) })
|
16
|
+
});
|
17
|
+
<% end %>
|
data/config/routes.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
ActionController::Routing::Routes.draw do |map|
|
2
|
+
if Radiant::Config['tags.results_page_url'].blank?
|
3
|
+
Radiant::Config['tags.results_page_url'] = TagsExtension::DEFAULT_RESULTS_URL if Radiant::Config['tags.results_page_url'].blank?
|
4
|
+
end
|
5
|
+
begin
|
6
|
+
if defined?(SiteLanguage) && SiteLanguage.count > 0
|
7
|
+
include Globalize
|
8
|
+
SiteLanguage.codes.each do |code|
|
9
|
+
langname = Locale.new(code).language.code
|
10
|
+
map.connect "#{langname}#{Radiant::Config['tags.results_page_url']}/:tag", :controller => 'site', :action => 'show_page', :url => Radiant::Config['tags.results_page_url'], :language => code
|
11
|
+
end
|
12
|
+
else if defined?(VhostExtension)
|
13
|
+
map.connect "#{Radiant::Config['tags.results_page_url']}/:tag", :controller => 'site', :action => 'show_page', :url => Radiant::Config['tags.results_page_url']
|
14
|
+
end
|
15
|
+
map.connect "#{Radiant::Config['tags.results_page_url']}/:tag", :controller => 'site', :action => 'show_page', :url => Radiant::Config['tags.results_page_url']
|
16
|
+
end
|
17
|
+
rescue
|
18
|
+
# dirty hack; need to get trough here to allow migrations to run..
|
19
|
+
end
|
20
|
+
end
|
data/lib/tagging_methods.rb
CHANGED
@@ -58,9 +58,7 @@ TaggingMethods = Proc.new do
|
|
58
58
|
sql << "AND taggings.meta_tag_id = meta_tags.id "
|
59
59
|
sql << "AND pages.site_id = #{current_site.id} " if self.respond_to?(:current_site)
|
60
60
|
|
61
|
-
|
62
|
-
|
63
|
-
sql << "AND (meta_tags.name IN (#{sanitize_sql(tag_list_condition)})) "
|
61
|
+
sql << "AND (#{sanitize_sql(['meta_tags.name IN (?)', tag_list.map{|name| name.strip.squeeze(' ')}])}) "
|
64
62
|
sql << "AND #{sanitize_sql(options[:conditions])} " if options[:conditions]
|
65
63
|
|
66
64
|
columns = column_names.map do |column|
|
Binary file
|
@@ -0,0 +1,527 @@
|
|
1
|
+
/*
|
2
|
+
Proto!MultiSelect
|
3
|
+
Copyright: InteRiders <http://interiders.com/> - Distributed under MIT - Keep this message!
|
4
|
+
*/
|
5
|
+
|
6
|
+
// Added key contstant for COMMA watching happiness
|
7
|
+
Object.extend(Event, { KEY_COMMA: 188, KEY_SPACE: 32 });
|
8
|
+
|
9
|
+
var ResizableTextbox = Class.create({
|
10
|
+
initialize: function(element, options) {
|
11
|
+
var that = this;
|
12
|
+
this.options = $H({
|
13
|
+
min: 5,
|
14
|
+
max: 500,
|
15
|
+
step: 7
|
16
|
+
});
|
17
|
+
this.options.update(options);
|
18
|
+
this.el = $(element);
|
19
|
+
this.width = this.el.offsetWidth;
|
20
|
+
this.el.observe(
|
21
|
+
'keyup', function() {
|
22
|
+
var newsize = that.options.get('step') * $F(this).length;
|
23
|
+
if(newsize <= that.options.get('min')) newsize = that.options.get('mix');
|
24
|
+
if(! ($F(this).length == this.retrieveData('rt-value') || newsize <= that.options.min || newsize >= that.options.max))
|
25
|
+
this.setStyle({'width': newsize});
|
26
|
+
}).observe('keydown', function() {
|
27
|
+
this.cacheData('rt-value', $F(this).length);
|
28
|
+
}
|
29
|
+
);
|
30
|
+
}
|
31
|
+
});
|
32
|
+
|
33
|
+
var TextboxList = Class.create({
|
34
|
+
initialize: function(element, options) {
|
35
|
+
this.options = $H({/*
|
36
|
+
onFocus: $empty,
|
37
|
+
onBlur: $empty,
|
38
|
+
onInputFocus: $empty,
|
39
|
+
onInputBlur: $empty,
|
40
|
+
onBoxFocus: $empty,
|
41
|
+
onBoxBlur: $empty,
|
42
|
+
onBoxDispose: $empty,*/
|
43
|
+
resizable: {},
|
44
|
+
className: 'bit',
|
45
|
+
separator: ',',
|
46
|
+
separatorKeyCode: Event.KEY_COMMA,
|
47
|
+
extrainputs: true,
|
48
|
+
startinput: true,
|
49
|
+
hideempty: true,
|
50
|
+
newValues: false,
|
51
|
+
newValueDelimiters: ['[',']'],
|
52
|
+
spaceReplace: '',
|
53
|
+
fetchFile: undefined,
|
54
|
+
fetchMethod: 'get',
|
55
|
+
results: 10,
|
56
|
+
maxResults: 0, // 0 = set to default (which is 10 (see FacebookList class)),
|
57
|
+
wordMatch: false,
|
58
|
+
onEmptyInput: function(input){},
|
59
|
+
caseSensitive: false,
|
60
|
+
regexSearch: true
|
61
|
+
});
|
62
|
+
this.current_input = "";
|
63
|
+
this.options.update(options);
|
64
|
+
this.element = $(element).hide();
|
65
|
+
this.bits = new Hash();
|
66
|
+
this.events = new Hash();
|
67
|
+
this.count = 0;
|
68
|
+
this.current = false;
|
69
|
+
this.maininput = this.createInput({'class': 'maininput'});
|
70
|
+
this.holder = new Element('ul', {
|
71
|
+
'class': 'holder'
|
72
|
+
}).insert(this.maininput);
|
73
|
+
this.element.insert({'before':this.holder});
|
74
|
+
this.holder.observe('click', function(event){
|
75
|
+
event.stop();
|
76
|
+
if(this.maininput != this.current) this.focus(this.maininput);
|
77
|
+
}.bind(this));
|
78
|
+
this.makeResizable(this.maininput);
|
79
|
+
this.setEvents();
|
80
|
+
},
|
81
|
+
|
82
|
+
setEvents: function() {
|
83
|
+
document.observe(Prototype.Browser.IE ? 'keydown' : 'keypress', function(e) {
|
84
|
+
if(! this.current) return;
|
85
|
+
if(this.current.retrieveData('type') == 'box' && e.keyCode == Event.KEY_BACKSPACE) e.stop();
|
86
|
+
}.bind(this));
|
87
|
+
|
88
|
+
document.observe(
|
89
|
+
'keyup', function(e) {
|
90
|
+
e.stop();
|
91
|
+
if(! this.current) return;
|
92
|
+
switch(e.keyCode){
|
93
|
+
case Event.KEY_LEFT: return this.move('left');
|
94
|
+
case Event.KEY_RIGHT: return this.move('right');
|
95
|
+
case Event.KEY_DELETE:
|
96
|
+
case Event.KEY_BACKSPACE: return this.moveDispose();
|
97
|
+
}
|
98
|
+
}.bind(this)).observe(
|
99
|
+
'click', function() { document.fire('blur'); }.bindAsEventListener(this)
|
100
|
+
);
|
101
|
+
},
|
102
|
+
|
103
|
+
update: function() {
|
104
|
+
this.element.value = this.bits.values().join(this.options.get('separator'));
|
105
|
+
if (!this.current_input.blank()){
|
106
|
+
this.element.value += (this.element.value.blank() ? "" : this.options.get('separator')) + this.current_input;
|
107
|
+
}
|
108
|
+
return this;
|
109
|
+
},
|
110
|
+
|
111
|
+
add: function(text, html) {
|
112
|
+
var id = this.id_base + '-' + this.count++;
|
113
|
+
var el = this.createBox($pick(html, text), {'id': id, 'class': this.options.get('className'), 'newValue' : text.newValue ? 'true' : 'false'});
|
114
|
+
(this.current || this.maininput).insert({'before':el});
|
115
|
+
el.observe('click', function(e) {
|
116
|
+
e.stop();
|
117
|
+
this.focus(el);
|
118
|
+
}.bind(this));
|
119
|
+
this.bits.set(id, text.value);
|
120
|
+
// Dynamic updating... why not?
|
121
|
+
this.update();
|
122
|
+
if(this.options.get('extrainputs') && (this.options.get('startinput') || el.previous())) this.addSmallInput(el,'before');
|
123
|
+
return el;
|
124
|
+
},
|
125
|
+
|
126
|
+
addSmallInput: function(el, where) {
|
127
|
+
var input = this.createInput({'class': 'smallinput'});
|
128
|
+
el.insert({}[where] = input);
|
129
|
+
input.cacheData('small', true);
|
130
|
+
this.makeResizable(input);
|
131
|
+
if(this.options.get('hideempty')) input.hide();
|
132
|
+
return input;
|
133
|
+
},
|
134
|
+
|
135
|
+
dispose: function(el) {
|
136
|
+
this.bits.unset(el.id);
|
137
|
+
// Dynamic updating... why not?
|
138
|
+
this.update();
|
139
|
+
if(el.previous() && el.previous().retrieveData('small')) el.previous().remove();
|
140
|
+
if(this.current == el) this.focus(el.next());
|
141
|
+
if(el.retrieveData('type') == 'box') el.onBoxDispose(this);
|
142
|
+
el.remove();
|
143
|
+
return this;
|
144
|
+
},
|
145
|
+
|
146
|
+
focus: function(el, nofocus) {
|
147
|
+
if(! this.current) el.fire('focus');
|
148
|
+
else if(this.current == el) return this;
|
149
|
+
this.blur();
|
150
|
+
el.addClassName(this.options.get('className') + '-' + el.retrieveData('type') + '-focus');
|
151
|
+
if(el.retrieveData('small')) el.setStyle({'display': 'block'});
|
152
|
+
if(el.retrieveData('type') == 'input') {
|
153
|
+
el.onInputFocus(this);
|
154
|
+
if(! nofocus) this.callEvent(el.retrieveData('input'), 'focus');
|
155
|
+
}
|
156
|
+
else el.fire('onBoxFocus');
|
157
|
+
this.current = el;
|
158
|
+
return this;
|
159
|
+
},
|
160
|
+
|
161
|
+
blur: function(noblur) {
|
162
|
+
if(! this.current) return this;
|
163
|
+
if(this.current.retrieveData('type') == 'input') {
|
164
|
+
var input = this.current.retrieveData('input');
|
165
|
+
if(! noblur) this.callEvent(input, 'blur');
|
166
|
+
input.onInputBlur(this);
|
167
|
+
}
|
168
|
+
else this.current.fire('onBoxBlur');
|
169
|
+
if(this.current.retrieveData('small') && ! input.get('value') && this.options.get('hideempty'))
|
170
|
+
this.current.hide();
|
171
|
+
this.current.removeClassName(this.options.get('className') + '-' + this.current.retrieveData('type') + '-focus');
|
172
|
+
this.current = false;
|
173
|
+
return this;
|
174
|
+
},
|
175
|
+
|
176
|
+
createBox: function(text, options) {
|
177
|
+
return new Element('li', options).addClassName(this.options.get('className') + '-box').update(text.caption).cacheData('type', 'box');
|
178
|
+
},
|
179
|
+
|
180
|
+
createInput: function(options) {
|
181
|
+
var li = new Element('li', {'class': this.options.get('className') + '-input'});
|
182
|
+
var el = new Element('input', Object.extend(options,{'type': 'text', 'autocomplete':'off'}));
|
183
|
+
el.observe('click', function(e) { e.stop(); }).observe('focus', function(e) { if(! this.isSelfEvent('focus')) this.focus(li, true); }.bind(this)).observe('blur', function() { if(! this.isSelfEvent('blur')) this.blur(true); }.bind(this)).observe('keydown', function(e) { this.cacheData('lastvalue', this.value).cacheData('lastcaret', this.getCaretPosition()); });
|
184
|
+
var tmp = li.cacheData('type', 'input').cacheData('input', el).insert(el);
|
185
|
+
return tmp;
|
186
|
+
},
|
187
|
+
|
188
|
+
callEvent: function(el, type) {
|
189
|
+
this.events.set(type, el);
|
190
|
+
el[type]();
|
191
|
+
},
|
192
|
+
|
193
|
+
isSelfEvent: function(type) {
|
194
|
+
return (this.events.get(type)) ? !! this.events.unset(type) : false;
|
195
|
+
},
|
196
|
+
|
197
|
+
makeResizable: function(li) {
|
198
|
+
var el = li.retrieveData('input');
|
199
|
+
el.cacheData('resizable', new ResizableTextbox(el, Object.extend(this.options.get('resizable'),{min: 150, max: 500})));
|
200
|
+
return this;
|
201
|
+
},
|
202
|
+
|
203
|
+
checkInput: function() {
|
204
|
+
var input = this.current.retrieveData('input');
|
205
|
+
return (! input.retrieveData('lastvalue') || (input.getCaretPosition() === 0 && input.retrieveData('lastcaret') === 0));
|
206
|
+
},
|
207
|
+
|
208
|
+
move: function(direction) {
|
209
|
+
var el = this.current[(direction == 'left' ? 'previous' : 'next')]();
|
210
|
+
if(el && (! this.current.retrieveData('input') || ((this.checkInput() || direction == 'right')))) this.focus(el);
|
211
|
+
return this;
|
212
|
+
},
|
213
|
+
|
214
|
+
moveDispose: function() {
|
215
|
+
if(this.current.retrieveData('type') == 'box') return this.dispose(this.current);
|
216
|
+
if(this.checkInput() && this.bits.keys().length && this.current.previous()) return this.focus(this.current.previous());
|
217
|
+
}
|
218
|
+
});
|
219
|
+
|
220
|
+
//helper functions
|
221
|
+
Element.addMethods({
|
222
|
+
getCaretPosition: function() {
|
223
|
+
if (this.createTextRange) {
|
224
|
+
var r = document.selection.createRange().duplicate();
|
225
|
+
r.moveEnd('character', this.value.length);
|
226
|
+
if (r.text === '') return this.value.length;
|
227
|
+
return this.value.lastIndexOf(r.text);
|
228
|
+
} else return this.selectionStart;
|
229
|
+
},
|
230
|
+
cacheData: function(element, key, value) {
|
231
|
+
if (Object.isUndefined(this[$(element).identify()]) || !Object.isHash(this[$(element).identify()]))
|
232
|
+
this[$(element).identify()] = $H();
|
233
|
+
this[$(element).identify()].set(key,value);
|
234
|
+
return element;
|
235
|
+
},
|
236
|
+
retrieveData: function(element,key) {
|
237
|
+
return this[$(element).identify()].get(key);
|
238
|
+
}
|
239
|
+
});
|
240
|
+
|
241
|
+
function $pick(){for(var B=0,A=arguments.length;B<A;B++){if(!Object.isUndefined(arguments[B])){return arguments[B];}}return null;}
|
242
|
+
|
243
|
+
var FacebookList = Class.create(TextboxList, {
|
244
|
+
initialize: function($super,element, autoholder, options, func) {
|
245
|
+
$super(element, options);
|
246
|
+
this.loptions = $H({
|
247
|
+
autocomplete: {
|
248
|
+
'opacity': 1,
|
249
|
+
'maxresults': 10,
|
250
|
+
'minchars': 1
|
251
|
+
}
|
252
|
+
});
|
253
|
+
|
254
|
+
this.id_base = $(element).identify() + "_" + this.options.get("className");
|
255
|
+
|
256
|
+
this.data = [];
|
257
|
+
this.data_searchable = [];
|
258
|
+
this.autoholder = $(autoholder).setOpacity(this.loptions.get('autocomplete').opacity);
|
259
|
+
this.autoholder.observe('mouseover',function() {this.curOn = true;}.bind(this)).observe('mouseout',function() {this.curOn = false;}.bind(this));
|
260
|
+
this.autoresults = this.autoholder.select('ul').first();
|
261
|
+
var children = this.autoresults.select('li');
|
262
|
+
children.each(function(el) { this.add({value:el.readAttribute('value'),caption:el.innerHTML}); }, this);
|
263
|
+
|
264
|
+
$F(this.element).split(this.options.get('separator')).each(function(item) {
|
265
|
+
this.add({value:item,caption:item});
|
266
|
+
}, this)
|
267
|
+
|
268
|
+
// Loading the options list only once at initialize.
|
269
|
+
// This would need to be further extended if the list was exceptionally long
|
270
|
+
if (!Object.isUndefined(this.options.get('fetchFile'))) {
|
271
|
+
new Ajax.Request(this.options.get('fetchFile'), {
|
272
|
+
method: this.options.get('fetchMethod'),
|
273
|
+
onSuccess: function(transport) {
|
274
|
+
transport.responseText.evalJSON(true).each(function(t) {
|
275
|
+
this.autoFeed(t) }.bind(this));
|
276
|
+
}.bind(this)
|
277
|
+
}
|
278
|
+
);
|
279
|
+
}
|
280
|
+
},
|
281
|
+
|
282
|
+
autoShow: function(search) {
|
283
|
+
this.autoholder.setStyle({'display': 'block'});
|
284
|
+
this.autoholder.descendants().each(function(e) { e.hide() });
|
285
|
+
if(! search || ! search.strip() || (! search.length || search.length < this.loptions.get('autocomplete').minchars)) {
|
286
|
+
this.autoholder.select('.default').first().setStyle({'display': 'block'});
|
287
|
+
this.resultsshown = false;
|
288
|
+
} else {
|
289
|
+
this.resultsshown = true;
|
290
|
+
this.autoresults.setStyle({'display': 'block'}).update('');
|
291
|
+
if (!this.options.get('regexSearch')) {
|
292
|
+
var matches = new Array();
|
293
|
+
if (search) {
|
294
|
+
if (!this.options.get('caseSensitive')) {
|
295
|
+
search = search.toLowerCase();
|
296
|
+
}
|
297
|
+
var matches_found = 0;
|
298
|
+
for (var i=0,len=this.data_searchable.length; i<len; i++) {
|
299
|
+
if (this.data_searchable[i].indexOf(search) >= 0) {
|
300
|
+
matches[matches_found++] = this.data[i];
|
301
|
+
}
|
302
|
+
}
|
303
|
+
}
|
304
|
+
} else {
|
305
|
+
if (this.options.get('wordMatch')) {
|
306
|
+
var regexp = new RegExp("(^|\\s)"+search,(!this.options.get('caseSensitive') ? 'i' : ''));
|
307
|
+
} else {
|
308
|
+
var regexp = new RegExp(search,(!this.options.get('caseSensitive') ? 'i' : ''));
|
309
|
+
var matches = this.data.filter(
|
310
|
+
function(str) {
|
311
|
+
return str ? regexp.test(str.evalJSON(true).caption) : false;
|
312
|
+
});
|
313
|
+
}
|
314
|
+
}
|
315
|
+
var count = 0;
|
316
|
+
matches.each(
|
317
|
+
function(result, ti) {
|
318
|
+
count++;
|
319
|
+
if(ti >= (this.options.get('maxResults') ? this.options.get('maxResults') : this.loptions.get('autocomplete').maxresults)) return;
|
320
|
+
var that = this;
|
321
|
+
var el = new Element('li');
|
322
|
+
el.observe('click',function(e) {
|
323
|
+
e.stop();
|
324
|
+
that.current_input = "";
|
325
|
+
that.autoAdd(this);
|
326
|
+
}
|
327
|
+
).observe('mouseover', function() { that.autoFocus(this); } ).update(
|
328
|
+
this.autoHighlight(result.evalJSON(true).caption, search)
|
329
|
+
);
|
330
|
+
this.autoresults.insert(el);
|
331
|
+
el.cacheData('result', result.evalJSON(true));
|
332
|
+
if(ti == 0) this.autoFocus(el);
|
333
|
+
},
|
334
|
+
this
|
335
|
+
);
|
336
|
+
}
|
337
|
+
if (count == 0) {
|
338
|
+
// if there are no results, hide everything so that KEY_ENTER has no effect
|
339
|
+
this.autoHide();
|
340
|
+
} else {
|
341
|
+
if (count > this.options.get('results'))
|
342
|
+
this.autoresults.setStyle({'height': (this.options.get('results')*24)+'px'});
|
343
|
+
else
|
344
|
+
this.autoresults.setStyle({'height': (count?(count*24):0)+'px'});
|
345
|
+
}
|
346
|
+
|
347
|
+
return this;
|
348
|
+
},
|
349
|
+
|
350
|
+
autoHighlight: function(html, highlight) {
|
351
|
+
return html.gsub(new RegExp(highlight,'i'), function(match) {
|
352
|
+
return '<em>' + match[0] + '</em>';
|
353
|
+
});
|
354
|
+
},
|
355
|
+
|
356
|
+
autoHide: function() {
|
357
|
+
this.resultsshown = false;
|
358
|
+
this.autoholder.hide();
|
359
|
+
return this;
|
360
|
+
},
|
361
|
+
|
362
|
+
autoFocus: function(el) {
|
363
|
+
if(! el) return;
|
364
|
+
if(this.autocurrent) this.autocurrent.removeClassName('auto-focus');
|
365
|
+
this.autocurrent = el.addClassName('auto-focus');
|
366
|
+
return this;
|
367
|
+
},
|
368
|
+
|
369
|
+
autoMove: function(direction) {
|
370
|
+
if(!this.resultsshown) return;
|
371
|
+
this.autoFocus(this.autocurrent[(direction == 'up' ? 'previous' : 'next')]());
|
372
|
+
this.autoresults.scrollTop = this.autocurrent.positionedOffset()[1]-this.autocurrent.getHeight();
|
373
|
+
return this;
|
374
|
+
},
|
375
|
+
|
376
|
+
autoFeed: function(text) {
|
377
|
+
var with_case = this.options.get('caseSensitive');
|
378
|
+
if (this.data.indexOf(Object.toJSON(text)) == -1) {
|
379
|
+
this.data.push(Object.toJSON(text));
|
380
|
+
this.data_searchable.push(with_case ? Object.toJSON(text).evalJSON(true).caption : Object.toJSON(text).evalJSON(true).caption.toLowerCase());
|
381
|
+
}
|
382
|
+
return this;
|
383
|
+
},
|
384
|
+
|
385
|
+
autoAdd: function(el) {
|
386
|
+
if(this.newvalue && this.options.get("newValues")) {
|
387
|
+
this.add({caption: el.value, value: el.value, newValue: true});
|
388
|
+
var input = el;
|
389
|
+
} else if(!el || ! el.retrieveData('result')) {
|
390
|
+
return;
|
391
|
+
} else {
|
392
|
+
this.add(el.retrieveData('result'));
|
393
|
+
delete this.data[this.data.indexOf(Object.toJSON(el.retrieveData('result')))];
|
394
|
+
var input = this.lastinput || this.current.retrieveData('input');
|
395
|
+
}
|
396
|
+
this.autoHide();
|
397
|
+
input.clear().focus();
|
398
|
+
return this;
|
399
|
+
},
|
400
|
+
|
401
|
+
createInput: function($super,options) {
|
402
|
+
var li = $super(options);
|
403
|
+
var input = li.retrieveData('input');
|
404
|
+
input.observe('keydown', function(e) {
|
405
|
+
this.dosearch = false;
|
406
|
+
this.newvalue = false;
|
407
|
+
|
408
|
+
switch(e.keyCode) {
|
409
|
+
case Event.KEY_UP: e.stop(); return this.autoMove('up');
|
410
|
+
case Event.KEY_DOWN: e.stop(); return this.autoMove('down');
|
411
|
+
|
412
|
+
case Event.KEY_RETURN:
|
413
|
+
// If the text input is blank and the user hits Enter call the
|
414
|
+
// onEmptyInput callback.
|
415
|
+
if (String('').valueOf() == String(this.current.retrieveData('input').getValue()).valueOf()) {
|
416
|
+
this.options.get("onEmptyInput")();
|
417
|
+
}
|
418
|
+
e.stop();
|
419
|
+
if(!this.autocurrent || !this.resultsshown) break;
|
420
|
+
this.current_input = "";
|
421
|
+
this.autoAdd(this.autocurrent);
|
422
|
+
this.autocurrent = false;
|
423
|
+
this.autoenter = true;
|
424
|
+
break;
|
425
|
+
case Event.KEY_ESC:
|
426
|
+
this.autoHide();
|
427
|
+
if(this.current && this.current.retrieveData('input'))
|
428
|
+
this.current.retrieveData('input').clear();
|
429
|
+
break;
|
430
|
+
default:
|
431
|
+
this.dosearch = true;
|
432
|
+
}
|
433
|
+
}.bind(this));
|
434
|
+
input.observe('keyup',function(e) {
|
435
|
+
switch(e.keyCode) {
|
436
|
+
case this.options.get('separatorKeyCode'):
|
437
|
+
if(this.options.get('newValues')) {
|
438
|
+
var separator = this.options.get('separator');
|
439
|
+
new_value_el = this.current.retrieveData('input');
|
440
|
+
if (!new_value_el.value.endsWith('<')) {
|
441
|
+
keep_input = "";
|
442
|
+
new_value_el.value = new_value_el.value.strip();
|
443
|
+
if (new_value_el.value.indexOf(separator) < (new_value_el.value.length - 1)){
|
444
|
+
comma_pos = new_value_el.value.indexOf(separator);
|
445
|
+
keep_input = new_value_el.value.substr(comma_pos + 1);
|
446
|
+
new_value_el.value = new_value_el.value.substr(0,comma_pos).escapeHTML().strip();
|
447
|
+
} else {
|
448
|
+
new_value_el.value = new_value_el.value.gsub(separator,"").escapeHTML().strip();
|
449
|
+
}
|
450
|
+
if(!this.options.get("spaceReplace").blank()) new_value_el.value.gsub(" ", this.options.get("spaceReplace"));
|
451
|
+
if(!new_value_el.value.blank()) {
|
452
|
+
e.stop();
|
453
|
+
this.newvalue = true;
|
454
|
+
this.current_input = keep_input.escapeHTML().strip();
|
455
|
+
this.autoAdd(new_value_el);
|
456
|
+
input.value = keep_input;
|
457
|
+
this.update();
|
458
|
+
}
|
459
|
+
}
|
460
|
+
}
|
461
|
+
break;
|
462
|
+
case Event.KEY_UP:
|
463
|
+
case Event.KEY_DOWN:
|
464
|
+
case Event.KEY_RETURN:
|
465
|
+
case Event.KEY_ESC:
|
466
|
+
break;
|
467
|
+
default:
|
468
|
+
// If the user doesn't add comma after, the value is discarded upon submit
|
469
|
+
this.current_input = input.value.strip().escapeHTML();
|
470
|
+
this.update();
|
471
|
+
|
472
|
+
// Removed Ajax.Request from here and moved to initialize,
|
473
|
+
// now doesn't create server queries every search but only
|
474
|
+
// refreshes the list on initialize (page load)
|
475
|
+
if(this.searchTimeout) clearTimeout(this.searchTimeout);
|
476
|
+
this.searchTimeout = setTimeout(function(){
|
477
|
+
var sanitizer = new RegExp("[({[^$*+?\\\]})]","g");
|
478
|
+
if(this.dosearch) this.autoShow(input.value.replace(sanitizer,"\\$1"));
|
479
|
+
}.bind(this), 250);
|
480
|
+
}
|
481
|
+
}.bind(this));
|
482
|
+
input.observe(Prototype.Browser.IE ? 'keydown' : 'keypress', function(e) {
|
483
|
+
if ((e.keyCode == Event.KEY_RETURN) && this.autoenter) e.stop();
|
484
|
+
this.autoenter = false;
|
485
|
+
}.bind(this));
|
486
|
+
return li;
|
487
|
+
},
|
488
|
+
|
489
|
+
createBox: function($super,text, options) {
|
490
|
+
var li = $super(text, options);
|
491
|
+
li.observe('mouseover',function() {
|
492
|
+
this.addClassName('bit-hover');
|
493
|
+
}).observe('mouseout',function() {
|
494
|
+
this.removeClassName('bit-hover')
|
495
|
+
});
|
496
|
+
var a = new Element('a', {
|
497
|
+
'href': '#',
|
498
|
+
'class': 'closebutton'
|
499
|
+
});
|
500
|
+
a.observe('click',function(e) {
|
501
|
+
e.stop();
|
502
|
+
if(! this.current) this.focus(this.maininput);
|
503
|
+
this.dispose(li);
|
504
|
+
}.bind(this));
|
505
|
+
li.insert(a).cacheData('text', Object.toJSON(text));
|
506
|
+
return li;
|
507
|
+
}
|
508
|
+
});
|
509
|
+
|
510
|
+
Element.addMethods({
|
511
|
+
onBoxDispose: function(item,obj) {
|
512
|
+
// Set to not to "add back" values in the drop-down upon delete if they were new values
|
513
|
+
item = item.retrieveData('text').evalJSON(true);
|
514
|
+
if(!item.newValue)
|
515
|
+
obj.autoFeed(item);
|
516
|
+
},
|
517
|
+
onInputFocus: function(el,obj) { obj.autoShow(); },
|
518
|
+
onInputBlur: function(el,obj) {
|
519
|
+
obj.lastinput = el;
|
520
|
+
if(!obj.curOn) {
|
521
|
+
obj.blurhide = obj.autoHide.bind(obj).delay(0.1);
|
522
|
+
}
|
523
|
+
},
|
524
|
+
filter: function(D,E) { var C=[];for(var B=0,A=this.length;B<A;B++){if(D.call(E,this[B],B,this)){C.push(this[B]);}} return C; }
|
525
|
+
});
|
526
|
+
|
527
|
+
/* Copyright: InteRiders <http://interiders.com/> - Distributed under MIT - Keep this message! */
|
@@ -0,0 +1,21 @@
|
|
1
|
+
tr.tags ul.holder { width: 99.5%; margin: 0; border: 1px solid #999; overflow: hidden; height: auto !important; height: 1%; padding: 4px 5px 0; background-color: white; display: block !important; }
|
2
|
+
*:first-child+html ul.holder { padding-bottom: 2px; } * html ul.holder { padding-bottom: 2px; } /* ie7 and below */
|
3
|
+
tr.tags ul.holder li { float: left; list-style-type: none; margin: 0 5px 4px 0;}
|
4
|
+
tr.tags ul.holder li.bit-box { -moz-border-radius: 6px; -webkit-border-radius: 6px; border-radius: 6px; border: 1px solid #CAD8F3; background: #DEE7F8; padding: 1px 5px 2px; }
|
5
|
+
tr.tags ul.holder li.bit-box-focus { border-color: #598BEC; background: #598BEC; color: #fff; }
|
6
|
+
tr.tags ul.holder li.bit-input input { width: 150px; margin: 0; border: none; outline: 0; padding: 3px 0 2px; } /* no left/right padding here please */
|
7
|
+
tr.tags ul.holder li.bit-box { padding-right: 15px !important; position: relative; color: #000 !important;}
|
8
|
+
tr.tags ul.holder li.bit-input { margin: 0;}
|
9
|
+
|
10
|
+
tr.tags ul.holder li.bit-box a.closebutton { position: absolute; right: 4px; top: 5px; display: block; width: 7px; height: 7px; font-size: 1px; background: url('/images/admin/tagclose.gif'); }
|
11
|
+
tr.tags ul.holder li.bit-box a.closebutton:hover { background-position: 7px; }
|
12
|
+
tr.tags ul.holder li.bit-box-focus a.closebutton, tr.tags ul.holder li.bit-box-focus a.closebutton:hover { background-position: bottom; }
|
13
|
+
|
14
|
+
|
15
|
+
#tag_container { display: none; position: relative; background: #eee; width: 101%; }
|
16
|
+
#tag_container .default { padding: 5px 7px; border: 1px solid #ccc; border-width: 0 1px 1px; color: black; text-align: left; }
|
17
|
+
#tag_container ul { display: none; margin: 0; padding: 0; overflow: auto }
|
18
|
+
#tag_container ul li { display: block; padding: 5px 12px; z-index: 1000; cursor: pointer; margin: 0; list-style-type: none; border: 1px solid #ccc; border-width: 0 1px 1px; font: 11px "Lucida Grande", "Verdana"; text-align: left; color: black;}
|
19
|
+
#tag_container ul li em { font-weight: bold; font-style: normal; background: #ccc; }
|
20
|
+
#tag_container ul li.auto-focus { background: #4173CC; color: #fff; }
|
21
|
+
#tag_container ul li.auto-focus em { background: none; }
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path('../lib', __FILE__)
|
3
|
+
require 'radiant-tags-extension'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'radiant-tags-extension'
|
7
|
+
s.version = RadiantTagsExtension::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ['Benny Degezelle']
|
10
|
+
s.email = ['benny@gorilla-webdesign.be']
|
11
|
+
s.homepage = 'http://ext.radiantcms.org/extensions/195-tags'
|
12
|
+
s.summary = %q{Tagging for Radiant CMS}
|
13
|
+
s.description = %q{This extension enhances the page model with tagging capabilities, tagging as in \"2.0" and tagclouds.}
|
14
|
+
|
15
|
+
# TODO: add gem dependency on this instead of bundling it
|
16
|
+
# s.add_dependency 'has_many_polymorphs'
|
17
|
+
|
18
|
+
s.files = `git ls-files`.split("\n")
|
19
|
+
s.test_files = `git ls-files -- {test,spec,features,vendor/plugins/*/test,vendor/plugins/*/spec,vendor/plugins/*/features}/*`.split("\n")
|
20
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
21
|
+
s.require_paths = ["lib"]
|
22
|
+
|
23
|
+
# TODO: update for usage with Bundler/Gemfile once Radiant gets that capability
|
24
|
+
s.post_install_message = %{
|
25
|
+
Add this to your radiant project by adding the following line to your environment.rb:
|
26
|
+
config.gem 'radiant-tags-extension', :version => '#{RadiantTagsExtension::VERSION}'
|
27
|
+
}
|
28
|
+
end
|
data/tags_extension.rb
CHANGED
@@ -9,27 +9,6 @@ class TagsExtension < Radiant::Extension
|
|
9
9
|
|
10
10
|
DEFAULT_RESULTS_URL = '/search/by-tag'
|
11
11
|
|
12
|
-
define_routes do |map|
|
13
|
-
if Radiant::Config['tags.results_page_url'].blank?
|
14
|
-
Radiant::Config['tags.results_page_url'] = TagsExtension::DEFAULT_RESULTS_URL if Radiant::Config['tags.results_page_url'].blank?
|
15
|
-
end
|
16
|
-
begin
|
17
|
-
if defined?(SiteLanguage) && SiteLanguage.count > 0
|
18
|
-
include Globalize
|
19
|
-
SiteLanguage.codes.each do |code|
|
20
|
-
langname = Locale.new(code).language.code
|
21
|
-
map.connect "#{langname}#{Radiant::Config['tags.results_page_url']}/:tag", :controller => 'site', :action => 'show_page', :url => Radiant::Config['tags.results_page_url'], :language => code
|
22
|
-
end
|
23
|
-
else if defined?(VhostExtension)
|
24
|
-
map.connect "#{Radiant::Config['tags.results_page_url']}/:tag", :controller => 'site', :action => 'show_page', :url => Radiant::Config['tags.results_page_url']
|
25
|
-
end
|
26
|
-
map.connect "#{Radiant::Config['tags.results_page_url']}/:tag", :controller => 'site', :action => 'show_page', :url => Radiant::Config['tags.results_page_url']
|
27
|
-
end
|
28
|
-
rescue
|
29
|
-
# dirty hack; need to get trough here to allow migrations to run..
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
12
|
def activate
|
34
13
|
raise "The Shards extension is required and must be loaded first!" unless defined?(admin.page)
|
35
14
|
if Radiant::Config.table_exists?
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: radiant-tags-extension
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 15
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 1.
|
8
|
+
- 6
|
9
|
+
- 0
|
10
|
+
version: 1.6.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Benny Degezelle
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-03-
|
18
|
+
date: 2011-03-29 00:00:00 +02:00
|
19
19
|
default_executable:
|
20
20
|
dependencies: []
|
21
21
|
|
@@ -39,10 +39,20 @@ files:
|
|
39
39
|
- app/models/tagging.rb
|
40
40
|
- app/views/admin/help/_using_tags.html.haml
|
41
41
|
- app/views/admin/pages/_tag_field.html.haml
|
42
|
+
- app/views/admin/pages/_tag_field_javascript.html.erb
|
43
|
+
- config/locales/de.yml
|
44
|
+
- config/locales/en.yml
|
45
|
+
- config/locales/nl.yml
|
46
|
+
- config/routes.rb
|
42
47
|
- db/migrate/001_add_tag_support.rb
|
48
|
+
- lib/radiant-tags-extension.rb
|
43
49
|
- lib/tagging_methods.rb
|
44
50
|
- lib/tasks/tags_extension_tasks.rake
|
51
|
+
- public/images/admin/tagclose.gif
|
52
|
+
- public/javascripts/admin/protomultiselect.js
|
53
|
+
- public/stylesheets/admin/tags.css
|
45
54
|
- public/stylesheets/tags.css
|
55
|
+
- radiant-tags-extension.gemspec
|
46
56
|
- tags_extension.rb
|
47
57
|
- test/fixtures/meta_tags.yml
|
48
58
|
- test/fixtures/page_parts.yml
|
@@ -232,7 +242,7 @@ has_rdoc: true
|
|
232
242
|
homepage: http://ext.radiantcms.org/extensions/195-tags
|
233
243
|
licenses: []
|
234
244
|
|
235
|
-
post_install_message: "\n Add this to your radiant project
|
245
|
+
post_install_message: "\n Add this to your radiant project by adding the following line to your environment.rb:\n config.gem 'radiant-tags-extension', :version => '1.6.0'\n "
|
236
246
|
rdoc_options: []
|
237
247
|
|
238
248
|
require_paths:
|