Commit ba61c659230154f74b5a7e22c07d447b330fae5c
Exists in
master
and in
29 other branches
Merge commit 'refs/merge-requests/283' of git://gitorious.org/noosfero/noosfero …
…into merge-requests/283 Conflicts: test/unit/comment_test.rb
Showing
34 changed files
with
1075 additions
and
58 deletions
Show diff stats
app/controllers/public/content_viewer_controller.rb
... | ... | @@ -98,10 +98,13 @@ class ContentViewerController < ApplicationController |
98 | 98 | session[:notice] = _("Notification of new comments to '%s' was successfully canceled") % params[:email] |
99 | 99 | end |
100 | 100 | end |
101 | + | |
102 | + @comments = @plugins.dispatch_first(:load_comments, @page) | |
103 | + @comments ||= @page.comments.without_spam.as_thread | |
104 | + @comments = [@comments] unless @comments.is_a?(Array) | |
105 | + | |
106 | + @comments_count = Comment.count_thread(@comments) | |
101 | 107 | |
102 | - comments = @page.comments.without_spam | |
103 | - @comments = comments.as_thread | |
104 | - @comments_count = comments.count | |
105 | 108 | if params[:slideshow] |
106 | 109 | render :action => 'slideshow', :layout => 'slideshow' |
107 | 110 | end | ... | ... |
app/helpers/application_helper.rb
... | ... | @@ -947,10 +947,7 @@ module ApplicationHelper |
947 | 947 | options.merge!(:page => params[:npage]) |
948 | 948 | content = article.to_html(options) |
949 | 949 | content = content.kind_of?(Proc) ? self.instance_eval(&content).html_safe : content.html_safe |
950 | - @plugins && @plugins.each do |plugin| | |
951 | - content = plugin.parse_content(content) | |
952 | - end | |
953 | - content | |
950 | + filter_html(content, article) | |
954 | 951 | end |
955 | 952 | |
956 | 953 | # Please, use link_to by default! |
... | ... | @@ -1407,6 +1404,38 @@ module ApplicationHelper |
1407 | 1404 | @no_design_blocks = true |
1408 | 1405 | end |
1409 | 1406 | |
1407 | + def filter_html(html, source) | |
1408 | + if @plugins | |
1409 | + html = convert_macro(html, source) | |
1410 | + end | |
1411 | + html | |
1412 | + end | |
1413 | + | |
1414 | + def convert_macro(html, source) | |
1415 | + doc = Hpricot(html) | |
1416 | + while element = doc.search('.macro').first | |
1417 | + macro_name = element['data-macro'] | |
1418 | + method_name = "macro_#{macro_name}" | |
1419 | + attrs = collect_macro_attributes(element) | |
1420 | + plugin_instance = Environment.macros[environment.id][method_name] | |
1421 | + if plugin_instance | |
1422 | + result = plugin_instance.send(method_name, attrs, element.inner_html, source) | |
1423 | + element.inner_html = result.kind_of?(Proc) ? self.instance_eval(&result) : result | |
1424 | + element['class'] = "parsed-macro #{macro_name}" | |
1425 | + else | |
1426 | + element.inner_html = _("Unsupported macro %s!") % macro_name | |
1427 | + element['class'] = "failed-macro #{macro_name}" | |
1428 | + end | |
1429 | + attrs.each {|key, value| element.remove_attribute("data-macro-#{key}")} | |
1430 | + end | |
1431 | + doc.html | |
1432 | + end | |
1433 | + | |
1434 | + def collect_macro_attributes(element) | |
1435 | + element.attributes.to_hash.select {|key, value| key[0..10] == 'data-macro-'}. | |
1436 | + inject({}){|result, a| result.merge({a[0][11..-1] => a[1]})}.with_indifferent_access | |
1437 | + end | |
1438 | + | |
1410 | 1439 | def default_folder_for_image_upload(profile) |
1411 | 1440 | default_folder = profile.folders.find_by_type('Gallery') |
1412 | 1441 | default_folder = profile.folders.find_by_type('Folder') if default_folder.nil? | ... | ... |
app/helpers/boxes_helper.rb
... | ... | @@ -99,8 +99,8 @@ module BoxesHelper |
99 | 99 | unless block.visible? |
100 | 100 | options[:title] = _("This block is invisible. Your visitors will not see it.") |
101 | 101 | end |
102 | - @controller.send(:content_editor?) || @plugins.each do |plugin| | |
103 | - result = plugin.parse_content(result) | |
102 | + if @controller.send(:content_editor?) | |
103 | + result = filter_html(result, block) | |
104 | 104 | end |
105 | 105 | box_decorator.block_target(block.box, block) + |
106 | 106 | content_tag('div', | ... | ... |
... | ... | @@ -0,0 +1,96 @@ |
1 | +module MacrosHelper | |
2 | + | |
3 | + def macros_in_menu | |
4 | + Environment.macros[@environment.id].reject{ |macro_name, plugin_instance| macro_configuration(macro_name)[:icon_path] } | |
5 | + end | |
6 | + | |
7 | + def macros_with_buttons | |
8 | + Environment.macros[@environment.id].reject{ |macro_name, plugin_instance| !macro_configuration(macro_name)[:icon_path] } | |
9 | + end | |
10 | + | |
11 | + def macro_configuration(macro_name) | |
12 | + plugin_instance = Environment.macros[@environment.id][macro_name] | |
13 | + plugin_instance.send("config_#{macro_name}") | |
14 | + end | |
15 | + | |
16 | + def macro_title(macro_name) | |
17 | + macro_configuration(macro_name)[:title] || macro_name.to_s.humanize | |
18 | + end | |
19 | + | |
20 | + def generate_macro_config_dialog(macro_name) | |
21 | + if macro_configuration(macro_name)[:skip_dialog] | |
22 | + "function(){#{macro_generator(macro_name)}}" | |
23 | + else | |
24 | + "function(){ | |
25 | + jQuery('<div>'+#{macro_configuration_dialog(macro_name).to_json}+'</div>').dialog({ | |
26 | + title: #{macro_title(macro_name).to_json}, | |
27 | + modal: true, | |
28 | + buttons: [ | |
29 | + {text: #{_('Ok').to_json}, click: function(){ | |
30 | + tinyMCE.activeEditor.execCommand('mceInsertContent', false, | |
31 | + (function(dialog){ #{macro_generator(macro_name)} })(this)); | |
32 | + jQuery(this).dialog('close'); | |
33 | + }}, | |
34 | + {text: #{_('Cancel').to_json}, click: function(){jQuery(this).dialog('close');}} | |
35 | + ] | |
36 | + }); | |
37 | + }" | |
38 | + end | |
39 | + end | |
40 | + | |
41 | + def include_macro_js_files | |
42 | + plugins_javascripts = [] | |
43 | + Environment.macros[environment.id].map do |macro_name, plugin_instance| | |
44 | + if macro_configuration(macro_name)[:js_files] | |
45 | + macro_configuration(macro_name)[:js_files].map { |js| plugins_javascripts << plugin_instance.class.public_path(js) } | |
46 | + end | |
47 | + end | |
48 | + javascript_include_tag(plugins_javascripts, :cache => 'cache/plugins-' + Digest::MD5.hexdigest(plugins_javascripts.to_s)) unless plugins_javascripts.empty? | |
49 | + end | |
50 | + | |
51 | + def macro_css_files | |
52 | + plugins_css = [] | |
53 | + Environment.macros[environment.id].map do |macro_name, plugin_instance| | |
54 | + if macro_configuration(macro_name)[:css_files] | |
55 | + macro_configuration(macro_name)[:css_files].map { |css| plugins_css << plugin_instance.class.public_path(css) } | |
56 | + end | |
57 | + end | |
58 | + plugins_css.join(',') | |
59 | + end | |
60 | + | |
61 | + protected | |
62 | + | |
63 | + def macro_generator(macro_name) | |
64 | + if macro_configuration(macro_name)[:generator] | |
65 | + macro_configuration(macro_name)[:generator] | |
66 | + else | |
67 | + macro_default_generator(macro_name) | |
68 | + end | |
69 | + end | |
70 | + | |
71 | + def macro_default_generator(macro_name) | |
72 | + code = "var params = {};" | |
73 | + configuration = macro_configuration(macro_name) | |
74 | + configuration[:params].map do |field| | |
75 | + code += "params.#{field[:name]} = jQuery('*[name=#{field[:name]}]', dialog).val();" | |
76 | + end | |
77 | + code + " | |
78 | + var html = jQuery('<div class=\"macro mceNonEditable\" data-macro=\"#{macro_name[6..-1]}\">'+#{macro_title(macro_name).to_json}+'</div>')[0]; | |
79 | + for(key in params) html.setAttribute('data-macro-'+key,params[key]); | |
80 | + return html.outerHTML; | |
81 | + " | |
82 | + end | |
83 | + | |
84 | + def macro_configuration_dialog(macro_name) | |
85 | + macro_configuration(macro_name)[:params].map do |field| | |
86 | + label_name = field[:label] || field[:name].to_s.humanize | |
87 | + case field[:type] | |
88 | + when 'text' | |
89 | + labelled_form_field(label_name, text_field_tag(field[:name], field[:default])) | |
90 | + when 'select' | |
91 | + labelled_form_field(label_name, select_tag(field[:name], options_for_select(field[:values], field[:default]))) | |
92 | + end | |
93 | + end.join("\n") | |
94 | + end | |
95 | + | |
96 | +end | ... | ... |
app/models/comment.rb
... | ... | @@ -152,6 +152,14 @@ class Comment < ActiveRecord::Base |
152 | 152 | @replies = comments_list |
153 | 153 | end |
154 | 154 | |
155 | + def self.count_thread(comments) | |
156 | + count = comments.length | |
157 | + comments.each do |c| | |
158 | + count+=count_thread(c.replies) | |
159 | + end | |
160 | + count | |
161 | + end | |
162 | + | |
155 | 163 | def self.as_thread |
156 | 164 | result = {} |
157 | 165 | root = [] | ... | ... |
app/models/environment.rb
... | ... | @@ -16,6 +16,10 @@ class Environment < ActiveRecord::Base |
16 | 16 | filename |
17 | 17 | end |
18 | 18 | |
19 | + class << self | |
20 | + attr_accessor :macros | |
21 | + end | |
22 | + | |
19 | 23 | PERMISSIONS['Environment'] = { |
20 | 24 | 'view_environment_admin_panel' => N_('View environment admin panel'), |
21 | 25 | 'edit_environment_features' => N_('Edit environment features'), | ... | ... |
app/views/comment/_comment_form.rhtml
... | ... | @@ -79,6 +79,8 @@ function check_captcha(button, confirm_action) { |
79 | 79 | <%= hidden_field_tag(:view, params[:view])%> |
80 | 80 | <%= f.hidden_field(:reply_of_id) %> |
81 | 81 | |
82 | + <%= @plugins.dispatch(:comment_form_extra_contents, local_assigns).collect { |content| instance_eval(&content) }.join("") %> | |
83 | + | |
82 | 84 | <% button_bar do %> |
83 | 85 | <%= submit_button('add', _('Post comment'), :onclick => "if(check_captcha(this)) { save_comment(this) } else { check_captcha(this, save_comment)};return false;") %> |
84 | 86 | <% if !edition_mode %> | ... | ... |
app/views/content_viewer/view_page.rhtml
app/views/shared/tiny_mce.rhtml
1 | +<% extend MacrosHelper %> | |
1 | 2 | <%= javascript_include_tag 'tinymce/jscripts/tiny_mce/tiny_mce.js' %> |
3 | +<%= include_macro_js_files %> | |
2 | 4 | <script type="text/javascript"> |
3 | - var myplugins = "searchreplace,print,table,contextmenu"; | |
5 | + var myplugins = "searchreplace,print,table,contextmenu,-macrosPlugin"; | |
4 | 6 | var first_line, second_line; |
5 | 7 | var mode = '<%= mode ||= false %>' |
6 | 8 | <% if mode %> |
... | ... | @@ -8,36 +10,80 @@ |
8 | 10 | second_line = "" |
9 | 11 | <% else %> |
10 | 12 | first_line = "print,separator,copy,paste,separator,undo,redo,separator,search,replace,separator,forecolor,fontsizeselect,formatselect" |
11 | - second_line = "bold,italic,underline,strikethrough,separator,bullist,numlist,separator,justifyleft,justifycenter,justifyright,justifyfull,separator,link,unlink,image,table,separator,cleanup,code" | |
13 | + second_line = "bold,italic,underline,strikethrough,separator,bullist,numlist,separator,justifyleft,justifycenter,justifyright,justifyfull,separator,link,unlink,image,table,separator,cleanup,code,macros" | |
14 | + <% macros_with_buttons.each do |macro_name, plugin_instance| %> | |
15 | + second_line += ',<%=macro_name %>' | |
16 | + <% end %> | |
12 | 17 | <% end %> |
13 | 18 | |
14 | 19 | if (tinymce.isIE) { |
15 | 20 | // the paste plugin is only useful in Internet Explorer |
16 | 21 | myplugins = "paste," + myplugins; |
17 | 22 | } |
23 | + | |
24 | +tinymce.create('tinymce.plugins.MacrosPlugin', { | |
25 | + createControl: function(n, cm) { | |
26 | + switch (n) { | |
27 | + case 'macros': | |
28 | + <% unless macros_in_menu.empty? %> | |
29 | + var c = cm.createMenuButton('macros', { | |
30 | + title : 'Macros', | |
31 | + image : '/designs/icons/tango/Tango/16x16/emblems/emblem-system.png', | |
32 | + icons : false | |
33 | + }); | |
34 | + | |
35 | + <% macros_in_menu.each do |macro_name, plugin_instance| %> | |
36 | + c.onRenderMenu.add(function(c, m) { | |
37 | + m.add({ | |
38 | + title: <%= macro_title(macro_name).to_json %>, | |
39 | + onclick: <%= generate_macro_config_dialog(macro_name) %> | |
40 | + }); | |
41 | + }); | |
42 | + <% end %> | |
43 | + | |
44 | + // Return the new menu button instance | |
45 | + return c; | |
46 | + <% end %> | |
47 | + } | |
48 | + return null; | |
49 | + } | |
50 | +}); | |
51 | + | |
52 | +// Register plugin with a short name | |
53 | +tinymce.PluginManager.add('macrosPlugin', tinymce.plugins.MacrosPlugin); | |
54 | + | |
18 | 55 | tinyMCE.init({ |
19 | - mode : "textareas", | |
20 | - editor_selector : "mceEditor", | |
21 | - theme : "advanced", | |
22 | - relative_urls : false, | |
23 | - remove_script_host : false, | |
24 | - document_base_url : <%= environment.top_url.to_json %>, | |
25 | - plugins: myplugins, | |
26 | - theme_advanced_toolbar_location : "top", | |
27 | - theme_advanced_layout_manager: 'SimpleLayout', | |
28 | - theme_advanced_buttons1 : first_line, | |
29 | - theme_advanced_buttons2 : second_line, | |
30 | - theme_advanced_buttons3 : "", | |
31 | - theme_advanced_blockformats :"p,address,pre,h2,h3,h4,h5,h6", | |
32 | - paste_auto_cleanup_on_paste : true, | |
33 | - paste_insert_word_content_callback : "convertWord", | |
34 | - paste_use_dialog: false, | |
35 | - apply_source_formatting : true, | |
36 | - extended_valid_elements : "applet[style|archive|codebase|code|height|width],comment,iframe[src|style|allowtransparency|frameborder|width|height|scrolling],embed[title|src|type|height|width]", | |
37 | - content_css: '/stylesheets/tinymce.css', | |
38 | - language: <%= tinymce_language.inspect %>, | |
39 | - entity_encoding: 'raw' | |
40 | - }); | |
56 | + mode : "textareas", | |
57 | + editor_selector : "mceEditor", | |
58 | + theme : "advanced", | |
59 | + relative_urls : false, | |
60 | + remove_script_host : false, | |
61 | + document_base_url : <%= environment.top_url.to_json %>, | |
62 | + plugins: myplugins, | |
63 | + theme_advanced_toolbar_location : "top", | |
64 | + theme_advanced_layout_manager: 'SimpleLayout', | |
65 | + theme_advanced_buttons1 : first_line, | |
66 | + theme_advanced_buttons2 : second_line, | |
67 | + theme_advanced_buttons3 : "", | |
68 | + theme_advanced_blockformats :"p,address,pre,h2,h3,h4,h5,h6", | |
69 | + paste_auto_cleanup_on_paste : true, | |
70 | + paste_insert_word_content_callback : "convertWord", | |
71 | + paste_use_dialog: false, | |
72 | + apply_source_formatting : true, | |
73 | + extended_valid_elements : "applet[style|archive|codebase|code|height|width],comment,iframe[src|style|allowtransparency|frameborder|width|height|scrolling],embed[title|src|type|height|width]", | |
74 | + content_css: '/stylesheets/tinymce.css,<%= macro_css_files %>', | |
75 | + language: <%= tinymce_language.inspect %>, | |
76 | + entity_encoding: 'raw', | |
77 | + setup : function(ed) { | |
78 | + <% macros_with_buttons.each do |macro_name, plugin_instance| %> | |
79 | + ed.addButton('<%= macro_name %>', { | |
80 | + title: <%= macro_title(macro_name).to_json %>, | |
81 | + onclick: <%= generate_macro_config_dialog(macro_name) %>, | |
82 | + image : '<%= macro_configuration(macro_name)[:icon_path]%>' | |
83 | + }); | |
84 | + <% end %> | |
85 | + } | |
86 | +}); | |
41 | 87 | |
42 | 88 | function convertWord(type, content) { |
43 | 89 | switch (type) { | ... | ... |
db/schema.rb
... | ... | @@ -131,7 +131,6 @@ ActiveRecord::Schema.define(:version => 20130606110602) do |
131 | 131 | t.integer "license_id" |
132 | 132 | end |
133 | 133 | |
134 | - add_index "articles", ["name"], :name => "index_articles_on_name" | |
135 | 134 | add_index "articles", ["parent_id"], :name => "index_articles_on_parent_id" |
136 | 135 | add_index "articles", ["profile_id"], :name => "index_articles_on_profile_id" |
137 | 136 | add_index "articles", ["slug"], :name => "index_articles_on_slug" |
... | ... | @@ -221,6 +220,7 @@ ActiveRecord::Schema.define(:version => 20130606110602) do |
221 | 220 | t.string "source_type" |
222 | 221 | t.string "user_agent" |
223 | 222 | t.string "referrer" |
223 | + t.integer "group_id" | |
224 | 224 | end |
225 | 225 | |
226 | 226 | add_index "comments", ["source_id", "spam"], :name => "index_comments_on_source_id_and_spam" | ... | ... |
lib/noosfero/plugin.rb
... | ... | @@ -5,6 +5,13 @@ class Noosfero::Plugin |
5 | 5 | |
6 | 6 | attr_accessor :context |
7 | 7 | |
8 | + def initialize(context=nil) | |
9 | + self.context = context | |
10 | + macro_methods.each do |method| | |
11 | + Environment.macros[context.environment.id][method] = self unless context.nil? | |
12 | + end | |
13 | + end | |
14 | + | |
8 | 15 | class << self |
9 | 16 | |
10 | 17 | def klass(dir) |
... | ... | @@ -242,8 +249,8 @@ class Noosfero::Plugin |
242 | 249 | |
243 | 250 | # -> Parse and possibly make changes of content (article, block, etc) during HTML rendering |
244 | 251 | # returns = content as string after parser and changes |
245 | - def parse_content(raw_content) | |
246 | - raw_content | |
252 | + def parse_content(args) | |
253 | + args | |
247 | 254 | end |
248 | 255 | |
249 | 256 | # -> Adds links to the admin panel |
... | ... | @@ -279,6 +286,18 @@ class Noosfero::Plugin |
279 | 286 | def filter_comment(comment) |
280 | 287 | end |
281 | 288 | |
289 | + # Define custom logic to load the article comments. | |
290 | + # | |
291 | + # Example: | |
292 | + # | |
293 | + # def load_comments(article) | |
294 | + # article.comments.find(:all, :conditions => ['spam IS NULL OR spam = ?', false]) | |
295 | + # end | |
296 | + # | |
297 | + def load_comments(article) | |
298 | + nil | |
299 | + end | |
300 | + | |
282 | 301 | # This method is called by the CommentHandler background job before sending |
283 | 302 | # the notification email. If the comment is marked as spam (i.e. by calling |
284 | 303 | # <tt>comment.spam!</tt>), then the notification email will *not* be sent. |
... | ... | @@ -355,7 +374,7 @@ class Noosfero::Plugin |
355 | 374 | def profile_info_extra_contents |
356 | 375 | nil |
357 | 376 | end |
358 | - | |
377 | + | |
359 | 378 | # -> Removes the invite friend button from the friends controller |
360 | 379 | # returns = boolean |
361 | 380 | def remove_invite_friends_button |
... | ... | @@ -417,6 +436,18 @@ class Noosfero::Plugin |
417 | 436 | def login_extra_contents |
418 | 437 | nil |
419 | 438 | end |
439 | + | |
440 | + # -> Adds adicional content to comment form | |
441 | + # returns = lambda block that creates html code | |
442 | + def comment_form_extra_contents(args) | |
443 | + nil | |
444 | + end | |
445 | + | |
446 | + # -> Register macro methods in environment | |
447 | + # returns = ['method1', 'method2', ...] | |
448 | + def macro_methods | |
449 | + [] | |
450 | + end | |
420 | 451 | |
421 | 452 | # -> Finds objects by their contents |
422 | 453 | # returns = {:results => [a, b, c, ...], ...} |
... | ... | @@ -504,6 +535,11 @@ class Noosfero::Plugin |
504 | 535 | # returns = string with reason of expiration |
505 | 536 | elsif method.to_s =~ /^content_expire_(#{content_actions.join('|')})$/ |
506 | 537 | nil |
538 | + # -> Answers to a specific macro | |
539 | + # expects: params, inner_html, content | |
540 | + # returns = html_code | |
541 | + elsif method.to_s =~ /^macro_(.+)$/ | |
542 | + nil | |
507 | 543 | else |
508 | 544 | super |
509 | 545 | end | ... | ... |
lib/noosfero/plugin/manager.rb
... | ... | @@ -6,6 +6,7 @@ class Noosfero::Plugin::Manager |
6 | 6 | def initialize(environment, context) |
7 | 7 | @environment = environment |
8 | 8 | @context = context |
9 | + Environment.macros = {environment.id => {}} unless environment.nil? | |
9 | 10 | end |
10 | 11 | |
11 | 12 | delegate :each, :to => :enabled_plugins |
... | ... | @@ -31,6 +32,15 @@ class Noosfero::Plugin::Manager |
31 | 32 | map { |plugin| plugin.send(event, *args) }.compact |
32 | 33 | end |
33 | 34 | |
35 | + def dispatch_first(event, *args) | |
36 | + value = nil | |
37 | + map do |plugin| | |
38 | + value = plugin.send(event, *args) | |
39 | + break if value | |
40 | + end | |
41 | + value | |
42 | + end | |
43 | + | |
34 | 44 | alias :dispatch_scopes :dispatch_without_flatten |
35 | 45 | |
36 | 46 | def first(event, *args) |
... | ... | @@ -55,9 +65,7 @@ class Noosfero::Plugin::Manager |
55 | 65 | |
56 | 66 | def enabled_plugins |
57 | 67 | @enabled_plugins ||= (Noosfero::Plugin.all & environment.enabled_plugins).map do |plugin| |
58 | - p = plugin.constantize.new | |
59 | - p.context = context | |
60 | - p | |
68 | + plugin.constantize.new(context) | |
61 | 69 | end |
62 | 70 | end |
63 | 71 | ... | ... |
plugins/comment_group_macro/controllers/profile/comment_group_macro_plugin_profile_controller.rb
0 → 100644
... | ... | @@ -0,0 +1,16 @@ |
1 | +class CommentGroupMacroPluginProfileController < ProfileController | |
2 | + append_view_path File.join(File.dirname(__FILE__) + '/../views') | |
3 | + | |
4 | + def view_comments | |
5 | + article_id = params[:article_id] | |
6 | + group_id = params[:group_id] | |
7 | + | |
8 | + article = profile.articles.find(article_id) | |
9 | + comments = article.group_comments.without_spam.in_group(group_id) | |
10 | + render :update do |page| | |
11 | + page.replace_html "comments_list_group_#{group_id}", :partial => 'comment/comment.rhtml', :collection => comments.as_thread | |
12 | + page.replace_html "comment-count-#{group_id}", comments.count | |
13 | + end | |
14 | + end | |
15 | + | |
16 | +end | ... | ... |
plugins/comment_group_macro/controllers/public/comment_group_macro_plugin_public_controller.rb
0 → 100644
plugins/comment_group_macro/db/migrate/20121220201629_add_group_id_to_comment.rb
0 → 100644
plugins/comment_group_macro/lib/comment_group_macro_plugin.rb
0 → 100644
... | ... | @@ -0,0 +1,50 @@ |
1 | +require_dependency 'comment_group_macro_plugin/ext/article' | |
2 | +require_dependency 'comment_group_macro_plugin/ext/comment' | |
3 | + | |
4 | +#FIXME See a better way to generalize this parameter. | |
5 | +ActionView::Base.sanitized_allowed_attributes += ['data-macro', 'data-macro-group_id'] | |
6 | + | |
7 | +class CommentGroupMacroPlugin < Noosfero::Plugin | |
8 | + | |
9 | + def self.plugin_name | |
10 | + "Comment Group" | |
11 | + end | |
12 | + | |
13 | + def self.plugin_description | |
14 | + _("A plugin that display comment groups.") | |
15 | + end | |
16 | + | |
17 | + def load_comments(article) | |
18 | + article.comments.without_spam.without_group.as_thread | |
19 | + end | |
20 | + | |
21 | + #FIXME make this test | |
22 | + def macro_display_comments(params, inner_html, source) | |
23 | + group_id = params[:group_id].to_i | |
24 | + article = source | |
25 | + count = article.group_comments.without_spam.in_group(group_id).count | |
26 | + | |
27 | + lambda {render :partial => 'plugins/comment_group_macro/views/comment_group.rhtml', :locals => {:group_id => group_id, :article_id => article.id, :inner_html => inner_html, :count => count, :profile_identifier => article.profile.identifier }} | |
28 | + end | |
29 | + | |
30 | + def macro_methods | |
31 | + 'macro_display_comments' | |
32 | + end | |
33 | + | |
34 | + def config_macro_display_comments | |
35 | + { :params => [], :skip_dialog => true, :generator => 'makeCommentable();', :js_files => 'comment_group.js', :icon_path => '/designs/icons/tango/Tango/16x16/emblems/emblem-system.png', :css_files => 'comment_group.css' } | |
36 | + end | |
37 | + | |
38 | + def comment_form_extra_contents(args) | |
39 | + comment = args[:comment] | |
40 | + group_id = comment.group_id || args[:group_id] | |
41 | + lambda { | |
42 | + hidden_field_tag('comment[group_id]', group_id) if group_id | |
43 | + } | |
44 | + end | |
45 | + | |
46 | + def js_files | |
47 | + 'comment_group_macro.js' | |
48 | + end | |
49 | + | |
50 | +end | ... | ... |
plugins/comment_group_macro/lib/comment_group_macro_plugin/ext/article.rb
0 → 100644
... | ... | @@ -0,0 +1,21 @@ |
1 | +require_dependency 'article' | |
2 | + | |
3 | +class Article | |
4 | + | |
5 | + #FIXME make this test | |
6 | + has_many :group_comments, :class_name => 'Comment', :foreign_key => 'source_id', :dependent => :destroy, :order => 'created_at asc', :conditions => [ 'group_id IS NOT NULL'] | |
7 | + | |
8 | + #FIXME make this test | |
9 | + validate :not_empty_group_comments_removed | |
10 | + | |
11 | + #FIXME make this test | |
12 | + def not_empty_group_comments_removed | |
13 | + if body | |
14 | + groups_with_comments = group_comments.collect {|comment| comment.group_id}.uniq | |
15 | + groups = Hpricot(body.to_s).search('.macro').collect{|element| element['data-macro-group_id'].to_i} | |
16 | + errors.add_to_base(N_('Not empty group comment cannot be removed')) unless (groups_with_comments-groups).empty? | |
17 | + end | |
18 | + end | |
19 | + | |
20 | +end | |
21 | + | ... | ... |
plugins/comment_group_macro/lib/comment_group_macro_plugin/ext/comment.rb
0 → 100644
... | ... | @@ -0,0 +1,47 @@ |
1 | +function getNextGroupId() { | |
2 | + max = -1; | |
3 | + groups = jQuery('#article_body_ifr').contents().find('.article_comments'); | |
4 | + groups.each(function(key, value) { | |
5 | + value = jQuery(value).attr('data-macro-group_id'); | |
6 | + if(value>max) max = parseInt(value); | |
7 | + }); | |
8 | + return max+1; | |
9 | +} | |
10 | + | |
11 | +function makeCommentable() { | |
12 | + tinyMCE.activeEditor.focus(); | |
13 | + start = jQuery(tinyMCE.activeEditor.selection.getStart()).closest('p'); | |
14 | + end = jQuery(tinyMCE.activeEditor.selection.getEnd()).closest('p'); | |
15 | + | |
16 | + //text = start.parent().children(); | |
17 | + text = jQuery('#article_body_ifr').contents().find('*'); | |
18 | + selection = text.slice(text.index(start), text.index(end)+1); | |
19 | + | |
20 | + hasTag = false; | |
21 | + selection.each(function(key, value) { | |
22 | + commentTag = jQuery(value).closest('.article_comments'); | |
23 | + if(commentTag.length) { | |
24 | + commentTag.children().unwrap('<div class=\"article_comments\"/>'); | |
25 | + hasTag = true; | |
26 | + } | |
27 | + }); | |
28 | + | |
29 | + if(!hasTag) { | |
30 | + tags = start.siblings().add(start); | |
31 | + tags = tags.slice(tags.index(start), tags.index(end)>=0?tags.index(end)+1:tags.index(start)+1); | |
32 | + tags.wrapAll('<div class=\"macro article_comments\" data-macro=\"display_comments\" data-macro-group_id=\"'+getNextGroupId()+'\"/>'); | |
33 | + | |
34 | + contents = jQuery('#article_body_ifr').contents(); | |
35 | + lastP = contents.find('p.article_comments_last_paragraph'); | |
36 | + if(lastP.text().trim().length > 0) { | |
37 | + lastP.removeClass('article_comments_last_paragraph'); | |
38 | + } else { | |
39 | + lastP.remove(); | |
40 | + } | |
41 | + lastDiv = contents.find('div.article_comments').last(); | |
42 | + if(lastDiv.next().length==0) { | |
43 | + lastDiv.after("<p class='article_comments_last_paragraph'> </p>"); | |
44 | + } | |
45 | + } | |
46 | +} | |
47 | + | ... | ... |
plugins/comment_group_macro/public/comment_group_macro.js
0 → 100644
... | ... | @@ -0,0 +1,39 @@ |
1 | +var comment_group_anchor; | |
2 | +jQuery(document).ready(function($) { | |
3 | + var anchor = window.location.hash; | |
4 | + if(anchor.length==0) return; | |
5 | + | |
6 | + var val = anchor.split('-'); //anchor format = #comment-\d+ | |
7 | + if(val.length!=2 || val[0]!='#comment') return; | |
8 | + if($('div[data-macro=display_comments]').length==0) return; //display_comments div must exists | |
9 | + var comment_id = val[1]; | |
10 | + if(!/^\d+$/.test(comment_id)) return; //test for integer | |
11 | + | |
12 | + comment_group_anchor = anchor; | |
13 | + var url = '/plugin/comment_group_macro/public/comment_group/'+comment_id; | |
14 | + $.getJSON(url, function(data) { | |
15 | + if(data.group_id!=null) { | |
16 | + var button = $('div.comment_group_'+ data.group_id + ' a'); | |
17 | + button.click(); | |
18 | + $.scrollTo(button); | |
19 | + } | |
20 | + }); | |
21 | +}); | |
22 | + | |
23 | +function toggleGroup(group) { | |
24 | + var div = jQuery('div.comments_list_toggle_group_'+group); | |
25 | + var visible = div.is(':visible'); | |
26 | + if(!visible) | |
27 | + jQuery('div.comment-group-loading-'+group).addClass('comment-button-loading'); | |
28 | + | |
29 | + div.toggle('fast'); | |
30 | + return visible; | |
31 | +} | |
32 | + | |
33 | +function loadCompleted(group) { | |
34 | + jQuery('div.comment-group-loading-'+group).removeClass('comment-button-loading') | |
35 | + if(comment_group_anchor) { | |
36 | + jQuery.scrollTo(jQuery(comment_group_anchor)); | |
37 | + comment_group_anchor = null; | |
38 | + } | |
39 | +} | ... | ... |
1.66 KB
plugins/comment_group_macro/test/functional/comment_group_macro_plugin_profile_controller_test.rb
0 → 100644
... | ... | @@ -0,0 +1,38 @@ |
1 | +require File.dirname(__FILE__) + '/../../../../test/test_helper' | |
2 | +require File.dirname(__FILE__) + '/../../controllers/profile/comment_group_macro_plugin_profile_controller' | |
3 | + | |
4 | +# Re-raise errors caught by the controller. | |
5 | +class CommentGroupMacroPluginProfileController; def rescue_action(e) raise e end; end | |
6 | + | |
7 | +class CommentGroupMacroPluginProfileControllerTest < ActionController::TestCase | |
8 | + | |
9 | + def setup | |
10 | + @controller = CommentGroupMacroPluginProfileController.new | |
11 | + @request = ActionController::TestRequest.new | |
12 | + @response = ActionController::TestResponse.new | |
13 | + | |
14 | + @profile = create_user('testuser').person | |
15 | + @article = profile.articles.build(:name => 'test') | |
16 | + @article.save! | |
17 | + end | |
18 | + attr_reader :article | |
19 | + attr_reader :profile | |
20 | + | |
21 | + should 'be able to show group comments' do | |
22 | + comment = fast_create(Comment, :source_id => article, :author_id => profile, :title => 'a comment', :body => 'lalala', :group_id => 0) | |
23 | + xhr :get, :view_comments, :profile => @profile.identifier, :article_id => article.id, :group_id => 0 | |
24 | + assert_template 'comment/_comment.rhtml' | |
25 | + assert_match /comments_list_group_0/, @response.body | |
26 | + assert_match /\"comment-count-0\", \"1\"/, @response.body | |
27 | + end | |
28 | + | |
29 | + should 'do not show global comments' do | |
30 | + comment = fast_create(Comment, :source_id => article, :author_id => profile, :title => 'global comment', :body => 'global', :group_id => nil) | |
31 | + comment = fast_create(Comment, :source_id => article, :author_id => profile, :title => 'a comment', :body => 'lalala', :group_id => 0) | |
32 | + xhr :get, :view_comments, :profile => @profile.identifier, :article_id => article.id, :group_id => 0 | |
33 | + assert_template 'comment/_comment.rhtml' | |
34 | + assert_match /comments_list_group_0/, @response.body | |
35 | + assert_match /\"comment-count-0\", \"1\"/, @response.body | |
36 | + end | |
37 | + | |
38 | +end | ... | ... |
plugins/comment_group_macro/test/functional/comment_group_macro_plugin_public_controller_test.rb
0 → 100644
... | ... | @@ -0,0 +1,33 @@ |
1 | +require File.dirname(__FILE__) + '/../../../../test/test_helper' | |
2 | +require File.dirname(__FILE__) + '/../../controllers/public/comment_group_macro_plugin_public_controller' | |
3 | + | |
4 | +# Re-raise errors caught by the controller. | |
5 | +class CommentGroupMacroPluginPublicController; def rescue_action(e) raise e end; end | |
6 | + | |
7 | +class CommentGroupMacroPluginPublicControllerTest < ActionController::TestCase | |
8 | + | |
9 | + def setup | |
10 | + @controller = CommentGroupMacroPluginPublicController.new | |
11 | + @request = ActionController::TestRequest.new | |
12 | + @response = ActionController::TestResponse.new | |
13 | + | |
14 | + @profile = create_user('testuser').person | |
15 | + @article = profile.articles.build(:name => 'test') | |
16 | + @article.save! | |
17 | + end | |
18 | + attr_reader :article | |
19 | + attr_reader :profile | |
20 | + | |
21 | + should 'be able to return group_id for a comment' do | |
22 | + comment = fast_create(Comment, :source_id => article, :author_id => profile, :title => 'a comment', :body => 'lalala', :group_id => 0) | |
23 | + xhr :get, :comment_group, :id => comment.id | |
24 | + assert_match /\{\"group_id\":0\}/, @response.body | |
25 | + end | |
26 | + | |
27 | + should 'return group_id=null for a global comment' do | |
28 | + comment = fast_create(Comment, :source_id => article, :author_id => profile, :title => 'a comment', :body => 'lalala' ) | |
29 | + xhr :get, :comment_group, :id => comment.id | |
30 | + assert_match /\{\"group_id\":null\}/, @response.body | |
31 | + end | |
32 | + | |
33 | +end | ... | ... |
plugins/comment_group_macro/test/unit/comment_group_macro_plugin_test.rb
0 → 100644
... | ... | @@ -0,0 +1,81 @@ |
1 | +require File.dirname(__FILE__) + '/../../../../test/test_helper' | |
2 | + | |
3 | +class CommentGroupMacroPluginTest < ActiveSupport::TestCase | |
4 | + | |
5 | + include Noosfero::Plugin::HotSpot | |
6 | + | |
7 | + def setup | |
8 | + @environment = Environment.default | |
9 | + end | |
10 | + | |
11 | + attr_reader :environment | |
12 | + | |
13 | + should 'register comment_group_macro in environment' do | |
14 | + Environment.macros = {} | |
15 | + Environment.macros[environment.id] = {} | |
16 | + macros = Environment.macros[environment.id] | |
17 | + context = mock() | |
18 | + context.stubs(:environment).returns(environment) | |
19 | + plugin = CommentGroupMacroPlugin.new(context) | |
20 | + assert_equal ['macro_display_comments'], macros.keys | |
21 | + end | |
22 | + | |
23 | + should 'load_comments returns all the comments wihout group of an article passed as parameter' do | |
24 | + article = fast_create(Article) | |
25 | + c1 = fast_create(Comment, :source_id => article.id, :group_id => 1) | |
26 | + c2 = fast_create(Comment, :source_id => article.id) | |
27 | + c3 = fast_create(Comment, :source_id => article.id) | |
28 | + | |
29 | + plugin = CommentGroupMacroPlugin.new | |
30 | + assert_equal [], [c2, c3] - plugin.load_comments(article) | |
31 | + assert_equal [], plugin.load_comments(article) - [c2, c3] | |
32 | + end | |
33 | + | |
34 | + should 'load_comments not returns spam comments' do | |
35 | + article = fast_create(Article) | |
36 | + c1 = fast_create(Comment, :source_id => article.id, :group_id => 1) | |
37 | + c2 = fast_create(Comment, :source_id => article.id) | |
38 | + c3 = fast_create(Comment, :source_id => article.id, :spam => true) | |
39 | + | |
40 | + plugin = CommentGroupMacroPlugin.new | |
41 | + assert_equal [], [c2] - plugin.load_comments(article) | |
42 | + assert_equal [], plugin.load_comments(article) - [c2] | |
43 | + end | |
44 | + | |
45 | + should 'load_comments returns only root comments of article' do | |
46 | + article = fast_create(Article) | |
47 | + c1 = fast_create(Comment, :source_id => article.id, :group_id => 1) | |
48 | + c2 = fast_create(Comment, :source_id => article.id) | |
49 | + c3 = fast_create(Comment, :source_id => article.id, :reply_of_id => c2.id) | |
50 | + | |
51 | + plugin = CommentGroupMacroPlugin.new | |
52 | + assert_equal [], [c2] - plugin.load_comments(article) | |
53 | + assert_equal [], plugin.load_comments(article) - [c2] | |
54 | + end | |
55 | + | |
56 | + should 'params of macro display comments configuration be an empty array' do | |
57 | + plugin = CommentGroupMacroPlugin.new | |
58 | + assert_equal [], plugin.config_macro_display_comments[:params] | |
59 | + end | |
60 | + | |
61 | + should 'skip_dialog of macro display comments configuration be true' do | |
62 | + plugin = CommentGroupMacroPlugin.new | |
63 | + assert plugin.config_macro_display_comments[:skip_dialog] | |
64 | + end | |
65 | + | |
66 | + should 'generator of macro display comments configuration be the makeCommentable function' do | |
67 | + plugin = CommentGroupMacroPlugin.new | |
68 | + assert_equal 'makeCommentable();', plugin.config_macro_display_comments[:generator] | |
69 | + end | |
70 | + | |
71 | + should 'js_files of macro display comments configuration return comment_group.js' do | |
72 | + plugin = CommentGroupMacroPlugin.new | |
73 | + assert_equal 'comment_group.js', plugin.config_macro_display_comments[:js_files] | |
74 | + end | |
75 | + | |
76 | + should 'css_files of macro display comments configuration return comment_group.css' do | |
77 | + plugin = CommentGroupMacroPlugin.new | |
78 | + assert_equal 'comment_group.css', plugin.config_macro_display_comments[:css_files] | |
79 | + end | |
80 | + | |
81 | +end | ... | ... |
... | ... | @@ -0,0 +1,30 @@ |
1 | +<div class="comments"> | |
2 | + <div class="comment_group_<%= group_id %>" style="float: left"> | |
3 | + <div style="float: left"> | |
4 | + <%= link_to_remote(image_tag("/plugins/comment_group_macro/images/comments.gif"), | |
5 | + :url => { :profile => profile_identifier, :controller => 'comment_group_macro_plugin_profile', :action => 'view_comments', :group_id => group_id, :article_id => article_id}, | |
6 | + :loaded => visual_effect(:highlight, "comments_list_group_#{group_id}"), | |
7 | + :method => :post, | |
8 | + :condition => "!toggleGroup(#{group_id})", | |
9 | + :complete => "loadCompleted(#{group_id})")%> | |
10 | + </div> | |
11 | + <!-- FIXME: css file --> | |
12 | + <div id="comments_group_count_<%= group_id %>" style="float: right; vertical-align: middle; padding-left: 3px; padding-right: 5px; color: #5AC1FC"><span id="comment-count-<%= group_id %>" class='comment-count'><%= count %></span></div> | |
13 | + | |
14 | + </div> | |
15 | + | |
16 | + <div> | |
17 | + <%= inner_html %> | |
18 | + </div> | |
19 | + | |
20 | + <div class="comment-group-loading-<%= group_id %>"/> | |
21 | + | |
22 | + <div class="comments_list_toggle_group_<%= group_id %>" style="display:none"> | |
23 | + <div class="article-comments-list"> | |
24 | + <div id="comments_list_group_<%= group_id %>"></div> | |
25 | + </div> | |
26 | + | |
27 | + <div id="page-comment-form-<%= group_id %>" class='post_comment_box closed'><%= render :partial => 'comment/comment_form', :locals => {:comment => Comment.new, :display_link => true, :cancel_triggers_hide => true, :group_id => group_id}%></div> | |
28 | + | |
29 | + </div> | |
30 | +</div> | ... | ... |
plugins/send_email/lib/send_email_plugin.rb
... | ... | @@ -12,12 +12,14 @@ class SendEmailPlugin < Noosfero::Plugin |
12 | 12 | true |
13 | 13 | end |
14 | 14 | |
15 | - def parse_content(raw_content) | |
15 | + def parse_content(args) | |
16 | + raw_content = args[:html] | |
16 | 17 | if context.profile |
17 | 18 | raw_content.gsub(/\{sendemail\}/, "/profile/#{context.profile.identifier}/plugin/send_email/deliver") |
18 | 19 | else |
19 | 20 | raw_content.gsub(/\{sendemail\}/, '/plugin/send_email/deliver') |
20 | 21 | end |
22 | + args.clone.merge({:html => raw_content}) | |
21 | 23 | end |
22 | 24 | |
23 | 25 | end | ... | ... |
test/functional/comment_controller_test.rb
... | ... | @@ -146,14 +146,6 @@ class CommentControllerTest < ActionController::TestCase |
146 | 146 | assert_equal page, assigns(:comment).article |
147 | 147 | end |
148 | 148 | |
149 | - should 'show comment form opened on error' do | |
150 | - login_as profile.identifier | |
151 | - page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text') | |
152 | - xhr :post, :create, :profile => @profile.identifier, :id => page.id, :comment => { :title => '', :body => '' }, :confirm => 'true' | |
153 | - response = JSON.parse @response.body | |
154 | - assert_match /<div class=\"post_comment_box opened\"/, response["html"] | |
155 | - end | |
156 | - | |
157 | 149 | should 'show validation error when body comment is missing' do |
158 | 150 | login_as @profile.identifier |
159 | 151 | page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text') |
... | ... | @@ -392,7 +384,7 @@ class CommentControllerTest < ActionController::TestCase |
392 | 384 | page = profile.articles.create(:name => 'myarticle', :body => 'the body of the text', :created_at => yesterday, :updated_at => yesterday) |
393 | 385 | Article.record_timestamps = true |
394 | 386 | |
395 | - login_as('ze') | |
387 | + login_as @profile.identifier | |
396 | 388 | xhr :post, :create, :profile => profile.identifier, :id => page.id, :comment => { :title => 'crap!', :body => 'I think that this article is crap' }, :confirm => 'true' |
397 | 389 | assert_not_equal yesterday, page.reload.updated_at |
398 | 390 | end | ... | ... |
test/functional/content_viewer_controller_test.rb
... | ... | @@ -1185,4 +1185,118 @@ class ContentViewerControllerTest < ActionController::TestCase |
1185 | 1185 | assert_equal 1, assigns(:comments_count) |
1186 | 1186 | end |
1187 | 1187 | |
1188 | + should 'add extra content on comment form from plugins' do | |
1189 | + class Plugin1 < Noosfero::Plugin | |
1190 | + def comment_form_extra_contents(args) | |
1191 | + lambda { | |
1192 | + hidden_field_tag('comment[some_field_id]', 1) | |
1193 | + } | |
1194 | + end | |
1195 | + end | |
1196 | + class Plugin2 < Noosfero::Plugin | |
1197 | + def comment_form_extra_contents(args) | |
1198 | + lambda { | |
1199 | + hidden_field_tag('comment[another_field_id]', 1) | |
1200 | + } | |
1201 | + end | |
1202 | + end | |
1203 | + | |
1204 | + Environment.default.enable_plugin(Plugin1.name) | |
1205 | + Environment.default.enable_plugin(Plugin2.name) | |
1206 | + | |
1207 | + page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text') | |
1208 | + | |
1209 | + get :view_page, :profile => profile.identifier, :page => [ 'myarticle' ] | |
1210 | + | |
1211 | + assert_tag :tag => 'input', :attributes => {:name => 'comment[some_field_id]', :type => 'hidden'} | |
1212 | + assert_tag :tag => 'input', :attributes => {:name => 'comment[another_field_id]', :type => 'hidden'} | |
1213 | + end | |
1214 | + | |
1215 | + should 'collect comments as plugin definition' do | |
1216 | + class Plugin1 < Noosfero::Plugin | |
1217 | + def load_comments(page) | |
1218 | + [page.comments.find(4)] | |
1219 | + end | |
1220 | + end | |
1221 | + Environment.default.enable_plugin(Plugin1.name) | |
1222 | + Comment.delete_all | |
1223 | + page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text') | |
1224 | + page.comments.build(:author => profile, :title => 'hi', :body => 'hello 1' ).save! | |
1225 | + page.comments.build(:author => profile, :title => 'hi', :body => 'hello 2' ).save! | |
1226 | + page.comments.build(:author => profile, :title => 'hi', :body => 'hello 3' ).save! | |
1227 | + c = page.comments.build(:author => profile, :title => 'hi', :body => 'hello 4' ) | |
1228 | + c.save! | |
1229 | + | |
1230 | + | |
1231 | + get :view_page, :profile => profile.identifier, :page => [ 'myarticle' ] | |
1232 | + | |
1233 | + assert_equal [c], assigns(:comments) | |
1234 | + end | |
1235 | + | |
1236 | + should 'not be a problem if loaded comments of plugins not be an array' do | |
1237 | + class Plugin1 < Noosfero::Plugin | |
1238 | + def load_comments(page) | |
1239 | + page.comments.find(4) | |
1240 | + end | |
1241 | + end | |
1242 | + Environment.default.enable_plugin(Plugin1.name) | |
1243 | + Comment.delete_all | |
1244 | + page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text') | |
1245 | + page.comments.build(:author => profile, :title => 'hi', :body => 'hello 1' ).save! | |
1246 | + page.comments.build(:author => profile, :title => 'hi', :body => 'hello 2' ).save! | |
1247 | + page.comments.build(:author => profile, :title => 'hi', :body => 'hello 3' ).save! | |
1248 | + c = page.comments.build(:author => profile, :title => 'hi', :body => 'hello 4' ) | |
1249 | + c.save! | |
1250 | + | |
1251 | + | |
1252 | + get :view_page, :profile => profile.identifier, :page => [ 'myarticle' ] | |
1253 | + | |
1254 | + assert_equal [c], assigns(:comments) | |
1255 | + end | |
1256 | + | |
1257 | + | |
1258 | + should 'take in consideration only the first plugin comments definition' do | |
1259 | + class Plugin1 < Noosfero::Plugin | |
1260 | + def load_comments(page) | |
1261 | + page.comments.first | |
1262 | + end | |
1263 | + end | |
1264 | + class Plugin2 < Noosfero::Plugin | |
1265 | + def load_comments(page) | |
1266 | + page.comments.last | |
1267 | + end | |
1268 | + end | |
1269 | + Environment.default.enable_plugin(Plugin1.name) | |
1270 | + Environment.default.enable_plugin(Plugin2.name) | |
1271 | + | |
1272 | + page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text') | |
1273 | + c1 = page.comments.build(:author => profile, :title => 'hi', :body => 'hello 1' ) | |
1274 | + c1.save! | |
1275 | + page.comments.build(:author => profile, :title => 'hi', :body => 'hello 2' ).save! | |
1276 | + page.comments.build(:author => profile, :title => 'hi', :body => 'hello 3' ).save! | |
1277 | + c2 = page.comments.build(:author => profile, :title => 'hi', :body => 'hello 4' ) | |
1278 | + c2.save! | |
1279 | + | |
1280 | + get :view_page, :profile => profile.identifier, :page => [ 'myarticle' ] | |
1281 | + | |
1282 | + assert_equal [c1], assigns(:comments) | |
1283 | + end | |
1284 | + | |
1285 | + should 'empty array of comments collected by plugin make the comments variable be an empty array' do | |
1286 | + class Plugin1 < Noosfero::Plugin | |
1287 | + def load_comments(page) | |
1288 | + [] | |
1289 | + end | |
1290 | + end | |
1291 | + Environment.default.enable_plugin(Plugin1.name) | |
1292 | + | |
1293 | + page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text') | |
1294 | + page.comments.build(:author => profile, :title => 'hi', :body => 'hello 1' ).save! | |
1295 | + page.comments.build(:author => profile, :title => 'hi', :body => 'hello 2' ).save! | |
1296 | + | |
1297 | + | |
1298 | + get :view_page, :profile => profile.identifier, :page => [ 'myarticle' ] | |
1299 | + | |
1300 | + assert_equal [], assigns(:comments) | |
1301 | + end | |
1188 | 1302 | end | ... | ... |
test/unit/application_helper_test.rb
... | ... | @@ -3,6 +3,7 @@ require File.dirname(__FILE__) + '/../test_helper' |
3 | 3 | class ApplicationHelperTest < ActiveSupport::TestCase |
4 | 4 | |
5 | 5 | include ApplicationHelper |
6 | + include Noosfero::Plugin::HotSpot | |
6 | 7 | |
7 | 8 | def setup |
8 | 9 | self.stubs(:session).returns({}) |
... | ... | @@ -658,6 +659,45 @@ class ApplicationHelperTest < ActiveSupport::TestCase |
658 | 659 | assert_not_nil add_zoom_to_images |
659 | 660 | end |
660 | 661 | |
662 | + should 'parse macros' do | |
663 | + class Plugin1 < Noosfero::Plugin | |
664 | + def macro_test1(params, inner_html, source) | |
665 | + 'Test1' | |
666 | + end | |
667 | + def macro_test2(params, inner_html, source) | |
668 | + 'Test2' | |
669 | + end | |
670 | + | |
671 | + def macro_methods | |
672 | + ['macro_test1', 'macro_test2'] | |
673 | + end | |
674 | + end | |
675 | + | |
676 | + @environment = Environment.default | |
677 | + Environment.macros = {@environment.id => {}} | |
678 | + context = mock() | |
679 | + context.stubs(:environment).returns(@environment) | |
680 | + Plugin1.new(context) | |
681 | + html = ' | |
682 | + <div class="macro nonEdit" data-macro="test1" data-macro-param="123"></div> | |
683 | + <div class="macro nonEdit" data-macro="test2"></div> | |
684 | + <div class="macro nonEdit" data-macro="unexistent" data-macro-param="987"></div> | |
685 | + ' | |
686 | + parsed_html = convert_macro(html, mock()) | |
687 | + parsed_divs = Hpricot(parsed_html).search('div') | |
688 | + expected_divs = Hpricot(' | |
689 | + <div data-macro="test1" class="parsed-macro test1">Test1</div> | |
690 | + <div data-macro="test2" class="parsed-macro test2">Test2</div> | |
691 | + <div data-macro="unexistent" class="failed-macro unexistent">Unsupported macro unexistent!</div> | |
692 | + ').search('div') | |
693 | + | |
694 | + # comparing div attributes between parsed and expected html | |
695 | + parsed_divs.each_with_index do |div, i| | |
696 | + assert_equal expected_divs[i].attributes.to_hash, div.attributes.to_hash | |
697 | + assert_equal expected_divs[i].inner_text, div.inner_text | |
698 | + end | |
699 | + end | |
700 | + | |
661 | 701 | should 'reference to article' do |
662 | 702 | c = fast_create(Community) |
663 | 703 | a = fast_create(TinyMceArticle, :profile_id => c.id) | ... | ... |
test/unit/comment_test.rb
... | ... | @@ -700,6 +700,21 @@ class CommentTest < ActiveSupport::TestCase |
700 | 700 | assert_equal c1, c1.comment_root |
701 | 701 | end |
702 | 702 | |
703 | + should 'count a thread of comments' do | |
704 | + Comment.delete_all | |
705 | + comments = [] | |
706 | + comments.push(create_comment) | |
707 | + c1 = create_comment | |
708 | + comments.push(c1) | |
709 | + create_comment(:reply_of_id => c1.id) | |
710 | + create_comment(:reply_of_id => c1.id) | |
711 | + c2 = create_comment | |
712 | + comments.push(c2) | |
713 | + create_comment(:reply_of_id => c2.id) | |
714 | + | |
715 | + assert_equal 6, Comment.count_thread(comments) | |
716 | + end | |
717 | + | |
703 | 718 | private |
704 | 719 | |
705 | 720 | def create_comment(args = {}) | ... | ... |
... | ... | @@ -0,0 +1,124 @@ |
1 | +require File.dirname(__FILE__) + '/../test_helper' | |
2 | + | |
3 | +class MacrosHelperTest < ActiveSupport::TestCase | |
4 | + include MacrosHelper | |
5 | + include ApplicationHelper | |
6 | + include ActionView::Helpers::FormOptionsHelper | |
7 | + include ActionView::Helpers::FormTagHelper | |
8 | + include ActionView::Helpers::TagHelper | |
9 | + | |
10 | + CONFIG = { | |
11 | + :params => [ | |
12 | + { :name => :identifier, :type => 'text'}, | |
13 | + { :name => :size, :type => 'select', | |
14 | + :values => [ | |
15 | + [_('Big'), :big], | |
16 | + [_('Icon'), :icon], | |
17 | + [_('Minor'), :minor], | |
18 | + [_('Portrait'), :portrait], | |
19 | + [_('Thumb'), :thumb] | |
20 | + ], | |
21 | + :default => :portrait | |
22 | + } | |
23 | + ], | |
24 | + :title => _('Profile Image Link') | |
25 | + } | |
26 | + | |
27 | + should 'generate html for macro configuration' do | |
28 | + @environment = Environment.default | |
29 | + Environment.macros = {} | |
30 | + macros = Environment.macros[@environment.id] = {} | |
31 | + plugin_instance = mock | |
32 | + plugin_instance.stubs('config_macro_example').returns(CONFIG) | |
33 | + macros['macro_example'] = plugin_instance | |
34 | + html = macro_configuration_dialog('macro_example') | |
35 | + assert_tag_in_string html, :tag => 'label', :content => _('Identifier') | |
36 | + assert_tag_in_string html, :tag => 'input', :attributes => {:name => 'identifier'} | |
37 | + assert_tag_in_string html, :tag => 'label', :content => 'size'.humanize | |
38 | + assert_tag_in_string html, :tag => 'select', :attributes => {:name => 'size'}, :descendant => {:tag => 'option', :attributes => {:value => 'big'}, :content => _('Big')} | |
39 | + assert_tag_in_string html, :tag => 'select', :attributes => {:name => 'size'}, :descendant => {:tag => 'option', :attributes => {:value => 'icon'}, :content => _('Icon')} | |
40 | + assert_tag_in_string html, :tag => 'select', :attributes => {:name => 'size'}, :descendant => {:tag => 'option', :attributes => {:value => 'minor'}, :content => _('Minor')} | |
41 | + assert_tag_in_string html, :tag => 'select', :attributes => {:name => 'size'}, :descendant => {:tag => 'option', :attributes => {:value => 'portrait', :selected => true}, :content => _('Portrait')} | |
42 | + assert_tag_in_string html, :tag => 'select', :attributes => {:name => 'size'}, :descendant => {:tag => 'option', :attributes => {:value => 'thumb'}, :content => _('Thumb')} | |
43 | + end | |
44 | + | |
45 | + should 'get macro title' do | |
46 | + @environment = Environment.default | |
47 | + Environment.macros = {} | |
48 | + macros = Environment.macros[@environment.id] = {} | |
49 | + plugin_instance = mock | |
50 | + plugin_instance.stubs('config_macro_example').returns(CONFIG) | |
51 | + macros['macro_example'] = plugin_instance | |
52 | + title = macro_title('macro_example') | |
53 | + assert _('Profile Image Link'), title | |
54 | + end | |
55 | + | |
56 | + should 'get only macros in menu' do | |
57 | + @environment = Environment.default | |
58 | + Environment.macros = {} | |
59 | + macros = Environment.macros[@environment.id] = {} | |
60 | + plugin_instance = mock | |
61 | + plugin_instance.stubs('config_macro_example').returns({}) | |
62 | + macros['macro_example'] = plugin_instance | |
63 | + plugin_instance_other = mock | |
64 | + plugin_instance_other.stubs('config_macro_example_other').returns({:icon_path => 'icon.png'}) | |
65 | + macros['macro_example_other'] = plugin_instance_other | |
66 | + assert_equal [plugin_instance], macros_in_menu.values | |
67 | + end | |
68 | + | |
69 | + should 'get only macros with buttons' do | |
70 | + @environment = Environment.default | |
71 | + Environment.macros = {} | |
72 | + macros = Environment.macros[@environment.id] = {} | |
73 | + plugin_instance = mock | |
74 | + plugin_instance.stubs('config_macro_example').returns({}) | |
75 | + macros['macro_example'] = plugin_instance | |
76 | + plugin_instance_other = mock | |
77 | + plugin_instance_other.stubs('config_macro_example_other').returns({:icon_path => 'icon.png'}) | |
78 | + macros['macro_example_other'] = plugin_instance_other | |
79 | + assert_equal [plugin_instance_other], macros_with_buttons.values | |
80 | + end | |
81 | + | |
82 | + should 'skip macro config dialog and call generator directly' do | |
83 | + @environment = Environment.default | |
84 | + Environment.macros = {} | |
85 | + macros = Environment.macros[@environment.id] = {} | |
86 | + plugin_instance = mock | |
87 | + plugin_instance.stubs('config_macro_example').returns({:skip_dialog => true, :generator => '', :params => [] }) | |
88 | + macros['macro_example'] = plugin_instance | |
89 | + assert_equal 'function(){}', generate_macro_config_dialog('macro_example') | |
90 | + end | |
91 | + | |
92 | + should 'include js files' do | |
93 | + @environment = Environment.default | |
94 | + Environment.macros = {} | |
95 | + macros = Environment.macros[@environment.id] = {} | |
96 | + plugin_instance = mock | |
97 | + plugin_instance.stubs('config_macro_example').returns({:js_files => 'macro.js' }) | |
98 | + plugin_instance.class.stubs(:public_path).with('macro.js').returns('macro.js') | |
99 | + macros['macro_example'] = plugin_instance | |
100 | + assert_equal '<script src="/javascripts/macro.js" type="text/javascript"></script>', include_macro_js_files | |
101 | + end | |
102 | + | |
103 | + should 'get macro css files' do | |
104 | + @environment = Environment.default | |
105 | + Environment.macros = {} | |
106 | + macros = Environment.macros[@environment.id] = {} | |
107 | + plugin_instance = mock | |
108 | + plugin_instance.stubs('config_macro_example').returns({:css_files => 'macro.css' }) | |
109 | + plugin_instance.class.stubs(:public_path).with('macro.css').returns('macro.css') | |
110 | + macros['macro_example'] = plugin_instance | |
111 | + assert_equal 'macro.css', macro_css_files | |
112 | + end | |
113 | + | |
114 | + should 'get macro specific generator' do | |
115 | + @environment = Environment.default | |
116 | + Environment.macros = {} | |
117 | + macros = Environment.macros[@environment.id] = {} | |
118 | + plugin_instance = mock | |
119 | + plugin_instance.stubs('config_macro_example').returns({:generator => 'macro_generator' }) | |
120 | + macros['macro_example'] = plugin_instance | |
121 | + assert_equal 'macro_generator', macro_generator('macro_example') | |
122 | + end | |
123 | + | |
124 | +end | ... | ... |
test/unit/plugin_manager_test.rb
... | ... | @@ -2,6 +2,8 @@ require File.dirname(__FILE__) + '/../test_helper' |
2 | 2 | |
3 | 3 | class PluginManagerTest < ActiveSupport::TestCase |
4 | 4 | |
5 | + include Noosfero::Plugin::HotSpot | |
6 | + | |
5 | 7 | def setup |
6 | 8 | @environment = Environment.default |
7 | 9 | @controller = mock() |
... | ... | @@ -26,8 +28,8 @@ class PluginManagerTest < ActiveSupport::TestCase |
26 | 28 | class Plugin4 < Noosfero::Plugin; end; |
27 | 29 | environment.stubs(:enabled_plugins).returns([Plugin1.to_s, Plugin2.to_s, Plugin4.to_s]) |
28 | 30 | Noosfero::Plugin.stubs(:all).returns([Plugin1.to_s, Plugin3.to_s, Plugin4.to_s]) |
29 | - plugins = manager.enabled_plugins.map { |instance| instance.class.to_s } | |
30 | - assert_equal [Plugin1.to_s, Plugin4.to_s], plugins | |
31 | + results = plugins.enabled_plugins.map { |instance| instance.class.to_s } | |
32 | + assert_equal [Plugin1.to_s, Plugin4.to_s], results | |
31 | 33 | end |
32 | 34 | |
33 | 35 | should 'map events to registered plugins' do |
... | ... | @@ -55,7 +57,33 @@ class PluginManagerTest < ActiveSupport::TestCase |
55 | 57 | p1 = Plugin1.new |
56 | 58 | p2 = Plugin2.new |
57 | 59 | |
58 | - assert_equal [p1.random_event, p2.random_event], manager.dispatch(:random_event) | |
60 | + assert_equal [p1.random_event, p2.random_event], plugins.dispatch(:random_event) | |
61 | + end | |
62 | + | |
63 | + should 'dispatch_first method returns the first plugin response if there is many plugins to responde the event' do | |
64 | + | |
65 | + class Plugin1 < Noosfero::Plugin | |
66 | + def random_event | |
67 | + 'Plugin 1 action.' | |
68 | + end | |
69 | + end | |
70 | + | |
71 | + class Plugin2 < Noosfero::Plugin | |
72 | + def random_event | |
73 | + 'Plugin 2 action.' | |
74 | + end | |
75 | + end | |
76 | + | |
77 | + class Plugin3 < Noosfero::Plugin | |
78 | + def random_event | |
79 | + 'Plugin 3 action.' | |
80 | + end | |
81 | + end | |
82 | + | |
83 | + environment.stubs(:enabled_plugins).returns([Plugin1.to_s, Plugin2.to_s, Plugin3.to_s]) | |
84 | + p1 = Plugin1.new | |
85 | + | |
86 | + assert_equal p1.random_event, plugins.dispatch_first(:random_event) | |
59 | 87 | end |
60 | 88 | |
61 | 89 | should 'return the first non-blank result' do |
... | ... | @@ -140,5 +168,32 @@ class PluginManagerTest < ActiveSupport::TestCase |
140 | 168 | assert_equal Plugin2, manager.first_plugin(:random_event) |
141 | 169 | end |
142 | 170 | |
171 | + should 'dispatch_first method returns the first plugin response if there is many plugins to responde the event and the first one respond nil' do | |
172 | + | |
173 | + class Plugin1 < Noosfero::Plugin | |
174 | + def random_event | |
175 | + nil | |
176 | + end | |
177 | + end | |
178 | + | |
179 | + class Plugin2 < Noosfero::Plugin | |
180 | + def random_event | |
181 | + 'Plugin 2 action.' | |
182 | + end | |
183 | + end | |
184 | + | |
185 | + class Plugin3 < Noosfero::Plugin | |
186 | + def random_event | |
187 | + 'Plugin 3 action.' | |
188 | + end | |
189 | + end | |
190 | + | |
191 | + environment.stubs(:enabled_plugins).returns([Plugin1.to_s, Plugin2.to_s, Plugin3.to_s]) | |
192 | + | |
193 | + p2 = Plugin2.new | |
194 | + | |
195 | + assert_equal p2.random_event, plugins.dispatch_first(:random_event) | |
196 | + end | |
197 | + | |
143 | 198 | end |
144 | 199 | ... | ... |
test/unit/plugin_test.rb
... | ... | @@ -7,6 +7,8 @@ class PluginTest < ActiveSupport::TestCase |
7 | 7 | end |
8 | 8 | attr_reader :environment |
9 | 9 | |
10 | + include Noosfero::Plugin::HotSpot | |
11 | + | |
10 | 12 | should 'keep the list of all loaded subclasses' do |
11 | 13 | class Plugin1 < Noosfero::Plugin |
12 | 14 | end |
... | ... | @@ -26,6 +28,61 @@ class PluginTest < ActiveSupport::TestCase |
26 | 28 | assert_equal({:controller => 'plugin_test/plugin1_admin', :action => 'index'}, Plugin1.admin_url) |
27 | 29 | end |
28 | 30 | |
31 | + should 'register its macros in the environment when instantiated' do | |
32 | + class Plugin1 < Noosfero::Plugin | |
33 | + def macro_example1(params, inner_html, source) | |
34 | + end | |
35 | + | |
36 | + def example2(params, inner_html, source) | |
37 | + end | |
38 | + | |
39 | + def not_macro | |
40 | + end | |
41 | + | |
42 | + def macro_methods | |
43 | + ['macro_example1', 'example2'] | |
44 | + end | |
45 | + end | |
46 | + | |
47 | + Environment.macros = {} | |
48 | + Environment.macros[environment.id] = {} | |
49 | + macros = Environment.macros[environment.id] | |
50 | + context = mock() | |
51 | + context.stubs(:environment).returns(environment) | |
52 | + | |
53 | + plugin_instance = Plugin1.new(context) | |
54 | + | |
55 | + assert_equal plugin_instance, macros['macro_example1'] | |
56 | + assert_equal plugin_instance, macros['example2'] | |
57 | + assert_nil macros['not_macro'] | |
58 | + end | |
59 | + | |
60 | + should 'load_comments return nil by default' do | |
61 | + | |
62 | + class Plugin1 < Noosfero::Plugin; end; | |
63 | + | |
64 | + environment.stubs(:enabled_plugins).returns([Plugin1.to_s]) | |
65 | + | |
66 | + profile = fast_create(Profile, :name => 'test profile', :identifier => 'test_profile') | |
67 | + a = fast_create(Article, :name => 'my article', :profile_id => profile.id) | |
68 | + assert_nil plugins.dispatch_first(:load_comments, a) | |
69 | + end | |
70 | + | |
71 | + should 'load_comments return the value defined by plugin' do | |
72 | + | |
73 | + class Plugin1 < Noosfero::Plugin | |
74 | + def load_comments(page) | |
75 | + 'some value' | |
76 | + end | |
77 | + end | |
78 | + | |
79 | + environment.stubs(:enabled_plugins).returns([Plugin1.to_s]) | |
80 | + | |
81 | + profile = fast_create(Profile, :name => 'test profile', :identifier => 'test_profile') | |
82 | + a = fast_create(Article, :name => 'my article', :profile_id => profile.id) | |
83 | + assert_equal 'some value', plugins.dispatch_first(:load_comments, a) | |
84 | + end | |
85 | + | |
29 | 86 | should 'returns empty hash for class method extra_blocks by default if no blocks are defined on plugin' do |
30 | 87 | |
31 | 88 | class SomePlugin1 < Noosfero::Plugin |
... | ... | @@ -494,8 +551,4 @@ class PluginTest < ActiveSupport::TestCase |
494 | 551 | end |
495 | 552 | end |
496 | 553 | |
497 | - | |
498 | - | |
499 | - | |
500 | - | |
501 | 554 | end | ... | ... |