radiant-tags-extension 1.5.1 → 1.6.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 +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:
|