Commit 29490ac8337e1b6cc16ea20d6be529075b4ced9a
Exists in
staging
and in
4 other branches
Merge branch 'AI3220_proposals' into rails3_stable
Showing
35 changed files
with
905 additions
and
6 deletions
Show diff stats
app/controllers/admin/features_controller.rb
| @@ -93,7 +93,7 @@ class FeaturesController < AdminController | @@ -93,7 +93,7 @@ class FeaturesController < AdminController | ||
| 93 | 93 | ||
| 94 | def search_members | 94 | def search_members |
| 95 | arg = params[:q].downcase | 95 | arg = params[:q].downcase |
| 96 | - result = environment.people.find(:all, :conditions => ['LOWER(name) LIKE ?', "%#{arg}%"]) | 96 | + result = environment.people.find(:all, :conditions => ['LOWER(name) LIKE ? OR identifier LIKE ?', "%#{arg}%", "%#{arg}%"]) |
| 97 | render :text => prepare_to_token_input(result).to_json | 97 | render :text => prepare_to_token_input(result).to_json |
| 98 | end | 98 | end |
| 99 | 99 |
app/helpers/categories_helper.rb
| @@ -12,7 +12,7 @@ module CategoriesHelper | @@ -12,7 +12,7 @@ module CategoriesHelper | ||
| 12 | end | 12 | end |
| 13 | 13 | ||
| 14 | def category_color_style(category) | 14 | def category_color_style(category) |
| 15 | - return '' if category.display_color.blank? | 15 | + return '' if category.nil? or category.display_color.blank? |
| 16 | 'background-color: #'+category.display_color+';' | 16 | 'background-color: #'+category.display_color+';' |
| 17 | end | 17 | end |
| 18 | 18 |
app/models/article.rb
| @@ -252,7 +252,7 @@ class Article < ActiveRecord::Base | @@ -252,7 +252,7 @@ class Article < ActiveRecord::Base | ||
| 252 | } | 252 | } |
| 253 | 253 | ||
| 254 | scope :public, | 254 | scope :public, |
| 255 | - :conditions => [ "advertise = ? AND published = ? AND profiles.visible = ? AND profiles.public_profile = ?", true, true, true, true ], :joins => [:profile] | 255 | + :conditions => [ "articles.advertise = ? AND articles.published = ? AND profiles.visible = ? AND profiles.public_profile = ?", true, true, true, true ], :joins => [:profile] |
| 256 | 256 | ||
| 257 | scope :more_recent, | 257 | scope :more_recent, |
| 258 | :conditions => [ "advertise = ? AND published = ? AND profiles.visible = ? AND profiles.public_profile = ? AND | 258 | :conditions => [ "advertise = ? AND published = ? AND profiles.visible = ? AND profiles.public_profile = ? AND |
features/step_definitions/web_steps.rb
| @@ -11,8 +11,14 @@ require File.expand_path(File.join(File.dirname(__FILE__), "..", "support", "pat | @@ -11,8 +11,14 @@ require File.expand_path(File.join(File.dirname(__FILE__), "..", "support", "pat | ||
| 11 | 11 | ||
| 12 | module WithinHelpers | 12 | module WithinHelpers |
| 13 | def with_scope(locator) | 13 | def with_scope(locator) |
| 14 | - locator = locator ? first(locator) : locator | ||
| 15 | - locator ? within(locator) { yield } : yield | 14 | + if locator |
| 15 | + locator = first(locator) || locator | ||
| 16 | + within(locator) do | ||
| 17 | + yield | ||
| 18 | + end | ||
| 19 | + else | ||
| 20 | + yield | ||
| 21 | + end | ||
| 16 | end | 22 | end |
| 17 | end | 23 | end |
| 18 | World(WithinHelpers) | 24 | World(WithinHelpers) |
plugins/proposals_discussion/controllers/myprofile/proposals_discussion_plugin_myprofile_controller.rb
0 → 100644
| @@ -0,0 +1,30 @@ | @@ -0,0 +1,30 @@ | ||
| 1 | +class ProposalsDiscussionPluginMyprofileController < MyProfileController | ||
| 2 | + | ||
| 3 | + before_filter :check_edit_permission_to_proposal, :only => :publish_proposal | ||
| 4 | + | ||
| 5 | + def select_topic | ||
| 6 | + @discussion = profile.articles.find(params[:parent_id]) | ||
| 7 | + render :file => 'select_topic' | ||
| 8 | + end | ||
| 9 | + | ||
| 10 | + def new_proposal | ||
| 11 | + redirect_to :controller => 'cms', :action => 'new', :type => "ProposalsDiscussionPlugin::Proposal", :parent_id => params[:discussion][:topic] | ||
| 12 | + end | ||
| 13 | + | ||
| 14 | + def publish_proposal | ||
| 15 | + if @proposal.update_attribute(:published, true) | ||
| 16 | + session[:notice] = _('Proposal published!') | ||
| 17 | + else | ||
| 18 | + session[:notice] = _('Failed to publish your proposal.') | ||
| 19 | + end | ||
| 20 | + redirect_to @proposal.topic.view_url | ||
| 21 | + end | ||
| 22 | + | ||
| 23 | + protected | ||
| 24 | + | ||
| 25 | + def check_edit_permission_to_proposal | ||
| 26 | + @proposal = profile.articles.find(params[:proposal_id]) | ||
| 27 | + render_access_denied unless @proposal.allow_edit?(user) | ||
| 28 | + end | ||
| 29 | + | ||
| 30 | +end |
plugins/proposals_discussion/controllers/public/proposals_discussion_plugin_public_controller.rb
0 → 100644
| @@ -0,0 +1,43 @@ | @@ -0,0 +1,43 @@ | ||
| 1 | +class ProposalsDiscussionPluginPublicController < ApplicationController | ||
| 2 | + | ||
| 3 | + needs_profile | ||
| 4 | + | ||
| 5 | + def load_proposals | ||
| 6 | + holder = profile.articles.find(params[:holder_id]) | ||
| 7 | + page = (params[:page] || 1).to_i | ||
| 8 | + set_rand_cookie if page == 1 | ||
| 9 | + order = params[:order] | ||
| 10 | + | ||
| 11 | + @proposals = order_proposals(holder.proposals.public, order) | ||
| 12 | + @proposals = @proposals.page(page).per_page(5) | ||
| 13 | + | ||
| 14 | + unless @proposals.empty? | ||
| 15 | + render :partial => 'content_viewer/proposals_list_content', :locals => {:proposals => @proposals, :holder => holder, :page => page+1, :order => order} | ||
| 16 | + else | ||
| 17 | + render :text => '' | ||
| 18 | + end | ||
| 19 | + end | ||
| 20 | + | ||
| 21 | + private | ||
| 22 | + | ||
| 23 | + def order_proposals(proposals, order) | ||
| 24 | + case order | ||
| 25 | + when 'alphabetical' | ||
| 26 | + proposals.reorder('name') | ||
| 27 | + else | ||
| 28 | + set_seed | ||
| 29 | + proposals.reorder('random()') | ||
| 30 | + end | ||
| 31 | + end | ||
| 32 | + | ||
| 33 | + def set_seed | ||
| 34 | + #XXX postgresql specific | ||
| 35 | + seed_val = profile.connection.quote(cookies[:_noosfero_proposals_discussion_rand_seed]) | ||
| 36 | + profile.connection.execute("select setseed(#{seed_val})") | ||
| 37 | + end | ||
| 38 | + | ||
| 39 | + def set_rand_cookie | ||
| 40 | + cookies[:_noosfero_proposals_discussion_rand_seed] = {value: rand, expires: Time.now + 600} | ||
| 41 | + end | ||
| 42 | + | ||
| 43 | +end |
plugins/proposals_discussion/lib/proposals_discussion_plugin.rb
0 → 100644
| @@ -0,0 +1,43 @@ | @@ -0,0 +1,43 @@ | ||
| 1 | +class ProposalsDiscussionPlugin < Noosfero::Plugin | ||
| 2 | + | ||
| 3 | + def self.plugin_name | ||
| 4 | + 'Discussion of Proposals' | ||
| 5 | + end | ||
| 6 | + | ||
| 7 | + def self.plugin_description | ||
| 8 | + _("Provide a structured way to promove a discussion over ideas proposed by users.") | ||
| 9 | + end | ||
| 10 | + | ||
| 11 | + def stylesheet? | ||
| 12 | + true | ||
| 13 | + end | ||
| 14 | + | ||
| 15 | + def content_types | ||
| 16 | + if context.respond_to?(:params) && context.params | ||
| 17 | + types = [] | ||
| 18 | + parent_id = context.params[:parent_id] | ||
| 19 | + parent = parent_id ? context.profile.articles.find(parent_id) : nil | ||
| 20 | + types << ProposalsDiscussionPlugin::Discussion unless parent | ||
| 21 | + types << ProposalsDiscussionPlugin::Topic if parent.kind_of?(ProposalsDiscussionPlugin::Discussion) | ||
| 22 | + types << ProposalsDiscussionPlugin::Proposal if parent.kind_of?(ProposalsDiscussionPlugin::Topic) | ||
| 23 | + types | ||
| 24 | + else | ||
| 25 | + [ProposalsDiscussionPlugin::Discussion, | ||
| 26 | + ProposalsDiscussionPlugin::Topic, | ||
| 27 | + ProposalsDiscussionPlugin::Proposal] | ||
| 28 | + end | ||
| 29 | + end | ||
| 30 | + | ||
| 31 | + def content_remove_new(page) | ||
| 32 | + page.kind_of?(ProposalsDiscussionPlugin::Discussion) || | ||
| 33 | + page.kind_of?(ProposalsDiscussionPlugin::Topic) || | ||
| 34 | + page.kind_of?(ProposalsDiscussionPlugin::Proposal) | ||
| 35 | + end | ||
| 36 | + | ||
| 37 | + def content_remove_upload(page) | ||
| 38 | + page.kind_of?(ProposalsDiscussionPlugin::Discussion) || | ||
| 39 | + page.kind_of?(ProposalsDiscussionPlugin::Topic) || | ||
| 40 | + page.kind_of?(ProposalsDiscussionPlugin::Proposal) | ||
| 41 | + end | ||
| 42 | + | ||
| 43 | +end |
plugins/proposals_discussion/lib/proposals_discussion_plugin/discussion.rb
0 → 100644
| @@ -0,0 +1,20 @@ | @@ -0,0 +1,20 @@ | ||
| 1 | +class ProposalsDiscussionPlugin::Discussion < Folder | ||
| 2 | + | ||
| 3 | + has_many :topics, :class_name => 'ProposalsDiscussionPlugin::Topic', :foreign_key => 'parent_id' | ||
| 4 | + has_many :proposals, :class_name => 'ProposalsDiscussionPlugin::Proposal', :through => :children, :source => :children | ||
| 5 | + | ||
| 6 | + def self.short_description | ||
| 7 | + _("Discussion") | ||
| 8 | + end | ||
| 9 | + | ||
| 10 | + def self.description | ||
| 11 | + _('Container for topics.') | ||
| 12 | + end | ||
| 13 | + | ||
| 14 | + def to_html(options = {}) | ||
| 15 | + proc do | ||
| 16 | + render :file => 'content_viewer/discussion' | ||
| 17 | + end | ||
| 18 | + end | ||
| 19 | + | ||
| 20 | +end |
plugins/proposals_discussion/lib/proposals_discussion_plugin/proposal.rb
0 → 100644
| @@ -0,0 +1,28 @@ | @@ -0,0 +1,28 @@ | ||
| 1 | +class ProposalsDiscussionPlugin::Proposal < TinyMceArticle | ||
| 2 | + | ||
| 3 | + scope :private, lambda {|user| {:conditions => {:last_changed_by_id => user.id, :published => false}}} | ||
| 4 | + | ||
| 5 | + alias :topic :parent | ||
| 6 | + | ||
| 7 | + def self.short_description | ||
| 8 | + _("Proposal") | ||
| 9 | + end | ||
| 10 | + | ||
| 11 | + def self.description | ||
| 12 | + _('Proposal') | ||
| 13 | + end | ||
| 14 | + | ||
| 15 | + validates_presence_of :abstract | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + def to_html(options = {}) | ||
| 19 | + proc do | ||
| 20 | + render :file => 'content_viewer/proposal' | ||
| 21 | + end | ||
| 22 | + end | ||
| 23 | + | ||
| 24 | + def allow_edit?(user) | ||
| 25 | + super || created_by == user | ||
| 26 | + end | ||
| 27 | + | ||
| 28 | +end |
plugins/proposals_discussion/lib/proposals_discussion_plugin/proposal_helper.rb
0 → 100644
plugins/proposals_discussion/lib/proposals_discussion_plugin/proposals_list_helper.rb
0 → 100644
| @@ -0,0 +1,7 @@ | @@ -0,0 +1,7 @@ | ||
| 1 | +module ProposalsDiscussionPlugin::ProposalsListHelper | ||
| 2 | + | ||
| 3 | + def more_proposals(text, holder, order, page=1) | ||
| 4 | + link_to '', url_for({:controller => 'proposals_discussion_plugin_public', :action => 'load_proposals', :holder_id => holder.id, :profile => profile.identifier, :order => order, :page => page }) | ||
| 5 | + end | ||
| 6 | + | ||
| 7 | +end |
plugins/proposals_discussion/lib/proposals_discussion_plugin/topic.rb
0 → 100644
| @@ -0,0 +1,35 @@ | @@ -0,0 +1,35 @@ | ||
| 1 | +class ProposalsDiscussionPlugin::Topic < Folder | ||
| 2 | + | ||
| 3 | + alias :discussion :parent | ||
| 4 | + | ||
| 5 | + has_many :proposals, :class_name => 'ProposalsDiscussionPlugin::Proposal', :foreign_key => 'parent_id' | ||
| 6 | + has_many :proposals_comments, :class_name => 'Comment', :through => :children, :source => :comments | ||
| 7 | + has_many :proposals_authors, :class_name => 'Person', :through => :children, :source => :created_by | ||
| 8 | + | ||
| 9 | + settings_items :color, :type => :string | ||
| 10 | + | ||
| 11 | + attr_accessible :color | ||
| 12 | + | ||
| 13 | + def self.short_description | ||
| 14 | + _("Discussion topic") | ||
| 15 | + end | ||
| 16 | + | ||
| 17 | + def self.description | ||
| 18 | + _('Container for proposals.') | ||
| 19 | + end | ||
| 20 | + | ||
| 21 | + def most_active_participants | ||
| 22 | + proposals_authors.group('profiles.id').order('count(articles.id) DESC').includes(:environment, :preferred_domain, :image) | ||
| 23 | + end | ||
| 24 | + | ||
| 25 | + def to_html(options = {}) | ||
| 26 | + proc do | ||
| 27 | + render :file => 'content_viewer/topic' | ||
| 28 | + end | ||
| 29 | + end | ||
| 30 | + | ||
| 31 | + def allow_create?(user) | ||
| 32 | + true | ||
| 33 | + end | ||
| 34 | + | ||
| 35 | +end |
1.66 KB
plugins/proposals_discussion/public/jquery.jscroll.min.js
0 → 100644
| @@ -0,0 +1,15 @@ | @@ -0,0 +1,15 @@ | ||
| 1 | +/*! | ||
| 2 | + * jScroll - jQuery Plugin for Infinite Scrolling / Auto-Paging - v2.2.4 | ||
| 3 | + * http://jscroll.com/ | ||
| 4 | + * | ||
| 5 | + * Copyright 2011-2013, Philip Klauzinski | ||
| 6 | + * http://klauzinski.com/ | ||
| 7 | + * Dual licensed under the MIT and GPL Version 2 licenses. | ||
| 8 | + * http://jscroll.com/#license | ||
| 9 | + * http://www.opensource.org/licenses/mit-license.php | ||
| 10 | + * http://www.gnu.org/licenses/gpl-2.0.html | ||
| 11 | + * | ||
| 12 | + * @author Philip Klauzinski | ||
| 13 | + * @requires jQuery v1.4.3+ | ||
| 14 | + */ | ||
| 15 | +(function(b){b.jscroll={defaults:{debug:false,autoTrigger:true,autoTriggerUntil:false,loadingHtml:"<small>Loading...</small>",padding:0,nextSelector:"a:last",contentSelector:"",pagingSelector:"",callback:false}};var a=function(e,g){var o=e.data("jscroll"),n=(typeof g==="function")?{callback:g}:g,p=b.extend({},b.jscroll.defaults,n,o||{}),c=(e.css("overflow-y")==="visible"),l=e.find(p.nextSelector).first(),v=b(window),h=b("body"),q=c?v:e,m=b.trim(l.attr("href")+" "+p.contentSelector);e.data("jscroll",b.extend({},o,{initialized:true,waiting:false,nextHref:m}));r();k();t();function k(){var x=b(p.loadingHtml).filter("img").attr("src");if(x){var w=new Image();w.src=x}}function r(){if(!e.find(".jscroll-inner").length){e.contents().wrapAll('<div class="jscroll-inner" />')}}function d(w){if(p.pagingSelector){var x=w.closest(p.pagingSelector).hide()}else{var x=w.parent().not(".jscroll-inner,.jscroll-added").addClass("jscroll-next-parent").hide();if(!x.length){w.wrap('<div class="jscroll-next-parent" />').parent().hide()}}}function j(){return q.unbind(".jscroll").removeData("jscroll").find(".jscroll-inner").children().unwrap().filter(".jscroll-added").children().unwrap()}function i(){r();var D=e.find("div.jscroll-inner").first(),B=e.data("jscroll"),C=parseInt(e.css("borderTopWidth")),y=isNaN(C)?0:C,x=parseInt(e.css("paddingTop"))+y,A=c?q.scrollTop():e.offset().top,z=D.length?D.offset().top:0,w=Math.ceil(A-z+q.height()+x);if(!B.waiting&&w+p.padding>=D.outerHeight()){f("info","jScroll:",D.outerHeight()-w,"from bottom. Loading next request...");return u()}}function s(w){w=w||e.data("jscroll");if(!w||!w.nextHref){f("warn","jScroll: nextSelector not found - destroying");j();return false}else{t();return true}}function t(){var w=e.find(p.nextSelector).first();if(p.autoTrigger&&(p.autoTriggerUntil===false||p.autoTriggerUntil>0)){d(w);if(h.height()<=v.height()){i()}q.unbind(".jscroll").bind("scroll.jscroll",function(){return i()});if(p.autoTriggerUntil>0){p.autoTriggerUntil--}}else{q.unbind(".jscroll");w.bind("click.jscroll",function(){d(w);u();return false})}}function u(){var x=e.find("div.jscroll-inner").first(),w=e.data("jscroll");w.waiting=true;x.append('<div class="jscroll-added" />').children(".jscroll-added").last().html('<div class="jscroll-loading">'+p.loadingHtml+"</div>");return e.animate({scrollTop:x.outerHeight()},0,function(){x.find("div.jscroll-added").last().load(w.nextHref,function(A,z,B){if(z==="error"){return j()}var y=b(this).find(p.nextSelector).first();w.waiting=false;w.nextHref=y.attr("href")?b.trim(y.attr("href")+" "+p.contentSelector):false;b(".jscroll-next-parent",e).remove();s();if(p.callback){p.callback.call(this)}f("dir",w)})})}function f(w){if(p.debug&&typeof console==="object"&&(typeof w==="object"||typeof console[w]==="function")){if(typeof w==="object"){var y=[];for(var x in w){if(typeof console[x]==="function"){y=(w[x].length)?w[x]:[w[x]];console[x].apply(console,y)}else{console.log.apply(console,y)}}}else{console[w].apply(console,Array.prototype.slice.call(arguments,1))}}}b.extend(e.jscroll,{destroy:j});return e};b.fn.jscroll=function(c){return this.each(function(){var f=b(this),e=f.data("jscroll");if(e&&e.initialized){return}var d=new a(f,c)})}})(jQuery); | ||
| 0 | \ No newline at end of file | 16 | \ No newline at end of file |
| @@ -0,0 +1,31 @@ | @@ -0,0 +1,31 @@ | ||
| 1 | +function initTwitterButton() { | ||
| 2 | + !function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs'); | ||
| 3 | +} | ||
| 4 | + | ||
| 5 | +jQuery(document).ready(function($) { | ||
| 6 | + initTwitterButton(); | ||
| 7 | + $('.proposals').on('mouseenter', '.proposal', function() { | ||
| 8 | + twttr.widgets.load(); | ||
| 9 | + $('.proposal .social').hide(); | ||
| 10 | + $(this).find('.social').show("fast"); | ||
| 11 | + }).on('mouseleave', '.proposal', function() { | ||
| 12 | + $(this).find('.social').hide("fast"); | ||
| 13 | + }); | ||
| 14 | + | ||
| 15 | + function proposalsScroll() { | ||
| 16 | + $('.proposals').data('jscroll', null); | ||
| 17 | + $('.proposals').jscroll({ | ||
| 18 | + loadingHtml: '<img src="/images/loading.gif" alt="Loading" />Loading...', | ||
| 19 | + nextSelector: 'div.more a' | ||
| 20 | + }); | ||
| 21 | + $('.proposals').trigger('scroll.jscroll'); | ||
| 22 | + } | ||
| 23 | + proposalsScroll(); | ||
| 24 | + | ||
| 25 | + $('.proposals_list .filters a.order').on('ajax:success', function(event, data, status, xhr) { | ||
| 26 | + $('.proposals_list .filters a.order').removeClass('selected'); | ||
| 27 | + $(this).addClass('selected'); | ||
| 28 | + $(this).parents('div.proposals_list').find('.proposals').html(data); | ||
| 29 | + proposalsScroll(); | ||
| 30 | + }); | ||
| 31 | +}); |
| @@ -0,0 +1,124 @@ | @@ -0,0 +1,124 @@ | ||
| 1 | +.private-proposals .proposal { | ||
| 2 | + background: url(/images/hachure.png); | ||
| 3 | + opacity: 0.5; | ||
| 4 | + filter: alpha(opacity=25); | ||
| 5 | + zoom: 1; | ||
| 6 | +} | ||
| 7 | + | ||
| 8 | +.proposal { | ||
| 9 | + background: rgb(236, 236, 236); | ||
| 10 | + width: 100%; | ||
| 11 | + min-width: 272px; | ||
| 12 | + vertical-align: top; | ||
| 13 | + margin: 12px 13px 12px 0; | ||
| 14 | + box-shadow: 5px 5px 5px -2px #ddd; | ||
| 15 | + height: 100px; | ||
| 16 | +} | ||
| 17 | + | ||
| 18 | +.article-body-proposals-discussion-plugin_discussion .actions, | ||
| 19 | +.article-body-proposals-discussion-plugin_topic .actions { | ||
| 20 | + margin: 10px 0 25px 0; | ||
| 21 | +} | ||
| 22 | + | ||
| 23 | +.proposal .content, .proposal .score, .proposal .topic { | ||
| 24 | + display: table-cell; | ||
| 25 | + border-right: 1px solid; | ||
| 26 | + border-color: rgb(201, 201, 201); | ||
| 27 | + padding: 5px; | ||
| 28 | + vertical-align: middle; | ||
| 29 | + height: 100%; | ||
| 30 | +} | ||
| 31 | +.proposal .topic { | ||
| 32 | + border-right: 0; | ||
| 33 | + text-align: center; | ||
| 34 | + width: 24%; | ||
| 35 | +} | ||
| 36 | +.proposal .score { | ||
| 37 | + width: 8%; | ||
| 38 | + text-align: center; | ||
| 39 | +} | ||
| 40 | + | ||
| 41 | +.proposal .content:hover, .proposal .topic:hover { | ||
| 42 | + background: rgba(0, 0, 0, 0.1); | ||
| 43 | +} | ||
| 44 | + | ||
| 45 | +.proposal .title { | ||
| 46 | + font-weight: bold; | ||
| 47 | + font-size: 15px; | ||
| 48 | +} | ||
| 49 | + | ||
| 50 | +#article .proposal a:visited, #article .proposal a { | ||
| 51 | + color: rgb(70, 70, 70); | ||
| 52 | + text-decoration: none; | ||
| 53 | + width: 100%; | ||
| 54 | + display: inline-block; | ||
| 55 | +} | ||
| 56 | + | ||
| 57 | +#article .proposal .title a { | ||
| 58 | + padding: 4px 0px; | ||
| 59 | +} | ||
| 60 | + | ||
| 61 | +.proposal .content { | ||
| 62 | + width: 68%; | ||
| 63 | + color: rgb(83, 83, 83); | ||
| 64 | + vertical-align: top; | ||
| 65 | + height: 90px; | ||
| 66 | +} | ||
| 67 | + | ||
| 68 | +.proposal .abstract { | ||
| 69 | + padding-top: 4px; | ||
| 70 | +} | ||
| 71 | + | ||
| 72 | +form .proposals-discussion-plugin textarea { | ||
| 73 | + width: 98%; | ||
| 74 | +} | ||
| 75 | + | ||
| 76 | +form .proposals-discussion-plugin .abstract textarea { | ||
| 77 | + height: 60px; | ||
| 78 | +} | ||
| 79 | + | ||
| 80 | +form .proposals-discussion-plugin .body textarea { | ||
| 81 | + height: 400px; | ||
| 82 | +} | ||
| 83 | + | ||
| 84 | +.topic-color { | ||
| 85 | + width: 9px; | ||
| 86 | + float: left; | ||
| 87 | + height: 100%; | ||
| 88 | +} | ||
| 89 | + | ||
| 90 | +.topics .topic { | ||
| 91 | + background-color: rgb(233, 233, 233); | ||
| 92 | + margin: 5px 0; | ||
| 93 | + height: 25px; | ||
| 94 | +} | ||
| 95 | + | ||
| 96 | +#article .proposal .topic a { | ||
| 97 | + font-weight: bold; | ||
| 98 | + color: #888a85; | ||
| 99 | + width: 100%; | ||
| 100 | + height: 100%; | ||
| 101 | +} | ||
| 102 | + | ||
| 103 | +#article .topics .topic a { | ||
| 104 | + text-decoration: none; | ||
| 105 | + display: inline-block; | ||
| 106 | + width: 95%; | ||
| 107 | + height: 100%; | ||
| 108 | + padding-left: 5px; | ||
| 109 | + font-weight: bold; | ||
| 110 | + font-size: 14px; | ||
| 111 | +} | ||
| 112 | + | ||
| 113 | +.proposals_list .filters { | ||
| 114 | + float: right; | ||
| 115 | +} | ||
| 116 | +#article .proposals_list .filters a { | ||
| 117 | + text-decoration: none; | ||
| 118 | + border-left: 1px solid rgb(185, 185, 185); | ||
| 119 | + padding: 0 5px; | ||
| 120 | + color: #555753; | ||
| 121 | +} | ||
| 122 | +.proposals_list .filters a.selected { | ||
| 123 | + font-weight: bold; | ||
| 124 | +} |
plugins/proposals_discussion/test/functional/proposals_discussion_plugin_myprofile_controller_test.rb
0 → 100644
| @@ -0,0 +1,44 @@ | @@ -0,0 +1,44 @@ | ||
| 1 | +require File.dirname(__FILE__) + '/../test_helper' | ||
| 2 | + | ||
| 3 | +class ProposalsDiscussionPluginMyprofileControllerTest < ActionController::TestCase | ||
| 4 | + | ||
| 5 | + def setup | ||
| 6 | + @profile = fast_create(Community) | ||
| 7 | + @discussion = fast_create(ProposalsDiscussionPlugin::Discussion, :profile_id => @profile.id) | ||
| 8 | + @topic = fast_create(ProposalsDiscussionPlugin::Topic, :parent_id => @discussion.id, :profile_id => @profile.id) | ||
| 9 | + @person = create_user_with_permission('testinguser', 'post_content') | ||
| 10 | + login_as :testinguser | ||
| 11 | + end | ||
| 12 | + | ||
| 13 | + attr_reader :profile, :discussion, :topic, :person | ||
| 14 | + | ||
| 15 | + should 'list topics for selection' do | ||
| 16 | + 3.times {fast_create(ProposalsDiscussionPlugin::Topic, :parent_id => discussion.id, :profile_id => profile.id)} | ||
| 17 | + get :select_topic, :profile => profile.identifier, :parent_id => discussion.id | ||
| 18 | + assert_equal discussion, assigns(:discussion) | ||
| 19 | + assert_select 'div#topics' do | ||
| 20 | + assert_select 'div.content', discussion.topics.count | ||
| 21 | + assert_select "input[name='discussion[topic]']", discussion.topics.count | ||
| 22 | + end | ||
| 23 | + assert_tag :form, :attributes => {:action => "/myprofile/#{profile.identifier}/plugin/proposals_discussion/myprofile/new_proposal"} | ||
| 24 | + end | ||
| 25 | + | ||
| 26 | + should 'new_proposal redirect to cms controller' do | ||
| 27 | + get :new_proposal, :profile => profile.identifier, :discussion => {:topic => topic.id} | ||
| 28 | + assert_redirected_to :controller => 'cms', :action => 'new', :type => "ProposalsDiscussionPlugin::Proposal", :parent_id => topic.id | ||
| 29 | + end | ||
| 30 | + | ||
| 31 | + should 'publish a proposal' do | ||
| 32 | + proposal = fast_create(ProposalsDiscussionPlugin::Proposal, :parent_id => topic.id, :profile_id => profile.id, :published => false, :created_by_id => person.id) | ||
| 33 | + get :publish_proposal, :proposal_id => proposal.id, :profile => profile.identifier | ||
| 34 | + assert proposal.reload.published | ||
| 35 | + end | ||
| 36 | + | ||
| 37 | + should 'do not publish if the logged user do not have edition permission' do | ||
| 38 | + proposal = fast_create(ProposalsDiscussionPlugin::Proposal, :parent_id => topic.id, :profile_id => profile.id, :published => false) | ||
| 39 | + get :publish_proposal, :proposal_id => proposal.id, :profile => profile.identifier | ||
| 40 | + assert_response 403 | ||
| 41 | + assert !proposal.reload.published | ||
| 42 | + end | ||
| 43 | + | ||
| 44 | +end |
plugins/proposals_discussion/test/functional/proposals_discussion_plugin_public_controller_test.rb
0 → 100644
| @@ -0,0 +1,38 @@ | @@ -0,0 +1,38 @@ | ||
| 1 | +require File.dirname(__FILE__) + '/../test_helper' | ||
| 2 | + | ||
| 3 | +class ProposalsDiscussionPluginPublicControllerTest < ActionController::TestCase | ||
| 4 | + | ||
| 5 | + def setup | ||
| 6 | + @profile = fast_create(Community) | ||
| 7 | + @discussion = fast_create(ProposalsDiscussionPlugin::Discussion, :profile_id => @profile.id) | ||
| 8 | + @topic = fast_create(ProposalsDiscussionPlugin::Topic, :parent_id => @discussion.id, :profile_id => @profile.id) | ||
| 9 | + end | ||
| 10 | + | ||
| 11 | + attr_reader :profile, :discussion, :topic | ||
| 12 | + | ||
| 13 | + should 'load proposals' do | ||
| 14 | + proposals = 3.times.map { fast_create(ProposalsDiscussionPlugin::Proposal, :name => 'proposal title', :abstract => 'proposal abstract', :profile_id => profile.id, :parent_id => topic.id)} | ||
| 15 | + get :load_proposals, :profile => profile.identifier, :holder_id => discussion.id | ||
| 16 | + assert_equivalent proposals, assigns(:proposals) | ||
| 17 | + end | ||
| 18 | + | ||
| 19 | + should 'add link to next page' do | ||
| 20 | + proposal = fast_create(ProposalsDiscussionPlugin::Proposal, :name => 'proposal title', :abstract => 'proposal abstract', :profile_id => profile.id, :parent_id => topic.id) | ||
| 21 | + get :load_proposals, :profile => profile.identifier, :holder_id => discussion.id | ||
| 22 | + assert_match /href=.*page=2/, response.body | ||
| 23 | + end | ||
| 24 | + | ||
| 25 | + should 'render blank text if it is the last page' do | ||
| 26 | + get :load_proposals, :profile => profile.identifier, :holder_id => discussion.id | ||
| 27 | + assert_equal '', response.body | ||
| 28 | + end | ||
| 29 | + | ||
| 30 | + should 'load proposals with alphabetical order' do | ||
| 31 | + proposal1 = fast_create(ProposalsDiscussionPlugin::Proposal, :name => 'z proposal', :abstract => 'proposal abstract', :profile_id => profile.id, :parent_id => topic.id) | ||
| 32 | + proposal2 = fast_create(ProposalsDiscussionPlugin::Proposal, :name => 'abc proposal', :abstract => 'proposal abstract', :profile_id => profile.id, :parent_id => topic.id) | ||
| 33 | + proposal3 = fast_create(ProposalsDiscussionPlugin::Proposal, :name => 'abd proposal', :abstract => 'proposal abstract', :profile_id => profile.id, :parent_id => topic.id) | ||
| 34 | + get :load_proposals, :profile => profile.identifier, :holder_id => discussion.id, :order => 'alphabetical' | ||
| 35 | + assert_equal [proposal2, proposal3, proposal1], assigns(:proposals) | ||
| 36 | + end | ||
| 37 | + | ||
| 38 | +end |
| @@ -0,0 +1 @@ | @@ -0,0 +1 @@ | ||
| 1 | +require File.dirname(__FILE__) + '/../../../test/test_helper' |
plugins/proposals_discussion/test/unit/discussion_test.rb
0 → 100644
| @@ -0,0 +1,27 @@ | @@ -0,0 +1,27 @@ | ||
| 1 | +require File.dirname(__FILE__) + '/../test_helper' | ||
| 2 | + | ||
| 3 | +class DiscussionTest < ActiveSupport::TestCase | ||
| 4 | + | ||
| 5 | + def setup | ||
| 6 | + @profile = fast_create(Community) | ||
| 7 | + @discussion = ProposalsDiscussionPlugin::Discussion.new(:name => 'test', :profile => @profile) | ||
| 8 | + end | ||
| 9 | + | ||
| 10 | + attr_reader :profile, :discussion | ||
| 11 | + | ||
| 12 | + should 'return list of topics' do | ||
| 13 | + discussion.save! | ||
| 14 | + topic1 = fast_create(ProposalsDiscussionPlugin::Topic, :parent_id => discussion.id) | ||
| 15 | + topic2 = fast_create(ProposalsDiscussionPlugin::Topic, :parent_id => discussion.id) | ||
| 16 | + assert_equivalent [topic1, topic2], discussion.topics | ||
| 17 | + end | ||
| 18 | + | ||
| 19 | + should 'return list of proposals' do | ||
| 20 | + discussion.save! | ||
| 21 | + topic = fast_create(ProposalsDiscussionPlugin::Topic, :parent_id => discussion.id) | ||
| 22 | + proposal1 = fast_create(ProposalsDiscussionPlugin::Proposal, :parent_id => topic.id) | ||
| 23 | + proposal2 = fast_create(ProposalsDiscussionPlugin::Proposal, :parent_id => topic.id) | ||
| 24 | + assert_equivalent [proposal1, proposal2], discussion.proposals | ||
| 25 | + end | ||
| 26 | + | ||
| 27 | +end |
| @@ -0,0 +1,32 @@ | @@ -0,0 +1,32 @@ | ||
| 1 | +require File.dirname(__FILE__) + '/../test_helper' | ||
| 2 | + | ||
| 3 | +class ProposalTest < ActiveSupport::TestCase | ||
| 4 | + | ||
| 5 | + def setup | ||
| 6 | + @profile = fast_create(Community) | ||
| 7 | + @person = fast_create(Person) | ||
| 8 | + @proposal = ProposalsDiscussionPlugin::Proposal.new(:name => 'test', :profile => @profile) | ||
| 9 | + @proposal.created_by = @person | ||
| 10 | + end | ||
| 11 | + | ||
| 12 | + attr_reader :profile, :proposal, :person | ||
| 13 | + | ||
| 14 | + should 'save a proposal' do | ||
| 15 | + proposal.abstract = 'abstract' | ||
| 16 | + assert proposal.save | ||
| 17 | + end | ||
| 18 | + | ||
| 19 | + should 'do not save a proposal without abstract' do | ||
| 20 | + proposal.save | ||
| 21 | + assert proposal.errors['abstract'].present? | ||
| 22 | + end | ||
| 23 | + | ||
| 24 | + should 'allow edition if user is the author' do | ||
| 25 | + assert proposal.allow_edit?(person) | ||
| 26 | + end | ||
| 27 | + | ||
| 28 | + should 'do not allow edition if user is not the author' do | ||
| 29 | + assert !proposal.allow_edit?(fast_create(Person)) | ||
| 30 | + end | ||
| 31 | + | ||
| 32 | +end |
plugins/proposals_discussion/test/unit/proposals_discussion_plugin_test.rb
0 → 100644
| @@ -0,0 +1,76 @@ | @@ -0,0 +1,76 @@ | ||
| 1 | +require File.dirname(__FILE__) + '/../test_helper' | ||
| 2 | + | ||
| 3 | +class ProposalsDiscussionPluginTest < ActiveSupport::TestCase | ||
| 4 | + | ||
| 5 | + def setup | ||
| 6 | + @plugin = ProposalsDiscussionPlugin.new | ||
| 7 | + @profile = fast_create(Community) | ||
| 8 | + @params = {} | ||
| 9 | + @plugin.stubs(:context).returns(self) | ||
| 10 | + end | ||
| 11 | + | ||
| 12 | + attr_reader :plugin, :profile, :params | ||
| 13 | + | ||
| 14 | + should 'has stylesheet' do | ||
| 15 | + assert @plugin.stylesheet? | ||
| 16 | + end | ||
| 17 | + | ||
| 18 | + should 'return Discussion as a content type' do | ||
| 19 | + @params[:parent_id] = nil | ||
| 20 | + assert_includes plugin.content_types, ProposalsDiscussionPlugin::Discussion | ||
| 21 | + end | ||
| 22 | + | ||
| 23 | + should 'do not return Discussion as a content type if it has a parent' do | ||
| 24 | + parent = fast_create(Folder, :profile_id => @profile.id) | ||
| 25 | + @params[:parent_id] = parent.id | ||
| 26 | + assert_not_includes plugin.content_types, ProposalsDiscussionPlugin::Discussion | ||
| 27 | + end | ||
| 28 | + | ||
| 29 | + should 'return Topic as a content type if parent is a Discussion' do | ||
| 30 | + parent = fast_create(ProposalsDiscussionPlugin::Discussion, :profile_id => @profile.id) | ||
| 31 | + @params[:parent_id] = parent.id | ||
| 32 | + assert_includes plugin.content_types, ProposalsDiscussionPlugin::Topic | ||
| 33 | + end | ||
| 34 | + | ||
| 35 | + should 'return Proposal as a content type if parent is a Topic' do | ||
| 36 | + parent = fast_create(ProposalsDiscussionPlugin::Topic, :profile_id => @profile.id) | ||
| 37 | + @params[:parent_id] = parent.id | ||
| 38 | + assert_includes plugin.content_types, ProposalsDiscussionPlugin::Proposal | ||
| 39 | + end | ||
| 40 | + | ||
| 41 | + should 'do not return Proposal as a content type if parent is nil' do | ||
| 42 | + @params[:parent_id] = nil | ||
| 43 | + assert_not_includes plugin.content_types, ProposalsDiscussionPlugin::Proposal | ||
| 44 | + end | ||
| 45 | + | ||
| 46 | + should 'remove new button from content page for a discussion' do | ||
| 47 | + page = fast_create(ProposalsDiscussionPlugin::Discussion, :profile_id => @profile.id) | ||
| 48 | + assert plugin.content_remove_new(page) | ||
| 49 | + end | ||
| 50 | + | ||
| 51 | + should 'remove upload button from content page for a discussion' do | ||
| 52 | + page = fast_create(ProposalsDiscussionPlugin::Discussion, :profile_id => @profile.id) | ||
| 53 | + assert plugin.content_remove_upload(page) | ||
| 54 | + end | ||
| 55 | + | ||
| 56 | + should 'remove new button from content page for a proposal' do | ||
| 57 | + page = fast_create(ProposalsDiscussionPlugin::Proposal, :profile_id => @profile.id) | ||
| 58 | + assert plugin.content_remove_new(page) | ||
| 59 | + end | ||
| 60 | + | ||
| 61 | + should 'remove upload button from content page for a proposal' do | ||
| 62 | + page = fast_create(ProposalsDiscussionPlugin::Proposal, :profile_id => @profile.id) | ||
| 63 | + assert plugin.content_remove_upload(page) | ||
| 64 | + end | ||
| 65 | + | ||
| 66 | + should 'do not remove new button from content page for others article types' do | ||
| 67 | + page = fast_create(Article, :profile_id => @profile.id) | ||
| 68 | + assert !plugin.content_remove_new(page) | ||
| 69 | + end | ||
| 70 | + | ||
| 71 | + should 'do not remove upload button from content page for others article types' do | ||
| 72 | + page = fast_create(Article, :profile_id => @profile.id) | ||
| 73 | + assert !plugin.content_remove_upload(page) | ||
| 74 | + end | ||
| 75 | + | ||
| 76 | +end |
| @@ -0,0 +1,50 @@ | @@ -0,0 +1,50 @@ | ||
| 1 | +require File.dirname(__FILE__) + '/../test_helper' | ||
| 2 | + | ||
| 3 | +class TopicTest < ActiveSupport::TestCase | ||
| 4 | + | ||
| 5 | + def setup | ||
| 6 | + @profile = fast_create(Community) | ||
| 7 | + @topic = ProposalsDiscussionPlugin::Topic.new(:name => 'test', :profile => @profile) | ||
| 8 | + end | ||
| 9 | + | ||
| 10 | + attr_reader :profile, :topic | ||
| 11 | + | ||
| 12 | + should 'return list of proposals' do | ||
| 13 | + topic.save! | ||
| 14 | + proposal1 = fast_create(ProposalsDiscussionPlugin::Proposal, :parent_id => topic.id) | ||
| 15 | + proposal2 = fast_create(ProposalsDiscussionPlugin::Proposal, :parent_id => topic.id) | ||
| 16 | + assert_equivalent [proposal1, proposal2], topic.proposals | ||
| 17 | + end | ||
| 18 | + | ||
| 19 | + should 'allow any user to create proposals in a topic' do | ||
| 20 | + assert topic.allow_create?(Person.new) | ||
| 21 | + end | ||
| 22 | + | ||
| 23 | + should 'return list of comments' do | ||
| 24 | + topic.save! | ||
| 25 | + proposal = fast_create(ProposalsDiscussionPlugin::Proposal, :parent_id => topic.id) | ||
| 26 | + comment1 = fast_create(Comment, :source_id => proposal.id) | ||
| 27 | + comment2 = fast_create(Comment, :source_id => proposal.id) | ||
| 28 | + assert_equivalent [comment1, comment2], topic.proposals_comments | ||
| 29 | + end | ||
| 30 | + | ||
| 31 | + should 'return list of authors' do | ||
| 32 | + topic.save! | ||
| 33 | + author1 = fast_create(Person) | ||
| 34 | + author2 = fast_create(Person) | ||
| 35 | + fast_create(ProposalsDiscussionPlugin::Proposal, :parent_id => topic.id, :created_by_id => author1) | ||
| 36 | + fast_create(ProposalsDiscussionPlugin::Proposal, :parent_id => topic.id, :created_by_id => author2) | ||
| 37 | + assert_equivalent [author1, author2], topic.proposals_authors | ||
| 38 | + end | ||
| 39 | + | ||
| 40 | + should 'return most active participants' do | ||
| 41 | + topic.save! | ||
| 42 | + author1 = fast_create(Person) | ||
| 43 | + author2 = fast_create(Person) | ||
| 44 | + fast_create(ProposalsDiscussionPlugin::Proposal, :parent_id => topic.id, :created_by_id => author1) | ||
| 45 | + fast_create(ProposalsDiscussionPlugin::Proposal, :parent_id => topic.id, :created_by_id => author2) | ||
| 46 | + fast_create(ProposalsDiscussionPlugin::Proposal, :parent_id => topic.id, :created_by_id => author2) | ||
| 47 | + assert_equal [author2, author1], topic.most_active_participants | ||
| 48 | + end | ||
| 49 | + | ||
| 50 | +end |
plugins/proposals_discussion/views/cms/proposals_discussion_plugin/_proposal.html.erb
0 → 100644
| @@ -0,0 +1,28 @@ | @@ -0,0 +1,28 @@ | ||
| 1 | +<%= required_fields_message %> | ||
| 2 | + | ||
| 3 | +<%= render :file => 'shared/tiny_mce' %> | ||
| 4 | + | ||
| 5 | +<% title_limit = 70 %> | ||
| 6 | +<% abstract_limit = 140 %> | ||
| 7 | + | ||
| 8 | +<div class="proposals-discussion-plugin"> | ||
| 9 | + <div class="title"> | ||
| 10 | + <%= required labelled_form_field _('Title'), limited_text_area(:article, :name, title_limit, 'title_textarea', :rows => 1) %> | ||
| 11 | + </div> | ||
| 12 | + | ||
| 13 | + <div class="abstract"> | ||
| 14 | + <%= required labelled_form_field _('Abstract'), limited_text_area(:article, :abstract, abstract_limit, 'abstract_textarea') %> | ||
| 15 | + </div> | ||
| 16 | + | ||
| 17 | + <div class="body"> | ||
| 18 | + <% editor_type = 'mceEditor' %> | ||
| 19 | + <%= labelled_form_field(_('Text'), text_area(:article, :body, :class => editor_type)) %> | ||
| 20 | + </div> | ||
| 21 | +</div> | ||
| 22 | + | ||
| 23 | +<script> | ||
| 24 | +jQuery( document ).ready(function( $ ) { | ||
| 25 | + limited_text_area('title_textarea', <%= title_limit %>); | ||
| 26 | + limited_text_area('abstract_textarea', <%= abstract_limit %>); | ||
| 27 | +}); | ||
| 28 | +</script> |
plugins/proposals_discussion/views/cms/proposals_discussion_plugin/_topic.html.erb
0 → 100644
| @@ -0,0 +1,13 @@ | @@ -0,0 +1,13 @@ | ||
| 1 | +<%= stylesheet_link_tag 'spectrum.css' %> | ||
| 2 | +<%= javascript_include_tag "spectrum.js" %> | ||
| 3 | +<%= javascript_include_tag "colorpicker-noosfero.js" %> | ||
| 4 | + | ||
| 5 | +<%= required_fields_message %> | ||
| 6 | + | ||
| 7 | +<%= required f.text_field('name', :size => '64', :maxlength => 150) %> | ||
| 8 | +<%= render :partial => 'general_fields' %> | ||
| 9 | + | ||
| 10 | +<%= labelled_form_field(_('Description:'), text_area(:article, :body, :rows => 3, :cols => 64)) %> | ||
| 11 | + | ||
| 12 | +<%= labelled_colorpicker_field(_('Color:'), :article, :color) %> | ||
| 13 | +<span id="color_preview" class = "color_marker" style="background-color: <%= @article.color %>" ></span> |
plugins/proposals_discussion/views/content_viewer/_proposal_card.html.erb
0 → 100644
| @@ -0,0 +1,20 @@ | @@ -0,0 +1,20 @@ | ||
| 1 | +<div class="proposal"> | ||
| 2 | + <div class="topic-color" style="background-color: <%= proposal_card.topic.color %>;"></div> | ||
| 3 | + <div class="content"> | ||
| 4 | + <div class="title"> | ||
| 5 | + <%= link_to proposal_card.name, proposal_card.view_url %> | ||
| 6 | + </div> | ||
| 7 | + <div class="social"> | ||
| 8 | + <%= render :partial => 'content_viewer/social', :locals => {:proposal => proposal_card} %> | ||
| 9 | + </div> | ||
| 10 | + <div class="abstract"> | ||
| 11 | + <%= link_to strip_tags(proposal_card.abstract), proposal_card.view_url %> | ||
| 12 | + </div> | ||
| 13 | + </div> | ||
| 14 | + <div class="score"> | ||
| 15 | + <%= proposal_card.comments_count %> | ||
| 16 | + </div> | ||
| 17 | + <div class="topic"> | ||
| 18 | + <%= link_to proposal_card.topic.title, proposal_card.topic.view_url %> | ||
| 19 | + </div> | ||
| 20 | +</div> |
plugins/proposals_discussion/views/content_viewer/_proposals_list.html.erb
0 → 100644
| @@ -0,0 +1,30 @@ | @@ -0,0 +1,30 @@ | ||
| 1 | +<script src="/javascripts/plugins/proposals_discussion/jquery.jscroll.min.js" type="text/javascript"></script> | ||
| 2 | +<%= javascript_include_tag 'plugins/proposals_discussion/proposals_list.js' %> | ||
| 3 | + | ||
| 4 | +<% extend ProposalsDiscussionPlugin::ProposalsListHelper %> | ||
| 5 | + | ||
| 6 | +<% private_proposals = user ? @page.proposals.private(user) : [] %> | ||
| 7 | +<% unless private_proposals.empty? %> | ||
| 8 | +<div class="private-proposals"> | ||
| 9 | + <h5><%= _('My private proposals') %></h5> | ||
| 10 | + <%= render :partial => 'content_viewer/proposal_card', :collection => private_proposals %> | ||
| 11 | +</div> | ||
| 12 | +<% end %> | ||
| 13 | + | ||
| 14 | +<% order ||= 'random' %> | ||
| 15 | +<div class="proposals_list"> | ||
| 16 | + <h5><%= _('Proposals') %></h5> | ||
| 17 | + <div class="filters"> | ||
| 18 | + <% [[_('Random'), :random], [_('Aplhabetical'), :alphabetical]].each_with_index do |order, i| %> | ||
| 19 | + <%= link_to order.first, url_for({:controller => 'proposals_discussion_plugin_public', :action => 'load_proposals', :holder_id => holder.id, :profile => profile.identifier, :order => order.second}), :remote => true, :class => "order #{order.second} #{i==0 ? 'selected':''}" %> | ||
| 20 | + <% end %> | ||
| 21 | + </div> | ||
| 22 | + <div class="clear"></div> | ||
| 23 | + | ||
| 24 | + <div class="proposals"> | ||
| 25 | + <div class="more"> | ||
| 26 | + <img src="/images/loading.gif" alt="Loading" /><%= _("Loading...") %> | ||
| 27 | + <%= more_proposals('', holder, order) %> | ||
| 28 | + </div> | ||
| 29 | + </div> | ||
| 30 | +</div> |
plugins/proposals_discussion/views/content_viewer/_proposals_list_content.html.erb
0 → 100644
plugins/proposals_discussion/views/content_viewer/_social.html.erb
0 → 100644
plugins/proposals_discussion/views/content_viewer/discussion.html.erb
0 → 100644
| @@ -0,0 +1,29 @@ | @@ -0,0 +1,29 @@ | ||
| 1 | +<div class="description"> | ||
| 2 | + <%= @page.body %> | ||
| 3 | +</div> | ||
| 4 | + | ||
| 5 | +<% if @page.allow_create?(user) %> | ||
| 6 | +<div class="actions"> | ||
| 7 | + <%= link_to url_for({:controller => 'cms', :action => 'new', :type => "ProposalsDiscussionPlugin::Topic", :parent_id => @page.id}), :class => 'button with-text icon-add' do %> | ||
| 8 | + <strong><%= _("New Topic") %></strong> | ||
| 9 | + <% end %> | ||
| 10 | +</div> | ||
| 11 | +<% end %> | ||
| 12 | + | ||
| 13 | +<div class="topics"> | ||
| 14 | + <h3><%= _('Discussion Topics') %></h3> | ||
| 15 | + <% @page.topics.includes(:profile).each do |topic| %> | ||
| 16 | + <div class="topic"> | ||
| 17 | + <div class="topic-color" style="background-color: <%= topic.color %>;"></div> | ||
| 18 | + <%= link_to topic.title, topic.view_url %> | ||
| 19 | + </div> | ||
| 20 | + <% end %> | ||
| 21 | +</div> | ||
| 22 | + | ||
| 23 | +<div class="actions"> | ||
| 24 | + <%= link_to url_for({:controller => 'proposals_discussion_plugin_myprofile', :action => 'select_topic', :parent_id => @page.id}), :class => 'button with-text icon-add' do %> | ||
| 25 | + <strong><%= _("Send my proposal") %></strong> | ||
| 26 | + <% end %> | ||
| 27 | +</div> | ||
| 28 | + | ||
| 29 | +<%= render :partial => 'content_viewer/proposals_list', :locals => {:holder => @page} %> |
plugins/proposals_discussion/views/content_viewer/proposal.html.erb
0 → 100644
| @@ -0,0 +1,27 @@ | @@ -0,0 +1,27 @@ | ||
| 1 | +<div class="topic"> | ||
| 2 | + <h5><%= _('Discussion Topic') %></h5> | ||
| 3 | + <div class="content"><%= @page.topic.title %></div> | ||
| 4 | +</div> | ||
| 5 | + | ||
| 6 | +<div class="title"> | ||
| 7 | + <h5><%= _('Title') %></h4> | ||
| 8 | + <div class="content"><%= @page.title %></div> | ||
| 9 | +</div> | ||
| 10 | + | ||
| 11 | +<div class="abstract"> | ||
| 12 | + <h5><%= _('Abstract') %></h4> | ||
| 13 | + <div class="content"><%= @page.abstract %></div> | ||
| 14 | +</div> | ||
| 15 | + | ||
| 16 | +<div class="body"> | ||
| 17 | + <h5><%= _('Body') %></h4> | ||
| 18 | + <div class="content"><%= @page.body %></div> | ||
| 19 | +</div> | ||
| 20 | + | ||
| 21 | +<% if @page.allow_edit?(user) && !@page.published %> | ||
| 22 | +<div class="actions"> | ||
| 23 | + <%= link_to url_for({:controller => 'proposals_discussion_plugin_myprofile', :action => 'publish_proposal', :proposal_id => @page.id}), :class => 'button with-text icon-suggest' do %> | ||
| 24 | + <strong><%= _("Publish") %></strong> | ||
| 25 | + <% end %> | ||
| 26 | +</div> | ||
| 27 | +<% end %> |
plugins/proposals_discussion/views/content_viewer/topic.html.erb
0 → 100644
| @@ -0,0 +1,33 @@ | @@ -0,0 +1,33 @@ | ||
| 1 | +<div class="description"> | ||
| 2 | + <%= @page.body %> | ||
| 3 | +</div> | ||
| 4 | +<h4><%= @page.discussion.title %></h4> | ||
| 5 | + | ||
| 6 | +<div class="proposals-count"> | ||
| 7 | + <span class="label"><%= _('Number of Proposals: ') %></span> | ||
| 8 | + <span class="content"><%= @page.proposals.count %></span> | ||
| 9 | +</div> | ||
| 10 | +<div class="participants-count"> | ||
| 11 | + <span class="label"><%= _('Number of Participants: ') %></span> | ||
| 12 | + <span class="content"><%= @page.proposals_authors.count %></span> | ||
| 13 | +</div> | ||
| 14 | +<div class="comments-count"> | ||
| 15 | + <span class="label"><%= _('Number of Comments: ') %></span> | ||
| 16 | + <span class="content"><%= @page.proposals_comments.count %></span> | ||
| 17 | +</div> | ||
| 18 | +<div class="active-participants"> | ||
| 19 | + <span class="label"><%= _('Most active: ') %></span> | ||
| 20 | + <span class="content"> | ||
| 21 | + <% @page.most_active_participants.each do |author| %> | ||
| 22 | + <%= link_to profile_image(author, :icon), author.url, :title => author.name %> | ||
| 23 | + <% end %> | ||
| 24 | + </span> | ||
| 25 | +</div> | ||
| 26 | + | ||
| 27 | +<div class="actions"> | ||
| 28 | + <%= link_to url_for({:controller => 'cms', :action => 'new', :type => "ProposalsDiscussionPlugin::Proposal", :parent_id => @page.id}), :class => 'button with-text icon-add' do %> | ||
| 29 | + <strong><%= _("Send my proposal") %></strong> | ||
| 30 | + <% end %> | ||
| 31 | +</div> | ||
| 32 | + | ||
| 33 | +<%= render :partial => 'content_viewer/proposals_list', :locals => {:holder => @page} %> |
plugins/proposals_discussion/views/select_topic.html.erb
0 → 100644
| @@ -0,0 +1,37 @@ | @@ -0,0 +1,37 @@ | ||
| 1 | +<script> | ||
| 2 | + jQuery(document).ready(function($){ | ||
| 3 | + $("#topics").accordion(); | ||
| 4 | + $('#topics input[type=radio],label').on('click',function(e){e.stopPropagation();}); | ||
| 5 | + $('#topics h4').click(function(e){ | ||
| 6 | + e.stopPropagation(); | ||
| 7 | + $(this).find('input:radio').prop('checked', true); | ||
| 8 | + }); | ||
| 9 | + }); | ||
| 10 | +</script> | ||
| 11 | + | ||
| 12 | +<div class="proposals-discussion-plugin select-topic"> | ||
| 13 | + <h1><%= @discussion.title %></h1> | ||
| 14 | + <%= form_for :discussion, :url => {:controller => 'proposals_discussion_plugin_myprofile', :action => 'new_proposal'} do %> | ||
| 15 | + | ||
| 16 | + <h3><%= _('Select topic') %></h3> | ||
| 17 | + <div id="topics"> | ||
| 18 | + | ||
| 19 | + <% @discussion.children.each do |topic| %> | ||
| 20 | + <h4> | ||
| 21 | + <%= radio_button_tag('discussion[topic]', topic.id) %> | ||
| 22 | + <%= topic.title %> | ||
| 23 | + </h4> | ||
| 24 | + <div class="content"> | ||
| 25 | + <%= topic.body %> | ||
| 26 | + </div> | ||
| 27 | + <% end %> | ||
| 28 | + | ||
| 29 | + <div class="clear"></div> | ||
| 30 | + </div> | ||
| 31 | + | ||
| 32 | + <div class="actions"> | ||
| 33 | + <%= submit_button(:next, _('Next')) %> | ||
| 34 | + <%= button :cancel, _('Cancel'), @discussion.view_url %> | ||
| 35 | + </div> | ||
| 36 | + <% end %> | ||
| 37 | +</div> |
test/functional/features_controller_test.rb
| @@ -146,7 +146,7 @@ class FeaturesControllerTest < ActionController::TestCase | @@ -146,7 +146,7 @@ class FeaturesControllerTest < ActionController::TestCase | ||
| 146 | assert_equal true, e.custom_community_fields['contact_person']['required'] | 146 | assert_equal true, e.custom_community_fields['contact_person']['required'] |
| 147 | end | 147 | end |
| 148 | 148 | ||
| 149 | - should 'search members' do | 149 | + should 'search members by name' do |
| 150 | uses_host 'anhetegua.net' | 150 | uses_host 'anhetegua.net' |
| 151 | person = fast_create(Person, :environment_id => Environment.find(2).id) | 151 | person = fast_create(Person, :environment_id => Environment.find(2).id) |
| 152 | xhr :get, :search_members, :q => person.name[0..2] | 152 | xhr :get, :search_members, :q => person.name[0..2] |
| @@ -154,4 +154,12 @@ class FeaturesControllerTest < ActionController::TestCase | @@ -154,4 +154,12 @@ class FeaturesControllerTest < ActionController::TestCase | ||
| 154 | assert_includes json_response, {"id"=>person.id, "name"=>person.name} | 154 | assert_includes json_response, {"id"=>person.id, "name"=>person.name} |
| 155 | end | 155 | end |
| 156 | 156 | ||
| 157 | + should 'search members by identifier' do | ||
| 158 | + uses_host 'anhetegua.net' | ||
| 159 | + person = fast_create(Person, :name => 'Some Name', :identifier => 'person-identifier', :environment_id => Environment.find(2).id) | ||
| 160 | + xhr :get, :search_members, :q => person.identifier | ||
| 161 | + json_response = ActiveSupport::JSON.decode(@response.body) | ||
| 162 | + assert_includes json_response, {"id"=>person.id, "name"=>person.name} | ||
| 163 | + end | ||
| 164 | + | ||
| 157 | end | 165 | end |
test/unit/categories_helper_test.rb
| @@ -31,4 +31,10 @@ class CategoriesHelperTest < ActiveSupport::TestCase | @@ -31,4 +31,10 @@ class CategoriesHelperTest < ActiveSupport::TestCase | ||
| 31 | assert_equal '', category_color_style(category2) | 31 | assert_equal '', category_color_style(category2) |
| 32 | end | 32 | end |
| 33 | 33 | ||
| 34 | + should 'not return category parent color if category is nil' do | ||
| 35 | + assert_nothing_raised do | ||
| 36 | + assert_equal '', category_color_style(nil) | ||
| 37 | + end | ||
| 38 | + end | ||
| 39 | + | ||
| 34 | end | 40 | end |