Commit b05904aa8a770154a028a642cd9051babe36d100
Exists in
master
and in
29 other branches
Merge branch 'master' into rails3
Conflicts: app/helpers/boxes_helper.rb app/models/comment.rb app/views/comment/_comment.rhtml app/views/comment/_comment_form.rhtml app/views/content_viewer/_comment.html.erb app/views/content_viewer/_comment.rhtml app/views/content_viewer/_comment_form.html.erb app/views/content_viewer/_comment_form.rhtml app/views/profile/send_mail.html.erb config/routes.rb debian/control
Showing
147 changed files
with
4492 additions
and
1529 deletions
Show diff stats
Too many changes.
To preserve performance only 100 of 147 files displayed.
AUTHORS
... | ... | @@ -110,6 +110,7 @@ Diego Martinez <diegoamc90@gmail.com> |
110 | 110 | Diego Martinez <diego@diego-K55A.(none)> |
111 | 111 | Diego + Renan <renanteruoc@gmail.com> |
112 | 112 | Fernanda Lopes <nanda.listas+psl@gmail.com> |
113 | +Francisco Marcelo de Araujo Lima Junior <79350259591@serpro-1457614.(none)> | |
113 | 114 | Grazieno Pellegrino <grazieno@gmail.com> |
114 | 115 | Isaac Canan <isaac@intelletto.com.br> |
115 | 116 | Italo Valcy <italo@dcc.ufba.br> |
... | ... | @@ -153,6 +154,7 @@ Leandro Nunes dos Santos <leandronunes@gmail.com> |
153 | 154 | Leandro Nunes dos Santos <leandro.santos@serpro.gov.br> |
154 | 155 | LinguÁgil 2010 <linguagil.bahia@gmail.com> |
155 | 156 | Lucas Melo <lucas@colivre.coop.br> |
157 | +Lucas Melo <lucaspradomelo@gmail.com> | |
156 | 158 | Luis David Aguilar Carlos <ludwig9003@gmail.com> |
157 | 159 | Martín Olivera <molivera@solar.org.ar> |
158 | 160 | Moises Machado <moises@colivre.coop.br> | ... | ... |
INSTALL.chat
... | ... | @@ -6,15 +6,13 @@ To configure XMPP/BOSH in Noosfero you need: |
6 | 6 | * SystemTimer - http://ph7spot.com/musings/system-timer |
7 | 7 | * Pidgin data files - http://www.pidgin.im/ |
8 | 8 | |
9 | -If you use Debian Lenny: | |
9 | +If you use Debian Wheezy: | |
10 | 10 | |
11 | -# apt-get install librestclient-ruby (from backports) | |
12 | -# apt-get install pidgin-data | |
13 | -# apt-get install ruby1.8-dev | |
11 | +# apt-get install librestclient-ruby pidgin-data ruby1.8-dev | |
14 | 12 | # gem install SystemTimer |
15 | 13 | |
16 | -Take a look at util/chat directory to see samples of config file to configure a | |
17 | -XMPP/BOSH server with ejabberd, postgresql and apache2. | |
14 | +The samples of config file to configure a XMPP/BOSH server with | |
15 | +ejabberd, postgresql and apache2 can be found at util/chat directory. | |
18 | 16 | |
19 | 17 | == XMPP/Chat Server Setup |
20 | 18 | |
... | ... | @@ -22,8 +20,7 @@ This is a step-by-step guide to get a XMPP service working, in a Debian system. |
22 | 20 | |
23 | 21 | 1. Install the required packages |
24 | 22 | |
25 | -# apt-get -t lenny-backports install ejabberd | |
26 | -# apt-get install odbc-postgresql | |
23 | +# apt-get install ejabberd odbc-postgresql | |
27 | 24 | |
28 | 25 | 2. Ejabberd configuration |
29 | 26 | |
... | ... | @@ -108,7 +105,7 @@ Unused modules can be disabled, for example: |
108 | 105 | * web_admin |
109 | 106 | * mod_pubsub |
110 | 107 | * mod_irc |
111 | - * mod_offine | |
108 | + * mod_offline | |
112 | 109 | * mod_admin_extra |
113 | 110 | * mod_register |
114 | 111 | |
... | ... | @@ -132,7 +129,7 @@ This will create a new schema inside the noosfero database, called 'ejabberd'. |
132 | 129 | |
133 | 130 | Note 'noosfero' user should have permission to create Postgresql schemas. Also, |
134 | 131 | there should be at least one domain with 'is_default = true' in 'domains' |
135 | -table, otherwise people couldn't see your friends online. | |
132 | +table, otherwise people won't be able to see their friends online. | |
136 | 133 | |
137 | 134 | |
138 | 135 | 4. ODBC configuration |
... | ... | @@ -168,9 +165,12 @@ Debug = 0 |
168 | 165 | CommLog = 1 |
169 | 166 | UsageCount = 3 |
170 | 167 | |
171 | - * testing all: | |
168 | + 4.1 testing all: | |
172 | 169 | |
173 | -# isql 'PostgreSQLEjabberdNoosfero' DBUSER | |
170 | +# isql 'PostgreSQLEjabberdNoosfero' | |
171 | + | |
172 | +If the configuration was done right, the message "Connected!" | |
173 | +will be displayed. | |
174 | 174 | |
175 | 175 | |
176 | 176 | 5. Enabling kernel polling and SMP in /etc/default/ejabberd | ... | ... |
RELEASING
... | ... | @@ -19,11 +19,12 @@ To prepare a release of noosfero, you must follow the steps below: |
19 | 19 | |
20 | 20 | * Finish all requirements and bugs assigned to the to-be-released version |
21 | 21 | * Make sure all tests pass |
22 | -* Change the version in lib/noosfero.rb and debian/changelog to the | |
23 | - to-be-released version (e.g. 0.2.0, 0.3.1) | |
24 | 22 | * Write release notes at the version's wiki topic |
25 | -* Generate packages with <tt>rake noosfero:release</tt>. Your tarball and deb | |
26 | - pkg will be under the pkg/ directory. This task will create a git tag too. | |
23 | +* Generate packages with <tt>rake noosfero:release[(stable|test)]</tt>. This task will: | |
24 | + * Update the version in lib/noosfero.rb and debian/changelog. | |
25 | + * Create the tarbal and the deb pkg under pkg/ directory. | |
26 | + * Create a git tag and push it. | |
27 | + * Upload the pkg to the configured repository (if configured) on ~/.dput.cf. | |
27 | 28 | * Test that the tarball and deb package are ok |
28 | 29 | * Go to the version's wiki topic and edit it to reflect the new reality |
29 | 30 | * Edit the topic WebPreferences and update DEBIAN_REPOSITORY_TOPICS setting |
... | ... | @@ -31,7 +32,6 @@ To prepare a release of noosfero, you must follow the steps below: |
31 | 32 | sha1 of the package (with sha1sum and paste the SHA1 hash as comment in the |
32 | 33 | attachment form) |
33 | 34 | * Download the attached and verify the MD5 hash |
34 | -* Push the new version tag (with git push --tags) | |
35 | 35 | * Update an eventual demonstration version that you run. |
36 | 36 | * Write an announcement e-mail to the relevant mailing lists pointing to the |
37 | 37 | release notes, and maybe to the demonstration version. | ... | ... |
... | ... | @@ -0,0 +1,82 @@ |
1 | +class TrustedSitesController < AdminController | |
2 | + protect 'manage_environment_trusted_sites', :environment | |
3 | + | |
4 | + def index | |
5 | + @sites = environment.trusted_sites_for_iframe | |
6 | + end | |
7 | + | |
8 | + def new | |
9 | + @site = "" | |
10 | + end | |
11 | + | |
12 | + def create | |
13 | + if add_trusted_site(params[:site]) | |
14 | + session[:notice] = _('New trusted site added.') | |
15 | + redirect_to :action => 'index' | |
16 | + else | |
17 | + session[:notice] = _('Failed to add trusted site.') | |
18 | + render :action => 'new' | |
19 | + end | |
20 | + end | |
21 | + | |
22 | + def edit | |
23 | + if is_trusted_site? params[:site] | |
24 | + @site = params[:site] | |
25 | + else | |
26 | + session[:notice] = _('Trusted site was not found') | |
27 | + redirect_to :action => 'index' | |
28 | + end | |
29 | + end | |
30 | + | |
31 | + def update | |
32 | + site = params[:site] | |
33 | + orig_site = params[:orig_site] | |
34 | + if rename_trusted_site(orig_site, site) | |
35 | + redirect_to :action => 'edit', :site => @site | |
36 | + else | |
37 | + session[:notice] = _('Failed to edit trusted site.') | |
38 | + render :action => 'edit' | |
39 | + end | |
40 | + end | |
41 | + | |
42 | + def destroy | |
43 | + if delete_trusted_site(params[:site]) | |
44 | + session[:notice] = _('Trusted site removed') | |
45 | + else | |
46 | + session[:notice] = _('Trusted site could not be removed') | |
47 | + end | |
48 | + redirect_to :action => 'index' | |
49 | + end | |
50 | + | |
51 | + protected | |
52 | + def add_trusted_site (site) | |
53 | + trusted_sites = environment.trusted_sites_for_iframe | |
54 | + trusted_sites << site | |
55 | + environment.trusted_sites_for_iframe = trusted_sites | |
56 | + environment.save | |
57 | + end | |
58 | + | |
59 | + def rename_trusted_site(orig_site, site) | |
60 | + trusted_sites = environment.trusted_sites_for_iframe | |
61 | + i = trusted_sites.index orig_site | |
62 | + if i.nil? | |
63 | + return false | |
64 | + else | |
65 | + trusted_sites[i] = site | |
66 | + environment.trusted_sites_for_iframe = trusted_sites | |
67 | + environment.save | |
68 | + end | |
69 | + end | |
70 | + | |
71 | + | |
72 | + def delete_trusted_site (site) | |
73 | + trusted_sites = environment.trusted_sites_for_iframe | |
74 | + trusted_sites.delete site | |
75 | + environment.trusted_sites_for_iframe = trusted_sites | |
76 | + environment.save | |
77 | + end | |
78 | + | |
79 | + def is_trusted_site? (site) | |
80 | + environment.trusted_sites_for_iframe.include? site | |
81 | + end | |
82 | +end | ... | ... |
app/controllers/application_controller.rb
... | ... | @@ -6,6 +6,20 @@ class ApplicationController < ActionController::Base |
6 | 6 | before_filter :setup_multitenancy |
7 | 7 | before_filter :detect_stuff_by_domain |
8 | 8 | before_filter :init_noosfero_plugins |
9 | + before_filter :allow_cross_domain_access | |
10 | + | |
11 | + def allow_cross_domain_access | |
12 | + origin = request.headers['Origin'] | |
13 | + return if origin.blank? | |
14 | + if environment.access_control_allow_origin.include? origin | |
15 | + response.headers["Access-Control-Allow-Origin"] = origin | |
16 | + unless environment.access_control_allow_methods.blank? | |
17 | + response.headers["Access-Control-Allow-Methods"] = environment.access_control_allow_methods | |
18 | + end | |
19 | + elsif environment.restrict_to_access_control_origins | |
20 | + render_access_denied _('Origin not in allowed.') | |
21 | + end | |
22 | + end | |
9 | 23 | |
10 | 24 | include ApplicationHelper |
11 | 25 | layout :get_layout |
... | ... | @@ -82,11 +96,10 @@ class ApplicationController < ActionController::Base |
82 | 96 | false |
83 | 97 | end |
84 | 98 | |
85 | - | |
86 | 99 | def user |
87 | 100 | current_user.person if logged_in? |
88 | 101 | end |
89 | - | |
102 | + | |
90 | 103 | alias :current_person :user |
91 | 104 | |
92 | 105 | # TODO: move this logic somewhere else (Domain class?) |
... | ... | @@ -104,6 +117,12 @@ class ApplicationController < ActionController::Base |
104 | 117 | else |
105 | 118 | @environment = @domain.environment |
106 | 119 | @profile = @domain.profile |
120 | + | |
121 | + # Check if the requested profile belongs to another domain | |
122 | + if @profile && !params[:profile].blank? && params[:profile] != @profile.identifier | |
123 | + @profile = @environment.profiles.find_by_identifier params[:profile] | |
124 | + redirect_to params.merge(:host => @profile.default_hostname) | |
125 | + end | |
107 | 126 | end |
108 | 127 | end |
109 | 128 | |
... | ... | @@ -156,7 +175,7 @@ class ApplicationController < ActionController::Base |
156 | 175 | def find_by_contents(asset, scope, query, paginate_options={:page => 1}, options={}) |
157 | 176 | scope = scope.send(options[:filter]) if options[:filter] |
158 | 177 | |
159 | - @plugins.first(:find_by_contents, asset, scope, query, paginate_options, options) || | |
178 | + @plugins.dispatch_first(:find_by_contents, asset, scope, query, paginate_options, options) || | |
160 | 179 | fallback_find_by_contents(asset, scope, query, paginate_options, options) |
161 | 180 | end |
162 | 181 | ... | ... |
app/controllers/my_profile/cms_controller.rb
... | ... | @@ -182,7 +182,14 @@ class CmsController < MyProfileController |
182 | 182 | if request.post? |
183 | 183 | @article.destroy |
184 | 184 | session[:notice] = _("\"#{@article.name}\" was removed.") |
185 | - redirect_to :action => (@article.parent ? 'view' : 'index'), :id => @article.parent | |
185 | + referer = ActionController::Routing::Routes.recognize_path URI.parse(request.referer).path rescue nil | |
186 | + if referer and referer[:controller] == 'cms' | |
187 | + redirect_to referer | |
188 | + elsif @article.parent | |
189 | + redirect_to @article.parent.url | |
190 | + else | |
191 | + redirect_to profile.url | |
192 | + end | |
186 | 193 | end |
187 | 194 | end |
188 | 195 | ... | ... |
app/controllers/public/account_controller.rb
... | ... | @@ -26,7 +26,8 @@ class AccountController < ApplicationController |
26 | 26 | |
27 | 27 | # action to perform login to the application |
28 | 28 | def login |
29 | - store_location(request.referer) unless session[:return_to] | |
29 | + store_location(request.referer) unless params[:return_to] or session[:return_to] | |
30 | + | |
30 | 31 | return unless request.post? |
31 | 32 | |
32 | 33 | self.current_user = plugins_alternative_authentication |
... | ... | @@ -125,7 +126,7 @@ class AccountController < ApplicationController |
125 | 126 | def change_password |
126 | 127 | if request.post? |
127 | 128 | @user = current_user |
128 | - begin | |
129 | + begin | |
129 | 130 | @user.change_password!(params[:current_password], |
130 | 131 | params[:new_password], |
131 | 132 | params[:new_password_confirmation]) |
... | ... | @@ -218,7 +219,7 @@ class AccountController < ApplicationController |
218 | 219 | @question = @enterprise.question |
219 | 220 | return unless check_answer |
220 | 221 | return unless check_acceptance_of_terms |
221 | - | |
222 | + | |
222 | 223 | activation = load_enterprise_activation |
223 | 224 | if activation && user |
224 | 225 | activation.requestor = user |
... | ... | @@ -355,7 +356,9 @@ class AccountController < ApplicationController |
355 | 356 | end |
356 | 357 | |
357 | 358 | def go_to_initial_page |
358 | - if environment.enabled?('allow_change_of_redirection_after_login') | |
359 | + if params[:return_to] | |
360 | + redirect_to params[:return_to] | |
361 | + elsif environment.enabled?('allow_change_of_redirection_after_login') | |
359 | 362 | case user.preferred_login_redirection |
360 | 363 | when 'keep_on_same_page' |
361 | 364 | redirect_back_or_default(user.admin_url) | ... | ... |
app/controllers/public/catalog_controller.rb
... | ... | @@ -5,15 +5,14 @@ class CatalogController < PublicController |
5 | 5 | before_filter :check_enterprise_and_environment |
6 | 6 | |
7 | 7 | def index |
8 | - @category = params[:level] ? ProductCategory.find(params[:level]) : nil | |
9 | - @products = @profile.products.from_category(@category).paginate(:order => 'available desc, highlighted desc, name asc', :per_page => 9, :page => params[:page]) | |
10 | - @categories = ProductCategory.on_level(params[:level]).order(:name) | |
8 | + extend CatalogHelper | |
9 | + catalog_load_index | |
11 | 10 | end |
12 | 11 | |
13 | 12 | protected |
14 | 13 | |
15 | 14 | def check_enterprise_and_environment |
16 | - unless @profile.kind_of?(Enterprise) && !@profile.environment.enabled?('disable_products_for_enterprises') | |
15 | + unless profile.kind_of?(Enterprise) && !profile.environment.enabled?('disable_products_for_enterprises') | |
17 | 16 | redirect_to :controller => 'profile', :profile => profile.identifier, :action => 'index' |
18 | 17 | end |
19 | 18 | end | ... | ... |
... | ... | @@ -0,0 +1,164 @@ |
1 | +class CommentController < ApplicationController | |
2 | + | |
3 | + needs_profile | |
4 | + | |
5 | + before_filter :can_update?, :only => [:edit, :update] | |
6 | + | |
7 | + def create | |
8 | + begin | |
9 | + @page = profile.articles.find(params[:id]) | |
10 | + rescue | |
11 | + @page = nil | |
12 | + end | |
13 | + | |
14 | + # page not found, give error | |
15 | + if @page.nil? | |
16 | + respond_to do |format| | |
17 | + format.js do | |
18 | + render :json => { :msg => _('Page not found.')} | |
19 | + end | |
20 | + end | |
21 | + return | |
22 | + end | |
23 | + | |
24 | + unless @page.accept_comments? | |
25 | + respond_to do |format| | |
26 | + format.js do | |
27 | + render :json => { :msg => _('Comment not allowed in this article')} | |
28 | + end | |
29 | + end | |
30 | + return | |
31 | + end | |
32 | + | |
33 | + @comment = Comment.new(params[:comment]) | |
34 | + @comment.author = user if logged_in? | |
35 | + @comment.article = @page | |
36 | + @comment.ip_address = request.remote_ip | |
37 | + @comment.user_agent = request.user_agent | |
38 | + @comment.referrer = request.referrer | |
39 | + @plugins.dispatch(:filter_comment, @comment) | |
40 | + | |
41 | + if @comment.rejected? | |
42 | + respond_to do |format| | |
43 | + format.js do | |
44 | + render :json => { :msg => _('Comment was rejected')} | |
45 | + end | |
46 | + end | |
47 | + return | |
48 | + end | |
49 | + | |
50 | + if !@comment.valid? || (not pass_without_comment_captcha? and not verify_recaptcha(:model => @comment, :message => _('Please type the words correctly'))) | |
51 | + respond_to do |format| | |
52 | + format.js do | |
53 | + render :json => { | |
54 | + :render_target => 'form', | |
55 | + :html => render_to_string(:partial => 'comment_form', :object => @comment, :locals => {:comment => @comment, :display_link => true, :show_form => true}) | |
56 | + } | |
57 | + end | |
58 | + end | |
59 | + return | |
60 | + end | |
61 | + | |
62 | + if @comment.need_moderation? | |
63 | + @comment.created_at = Time.now | |
64 | + ApproveComment.create!(:requestor => @comment.author, :target => profile, :comment_attributes => @comment.attributes.to_json) | |
65 | + | |
66 | + respond_to do |format| | |
67 | + format.js do | |
68 | + render :json => { :render_target => nil, :msg => _('Your comment is waiting for approval.') } | |
69 | + end | |
70 | + end | |
71 | + return | |
72 | + end | |
73 | + | |
74 | + @comment.save | |
75 | + | |
76 | + respond_to do |format| | |
77 | + format.js do | |
78 | + comment_to_render = @comment.comment_root | |
79 | + render :json => { | |
80 | + :render_target => comment_to_render.anchor, | |
81 | + :html => render_to_string(:partial => 'comment', :locals => {:comment => comment_to_render, :display_link => true}), | |
82 | + :msg => _('Comment successfully created.') | |
83 | + } | |
84 | + end | |
85 | + end | |
86 | + end | |
87 | + | |
88 | + def destroy | |
89 | + comment = profile.comments_received.find(params[:id]) | |
90 | + | |
91 | + if comment && comment.can_be_destroyed_by?(user) && comment.destroy | |
92 | + render :text => {'ok' => true}.to_json, :content_type => 'application/json' | |
93 | + else | |
94 | + session[:notice] = _("The comment was not removed.") | |
95 | + render :text => {'ok' => false}.to_json, :content_type => 'application/json' | |
96 | + end | |
97 | + end | |
98 | + | |
99 | + def mark_as_spam | |
100 | + comment = profile.comments_received.find(params[:id]) | |
101 | + if comment.can_be_marked_as_spam_by?(user) | |
102 | + comment.spam! | |
103 | + render :text => {'ok' => true}.to_json, :content_type => 'application/json' | |
104 | + else | |
105 | + session[:notice] = _("You couldn't mark this comment as spam.") | |
106 | + render :text => {'ok' => false}.to_json, :content_type => 'application/json' | |
107 | + end | |
108 | + end | |
109 | + | |
110 | + def edit | |
111 | + render :partial => "comment_form", :locals => {:comment => @comment, :display_link => params[:reply_of_id].present?, :edition_mode => true, :show_form => true} | |
112 | + end | |
113 | + | |
114 | + def update | |
115 | + if @comment.update_attributes(params[:comment]) | |
116 | + respond_to do |format| | |
117 | + format.js do | |
118 | + comment_to_render = @comment.comment_root | |
119 | + render :json => { | |
120 | + :ok => true, | |
121 | + :render_target => comment_to_render.anchor, | |
122 | + :html => render_to_string(:partial => 'comment', :locals => {:comment => comment_to_render}) | |
123 | + } | |
124 | + end | |
125 | + end | |
126 | + else | |
127 | + respond_to do |format| | |
128 | + format.js do | |
129 | + render :json => { | |
130 | + :ok => false, | |
131 | + :render_target => 'form', | |
132 | + :html => render_to_string(:partial => 'comment_form', :object => @comment, :locals => {:comment => @comment, :display_link => false, :edition_mode => true, :show_form => true}) | |
133 | + } | |
134 | + end | |
135 | + end | |
136 | + end | |
137 | + end | |
138 | + | |
139 | + def check_actions | |
140 | + comment = profile.comments_received.find(params[:id]) | |
141 | + ids = @plugins.dispatch(:check_comment_actions, comment).collect do |action| | |
142 | + action.kind_of?(Proc) ? self.instance_eval(&action) : action | |
143 | + end.flatten.compact | |
144 | + render :json => {:ids => ids} | |
145 | + end | |
146 | + | |
147 | + protected | |
148 | + | |
149 | + def pass_without_comment_captcha? | |
150 | + logged_in? && !environment.enabled?('captcha_for_logged_users') | |
151 | + end | |
152 | + helper_method :pass_without_comment_captcha? | |
153 | + | |
154 | + def can_update? | |
155 | + begin | |
156 | + @comment = profile.comments_received.find(params[:id]) | |
157 | + raise ActiveRecord::RecordNotFound unless @comment.can_be_updated_by?(user) # Not reveal that the comment exists | |
158 | + rescue ActiveRecord::RecordNotFound | |
159 | + render_not_found | |
160 | + return | |
161 | + end | |
162 | + end | |
163 | + | |
164 | +end | ... | ... |
app/controllers/public/content_viewer_controller.rb
... | ... | @@ -2,8 +2,6 @@ class ContentViewerController < ApplicationController |
2 | 2 | |
3 | 3 | needs_profile |
4 | 4 | |
5 | - before_filter :comment_author, :only => :edit_comment | |
6 | - | |
7 | 5 | helper ProfileHelper |
8 | 6 | helper TagsHelper |
9 | 7 | |
... | ... | @@ -70,24 +68,8 @@ class ContentViewerController < ApplicationController |
70 | 68 | |
71 | 69 | @form_div = params[:form] |
72 | 70 | |
73 | - if params[:comment] && params[:confirm] == 'true' | |
74 | - @comment = Comment.new(params[:comment]) | |
75 | - if request.post? && @page.accept_comments? | |
76 | - add_comment | |
77 | - end | |
78 | - else | |
79 | - @comment = Comment.new | |
80 | - end | |
81 | - | |
82 | - if request.post? | |
83 | - if params[:remove_comment] | |
84 | - remove_comment | |
85 | - return | |
86 | - elsif params[:mark_comment_as_spam] | |
87 | - mark_comment_as_spam | |
88 | - return | |
89 | - end | |
90 | - end | |
71 | + #FIXME see a better way to do this. It's not need to pass this variable anymore | |
72 | + @comment = Comment.new | |
91 | 73 | |
92 | 74 | if @page.has_posts? |
93 | 75 | posts = if params[:year] and params[:month] |
... | ... | @@ -117,89 +99,18 @@ class ContentViewerController < ApplicationController |
117 | 99 | end |
118 | 100 | end |
119 | 101 | |
120 | - comments = @page.comments.without_spam | |
121 | - @comments = comments.as_thread | |
122 | - @comments_count = comments.count | |
102 | + @comments = @page.comments.without_spam | |
103 | + @comments_count = @comments.count | |
104 | + @comments = @plugins.filter(:unavailable_comments, @comments.without_reply) | |
105 | + @comments = @comments.paginate(:per_page => per_page, :page => params[:comment_page] ) | |
106 | + | |
123 | 107 | if params[:slideshow] |
124 | 108 | render :action => 'slideshow', :layout => 'slideshow' |
125 | 109 | end |
126 | 110 | end |
127 | 111 | |
128 | - def edit_comment | |
129 | - path = params[:page].join('/') | |
130 | - @page = profile.articles.find_by_path(path) | |
131 | - @form_div = 'opened' | |
132 | - @comment = @page.comments.find_by_id(params[:id]) | |
133 | - if @comment | |
134 | - if request.post? | |
135 | - begin | |
136 | - @comment.update_attributes(params[:comment]) | |
137 | - session[:notice] = _('Comment succesfully updated') | |
138 | - redirect_to :action => 'view_page', :profile => profile.identifier, :page => @comment.article.explode_path | |
139 | - rescue | |
140 | - session[:notice] = _('Comment could not be updated') | |
141 | - end | |
142 | - end | |
143 | - else | |
144 | - redirect_to @page.view_url | |
145 | - session[:notice] = _('Could not find the comment in the article') | |
146 | - end | |
147 | - end | |
148 | - | |
149 | 112 | protected |
150 | 113 | |
151 | - def add_comment | |
152 | - @comment.author = user if logged_in? | |
153 | - @comment.article = @page | |
154 | - @comment.ip_address = request.remote_ip | |
155 | - @comment.user_agent = request.user_agent | |
156 | - @comment.referrer = request.referrer | |
157 | - plugins_filter_comment(@comment) | |
158 | - return if @comment.rejected? | |
159 | - if (pass_without_comment_captcha? || verify_recaptcha(:model => @comment, :message => _('Please type the words correctly'))) && @comment.save | |
160 | - @page.touch | |
161 | - @comment = nil # clear the comment form | |
162 | - redirect_to :action => 'view_page', :profile => params[:profile], :page => @page.explode_path, :view => params[:view] | |
163 | - else | |
164 | - @form_div = 'opened' if params[:comment][:reply_of_id].blank? | |
165 | - end | |
166 | - end | |
167 | - | |
168 | - def plugins_filter_comment(comment) | |
169 | - @plugins.each do |plugin| | |
170 | - plugin.filter_comment(comment) | |
171 | - end | |
172 | - end | |
173 | - | |
174 | - def pass_without_comment_captcha? | |
175 | - logged_in? && !environment.enabled?('captcha_for_logged_users') | |
176 | - end | |
177 | - helper_method :pass_without_comment_captcha? | |
178 | - | |
179 | - def remove_comment | |
180 | - @comment = @page.comments.find(params[:remove_comment]) | |
181 | - if (user == @comment.author || user == @page.profile || user.has_permission?(:moderate_comments, @page.profile)) | |
182 | - @comment.destroy | |
183 | - end | |
184 | - finish_comment_handling | |
185 | - end | |
186 | - | |
187 | - def mark_comment_as_spam | |
188 | - @comment = @page.comments.find(params[:mark_comment_as_spam]) | |
189 | - if logged_in? && (user == @page.profile || user.has_permission?(:moderate_comments, @page.profile)) | |
190 | - @comment.spam! | |
191 | - end | |
192 | - finish_comment_handling | |
193 | - end | |
194 | - | |
195 | - def finish_comment_handling | |
196 | - if request.xhr? | |
197 | - render :text => {'ok' => true}.to_json, :content_type => 'application/json' | |
198 | - else | |
199 | - redirect_to :action => 'view_page', :profile => params[:profile], :page => @page.explode_path, :view => params[:view] | |
200 | - end | |
201 | - end | |
202 | - | |
203 | 114 | def per_page |
204 | 115 | 12 |
205 | 116 | end |
... | ... | @@ -223,13 +134,9 @@ class ContentViewerController < ApplicationController |
223 | 134 | end |
224 | 135 | end |
225 | 136 | |
226 | - def comment_author | |
227 | - comment = Comment.find_by_id(params[:id]) | |
228 | - if comment | |
229 | - render_access_denied if comment.author.blank? || comment.author != user | |
230 | - else | |
231 | - render_not_found | |
232 | - end | |
137 | + def pass_without_comment_captcha? | |
138 | + logged_in? && !environment.enabled?('captcha_for_logged_users') | |
233 | 139 | end |
140 | + helper_method :pass_without_comment_captcha? | |
234 | 141 | |
235 | 142 | end | ... | ... |
app/controllers/public/profile_controller.rb
app/helpers/application_helper.rb
... | ... | @@ -32,6 +32,8 @@ module ApplicationHelper |
32 | 32 | |
33 | 33 | include AccountHelper |
34 | 34 | |
35 | + include CommentHelper | |
36 | + | |
35 | 37 | include BlogHelper |
36 | 38 | |
37 | 39 | include ContentViewerHelper |
... | ... | @@ -956,10 +958,7 @@ module ApplicationHelper |
956 | 958 | options.merge!(:page => params[:npage]) |
957 | 959 | content = article.to_html(options) |
958 | 960 | content = content.kind_of?(Proc) ? self.instance_eval(&content).html_safe : content.html_safe |
959 | - @plugins && @plugins.each do |plugin| | |
960 | - content = plugin.parse_content(content) | |
961 | - end | |
962 | - content | |
961 | + filter_html(content, article) | |
963 | 962 | end |
964 | 963 | |
965 | 964 | # Please, use link_to by default! |
... | ... | @@ -1357,10 +1356,7 @@ module ApplicationHelper |
1357 | 1356 | options[:on_ready] ||= 'null' |
1358 | 1357 | |
1359 | 1358 | result = text_field_tag(name, nil, text_field_options.merge(html_options.merge({:id => element_id}))) |
1360 | - result += | |
1361 | - " | |
1362 | - <script type='text/javascript'> | |
1363 | - jQuery('##{element_id}') | |
1359 | + result += javascript_tag("jQuery('##{element_id}') | |
1364 | 1360 | .tokenInput('#{url_for(search_action)}', { |
1365 | 1361 | minChars: #{options[:min_chars].to_json}, |
1366 | 1362 | prePopulate: #{options[:pre_populate].to_json}, |
... | ... | @@ -1376,16 +1372,15 @@ module ApplicationHelper |
1376 | 1372 | onAdd: #{options[:on_add]}, |
1377 | 1373 | onDelete: #{options[:on_delete]}, |
1378 | 1374 | onReady: #{options[:on_ready]}, |
1379 | - }) | |
1380 | - " | |
1381 | - result += options[:focus] ? ".focus();" : ";" | |
1375 | + }); | |
1376 | + ") | |
1377 | + result += javascript_tag("jQuery('##{element_id}').focus();") if options[:focus] | |
1382 | 1378 | if options[:avoid_enter] |
1383 | - result += "jQuery('#token-input-#{element_id}') | |
1379 | + result += javascript_tag("jQuery('#token-input-#{element_id}') | |
1384 | 1380 | .live('keydown', function(event){ |
1385 | 1381 | if(event.keyCode == '13') return false; |
1386 | - });" | |
1382 | + });") | |
1387 | 1383 | end |
1388 | - result += "</script>" | |
1389 | 1384 | result |
1390 | 1385 | end |
1391 | 1386 | |
... | ... | @@ -1396,12 +1391,12 @@ module ApplicationHelper |
1396 | 1391 | end |
1397 | 1392 | |
1398 | 1393 | def expirable_button(content, action, text, url, options = {}) |
1399 | - options[:class] = "button with-text icon-#{action.to_s}" | |
1394 | + options[:class] = ["button with-text icon-#{action.to_s}", options[:class]].compact.join(' ') | |
1400 | 1395 | expirable_content_reference content, action, text, url, options |
1401 | 1396 | end |
1402 | 1397 | |
1403 | 1398 | def expirable_comment_link(content, action, text, url, options = {}) |
1404 | - options[:class] = "comment-footer comment-footer-link comment-footer-hide" | |
1399 | + options[:class] = ["comment-footer comment-footer-link comment-footer-hide", options[:class]].compact.join(' ') | |
1405 | 1400 | expirable_content_reference content, action, text, url, options |
1406 | 1401 | end |
1407 | 1402 | |
... | ... | @@ -1440,6 +1435,29 @@ module ApplicationHelper |
1440 | 1435 | @no_design_blocks = true |
1441 | 1436 | end |
1442 | 1437 | |
1438 | + def filter_html(html, source) | |
1439 | + if @plugins | |
1440 | + html = convert_macro(html, source) | |
1441 | + #TODO This parse should be done through the macro infra, but since there | |
1442 | + # are old things that do not support it we are keeping this hot spot. | |
1443 | + html = @plugins.pipeline(:parse_content, html, source).first | |
1444 | + end | |
1445 | + html | |
1446 | + end | |
1447 | + | |
1448 | + def convert_macro(html, source) | |
1449 | + doc = Hpricot(html) | |
1450 | + #TODO This way is more efficient but do not support macro inside of | |
1451 | + # macro. You must parse them from the inside-out in order to enable | |
1452 | + # that. | |
1453 | + doc.search('.macro').each do |macro| | |
1454 | + macro_name = macro['data-macro'] | |
1455 | + result = @plugins.parse_macro(macro_name, macro, source) | |
1456 | + macro.inner_html = result.kind_of?(Proc) ? self.instance_eval(&result) : result | |
1457 | + end | |
1458 | + doc.html | |
1459 | + end | |
1460 | + | |
1443 | 1461 | def default_folder_for_image_upload(profile) |
1444 | 1462 | default_folder = profile.folders.find_by_type('Gallery') |
1445 | 1463 | default_folder = profile.folders.find_by_type('Folder') if default_folder.nil? | ... | ... |
app/helpers/article_helper.rb
... | ... | @@ -35,7 +35,13 @@ module ArticleHelper |
35 | 35 | 'div', |
36 | 36 | check_box(:article, :notify_comments) + |
37 | 37 | content_tag('label', _('I want to receive a notification of each comment written by e-mail'), :for => 'article_notify_comments') + |
38 | - observe_field(:article_accept_comments, :function => "$('article_notify_comments').disabled = ! $('article_accept_comments').checked") | |
38 | + observe_field(:article_accept_comments, :function => "$('article_notify_comments').disabled = ! $('article_accept_comments').checked;$('article_moderate_comments').disabled = ! $('article_accept_comments').checked") | |
39 | + ) + | |
40 | + | |
41 | + content_tag( | |
42 | + 'div', | |
43 | + check_box(:article, :moderate_comments) + | |
44 | + content_tag('label', _('I want to approve comments on this article'), :for => 'article_moderate_comments') | |
39 | 45 | ) + |
40 | 46 | |
41 | 47 | (article.can_display_hits? ? | ... | ... |
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', | ... | ... |
app/helpers/catalog_helper.rb
... | ... | @@ -3,9 +3,18 @@ module CatalogHelper |
3 | 3 | include DisplayHelper |
4 | 4 | include ManageProductsHelper |
5 | 5 | |
6 | + def catalog_load_index options = {:page => params[:page], :show_categories => true} | |
7 | + if options[:show_categories] | |
8 | + @category = params[:level] ? ProductCategory.find(params[:level]) : nil | |
9 | + @categories = ProductCategory.on_level(params[:level]).order(:name) | |
10 | + end | |
11 | + | |
12 | + @products = profile.products.from_category(@category).paginate(:order => 'available desc, highlighted desc, name asc', :per_page => 9, :page => options[:page]) | |
13 | + end | |
14 | + | |
6 | 15 | def breadcrumb(category) |
7 | - start = link_to(_('Start'), {:action => 'index'}) | |
8 | - ancestors = category.ancestors.map { |c| link_to(c.name, {:action => 'index', :level => c.id}) }.reverse | |
16 | + start = link_to(_('Start'), {:controller => :catalog, :action => 'index'}) | |
17 | + ancestors = category.ancestors.map { |c| link_to(c.name, {:controller => :catalog, :action => 'index', :level => c.id}) }.reverse | |
9 | 18 | current_level = content_tag('strong', category.name) |
10 | 19 | all_items = [start] + ancestors + [current_level] |
11 | 20 | content_tag('div', all_items.join(' → '), :id => 'breadcrumb') |
... | ... | @@ -15,7 +24,7 @@ module CatalogHelper |
15 | 24 | count = profile.products.from_category(category).count |
16 | 25 | name = truncate(category.name, :length => 22 - count.to_s.size) |
17 | 26 | link_name = sub ? name : content_tag('strong', name) |
18 | - link = link_to(link_name, {:action => 'index', :level => category.id}, :title => category.name) | |
27 | + link = link_to(link_name, {:controller => :catalog, :action => 'index', :level => category.id}, :title => category.name) | |
19 | 28 | content_tag('li', "#{link} (#{count})") if count > 0 |
20 | 29 | end |
21 | 30 | ... | ... |
... | ... | @@ -0,0 +1,70 @@ |
1 | +module CommentHelper | |
2 | + | |
3 | + def article_title(article, args = {}) | |
4 | + title = article.title | |
5 | + title = article.display_title if article.kind_of?(UploadedFile) && article.image? | |
6 | + title = content_tag('h1', h(title), :class => 'title') | |
7 | + if article.belongs_to_blog? | |
8 | + unless args[:no_link] | |
9 | + title = content_tag('h1', link_to(article.name, article.url), :class => 'title') | |
10 | + end | |
11 | + comments = '' | |
12 | + unless args[:no_comments] || !article.accept_comments | |
13 | + comments = (" - %s") % link_to_comments(article) | |
14 | + end | |
15 | + title << content_tag('span', | |
16 | + content_tag('span', show_date(article.published_at), :class => 'date') + | |
17 | + content_tag('span', [_(", by %s") % link_to(article.author_name, article.author_url)], :class => 'author') + | |
18 | + content_tag('span', comments, :class => 'comments'), | |
19 | + :class => 'created-at' | |
20 | + ) | |
21 | + end | |
22 | + title | |
23 | + end | |
24 | + | |
25 | + def comment_actions(comment) | |
26 | + url = url_for(:profile => profile.identifier, :controller => :comment, :action => :check_actions, :id => comment.id) | |
27 | + links = links_for_comment_actions(comment) | |
28 | + content_tag(:li, link_to(content_tag(:span, _('Contents menu')), '#', :onclick => "toggleSubmenu(this,'',#{links.to_json}); return false", :class => 'menu-submenu-trigger comment-trigger', :url => url), :class=> 'vcard') unless links.empty? | |
29 | + end | |
30 | + | |
31 | + private | |
32 | + | |
33 | + def links_for_comment_actions(comment) | |
34 | + actions = [link_for_report_abuse(comment), link_for_spam(comment), link_for_edit(comment), link_for_remove(comment)] | |
35 | + @plugins.dispatch(:comment_actions, comment).collect do |action| | |
36 | + actions << (action.kind_of?(Proc) ? self.instance_eval(&action) : action) | |
37 | + end | |
38 | + actions.flatten.compact | |
39 | + end | |
40 | + | |
41 | + def link_for_report_abuse(comment) | |
42 | + if comment.author | |
43 | + report_abuse_link = report_abuse(comment.author, :comment_link, comment) | |
44 | + {:link => report_abuse_link} if report_abuse_link | |
45 | + end | |
46 | + end | |
47 | + | |
48 | + def link_for_spam(comment) | |
49 | + if comment.can_be_marked_as_spam_by?(user) | |
50 | + if comment.spam? | |
51 | + {:link => link_to_function(_('Mark as NOT SPAM'), 'remove_comment(this, %s); return false;' % url_for(:profile => profile.identifier, :mark_comment_as_ham => comment.id).to_json, :class => 'comment-footer comment-footer-link comment-footer-hide')} | |
52 | + else | |
53 | + {:link => link_to_function(_('Mark as SPAM'), 'remove_comment(this, %s, %s); return false;' % [url_for(:profile => profile.identifier, :controller => 'comment', :action => :mark_as_spam, :id => comment.id).to_json, _('Are you sure you want to mark this comment as SPAM?').to_json], :class => 'comment-footer comment-footer-link comment-footer-hide')} | |
54 | + end | |
55 | + end | |
56 | + end | |
57 | + | |
58 | + def link_for_edit(comment) | |
59 | + if comment.can_be_updated_by?(user) | |
60 | + {:link => expirable_comment_link(comment, :edit, _('Edit'), url_for(:profile => profile.identifier, :controller => :comment, :action => :edit, :id => comment.id),:class => 'colorbox')} | |
61 | + end | |
62 | + end | |
63 | + | |
64 | + def link_for_remove(comment) | |
65 | + if comment.can_be_destroyed_by?(user) | |
66 | + {:link => link_to_function(_('Remove'), 'remove_comment(this, %s, %s); return false ;' % [url_for(:profile => profile.identifier, :controller => 'comment', :action => :destroy, :id => comment.id).to_json, _('Are you sure you want to remove this comment and all its replies?').to_json], :class => 'comment-footer comment-footer-link comment-footer-hide remove-children')} | |
67 | + end | |
68 | + end | |
69 | + | |
70 | +end | ... | ... |
app/helpers/content_viewer_helper.rb
... | ... | @@ -3,13 +3,14 @@ module ContentViewerHelper |
3 | 3 | include BlogHelper |
4 | 4 | include ForumHelper |
5 | 5 | |
6 | + def display_number_of_comments(n) | |
7 | + base_str = "<span class='comment-count hide'>#{n}</span>" | |
8 | + amount_str = n == 0 ? _('no comments yet') : (n == 1 ? _('One comment') : _('%s comments') % n) | |
9 | + base_str + "<span class='comment-count-write-out'>#{amount_str}</span>" | |
10 | + end | |
11 | + | |
6 | 12 | def number_of_comments(article) |
7 | - n = article.comments.without_spam.count | |
8 | - if n == 0 | |
9 | - _('No comments yet') | |
10 | - else | |
11 | - n_('One comment', '<span class="comment-count">%{comments}</span> comments', n) % { :comments => n } | |
12 | - end | |
13 | + display_number_of_comments(article.comments.without_spam.count) | |
13 | 14 | end |
14 | 15 | |
15 | 16 | def article_title(article, args = {}) | ... | ... |
app/helpers/forms_helper.rb
app/helpers/forum_helper.rb
... | ... | @@ -29,9 +29,9 @@ module ForumHelper |
29 | 29 | css_add << 'not-published' if !art.published? |
30 | 30 | css_add << position |
31 | 31 | content << content_tag('tr', |
32 | - content_tag('td', link_to(art.title, art.url)) + | |
33 | - content_tag('td', link_to(art.comments.count, art.url.merge(:anchor => 'comments_list'))) + | |
34 | - content_tag('td', last_topic_update(art)), | |
32 | + content_tag('td', link_to(art.title, art.url), :class => "forum-post-title") + | |
33 | + content_tag('td', link_to(art.comments.count, art.url.merge(:anchor => 'comments_list')), :class => "forum-post-answers") + | |
34 | + content_tag('td', last_topic_update(art), :class => "forum-post-last-answer"), | |
35 | 35 | :class => 'forum-post ' + css_add.join(' '), |
36 | 36 | :id => "post-#{art.id}" |
37 | 37 | ) | ... | ... |
app/helpers/layout_helper.rb
... | ... | @@ -0,0 +1,92 @@ |
1 | +module MacrosHelper | |
2 | + | |
3 | + def macros_in_menu | |
4 | + @plugins.dispatch(:macros).reject{ |macro| macro.configuration[:icon_path] } | |
5 | + end | |
6 | + | |
7 | + def macros_with_buttons | |
8 | + @plugins.dispatch(:macros).reject{ |macro| !macro.configuration[:icon_path] } | |
9 | + end | |
10 | + | |
11 | + def macro_title(macro) | |
12 | + macro.configuration[:title] || macro.name.humanize | |
13 | + end | |
14 | + | |
15 | + def generate_macro_config_dialog(macro) | |
16 | + if macro.configuration[:skip_dialog] | |
17 | + "function(){#{macro_generator(macro)}}" | |
18 | + else | |
19 | + "function(){ | |
20 | + jQuery('<div>'+#{macro_configuration_dialog(macro).to_json}+'</div>').dialog({ | |
21 | + title: #{macro_title(macro).to_json}, | |
22 | + modal: true, | |
23 | + buttons: [ | |
24 | + {text: #{_('Ok').to_json}, click: function(){ | |
25 | + tinyMCE.activeEditor.execCommand('mceInsertContent', false, | |
26 | + (function(dialog){ #{macro_generator(macro)} })(this)); | |
27 | + jQuery(this).dialog('close'); | |
28 | + }}, | |
29 | + {text: #{_('Cancel').to_json}, click: function(){jQuery(this).dialog('close');}} | |
30 | + ] | |
31 | + }); | |
32 | + }" | |
33 | + end | |
34 | + end | |
35 | + | |
36 | + def include_macro_js_files | |
37 | + plugins_javascripts = [] | |
38 | + @plugins.dispatch(:macros).map do |macro| | |
39 | + if macro.configuration[:js_files] | |
40 | + macro.configuration[:js_files].map { |js| plugins_javascripts << macro.plugin.public_path(js) } | |
41 | + end | |
42 | + end | |
43 | + javascript_include_tag(plugins_javascripts, :cache => 'cache/plugins-' + Digest::MD5.hexdigest(plugins_javascripts.to_s)) unless plugins_javascripts.empty? | |
44 | + end | |
45 | + | |
46 | + def macro_css_files | |
47 | + plugins_css = [] | |
48 | + @plugins.dispatch(:macros).map do |macro| | |
49 | + if macro.configuration[:css_files] | |
50 | + macro.configuration[:css_files].map { |css| plugins_css << macro.plugin.public_path(css) } | |
51 | + end | |
52 | + end | |
53 | + plugins_css.join(',') | |
54 | + end | |
55 | + | |
56 | + protected | |
57 | + | |
58 | + def macro_generator(macro) | |
59 | + if macro.configuration[:generator] | |
60 | + macro.configuration[:generator] | |
61 | + else | |
62 | + macro_default_generator(macro) | |
63 | + end | |
64 | + | |
65 | + end | |
66 | + | |
67 | + def macro_default_generator(macro) | |
68 | + code = "var params = {};" | |
69 | + configuration = macro_configuration(macro) | |
70 | + configuration[:params].map do |field| | |
71 | + code += "params.#{field[:name]} = jQuery('*[name=#{field[:name]}]', dialog).val();" | |
72 | + end | |
73 | + code + " | |
74 | + var html = jQuery('<div class=\"macro mceNonEditable\" data-macro=\"#{macro.identifier}\">'+#{macro_title(macro).to_json}+'</div>')[0]; | |
75 | + for(key in params) html.setAttribute('data-macro-'+key,params[key]); | |
76 | + return html.outerHTML; | |
77 | + " | |
78 | + end | |
79 | + | |
80 | + def macro_configuration_dialog(macro) | |
81 | + macro.configuration[:params].map do |field| | |
82 | + label_name = field[:label] || field[:name].to_s.humanize | |
83 | + case field[:type] | |
84 | + when 'text' | |
85 | + labelled_form_field(label_name, text_field_tag(field[:name], field[:default])) | |
86 | + when 'select' | |
87 | + labelled_form_field(label_name, select_tag(field[:name], options_for_select(field[:values], field[:default]))) | |
88 | + end | |
89 | + end.join("\n") | |
90 | + end | |
91 | + | |
92 | +end | ... | ... |
... | ... | @@ -0,0 +1,104 @@ |
1 | +class ApproveComment < Task | |
2 | + validates_presence_of :target_id | |
3 | + | |
4 | + settings_items :comment_attributes, :closing_statment | |
5 | + | |
6 | + validates_presence_of :comment_attributes | |
7 | + | |
8 | + def comment | |
9 | + @comment ||= Comment.new(JSON.parse(self.comment_attributes)) unless self.comment_attributes.nil? | |
10 | + end | |
11 | + | |
12 | + def requestor_name | |
13 | + requestor ? requestor.name : (comment.name || _('Anonymous')) | |
14 | + end | |
15 | + | |
16 | + def article | |
17 | + Article.find_by_id comment.source_id unless self.comment.nil? | |
18 | + end | |
19 | + | |
20 | + def article_name | |
21 | + article ? article.name : _("Article removed.") | |
22 | + end | |
23 | + | |
24 | + def perform | |
25 | + comment.save! | |
26 | + end | |
27 | + | |
28 | + def title | |
29 | + _("New comment to article") | |
30 | + end | |
31 | + | |
32 | + def icon | |
33 | + result = {:type => :defined_image, :src => '/images/icons-app/article-minor.png'} | |
34 | + result.merge!({:url => article.url}) if article | |
35 | + result | |
36 | + end | |
37 | + | |
38 | + def linked_subject | |
39 | + {:text => article_name, :url => article.url} if article | |
40 | + end | |
41 | + | |
42 | + def information | |
43 | + if article | |
44 | + if requestor | |
45 | + {:message => _('%{requestor} commented on the the article: %{linked_subject}.')} | |
46 | + else | |
47 | + { :message => _('%{requestor} commented on the the article: %{linked_subject}.'), | |
48 | + :variables => {:requestor => requestor_name} } | |
49 | + end | |
50 | + else | |
51 | + {:message => _("The article was removed.")} | |
52 | + end | |
53 | + end | |
54 | + | |
55 | + def accept_details | |
56 | + true | |
57 | + end | |
58 | + | |
59 | + def reject_details | |
60 | + true | |
61 | + end | |
62 | + | |
63 | + def default_decision | |
64 | + if article | |
65 | + 'skip' | |
66 | + else | |
67 | + 'reject' | |
68 | + end | |
69 | + end | |
70 | + | |
71 | + def accept_disabled? | |
72 | + article.blank? | |
73 | + end | |
74 | + | |
75 | + def target_notification_description | |
76 | + if article | |
77 | + _('%{requestor} wants to comment the article: %{article}.') % {:requestor => requestor_name, :article => article.name} | |
78 | + else | |
79 | + _('%{requestor} wanted to comment the article but it was removed.') % {:requestor => requestor_name} | |
80 | + end | |
81 | + end | |
82 | + | |
83 | + def target_notification_message | |
84 | + target_notification_description + "\n\n" + | |
85 | + _('You need to login on %{system} in order to approve or reject this comment.') % { :system => target.environment.name } | |
86 | + end | |
87 | + | |
88 | + def task_finished_message | |
89 | + if !closing_statment.blank? | |
90 | + _("Your comment to the article \"%{article}\" was approved. Here is the comment left by the admin who approved your comment:\n\n%{comment} ") % {:article => article_name, :comment => closing_statment} | |
91 | + else | |
92 | + _('Your request for comment the article "%{article}" was approved.') % {:article => article_name} | |
93 | + end | |
94 | + end | |
95 | + | |
96 | + def task_cancelled_message | |
97 | + message = _('Your request for commenting the article "%{article}" was rejected.') % {:article => article_name} | |
98 | + if !reject_explanation.blank? | |
99 | + message += " " + _("Here is the reject explanation left by the administrator who rejected your comment: \n\n%{reject_explanation}") % {:reject_explanation => reject_explanation} | |
100 | + end | |
101 | + message | |
102 | + end | |
103 | + | |
104 | +end | ... | ... |
app/models/article.rb
... | ... | @@ -67,6 +67,7 @@ class Article < ActiveRecord::Base |
67 | 67 | settings_items :display_hits, :type => :boolean, :default => true |
68 | 68 | settings_items :author_name, :type => :string, :default => "" |
69 | 69 | settings_items :allow_members_to_edit, :type => :boolean, :default => false |
70 | + settings_items :moderate_comments, :type => :boolean, :default => false | |
70 | 71 | settings_items :followers, :type => Array, :default => [] |
71 | 72 | |
72 | 73 | belongs_to :reference_article, :class_name => "Article", :foreign_key => 'reference_article_id' |
... | ... | @@ -329,6 +330,14 @@ class Article < ActiveRecord::Base |
329 | 330 | @view_url ||= image? ? url.merge(:view => true) : url |
330 | 331 | end |
331 | 332 | |
333 | + def comment_url_structure(comment, action = :edit) | |
334 | + if comment.new_record? | |
335 | + profile.url.merge(:page => path.split("/"), :controller => :comment, :action => :create) | |
336 | + else | |
337 | + profile.url.merge(:page => path.split("/"), :controller => :comment, :action => action || :edit, :id => comment.id) | |
338 | + end | |
339 | + end | |
340 | + | |
332 | 341 | def allow_children? |
333 | 342 | true |
334 | 343 | end |
... | ... | @@ -491,6 +500,14 @@ class Article < ActiveRecord::Base |
491 | 500 | allow_post_content?(user) || user && allow_members_to_edit && user.is_member_of?(profile) |
492 | 501 | end |
493 | 502 | |
503 | + def moderate_comments? | |
504 | + moderate_comments == true | |
505 | + end | |
506 | + | |
507 | + def comments_updated | |
508 | + solr_save | |
509 | + end | |
510 | + | |
494 | 511 | def accept_category?(cat) |
495 | 512 | !cat.is_a?(ProductCategory) |
496 | 513 | end |
... | ... | @@ -597,7 +614,7 @@ class Article < ActiveRecord::Base |
597 | 614 | end |
598 | 615 | |
599 | 616 | def lead |
600 | - abstract.blank? ? first_paragraph : abstract | |
617 | + abstract.blank? ? first_paragraph.html_safe : abstract.html_safe | |
601 | 618 | end |
602 | 619 | |
603 | 620 | def short_lead | ... | ... |
app/models/blog.rb
1 | 1 | class Blog < Folder |
2 | 2 | |
3 | 3 | acts_as_having_posts |
4 | + include PostsLimit | |
4 | 5 | |
5 | 6 | #FIXME This should be used until there is a migration to fix all blogs that |
6 | 7 | # already have folders inside them |
... | ... | @@ -81,4 +82,8 @@ class Blog < Folder |
81 | 82 | posts.empty? |
82 | 83 | end |
83 | 84 | |
85 | + def last_posts(limit=3) | |
86 | + posts.where("type != 'RssFeed'").order(:updated_at).limit(limit) | |
87 | + end | |
88 | + | |
84 | 89 | end | ... | ... |
app/models/blog_archives_block.rb
... | ... | @@ -21,18 +21,22 @@ class BlogArchivesBlock < Block |
21 | 21 | end |
22 | 22 | |
23 | 23 | def visible_posts(person) |
24 | - blog.posts.native_translations.select {|post| post.display_to?(person)} | |
24 | + #FIXME Performance issues with display_to. Must convert it to a scope. | |
25 | + # Checkout this page for further information: http://noosfero.org/Development/ActionItem2705 | |
26 | + blog.posts.published.native_translations #.select {|post| post.display_to?(person)} | |
25 | 27 | end |
26 | 28 | |
27 | 29 | def content(args={}) |
28 | 30 | owner_blog = self.blog |
29 | 31 | return nil unless owner_blog |
30 | 32 | results = '' |
31 | - visible_posts(args[:person]).group_by {|i| i.published_at.year }.sort_by { |year,count| -year }.each do |year, results_by_year| | |
32 | - results << content_tag('li', content_tag('strong', "#{year} (#{results_by_year.size})")) | |
33 | + posts = visible_posts(args[:person]) | |
34 | + posts.count(:all, :group => 'EXTRACT(YEAR FROM published_at)').sort_by {|year, count| -year.to_i}.each do |year, count| | |
35 | + results << content_tag('li', content_tag('strong', "#{year} (#{count})")) | |
33 | 36 | results << "<ul class='#{year}-archive'>" |
34 | - results_by_year.group_by{|i| [ ('%02d' % i.published_at.month()), gettext(MONTHS[i.published_at.month() - 1])]}.sort.reverse.each do |month, results_by_month| | |
35 | - results << content_tag('li', link_to("#{month[1]} (#{results_by_month.size})", owner_blog.url.merge(:year => year, :month => month[0]))) | |
37 | + posts.count(:all, :conditions => ['EXTRACT(YEAR FROM published_at)=?', year], :group => 'EXTRACT(MONTH FROM published_at)').sort_by {|month, count| -month.to_i}.each do |month, count| | |
38 | + month_name = gettext(MONTHS[month.to_i - 1]) | |
39 | + results << content_tag('li', link_to("#{month_name} (#{count})", owner_blog.url.merge(:year => year, :month => month))) | |
36 | 40 | end |
37 | 41 | results << "</ul>" |
38 | 42 | end | ... | ... |
app/models/comment.rb
... | ... | @@ -17,6 +17,7 @@ class Comment < ActiveRecord::Base |
17 | 17 | belongs_to :reply_of, :class_name => 'Comment', :foreign_key => 'reply_of_id' |
18 | 18 | |
19 | 19 | scope :without_spam, :conditions => ['spam IS NULL OR spam = ?', false] |
20 | + scope :without_reply, :conditions => ['reply_of_id IS NULL'] | |
20 | 21 | scope :spam, :conditions => ['spam = ?', true] |
21 | 22 | |
22 | 23 | # unauthenticated authors: |
... | ... | @@ -34,7 +35,9 @@ class Comment < ActiveRecord::Base |
34 | 35 | |
35 | 36 | xss_terminate :only => [ :body, :title, :name ], :on => 'validation' |
36 | 37 | |
37 | - delegate :environment, :to => :source | |
38 | + def comment_root | |
39 | + (reply_of && reply_of.comment_root) || self | |
40 | + end | |
38 | 41 | |
39 | 42 | def action_tracker_target |
40 | 43 | self.article.profile |
... | ... | @@ -150,21 +153,6 @@ class Comment < ActiveRecord::Base |
150 | 153 | @replies = comments_list |
151 | 154 | end |
152 | 155 | |
153 | - def self.as_thread | |
154 | - result = {} | |
155 | - root = [] | |
156 | - order(:id).each do |c| | |
157 | - c.replies = [] | |
158 | - result[c.id] ||= c | |
159 | - if result[c.reply_of_id] | |
160 | - result[c.reply_of_id].replies << c | |
161 | - else | |
162 | - root << c | |
163 | - end | |
164 | - end | |
165 | - root | |
166 | - end | |
167 | - | |
168 | 156 | include ApplicationHelper |
169 | 157 | def reported_version(options = {}) |
170 | 158 | comment = self |
... | ... | @@ -248,4 +236,22 @@ class Comment < ActiveRecord::Base |
248 | 236 | plugins.dispatch(:comment_marked_as_ham, self) |
249 | 237 | end |
250 | 238 | |
239 | + def need_moderation? | |
240 | + article.moderate_comments? && (author.nil? || article.author != author) | |
241 | + end | |
242 | + | |
243 | + def can_be_destroyed_by?(user) | |
244 | + return if user.nil? | |
245 | + user == author || user == profile || user.has_permission?(:moderate_comments, profile) | |
246 | + end | |
247 | + | |
248 | + def can_be_marked_as_spam_by?(user) | |
249 | + return if user.nil? | |
250 | + user == profile || user.has_permission?(:moderate_comments, profile) | |
251 | + end | |
252 | + | |
253 | + def can_be_updated_by?(user) | |
254 | + user.present? && user == author | |
255 | + end | |
256 | + | |
251 | 257 | end | ... | ... |
app/models/enterprise_homepage.rb
... | ... | @@ -20,7 +20,8 @@ class EnterpriseHomepage < Article |
20 | 20 | enterprise_homepage = self |
21 | 21 | lambda do |
22 | 22 | extend EnterpriseHomepageHelper |
23 | - @products = profile.products.paginate(:order => 'id asc', :per_page => 9, :page => 1) | |
23 | + extend CatalogHelper | |
24 | + catalog_load_index :page => 1, :show_categories => false | |
24 | 25 | render :partial => 'content_viewer/enterprise_homepage', :object => enterprise_homepage |
25 | 26 | end |
26 | 27 | end | ... | ... |
app/models/environment.rb
... | ... | @@ -28,6 +28,7 @@ class Environment < ActiveRecord::Base |
28 | 28 | 'manage_environment_users' => N_('Manage environment users'), |
29 | 29 | 'manage_environment_templates' => N_('Manage environment templates'), |
30 | 30 | 'manage_environment_licenses' => N_('Manage environment licenses'), |
31 | + 'manage_environment_trusted_sites' => N_('Manage environment trusted sites') | |
31 | 32 | } |
32 | 33 | |
33 | 34 | module Roles |
... | ... | @@ -270,6 +271,13 @@ class Environment < ActiveRecord::Base |
270 | 271 | |
271 | 272 | settings_items :search_hints, :type => Hash, :default => {} |
272 | 273 | |
274 | + # Set to return http forbidden to host not on the allow origin list bellow | |
275 | + settings_items :restrict_to_access_control_origins, :default => false | |
276 | + # Set this according to http://www.w3.org/TR/cors/. Headers are set at every response | |
277 | + # For multiple domains acts as suggested in http://stackoverflow.com/questions/1653308/access-control-allow-origin-multiple-origin-domains | |
278 | + settings_items :access_control_allow_origin, :type => Array | |
279 | + settings_items :access_control_allow_methods, :type => String | |
280 | + | |
273 | 281 | def news_amount_by_folder=(amount) |
274 | 282 | settings[:news_amount_by_folder] = amount.to_i |
275 | 283 | end | ... | ... |
app/models/event.rb
... | ... | @@ -16,7 +16,6 @@ class Event < Article |
16 | 16 | maybe_add_http(self.setting[:link]) |
17 | 17 | end |
18 | 18 | |
19 | - xss_terminate :only => [ :link ], :on => 'validation' | |
20 | 19 | xss_terminate :only => [ :body, :link, :address ], :with => 'white_list', :on => 'validation' |
21 | 20 | |
22 | 21 | def initialize(*args) | ... | ... |
app/models/forum.rb
app/models/layout_template.rb
... | ... | @@ -0,0 +1,21 @@ |
1 | +module PostsLimit | |
2 | + module ClassMethods | |
3 | + def posts_per_page_limit | |
4 | + 15 | |
5 | + end | |
6 | + | |
7 | + def posts_per_page_options | |
8 | + [5, 10, 15] | |
9 | + end | |
10 | + end | |
11 | + | |
12 | + def self.included(klass) | |
13 | + klass.send(:extend, PostsLimit::ClassMethods) | |
14 | + klass.class_eval do | |
15 | + def posts_per_page_with_limit | |
16 | + [self.class.posts_per_page_limit, posts_per_page_without_limit].min | |
17 | + end | |
18 | + alias_method_chain :posts_per_page, :limit | |
19 | + end | |
20 | + end | |
21 | +end | ... | ... |
app/models/task.rb
... | ... | @@ -74,7 +74,7 @@ class Task < ActiveRecord::Base |
74 | 74 | end |
75 | 75 | |
76 | 76 | def self.all_types |
77 | - %w[Invitation EnterpriseActivation AddMember Ticket SuggestArticle AddFriend CreateCommunity AbuseComplaint ApproveArticle CreateEnterprise ChangePassword EmailActivation InviteFriend InviteMember] | |
77 | + %w[Invitation EnterpriseActivation AddMember Ticket SuggestArticle AddFriend CreateCommunity AbuseComplaint ApproveComment ApproveArticle CreateEnterprise ChangePassword EmailActivation InviteFriend InviteMember] | |
78 | 78 | end |
79 | 79 | |
80 | 80 | # this method finished the task. It calls #perform, which must be overriden | ... | ... |
app/models/user.rb
... | ... | @@ -312,7 +312,8 @@ class User < ActiveRecord::Base |
312 | 312 | 'email_domain' => self.enable_email ? self.email_domain : nil, |
313 | 313 | 'friends_list' => friends_list, |
314 | 314 | 'enterprises' => enterprises, |
315 | - 'amount_of_friends' => friends_list.count | |
315 | + 'amount_of_friends' => friends_list.count, | |
316 | + 'chat_enabled' => person.environment.enabled?('xmpp_chat') | |
316 | 317 | } |
317 | 318 | end |
318 | 319 | ... | ... |
app/views/admin_panel/index.html.erb
... | ... | @@ -9,6 +9,7 @@ |
9 | 9 | <tr><td><%= link_to _('Sideboxes'), :controller => 'environment_design'%></td></tr> |
10 | 10 | <tr><td><%= link_to _('Homepage'), :action => 'set_portal_community' %></td></tr> |
11 | 11 | <tr><td><%= link_to _('Licenses'), :controller =>'licenses' %></td></tr> |
12 | + <tr><td><%= link_to _('Trusted sites'), :controller =>'trusted_sites' %></td></tr> | |
12 | 13 | </table> |
13 | 14 | |
14 | 15 | <h2><%= _('Profiles') %></h2> | ... | ... |
app/views/catalog/index.html.erb
... | ... | @@ -3,26 +3,28 @@ |
3 | 3 | |
4 | 4 | <h1><%= _('Products/Services') %></h1> |
5 | 5 | |
6 | -<%= breadcrumb(@category) if params[:level] %> | |
7 | - | |
8 | -<div class='l-sidebar-left-bar'> | |
9 | - <ul> | |
10 | - <%= content_tag('li', link_to(_('Homepage'), profile.url), :class => 'catalog-categories-link') %> | |
11 | - <%= content_tag('li', link_to(_('Catalog start'), profile.catalog_url), :class => 'catalog-categories-link') %> | |
12 | - <% if @categories.present? %> | |
13 | - <% @categories.each do |category| %> | |
14 | - <%= category_link(category) %> | |
15 | - <%= category_sub_links(category) %> | |
6 | +<% if @categories %> | |
7 | + <%= breadcrumb(@category) if params[:level] %> | |
8 | + | |
9 | + <div class='l-sidebar-left-bar'> | |
10 | + <ul> | |
11 | + <%= content_tag('li', link_to(_('Homepage'), profile.url), :class => 'catalog-categories-link') %> | |
12 | + <%= content_tag('li', link_to(_('Catalog start'), profile.catalog_url), :class => 'catalog-categories-link') %> | |
13 | + <% if @categories.present? %> | |
14 | + <% @categories.each do |category| %> | |
15 | + <%= category_link(category) %> | |
16 | + <%= category_sub_links(category) %> | |
17 | + <% end %> | |
18 | + <% elsif @category.present? %> | |
19 | + <%= content_tag('li', _('There are no sub-categories for %s') % @category.name, :id => 'catalog-categories-notice') %> | |
20 | + <% else %> | |
21 | + <%= content_tag('li', _('There are no categories available.'), :id => 'catalog-categories-notice') %> | |
16 | 22 | <% end %> |
17 | - <% elsif @category.present? %> | |
18 | - <%= content_tag('li', _('There are no sub-categories for %s') % @category.name, :id => 'catalog-categories-notice') %> | |
19 | - <% else %> | |
20 | - <%= content_tag('li', _('There are no categories available.'), :id => 'catalog-categories-notice') %> | |
21 | - <% end %> | |
22 | - </ul> | |
23 | -</div> | |
24 | - | |
25 | -<ul id="product-list" class="l-sidebar-left-content"> | |
23 | + </ul> | |
24 | + </div> | |
25 | +<% end %> | |
26 | + | |
27 | +<ul id="product-list" class="<%="l-sidebar-left-content" if @categories %>"> | |
26 | 28 | <% @products.each do |product| %> |
27 | 29 | <% extra_content = @plugins.dispatch(:catalog_item_extras, product).collect { |content| instance_eval(&content) } %> |
28 | 30 | <% extra_content_list = @plugins.dispatch(:catalog_list_item_extras, product).collect { |content| instance_eval(&content) } %> | ... | ... |
app/views/cms/_blog.html.erb
... | ... | @@ -56,7 +56,7 @@ |
56 | 56 | |
57 | 57 | <%= labelled_form_field(_('How to display posts:'), f.select(:visualization_format, [ [ _('Full post'), 'full'], [ _('First paragraph'), 'short'] ])) %> |
58 | 58 | |
59 | -<%= labelled_form_field(_('Posts per page:'), f.select(:posts_per_page, [5, 10, 20, 50, 100])) %> | |
59 | +<%= labelled_form_field(_('Posts per page:'), f.select(:posts_per_page, Blog.posts_per_page_options)) %> | |
60 | 60 | |
61 | 61 | <%= labelled_check_box(_("List only translated posts"), 'article[display_posts_in_current_language]', '1', @article.display_posts_in_current_language?) %> |
62 | 62 | ... | ... |
app/views/cms/_forum.html.erb
... | ... | @@ -10,4 +10,4 @@ |
10 | 10 | |
11 | 11 | <%= labelled_form_field(_('Description:'), text_area(:article, :body, :cols => 64, :rows => 10)) %> |
12 | 12 | |
13 | -<%= labelled_form_field(_('Posts per page:'), f.select(:posts_per_page, [5, 10, 20, 50, 100])) %> | |
13 | +<%= labelled_form_field(_('Posts per page:'), f.select(:posts_per_page, Forum.posts_per_page_options)) %> | ... | ... |
... | ... | @@ -0,0 +1,85 @@ |
1 | +<li id="<%= comment.anchor %>" class="article-comment"> | |
2 | + <div class="article-comment-inner"> | |
3 | + | |
4 | + <div class="comment-content comment-logged-<%= comment.author ? 'in' : 'out' %> <%= 'comment-from-owner' if ( comment.author && (profile == comment.author) ) %>"> | |
5 | + | |
6 | + <% if comment.author %> | |
7 | + <%= link_to image_tag(profile_icon(comment.author, :minor)) + | |
8 | + content_tag('span', comment.author_name, :class => 'comment-info'), | |
9 | + comment.author.url, | |
10 | + :class => 'comment-picture', | |
11 | + :title => comment.author_name | |
12 | + %> | |
13 | + <% else %> | |
14 | + <% url_image, status_class = comment.author_id ? | |
15 | + [comment.removed_user_image, 'icon-user-removed'] : | |
16 | + [str_gravatar_url_for( comment.email, :size => 50, :d=>404 ), 'icon-user-unknown'] %> | |
17 | + | |
18 | + <%= link_to( | |
19 | + image_tag(url_image, :onerror=>'gravatarCommentFailback(this)', | |
20 | + 'data-gravatar'=>str_gravatar_url_for(comment.email, :size=>50)) + | |
21 | + content_tag('span', comment.author_name, :class => 'comment-info') + | |
22 | + content_tag('span', comment.message, | |
23 | + :class => 'comment-user-status ' + status_class), | |
24 | + gravatar_profile_url(comment.email), | |
25 | + :target => '_blank', | |
26 | + :class => 'comment-picture', | |
27 | + :title => '%s %s' % [comment.author_name, comment.message] | |
28 | + )%> | |
29 | + <% end %> | |
30 | + | |
31 | + <% comment_balloon do %> | |
32 | + | |
33 | + <div class="comment-details"> | |
34 | + <div class="comment-header"> | |
35 | + <ul> | |
36 | + <div class="comment-actions"> | |
37 | + <%= comment_actions(comment) %> | |
38 | + </div> | |
39 | + </ul> | |
40 | + <% unless comment.spam? %> | |
41 | + <%= link_to_function '', | |
42 | + "var f = add_comment_reply_form(this, %s); f.find('comment_title, textarea').val(''); return false" % comment.id, | |
43 | + :class => 'comment-footer comment-footer-link comment-footer-hide comment-actions-reply button', | |
44 | + :id => 'comment-reply-to-' + comment.id.to_s, | |
45 | + :title => _('Reply') | |
46 | + %> | |
47 | + <% end %> | |
48 | + </div> | |
49 | + | |
50 | + <div class="comment-created-at"> | |
51 | + <%= show_time(comment.created_at) %> | |
52 | + </div> | |
53 | + <h4><%= comment.title.blank? && ' ' || comment.title %></h4> | |
54 | + <div class="comment-text"> | |
55 | + <p/> | |
56 | + <%= txt2html comment.body %> | |
57 | + </div> | |
58 | + </div> | |
59 | + | |
60 | + <div class="comment_reply post_comment_box closed" id="comment_reply_to_<%= comment.id %>"> | |
61 | + <% if @comment && @comment.errors.any? && @comment.reply_of_id.to_i == comment.id %> | |
62 | + <%= error_messages_for :comment %> | |
63 | + <script type="text/javascript"> | |
64 | + jQuery(function() { | |
65 | + document.location.href = '#<%= comment.anchor %>'; | |
66 | + add_comment_reply_form('#comment-reply-to-<%= comment.id %>', <%= comment.id %>); | |
67 | + }); | |
68 | + </script> | |
69 | + <% end %> | |
70 | + </div> | |
71 | + | |
72 | + <% end %> | |
73 | + | |
74 | + </div> | |
75 | + | |
76 | + <% unless comment.replies.blank? || comment.spam? %> | |
77 | + <ul class="comment-replies"> | |
78 | + <% comment.replies.each do |reply| %> | |
79 | + <%= render :partial => 'comment/comment', :locals => { :comment => reply } %> | |
80 | + <% end %> | |
81 | + </ul> | |
82 | + <% end %> | |
83 | + | |
84 | + </div> | |
85 | +</li> | ... | ... |
... | ... | @@ -0,0 +1,98 @@ |
1 | +<% edition_mode = (defined? edition_mode) ? edition_mode : false %> | |
2 | +<div class="<%= edition_mode ? '' : 'page-comment-form' %>"> | |
3 | + | |
4 | +<% focus_on = logged_in? ? 'title' : 'name' %> | |
5 | + | |
6 | + | |
7 | +<% if !edition_mode && !pass_without_comment_captcha? %> | |
8 | + <div id="recaptcha-container" style="display: none"> | |
9 | + <h3><%= _('Please type the two words below') %></h3> | |
10 | + <%= recaptcha_tags(:display => { :theme => 'clean' }, :ajax => true) %> | |
11 | + <% button_bar do %> | |
12 | + <%= button_to_function :add, _('Confirm'), "return false", :id => "confirm-captcha" %> | |
13 | + <%= button_to_function :cancel, _('Cancel'), "jQuery.colorbox.close()" %> | |
14 | + <% end %> | |
15 | + </div> | |
16 | + | |
17 | + <script type="text/javascript"> | |
18 | + jQuery(document).bind('cbox_cleanup', function() { | |
19 | + jQuery('#recaptcha-container').hide(); | |
20 | + }); | |
21 | + </script> | |
22 | +<% end %> | |
23 | + | |
24 | +<script type="text/javascript"> | |
25 | +function check_captcha(button, confirm_action) { | |
26 | + <% if edition_mode %> | |
27 | + return true; | |
28 | + <% elsif pass_without_comment_captcha? %> | |
29 | + button.form.confirm.value = 'true'; | |
30 | + button.disabled = true; | |
31 | + return true; | |
32 | + <% else %> | |
33 | + jQuery('#recaptcha-container').show(); | |
34 | + jQuery.colorbox({ inline : true, href : '#recaptcha-container', maxWidth : '600px', maxHeight : '300px' }); | |
35 | + jQuery('#confirm-captcha').unbind('click'); | |
36 | + jQuery('#confirm-captcha').bind('click', function() { | |
37 | + jQuery.colorbox.close(); | |
38 | + button.form.recaptcha_response_field.value = jQuery('#recaptcha_response_field').val(); | |
39 | + button.form.recaptcha_challenge_field.value = jQuery('#recaptcha_challenge_field').val(); | |
40 | + button.form.confirm.value = 'true'; | |
41 | + button.disabled = false; | |
42 | + confirm_action(button); | |
43 | + }); | |
44 | + return false; | |
45 | + <% end %> | |
46 | +} | |
47 | +</script> | |
48 | + | |
49 | +<% if @comment && @comment.errors.any? %> | |
50 | + <%= error_messages_for :comment %> | |
51 | +<% end %> | |
52 | + | |
53 | +<div class="post_comment_box <%= ((defined? show_form) && show_form) ? 'opened' : 'closed' %>"> | |
54 | + | |
55 | + <%= link_to(_('Post a comment'), '#', :class => 'display-comment-form') if display_link && @comment.reply_of_id.blank? %> | |
56 | +<% remote_form_for(:comment, comment, :url => {:profile => profile.identifier, :controller => 'comment', :action => (edition_mode ? 'update' : 'create'), :id => (edition_mode ? comment.id : @page.id)}, :html => { :class => 'comment_form' } ) do |f| %> | |
57 | + | |
58 | + <%= required_fields_message %> | |
59 | + | |
60 | + <% unless logged_in? %> | |
61 | + | |
62 | + <%= required labelled_form_field(_('Name'), f.text_field(:name)) %> | |
63 | + <%= required labelled_form_field(_('e-mail'), f.text_field(:email)) %> | |
64 | + <p> | |
65 | + <%= _('If you are a registered user, you can login and be automatically recognized.') %> | |
66 | + </p> | |
67 | + | |
68 | + <% end %> | |
69 | + | |
70 | + <% if !edition_mode && !pass_without_comment_captcha? %> | |
71 | + <%= hidden_field_tag(:recaptcha_response_field, nil, :id => nil) %> | |
72 | + <%= hidden_field_tag(:recaptcha_challenge_field, nil, :id => nil) %> | |
73 | + <% end %> | |
74 | + | |
75 | + <%= labelled_form_field(_('Title'), f.text_field(:title)) %> | |
76 | + <%= required labelled_form_field(_('Enter your comment'), f.text_area(:body, :rows => 5)) %> | |
77 | + | |
78 | + <%= hidden_field_tag(:confirm, 'false') %> | |
79 | + <%= hidden_field_tag(:view, params[:view])%> | |
80 | + <%= f.hidden_field(:reply_of_id) %> | |
81 | + | |
82 | + <%= @plugins.dispatch(:comment_form_extra_contents, local_assigns).collect { |content| instance_eval(&content) }.join("") %> | |
83 | + | |
84 | + <% button_bar do %> | |
85 | + <%= submit_button('add', _('Post comment'), :onclick => "if(check_captcha(this)) { save_comment(this) } else { check_captcha(this, save_comment)};return false;") %> | |
86 | + <% if !edition_mode %> | |
87 | + <%= button :cancel, _('Cancel'), '', :id => 'cancel-comment' %> | |
88 | + <% else %> | |
89 | + <%= button :cancel, _('Cancel'), '#', :onclick => "jQuery.colorbox.close();" %> | |
90 | + <% end %> | |
91 | + <% end %> | |
92 | +<% end %> | |
93 | + | |
94 | + | |
95 | +</div><!-- end class="post_comment_box" --> | |
96 | +</div><!-- end class="page-comment-form" --> | |
97 | + | |
98 | +<%= javascript_include_tag 'comment_form'%> | ... | ... |
app/views/contact/new.html.erb
... | ... | @@ -19,7 +19,10 @@ |
19 | 19 | <%= labelled_form_field _('City and state'), location_fields %> |
20 | 20 | <% end %> |
21 | 21 | <%= required f.text_field(:subject) %> |
22 | - <%= required f.text_area(:message, :rows => 10, :cols => 60) %> | |
22 | + | |
23 | + <%= render :file => 'shared/tiny_mce' %> | |
24 | + <%= required f.text_area(:message, :class => 'mceEditor') %> | |
25 | + | |
23 | 26 | <%= labelled_form_field check_box(:contact, :receive_a_copy) + _('I want to receive a copy of the message in my e-mail.'), '' %> |
24 | 27 | |
25 | 28 | <%= submit_button(:send, _('Send'), :onclick => "$('confirm').value = 'true'") %> | ... | ... |
app/views/content_viewer/edit_comment.html.erb
app/views/content_viewer/view_page.html.erb
... | ... | @@ -8,6 +8,12 @@ |
8 | 8 | |
9 | 9 | <%= render :partial => 'confirm_unfollow' %> |
10 | 10 | |
11 | +<script type="text/javascript"> | |
12 | + window.ONE_COMMENT = "<%= _('One comment') %>"; | |
13 | + window.COMMENT_PLURAL = "<%= _('comments') %>"; | |
14 | + window.NO_COMMENT_YET = "<%= _('No comments yet') %>"; | |
15 | +</script> | |
16 | + | |
11 | 17 | <div id="article-toolbar"></div> |
12 | 18 | |
13 | 19 | <script type="text/javascript"> |
... | ... | @@ -60,7 +66,6 @@ |
60 | 66 | addthis_options = '<%= escape_javascript( NOOSFERO_CONF['addthis_options'] ) %>'; |
61 | 67 | </script> |
62 | 68 | <a href="http://www.addthis.com/bookmark.php" id="bt_addThis" target="_blank" onmouseover="return addthis_open(this, '', '[URL]')" onmouseout="addthis_close()" onclick="return addthis_sendto()"><%= addthis_image_tag %></a> |
63 | -<script type="text/javascript" src="http://s7.addthis.com/js/152/addthis_widget.js"></script> | |
64 | 69 | </div> |
65 | 70 | <% end %> |
66 | 71 | |
... | ... | @@ -85,20 +90,21 @@ |
85 | 90 | |
86 | 91 | <% if @page.accept_comments? || @comments_count > 0 %> |
87 | 92 | <h3 <%= 'class="no-comments-yet"' if @comments_count == 0 %>> |
88 | - <%= number_of_comments(@page) %> | |
93 | + <%= display_number_of_comments(@comments_count) %> | |
89 | 94 | </h3> |
90 | 95 | <% end %> |
91 | 96 | |
92 | - <% if @page.accept_comments? && @comments_count > 1 %> | |
93 | - <%= link_to(_('Post a comment'), '#', :class => 'display-comment-form', :id => 'top-post-comment-button') %> | |
97 | + <% if @page.accept_comments? && @comments.count > 1 %> | |
98 | + <%= link_to(_('Post a comment'), '#', :class => 'display-comment-form', :id => 'top-post-comment-button', :onclick => "jQuery('#page-comment-form .display-comment-form').first().click();") %> | |
94 | 99 | <% end %> |
95 | 100 | |
96 | 101 | <ul class="article-comments-list"> |
97 | - <%= render :partial => 'comment', :collection => @comments %> | |
102 | + <%= render :partial => 'comment/comment', :collection => @comments %> | |
103 | + <%= pagination_links @comments, :param_name => 'comment_page' %> | |
98 | 104 | </ul> |
99 | 105 | |
100 | 106 | <% if @page.accept_comments? %> |
101 | - <div id="page-comment-form"><%= render :partial => 'comment_form', :locals => {:url => url_for(@page.view_url.merge({:only_path => true})), :display_link => true, :cancel_triggers_hide => true}%></div> | |
107 | + <div id='page-comment-form' class='page-comment-form'><%= render :partial => 'comment/comment_form', :locals =>{:comment => Comment.new, :url => {:controller => :comment, :action => :create}, :display_link => true, :cancel_triggers_hide => true}%></div> | |
102 | 108 | <% end %> |
103 | 109 | </div><!-- end class="comments" --> |
104 | 110 | ... | ... |
app/views/layouts/application-ng.html.erb
... | ... | @@ -17,6 +17,10 @@ |
17 | 17 | content.respond_to?(:call) ? content.call : content |
18 | 18 | end.join("\n") |
19 | 19 | %> |
20 | + | |
21 | + <script type='text/javascript'> | |
22 | + DEFAULT_LOADING_MESSAGE = <%="'#{ _('loading...') }'" %>; | |
23 | + </script> | |
20 | 24 | </head> |
21 | 25 | <body class="<%= body_classes %>"> |
22 | 26 | <a href="#content" id="link-go-content"><span><%= _("Go to the content") %></span></a> |
... | ... | @@ -56,5 +60,6 @@ |
56 | 60 | </div><!-- end id="theme-footer" --> |
57 | 61 | <%= noosfero_layout_features %> |
58 | 62 | <%= theme_javascript_ng %> |
63 | + <%= addthis_javascript %> | |
59 | 64 | </body> |
60 | 65 | </html> | ... | ... |
app/views/profile/report_abuse.html.erb
... | ... | @@ -4,7 +4,9 @@ |
4 | 4 | <%= hidden_field_tag(:content_type, params[:content_type]) %> |
5 | 5 | <%= hidden_field_tag(:content_id, params[:content_id]) %> |
6 | 6 | |
7 | - <p><%= recaptcha_tags :ajax => true, :display => {:theme => 'clean'} %> </p> | |
7 | + <% unless user.is_admin? %> | |
8 | + <p><%= recaptcha_tags :ajax => true, :display => {:theme => 'clean'} %> </p> | |
9 | + <% end %> | |
8 | 10 | |
9 | 11 | <%= submit_button(:send, _('Report profile'), :style => 'float: left; cursor: pointer;', :id => 'report-abuse-submit-button', :onclick => "jQuery('#form-submit-loading').show()") %> |
10 | 12 | <%= button(:cancel, _('Cancel'), {}, :style => 'float: left; padding-top: 0px; padding-bottom: 0px;', :onclick => 'jQuery.colorbox.close(); return false;')%> | ... | ... |
app/views/profile/send_mail.html.erb
... | ... | @@ -4,11 +4,13 @@ |
4 | 4 | |
5 | 5 | <%= error_messages_for :mailing %> |
6 | 6 | |
7 | -<%= render :file => 'shared/tiny_mce' %> | |
8 | - | |
9 | 7 | <%= form_for :mailing, :url => {:action => 'send_mail'}, :html => {:id => 'mailing-form'} do |f| %> |
8 | + | |
10 | 9 | <%= labelled_form_field(_('Subject:'), f.text_field(:subject)) %> |
10 | + | |
11 | + <%= render :file => 'shared/tiny_mce' %> | |
11 | 12 | <%= labelled_form_field(_('Body:'), f.text_area(:body, :class => 'mceEditor')) %> |
13 | + | |
12 | 14 | <%= submit_button(:send, _('Send')) %> |
13 | 15 | <%= button :cancel, _('Cancel e-mail'), :back %> |
14 | 16 | <% end %> | ... | ... |
app/views/search/_full_blog.html.erb
... | ... | @@ -7,12 +7,12 @@ |
7 | 7 | <tr class="search-blog-items"> |
8 | 8 | <td class="search-field-label"><%= _("Last posts") %></td> |
9 | 9 | |
10 | - <% r = blog.children.find(:all, :order => :updated_at, :conditions => ['type != ?', 'RssFeed']).last(3) %> | |
11 | - <td class="<%= "search-field-none" if r.empty? %>"> | |
12 | - <% r.each do |a| %> | |
13 | - <%= link_to a.title, a.view_url, :class => 'search-blog-sample-item '+icon_for_article(a) %> | |
10 | + <% last_posts = blog.last_posts %> | |
11 | + <td class="<%= "search-field-none" if last_posts.empty? %>"> | |
12 | + <% last_posts.each do |post| %> | |
13 | + <%= link_to post.title, post.view_url, :class => 'search-blog-sample-item '+icon_for_article(post) %> | |
14 | 14 | <% end %> |
15 | - <%= _('None') if r.empty? %> | |
15 | + <%= _('None') if last_posts.empty? %> | |
16 | 16 | </td> |
17 | 17 | </tr> |
18 | 18 | ... | ... |
app/views/shared/logged_in/xmpp_chat.html.erb
... | ... | @@ -6,6 +6,6 @@ |
6 | 6 | </div> |
7 | 7 | </div> |
8 | 8 | <a href='#' id='chat-online-users-title' onclick='return false;'> |
9 | - <div class='header'><i class='icon-chat'></i><span><%= _("Friends in chat <span class='amount_of_friends'>(%{amount})</span>") %></span></div> | |
9 | + <div class='header'><i class='icon-chat'></i><span><%= _("Friends in chat (<span class='amount_of_friends'>%{amount}</span>)") %></span></div> | |
10 | 10 | </a> |
11 | 11 | </div> | ... | ... |
app/views/shared/tiny_mce.html.erb
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| %> | |
15 | + second_line += ',<%=macro.identifier %>' | |
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| %> | |
36 | + c.onRenderMenu.add(function(c, m) { | |
37 | + m.add({ | |
38 | + title: <%= macro_title(macro).to_json %>, | |
39 | + onclick: <%= generate_macro_config_dialog(macro) %> | |
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| %> | |
79 | + ed.addButton('<%= macro.identifier %>', { | |
80 | + title: <%= macro_title(macro).to_json %>, | |
81 | + onclick: <%= generate_macro_config_dialog(macro) %>, | |
82 | + image : '<%= macro.configuration[:icon_path]%>' | |
83 | + }); | |
84 | + <% end %> | |
85 | + } | |
86 | +}); | |
41 | 87 | |
42 | 88 | function convertWord(type, content) { |
43 | 89 | switch (type) { | ... | ... |
app/views/spam/index.html.erb
... | ... | @@ -8,7 +8,7 @@ |
8 | 8 | <div id='article'> |
9 | 9 | <div class="comments" id="comments_list"> |
10 | 10 | <ul class="article-comments-list"> |
11 | - <%= render :partial => 'content_viewer/comment', :collection => @spam %> | |
11 | + <%= render :partial => 'comment/comment', :collection => @spam %> | |
12 | 12 | </ul> |
13 | 13 | </div> |
14 | 14 | </div> | ... | ... |
app/views/themes/index.html.erb
... | ... | @@ -12,7 +12,7 @@ |
12 | 12 | "/designs/templates/#{template.id}/thumbnail.png", |
13 | 13 | :alt => _('The "%s" template')) + |
14 | 14 | '<div class="opt-info">'.html_safe + |
15 | - content_tag('strong', template.id, :class => 'name') + | |
15 | + content_tag('strong', template.name, :class => 'name') + | |
16 | 16 | ' <br/> '.html_safe |
17 | 17 | |
18 | 18 | if @current_template == template.id # selected | ... | ... |
... | ... | @@ -0,0 +1,15 @@ |
1 | +<h2> <%= _("Editing trusted site") %> </h2> | |
2 | + | |
3 | +<% form_tag :action => :update do %> | |
4 | + | |
5 | + <%= text_field_tag :site, @site %> | |
6 | + <%= hidden_field_tag :orig_site, @site %> | |
7 | + | |
8 | + <% button_bar do %> | |
9 | + <%= submit_button('save', _('Save changes'), :cancel => {:action => 'index'} ) %> | |
10 | + <% end %> | |
11 | +<% end %> | |
12 | + | |
13 | +<script> | |
14 | + jQuery(function() { jQuery('input#site').focus(); } ); | |
15 | +</script> | ... | ... |
... | ... | @@ -0,0 +1,28 @@ |
1 | +<h1><%= _('Manage trusted sites') %></h1> | |
2 | + | |
3 | +<p> | |
4 | +<%= _('Here you can manage the list of trusted sites of your environment. A trusted site is a site that you consider safe enough to incorporate their content through <em>iframes</em>.') %> | |
5 | +</p> | |
6 | + | |
7 | +<table> | |
8 | + <tr> | |
9 | + <th><%= _('Site') %></th> | |
10 | + <th><%= _('Actions') %></th> | |
11 | + </tr> | |
12 | + <% @sites.each do |site| %> | |
13 | + <tr> | |
14 | + <td> | |
15 | + <%= link_to site, :action => 'show', :site => site %> | |
16 | + </td> | |
17 | + <td style='white-space: nowrap;'> | |
18 | + <%= button_without_text :edit, _('Edit'), :action => 'edit', :site => site %> | |
19 | + <%= button_without_text :remove, _('Remove'), {:action => :destroy, :site => site}, :method => :delete, :confirm => _('Are you sure you want to remove this site from the list of trusted sites?') %> | |
20 | + </td> | |
21 | + </tr> | |
22 | + <% end %> | |
23 | +</table> | |
24 | + | |
25 | +<% button_bar do %> | |
26 | + <%= button :add, _('Add a trusted site'), :action => 'new' %> | |
27 | + <%= button :back, _('Back to admin panel'), :controller => 'admin_panel' %> | |
28 | +<% end %> | ... | ... |
... | ... | @@ -0,0 +1,14 @@ |
1 | +<h2> <%= _("Add a new trusted site") %> </h2> | |
2 | + | |
3 | +<% form_tag :action => :create do %> | |
4 | + | |
5 | + <%= text_field_tag :site, @site %> | |
6 | + | |
7 | + <% button_bar do %> | |
8 | + <%= submit_button('save', _('Add trusted site'), :cancel => {:action => 'index'} ) %> | |
9 | + <% end %> | |
10 | +<% end %> | |
11 | + | |
12 | +<script> | |
13 | + jQuery(function() { jQuery('input#site').focus(); } ); | |
14 | +</script> | ... | ... |
config/routes.rb
... | ... | @@ -76,6 +76,9 @@ Noosfero::Application.routes.draw do |
76 | 76 | # profile search |
77 | 77 | match 'profile/:profile/search', :controller => 'profile_search', :action => 'index', :profile => /#{Noosfero.identifier_format}/ |
78 | 78 | |
79 | + # comments | |
80 | + map.comment 'profile/:profile/comment/:action/:id', :controller => 'comment', :profile => /#{Noosfero.identifier_format}/ | |
81 | + | |
79 | 82 | # public profile information |
80 | 83 | match 'profile/:profile(/:action(/:id))', :controller => 'profile', :action => 'index', :id => /[^\/]*/, :profile => /#{Noosfero.identifier_format}/ |
81 | 84 | |
... | ... | @@ -121,8 +124,6 @@ Noosfero::Application.routes.draw do |
121 | 124 | # cache stuff - hack |
122 | 125 | match 'public/:action/:id', :controller => 'public' |
123 | 126 | |
124 | - match ':profile/edit_comment/:id/*page', :controller => 'content_viewer', :action => 'edit_comment', :profile => /#{Noosfero.identifier_format}/ | |
125 | - | |
126 | 127 | # match requests for profiles that don't have a custom domain |
127 | 128 | match ':profile(/*page)', :controller => 'content_viewer', :action => 'view_page', :profile => /#{Noosfero.identifier_format}/, :constraints => EnvironmentDomainConstraint.new |
128 | 129 | ... | ... |
db/migrate/20130605135210_change_article_published_at_from_date_to_datetime.rb
0 → 100644
db/migrate/20130606110602_change_article_versions_published_at_from_date_to_datetime.rb
0 → 100644
db/migrate/20130711213046_add_manage_environment_trusted_sites_to_admin_role.rb
0 → 100644
... | ... | @@ -0,0 +1,17 @@ |
1 | +class AddManageEnvironmentTrustedSitesToAdminRole < ActiveRecord::Migration | |
2 | + def self.up | |
3 | + Environment.all.map(&:id).each do |id| | |
4 | + role = Environment::Roles.admin(id) | |
5 | + role.permissions << 'manage_environment_trusted_sites' | |
6 | + role.save! | |
7 | + end | |
8 | + end | |
9 | + | |
10 | + def self.down | |
11 | + Environment.all.map(&:id).each do |id| | |
12 | + role = Environment::Roles.admin(id) | |
13 | + role.permissions -= ['manage_environment_trusted_sites'] | |
14 | + role.save! | |
15 | + end | |
16 | + end | |
17 | +end | ... | ... |
db/schema.rb
... | ... | @@ -9,7 +9,7 @@ |
9 | 9 | # |
10 | 10 | # It's strongly recommended to check this file into your version control system. |
11 | 11 | |
12 | -ActiveRecord::Schema.define(:version => 20130429214630) do | |
12 | +ActiveRecord::Schema.define(:version => 20130606110602) do | |
13 | 13 | |
14 | 14 | create_table "abuse_reports", :force => true do |t| |
15 | 15 | t.integer "reporter_id" |
... | ... | @@ -76,7 +76,7 @@ ActiveRecord::Schema.define(:version => 20130429214630) do |
76 | 76 | t.text "setting" |
77 | 77 | t.boolean "notify_comments", :default => false |
78 | 78 | t.integer "hits", :default => 0 |
79 | - t.date "published_at" | |
79 | + t.datetime "published_at" | |
80 | 80 | t.string "source" |
81 | 81 | t.boolean "highlighted", :default => false |
82 | 82 | t.string "external_link" |
... | ... | @@ -119,7 +119,7 @@ ActiveRecord::Schema.define(:version => 20130429214630) do |
119 | 119 | t.text "setting" |
120 | 120 | t.boolean "notify_comments", :default => true |
121 | 121 | t.integer "hits", :default => 0 |
122 | - t.date "published_at" | |
122 | + t.datetime "published_at" | |
123 | 123 | t.string "source" |
124 | 124 | t.boolean "highlighted", :default => false |
125 | 125 | t.string "external_link" |
... | ... | @@ -221,6 +221,7 @@ ActiveRecord::Schema.define(:version => 20130429214630) do |
221 | 221 | t.string "source_type" |
222 | 222 | t.string "user_agent" |
223 | 223 | t.string "referrer" |
224 | + t.integer "group_id" | |
224 | 225 | end |
225 | 226 | |
226 | 227 | add_index "comments", ["source_id", "spam"], :name => "index_comments_on_source_id_and_spam" |
... | ... | @@ -264,11 +265,11 @@ ActiveRecord::Schema.define(:version => 20130429214630) do |
264 | 265 | t.text "design_data" |
265 | 266 | t.text "custom_header" |
266 | 267 | t.text "custom_footer" |
267 | - t.string "theme", :default => "default", :null => false | |
268 | + t.string "theme", :default => "default", :null => false | |
268 | 269 | t.text "terms_of_use_acceptance_text" |
269 | 270 | t.datetime "created_at" |
270 | 271 | t.datetime "updated_at" |
271 | - t.integer "reports_lower_bound", :default => 0, :null => false | |
272 | + t.integer "reports_lower_bound", :default => 0, :null => false | |
272 | 273 | t.string "redirection_after_login", :default => "keep_on_same_page" |
273 | 274 | t.text "signup_welcome_text" |
274 | 275 | t.string "languages" |
... | ... | @@ -428,7 +429,7 @@ ActiveRecord::Schema.define(:version => 20130429214630) do |
428 | 429 | t.string "type" |
429 | 430 | t.string "identifier" |
430 | 431 | t.integer "environment_id" |
431 | - t.boolean "active", :default => true | |
432 | + t.boolean "active", :default => true | |
432 | 433 | t.string "address" |
433 | 434 | t.string "contact_phone" |
434 | 435 | t.integer "home_page_id" |
... | ... | @@ -439,21 +440,21 @@ ActiveRecord::Schema.define(:version => 20130429214630) do |
439 | 440 | t.float "lat" |
440 | 441 | t.float "lng" |
441 | 442 | t.integer "geocode_precision" |
442 | - t.boolean "enabled", :default => true | |
443 | - t.string "nickname", :limit => 16 | |
443 | + t.boolean "enabled", :default => true | |
444 | + t.string "nickname", :limit => 16 | |
444 | 445 | t.text "custom_header" |
445 | 446 | t.text "custom_footer" |
446 | 447 | t.string "theme" |
447 | - t.boolean "public_profile", :default => true | |
448 | + t.boolean "public_profile", :default => true | |
448 | 449 | t.date "birth_date" |
449 | 450 | t.integer "preferred_domain_id" |
450 | 451 | t.datetime "updated_at" |
451 | - t.boolean "visible", :default => true | |
452 | + t.boolean "visible", :default => true | |
452 | 453 | t.integer "image_id" |
453 | - t.boolean "validated", :default => true | |
454 | + t.boolean "validated", :default => true | |
454 | 455 | t.string "cnpj" |
455 | 456 | t.string "national_region_code" |
456 | - t.boolean "is_template", :default => false | |
457 | + t.boolean "is_template", :default => false | |
457 | 458 | t.integer "template_id" |
458 | 459 | t.string "redirection_after_login" |
459 | 460 | end | ... | ... |
debian/changelog
1 | +noosfero (0.44.0) unstable; urgency=low | |
2 | + | |
3 | + * New features release | |
4 | + | |
5 | + -- Rodrigo Souto <rodrigo@colivre.coop.br> Wed, 17 Jul 2013 02:19:44 -0300 | |
6 | + | |
7 | +noosfero (0.43.1) unstable; urgency=low | |
8 | + | |
9 | + * Bugfix release | |
10 | + | |
11 | + -- Rodrigo Souto <rodrigo@colivre.coop.br> Wed, 17 Jul 2013 01:20:46 -0300 | |
12 | + | |
13 | +noosfero (0.43.0) unstable; urgency=low | |
14 | + | |
15 | + * Solr search converted to a plugin | |
16 | + | |
17 | + -- Rodrigo Souto <rodrigo@ravioli> Tue, 18 Jun 2013 11:14:56 -0300 | |
18 | + | |
1 | 19 | noosfero (0.43.0~rc20130529152434) squeeze-test; urgency=low |
2 | 20 | |
3 | 21 | * RC of 0.43.0 with solr as a plugin | ... | ... |
debian/control
... | ... | @@ -0,0 +1,21 @@ |
1 | +#!/usr/bin/ruby | |
2 | + | |
3 | +require 'yaml' | |
4 | + | |
5 | +DBCONFIG = ARGV.first || '/etc/database/database.yml' | |
6 | + | |
7 | +$dbconfig = {} | |
8 | + | |
9 | +checks = [ | |
10 | + lambda { File.exists?(DBCONFIG) }, | |
11 | + lambda { $dbconfig = YAML.load_file(DBCONFIG) }, | |
12 | + lambda { $dbconfig['production'] }, | |
13 | + lambda { $dbconfig['production']['adapter'] }, | |
14 | + lambda { $dbconfig['production']['database'] }, | |
15 | +] | |
16 | + | |
17 | +if checks.all?(&:call) | |
18 | + exit 0 | |
19 | +else | |
20 | + exit 1 | |
21 | +end | ... | ... |
debian/noosfero.install
etc/init.d/noosfero
... | ... | @@ -44,6 +44,13 @@ if [ -z "$NOOSFERO_DIR" ] || [ -z "$NOOSFERO_USER" ]; then |
44 | 44 | exit 0 |
45 | 45 | fi |
46 | 46 | |
47 | +if test -x /usr/sbin/noosfero-check-dbconfig ; then | |
48 | + if ! noosfero-check-dbconfig; then | |
49 | + echo "Noosfero database access not configured, service disabled." | |
50 | + exit 0 | |
51 | + fi | |
52 | +fi | |
53 | + | |
47 | 54 | ###################### |
48 | 55 | |
49 | 56 | main_script() { | ... | ... |
features/comment.feature
... | ... | @@ -17,14 +17,6 @@ Feature: comment |
17 | 17 | And feature "captcha_for_logged_users" is disabled on environment |
18 | 18 | And I am logged in as "booking" |
19 | 19 | |
20 | - Scenario: not post a comment without javascript | |
21 | - Given I am on /booking/article-to-comment | |
22 | - And I follow "Post a comment" | |
23 | - And I fill in "Title" with "Hey ho, let's go!" | |
24 | - And I fill in "Enter your comment" with "Hey ho, let's go!" | |
25 | - When I press "Post comment" | |
26 | - Then I should not see "Hey ho, let's go" | |
27 | - | |
28 | 20 | # This test requires some way to overcome the captcha with unauthenticated |
29 | 21 | # user. |
30 | 22 | @selenium-fixme |
... | ... | @@ -79,14 +71,14 @@ Feature: comment |
79 | 71 | |
80 | 72 | @selenium |
81 | 73 | Scenario: render comment form and go to bottom |
82 | - Given I am on /booking/article-with-comment | |
74 | + Given I am on /booking/article-to-comment | |
83 | 75 | When I follow "Post a comment" |
84 | 76 | Then I should see "Enter your comment" |
85 | - And I should be on /booking/article-with-comment | |
77 | + And I should be on /booking/article-to-comment | |
86 | 78 | |
87 | 79 | @selenium |
88 | 80 | Scenario: keep comments field filled while trying to do a comment |
89 | - Given I am on /booking/article-with-comment | |
81 | + Given I am on /booking/article-to-comment | |
90 | 82 | And I follow "Post a comment" |
91 | 83 | And I fill in "Title" with "Joey Ramone" |
92 | 84 | When I press "Post comment" | ... | ... |
features/comment_reply.feature
... | ... | @@ -15,15 +15,9 @@ Feature: comment |
15 | 15 | | article to comment | booking | root comment | this comment is not a reply | |
16 | 16 | | another article | booking | some comment | this is my very own comment | |
17 | 17 | |
18 | - Scenario: not post a comment without javascript | |
19 | - Given I am on /booking/article-to-comment | |
20 | - When I follow "Reply" within ".comment-balloon" | |
21 | - Then I should not see "Enter your comment" within "div.comment-balloon" | |
22 | - | |
23 | 18 | Scenario: not show any reply form by default |
24 | 19 | When I go to /booking/article-to-comment |
25 | 20 | Then I should not see "Enter your comment" within "div.comment-balloon" |
26 | - And I should see "Reply" within "div.comment-balloon" | |
27 | 21 | |
28 | 22 | @selenium-fixme |
29 | 23 | Scenario: show error messages when make a blank comment reply |
... | ... | @@ -35,18 +29,13 @@ Feature: comment |
35 | 29 | And I should see "Body can't be blank" within "div.comment_reply" |
36 | 30 | |
37 | 31 | @selenium |
38 | - Scenario: not show any reply form by default | |
39 | - When I go to /booking/article-to-comment | |
40 | - Then I should not see "Enter your comment" within "div.comment-balloon" | |
41 | - And I should see "Reply" within "div.comment-balloon" | |
42 | - | |
43 | - @selenium | |
44 | 32 | Scenario: render reply form |
45 | 33 | Given I am on /booking/article-to-comment |
46 | 34 | When I follow "Reply" within ".comment-balloon" |
47 | 35 | Then I should see "Enter your comment" within "div.comment_reply.opened" |
48 | 36 | |
49 | - @selenium | |
37 | + # The text is hidden but the detector gets it anyway | |
38 | + @selenium-fixme | |
50 | 39 | Scenario: cancel comment reply |
51 | 40 | Given I am on /booking/article-to-comment |
52 | 41 | When I follow "Reply" within ".comment-balloon" | ... | ... |
features/search.feature
... | ... | @@ -39,7 +39,7 @@ Feature: search |
39 | 39 | | login | name | |
40 | 40 | | joaosilva | Joao Silva | |
41 | 41 | And the following articles |
42 | - | owner | name | | |
42 | + | owner | name | | |
43 | 43 | | joaosilva | article #1 | |
44 | 44 | | joaosilva | article #2 | |
45 | 45 | | joaosilva | article #3 | |
... | ... | @@ -52,8 +52,6 @@ Feature: search |
52 | 52 | When I go to the search page |
53 | 53 | And I fill in "search-input" with "article" |
54 | 54 | And I press "Search" |
55 | - Then I should see "article #8" within ".search-results-articles" | |
56 | - And I should not see "article #9" within ".search-results-articles" | |
57 | 55 | And I should see "see all (9)" |
58 | 56 | When I follow "see all (9)" |
59 | 57 | Then I should be on the search articles page | ... | ... |
lib/acts_as_having_boxes.rb
... | ... | @@ -29,7 +29,7 @@ module ActsAsHavingBoxes |
29 | 29 | # returns 3 unless the class table has a boxes_limit column. In that case |
30 | 30 | # return the value of the column. |
31 | 31 | def boxes_limit |
32 | - LayoutTemplate.find(layout_template).number_of_boxes || 3 | |
32 | + @boxes_limit ||= LayoutTemplate.find(layout_template).number_of_boxes || 3 | |
33 | 33 | end |
34 | 34 | |
35 | 35 | end | ... | ... |
lib/noosfero.rb
lib/noosfero/plugin.rb
... | ... | @@ -4,6 +4,10 @@ class Noosfero::Plugin |
4 | 4 | |
5 | 5 | attr_accessor :context |
6 | 6 | |
7 | + def initialize(context=nil) | |
8 | + self.context = context | |
9 | + end | |
10 | + | |
7 | 11 | class << self |
8 | 12 | |
9 | 13 | attr_writer :should_load |
... | ... | @@ -151,6 +155,12 @@ class Noosfero::Plugin |
151 | 155 | blocks || [] |
152 | 156 | end |
153 | 157 | |
158 | + def macros | |
159 | + self.class.constants.map do |constant_name| | |
160 | + self.class.const_get(constant_name) | |
161 | + end.select {|klass| klass < Noosfero::Plugin::Macro} | |
162 | + end | |
163 | + | |
154 | 164 | # Here the developer may specify the events to which the plugins can |
155 | 165 | # register and must return true or false. The default value must be false. |
156 | 166 | |
... | ... | @@ -253,8 +263,8 @@ class Noosfero::Plugin |
253 | 263 | |
254 | 264 | # -> Parse and possibly make changes of content (article, block, etc) during HTML rendering |
255 | 265 | # returns = content as string after parser and changes |
256 | - def parse_content(raw_content) | |
257 | - raw_content | |
266 | + def parse_content(html, source) | |
267 | + [html, source] | |
258 | 268 | end |
259 | 269 | |
260 | 270 | # -> Adds links to the admin panel |
... | ... | @@ -290,6 +300,18 @@ class Noosfero::Plugin |
290 | 300 | def filter_comment(comment) |
291 | 301 | end |
292 | 302 | |
303 | + # Define custom logic to filter loaded comments. | |
304 | + # | |
305 | + # Example: | |
306 | + # | |
307 | + # def unavailable_comments(scope) | |
308 | + # scope.without_spams | |
309 | + # end | |
310 | + # | |
311 | + def unavailable_comments(scope) | |
312 | + scope | |
313 | + end | |
314 | + | |
293 | 315 | # This method is called by the CommentHandler background job before sending |
294 | 316 | # the notification email. If the comment is marked as spam (i.e. by calling |
295 | 317 | # <tt>comment.spam!</tt>), then the notification email will *not* be sent. |
... | ... | @@ -331,6 +353,30 @@ class Noosfero::Plugin |
331 | 353 | def comment_marked_as_ham(comment) |
332 | 354 | end |
333 | 355 | |
356 | + # Adds extra actions for comments | |
357 | + # returns = list of hashes or lambda block that creates a list of hashes | |
358 | + # example: | |
359 | + # | |
360 | + # def comment_actions(comment) | |
361 | + # [{:link => link_to_function(...)}] | |
362 | + # end | |
363 | + # | |
364 | + def comment_actions(comment) | |
365 | + nil | |
366 | + end | |
367 | + | |
368 | + # This method is called when the user click on comment actions menu. | |
369 | + # returns = list or lambda block that return ids of enabled menu items for comments | |
370 | + # example: | |
371 | + # | |
372 | + # def check_comment_actions(comment) | |
373 | + # ['#action1', '#action2'] | |
374 | + # end | |
375 | + # | |
376 | + def check_comment_actions(comment) | |
377 | + [] | |
378 | + end | |
379 | + | |
334 | 380 | # -> Adds fields to the signup form |
335 | 381 | # returns = lambda block that creates html code |
336 | 382 | def signup_extra_contents |
... | ... | @@ -405,6 +451,12 @@ class Noosfero::Plugin |
405 | 451 | nil |
406 | 452 | end |
407 | 453 | |
454 | + # -> Adds adicional content to comment form | |
455 | + # returns = lambda block that creates html code | |
456 | + def comment_form_extra_contents(args) | |
457 | + nil | |
458 | + end | |
459 | + | |
408 | 460 | # -> Finds objects by their contents |
409 | 461 | # returns = {:results => [a, b, c, ...], ...} |
410 | 462 | # P.S.: The plugin might add other informations on the return hash for its | ... | ... |
... | ... | @@ -0,0 +1,63 @@ |
1 | +class Noosfero::Plugin::Macro | |
2 | + | |
3 | + attr_accessor :context | |
4 | + | |
5 | + class << self | |
6 | + # Options | |
7 | + # | |
8 | + # [:icon_path] Determines the path to icon to be used in the button on | |
9 | + # tinymce | |
10 | + # [:title] Former name of the macro | |
11 | + # [:skip_dialog] Skip configuration dialog on tinymce | |
12 | + # [:js_files] Javascripts that should be included on tinymce | |
13 | + # [:css_files Css files that should be included on tinymce | |
14 | + # [:generator] Javascript code that will be loaded when the macro button | |
15 | + # is clicked on tinymce | |
16 | + # [:params] Hash of macro fields that the user might configure | |
17 | + # | |
18 | + def configuration | |
19 | + {} | |
20 | + end | |
21 | + | |
22 | + def plugin | |
23 | + name.split('::')[0...-1].join('::').constantize | |
24 | + end | |
25 | + | |
26 | + def identifier | |
27 | + name.underscore | |
28 | + end | |
29 | + end | |
30 | + | |
31 | + def initialize(context=nil) | |
32 | + self.context = context | |
33 | + end | |
34 | + | |
35 | + def attributes(macro) | |
36 | + macro.attributes.to_hash. | |
37 | + select {|key, value| key[0..10] == 'data-macro-'}. | |
38 | + inject({}){|result, a| result.merge({a[0][11..-1] => a[1]})}. | |
39 | + with_indifferent_access | |
40 | + end | |
41 | + | |
42 | + def convert(macro, source) | |
43 | + macro_name = macro['data-macro'] | |
44 | + attrs = attributes(macro) | |
45 | + | |
46 | + begin | |
47 | + content = parse(attrs, macro.inner_html, source) | |
48 | + macro['class'] = "parsed-macro #{macro_name}" | |
49 | + rescue Exception => exception | |
50 | + content = _("Unsupported macro %s!") % macro_name | |
51 | + macro['class'] = "failed-macro #{macro_name}" | |
52 | + end | |
53 | + | |
54 | + attrs.each {|key, value| macro.remove_attribute("data-macro-#{key}")} | |
55 | + content | |
56 | + end | |
57 | + | |
58 | + # This is the method the macros should override | |
59 | + def parse(attrs, inner_html, source) | |
60 | + raise | |
61 | + end | |
62 | + | |
63 | +end | ... | ... |
lib/noosfero/plugin/manager.rb
... | ... | @@ -23,7 +23,7 @@ class Noosfero::Plugin::Manager |
23 | 23 | dispatch_without_flatten(event, *args).flatten |
24 | 24 | end |
25 | 25 | |
26 | - def dispatch_plugins(event, *args) | |
26 | + def fetch_plugins(event, *args) | |
27 | 27 | map { |plugin| plugin.class if plugin.send(event, *args) }.compact.flatten |
28 | 28 | end |
29 | 29 | |
... | ... | @@ -33,7 +33,7 @@ class Noosfero::Plugin::Manager |
33 | 33 | |
34 | 34 | alias :dispatch_scopes :dispatch_without_flatten |
35 | 35 | |
36 | - def first(event, *args) | |
36 | + def dispatch_first(event, *args) | |
37 | 37 | result = nil |
38 | 38 | each do |plugin| |
39 | 39 | result = plugin.send(event, *args) |
... | ... | @@ -42,7 +42,7 @@ class Noosfero::Plugin::Manager |
42 | 42 | result |
43 | 43 | end |
44 | 44 | |
45 | - def first_plugin(event, *args) | |
45 | + def fetch_first_plugin(event, *args) | |
46 | 46 | result = nil |
47 | 47 | each do |plugin| |
48 | 48 | if plugin.send(event, *args) |
... | ... | @@ -53,11 +53,38 @@ class Noosfero::Plugin::Manager |
53 | 53 | result |
54 | 54 | end |
55 | 55 | |
56 | + def pipeline(event, *args) | |
57 | + each do |plugin| | |
58 | + result = plugin.send(event, *args) | |
59 | + result = result.kind_of?(Array) ? result : [result] | |
60 | + raise ArgumentError, "Pipeline broken by #{plugin.class.name} on #{event} with #{result.length} arguments instead of #{args.length}." if result.length != args.length | |
61 | + args = result | |
62 | + end | |
63 | + args.length < 2 ? args.first : args | |
64 | + end | |
65 | + | |
66 | + def filter(property, data) | |
67 | + inject(data) {|data, plugin| data = plugin.send(property, data)} | |
68 | + end | |
69 | + | |
70 | + def parse_macro(macro_name, macro, source = nil) | |
71 | + macro_instance = enabled_macros[macro_name] || default_macro | |
72 | + macro_instance.convert(macro, source) | |
73 | + end | |
74 | + | |
56 | 75 | def enabled_plugins |
57 | 76 | @enabled_plugins ||= (Noosfero::Plugin.all & environment.enabled_plugins).map do |plugin| |
58 | - p = plugin.constantize.new | |
59 | - p.context = context | |
60 | - p | |
77 | + plugin.constantize.new(context) | |
78 | + end | |
79 | + end | |
80 | + | |
81 | + def default_macro | |
82 | + @default_macro ||= Noosfero::Plugin::Macro.new(context) | |
83 | + end | |
84 | + | |
85 | + def enabled_macros | |
86 | + @enabled_macros ||= dispatch(:macros).inject({}) do |memo, macro| | |
87 | + memo.merge!(macro.identifier => macro.new(context)) | |
61 | 88 | end |
62 | 89 | end |
63 | 90 | ... | ... |
plugins/comment_group/controllers/profile/comment_group_plugin_profile_controller.rb
0 → 100644
... | ... | @@ -0,0 +1,16 @@ |
1 | +class CommentGroupPluginProfileController < 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 | |
12 | + page.replace_html "comment-count-#{group_id}", comments.count | |
13 | + end | |
14 | + end | |
15 | + | |
16 | +end | ... | ... |
plugins/comment_group/controllers/public/comment_group_plugin_public_controller.rb
0 → 100644
plugins/comment_group/db/migrate/20121220201629_add_group_id_to_comment.rb
0 → 100644
... | ... | @@ -0,0 +1,32 @@ |
1 | +require_dependency 'comment_group_plugin/ext/article' | |
2 | +require_dependency 'comment_group_plugin/ext/comment' | |
3 | + | |
4 | +class CommentGroupPlugin < Noosfero::Plugin | |
5 | + | |
6 | + def self.plugin_name | |
7 | + "Comment Group" | |
8 | + end | |
9 | + | |
10 | + def self.plugin_description | |
11 | + _("A plugin that display comment groups.") | |
12 | + end | |
13 | + | |
14 | + def unavailable_comments(scope) | |
15 | + scope.without_group | |
16 | + end | |
17 | + | |
18 | + def comment_form_extra_contents(args) | |
19 | + comment = args[:comment] | |
20 | + group_id = comment.group_id || args[:group_id] | |
21 | + lambda { | |
22 | + hidden_field_tag('comment[group_id]', group_id) if group_id | |
23 | + } | |
24 | + end | |
25 | + | |
26 | + def js_files | |
27 | + 'comment_group_macro.js' | |
28 | + end | |
29 | + | |
30 | +end | |
31 | + | |
32 | +require_dependency 'comment_group_plugin/macros/allow_comment' | ... | ... |
plugins/comment_group/lib/comment_group_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/lib/comment_group_plugin/ext/comment.rb
0 → 100644
plugins/comment_group/lib/comment_group_plugin/macros/allow_comment.rb
0 → 100644
... | ... | @@ -0,0 +1,22 @@ |
1 | +#FIXME See a better way to generalize this parameter. | |
2 | +ActionView::Base.sanitized_allowed_attributes += ['data-macro', 'data-macro-group_id'] | |
3 | + | |
4 | +class CommentGroupPlugin::AllowComment < Noosfero::Plugin::Macro | |
5 | + def self.configuration | |
6 | + { :params => [], | |
7 | + :skip_dialog => true, | |
8 | + :generator => 'makeCommentable();', | |
9 | + :js_files => 'comment_group.js', | |
10 | + :icon_path => '/designs/icons/tango/Tango/16x16/emblems/emblem-system.png', | |
11 | + :css_files => 'comment_group.css' } | |
12 | + end | |
13 | + | |
14 | + #FIXME Make this test | |
15 | + def parse(params, inner_html, source) | |
16 | + group_id = params[:group_id].to_i | |
17 | + article = source | |
18 | + count = article.group_comments.without_spam.in_group(group_id).count | |
19 | + | |
20 | + lambda {render :partial => 'plugins/comment_group/views/comment_group.rhtml', :locals => {:group_id => group_id, :article_id => article.id, :inner_html => inner_html, :count => count, :profile_identifier => article.profile.identifier }} | |
21 | + end | |
22 | +end | ... | ... |
... | ... | @@ -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=\"comment_group_plugin/allow_comment\" 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 | + | ... | ... |
... | ... | @@ -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=comment_group_plugin/allow_comment]').length==0) return; //comment_group_plugin/allow_comment 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/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/test/functional/comment_group_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_plugin_profile_controller' | |
3 | + | |
4 | +# Re-raise errors caught by the controller. | |
5 | +class CommentGroupPluginProfileController; def rescue_action(e) raise e end; end | |
6 | + | |
7 | +class CommentGroupPluginProfileControllerTest < ActionController::TestCase | |
8 | + | |
9 | + def setup | |
10 | + @controller = CommentGroupPluginProfileController.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/test/functional/comment_group_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_plugin_public_controller' | |
3 | + | |
4 | +# Re-raise errors caught by the controller. | |
5 | +class CommentGroupPluginPublicController; def rescue_action(e) raise e end; end | |
6 | + | |
7 | +class CommentGroupPluginPublicControllerTest < ActionController::TestCase | |
8 | + | |
9 | + def setup | |
10 | + @controller = CommentGroupPluginPublicController.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/test/unit/comment_group_plugin_test.rb
0 → 100644
... | ... | @@ -0,0 +1,24 @@ |
1 | +require File.dirname(__FILE__) + '/../../../../test/test_helper' | |
2 | + | |
3 | +class CommentGroupPluginTest < 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 'filter_comments returns all the comments wihout group of an article passed as parameter' do | |
14 | + article = fast_create(Article) | |
15 | + c1 = fast_create(Comment, :source_id => article.id, :group_id => 1) | |
16 | + c2 = fast_create(Comment, :source_id => article.id) | |
17 | + c3 = fast_create(Comment, :source_id => article.id) | |
18 | + | |
19 | + plugin = CommentGroupPlugin.new | |
20 | + assert_equal [], [c2, c3] - plugin.filter_comments(article.comments) | |
21 | + assert_equal [], plugin.filter_comments(article.comments) - [c2, c3] | |
22 | + end | |
23 | + | |
24 | +end | ... | ... |
... | ... | @@ -0,0 +1,26 @@ |
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/images/comments.gif"), | |
5 | + :url => { :profile => profile_identifier, :controller => 'comment_group_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" id="comments_list_group_<%= group_id %>"></div> | |
24 | + <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> | |
25 | + </div> | |
26 | +</div> | ... | ... |
plugins/custom_forms/controllers/custom_forms_plugin_myprofile_controller.rb
... | ... | @@ -70,7 +70,8 @@ class CustomFormsPluginMyprofileController < MyProfileController |
70 | 70 | private |
71 | 71 | |
72 | 72 | def new_fields(params) |
73 | - result = params[:fields].map {|id, hash| hash.has_key?(:real_id) ? nil : hash}.compact | |
73 | + keys = params[:fields].keys.sort{|a, b| a.to_i <=> b.to_i} | |
74 | + result = keys.map { |id| (hash = params[:fields][id]).has_key?(:real_id) ? nil : hash}.compact | |
74 | 75 | result.delete_if {|field| field[:name].blank?} |
75 | 76 | result |
76 | 77 | end | ... | ... |
plugins/custom_forms/controllers/custom_forms_plugin_profile_controller.rb
... | ... | @@ -27,6 +27,7 @@ class CustomFormsPluginProfileController < ProfileController |
27 | 27 | failed_answers.each do |answer| |
28 | 28 | @submission.errors.add(answer.field.name.to_sym, answer.errors[answer.field.slug.to_sym]) |
29 | 29 | end |
30 | + raise 'Submission failed: answers not valid' | |
30 | 31 | end |
31 | 32 | session[:notice] = _('Submission saved') |
32 | 33 | redirect_to :action => 'show' | ... | ... |
plugins/custom_forms/db/migrate/20130702120732_add_position_to_custom_forms_plugin_fields.rb
0 → 100644
... | ... | @@ -0,0 +1,13 @@ |
1 | +class AddPositionToCustomFormsPluginFields < ActiveRecord::Migration | |
2 | + def self.up | |
3 | + change_table :custom_forms_plugin_fields do |t| | |
4 | + t.integer :position, :default => 0 | |
5 | + end | |
6 | + end | |
7 | + | |
8 | + def self.down | |
9 | + change_table :custom_forms_plugin_fields do |t| | |
10 | + t.remove :position | |
11 | + end | |
12 | + end | |
13 | +end | ... | ... |
plugins/custom_forms/lib/custom_forms_plugin/field.rb
... | ... | @@ -12,5 +12,11 @@ class CustomFormsPlugin::Field < ActiveRecord::Base |
12 | 12 | before_validation do |field| |
13 | 13 | field.slug = field.name.to_slug if field.name.present? |
14 | 14 | end |
15 | + | |
16 | + before_create do |field| | |
17 | + if field.form.fields.exists? | |
18 | + field.position = field.form.fields.order('position').last.position + 1 | |
19 | + end | |
20 | + end | |
15 | 21 | end |
16 | 22 | ... | ... |
plugins/custom_forms/lib/custom_forms_plugin/form.rb
1 | 1 | class CustomFormsPlugin::Form < Noosfero::Plugin::ActiveRecord |
2 | 2 | belongs_to :profile |
3 | 3 | |
4 | - has_many :fields, :class_name => 'CustomFormsPlugin::Field', :dependent => :destroy | |
4 | + has_many :fields, :class_name => 'CustomFormsPlugin::Field', :dependent => :destroy, :order => 'position' | |
5 | 5 | has_many :submissions, :class_name => 'CustomFormsPlugin::Submission' |
6 | 6 | |
7 | 7 | serialize :access | ... | ... |
plugins/custom_forms/lib/custom_forms_plugin/helper.rb
... | ... | @@ -103,14 +103,17 @@ module CustomFormsPlugin::Helper |
103 | 103 | |
104 | 104 | def build_answers(submission, form) |
105 | 105 | answers = [] |
106 | - submission.each do |slug, value| | |
107 | - field = form.fields.select {|field| field.slug==slug}.first | |
108 | - if value.kind_of?(String) | |
109 | - final_value = value | |
110 | - elsif value.kind_of?(Array) | |
111 | - final_value = value.join(',') | |
112 | - elsif value.kind_of?(Hash) | |
113 | - final_value = value.map {|option, present| present == '1' ? option : nil}.compact.join(',') | |
106 | + form.fields.each do |field| | |
107 | + final_value = '' | |
108 | + if submission.has_key?(field.slug) | |
109 | + value = submission[field.slug] | |
110 | + if value.kind_of?(String) | |
111 | + final_value = value | |
112 | + elsif value.kind_of?(Array) | |
113 | + final_value = value.join(',') | |
114 | + elsif value.kind_of?(Hash) | |
115 | + final_value = value.map {|option, present| present == '1' ? option : nil}.compact.join(',') | |
116 | + end | |
114 | 117 | end |
115 | 118 | answers << CustomFormsPlugin::Answer.new(:field => field, :value => final_value) |
116 | 119 | end | ... | ... |
plugins/custom_forms/public/field.js
... | ... | @@ -8,7 +8,6 @@ jQuery('.icon-edit').live('click', function() { |
8 | 8 | id = jQuery(elem).attr('field_id'); |
9 | 9 | type = jQuery('#fields_'+id+'_type').val().split('_')[0]; |
10 | 10 | selector = '#edit-'+type+'-'+id |
11 | - jQuery(selector).show(); | |
12 | 11 | return selector |
13 | 12 | } |
14 | 13 | }); | ... | ... |
plugins/custom_forms/public/style.css
plugins/custom_forms/test/functional/custom_forms_plugin_myprofile_controller_test.rb
... | ... | @@ -40,7 +40,7 @@ class CustomFormsPluginMyprofileControllerTest < ActionController::TestCase |
40 | 40 | should 'create a form' do |
41 | 41 | format = '%Y-%m-%d %H:%M' |
42 | 42 | begining = Time.now.strftime(format) |
43 | - ending = (Time.now + 1.day).strftime('%Y-%m-%d %H:%M') | |
43 | + ending = (Time.now + 1.day).strftime(format) | |
44 | 44 | assert_difference CustomFormsPlugin::Form, :count, 1 do |
45 | 45 | post :create, :profile => profile.identifier, |
46 | 46 | :form => { |
... | ... | @@ -90,12 +90,42 @@ class CustomFormsPluginMyprofileControllerTest < ActionController::TestCase |
90 | 90 | assert f2.kind_of?(CustomFormsPlugin::SelectField) |
91 | 91 | end |
92 | 92 | |
93 | + should 'create fields in the order they are sent' do | |
94 | + format = '%Y-%m-%d %H:%M' | |
95 | + num_fields = 10 | |
96 | + begining = Time.now.strftime(format) | |
97 | + ending = (Time.now + 1.day).strftime(format) | |
98 | + fields = {} | |
99 | + num_fields.times do |i| | |
100 | + fields[i.to_s] = { | |
101 | + :name => i.to_s, | |
102 | + :default_value => '', | |
103 | + :type => 'text_field' | |
104 | + } | |
105 | + end | |
106 | + assert_difference CustomFormsPlugin::Form, :count, 1 do | |
107 | + post :create, :profile => profile.identifier, | |
108 | + :form => { | |
109 | + :name => 'My Form', | |
110 | + :access => 'logged', | |
111 | + :begining => begining, | |
112 | + :ending => ending, | |
113 | + :description => 'Cool form'}, | |
114 | + :fields => fields | |
115 | + end | |
116 | + form = CustomFormsPlugin::Form.find_by_name('My Form') | |
117 | + assert_equal num_fields, form.fields.count | |
118 | + form.fields.find_each do |f| | |
119 | + assert_equal f.position, f.name.to_i | |
120 | + end | |
121 | + end | |
122 | + | |
93 | 123 | should 'edit a form' do |
94 | 124 | form = CustomFormsPlugin::Form.create!(:profile => profile, :name => 'Free Software') |
95 | 125 | field = CustomFormsPlugin::TextField.create!(:form => form, :name => 'License') |
96 | 126 | format = '%Y-%m-%d %H:%M' |
97 | 127 | begining = Time.now.strftime(format) |
98 | - ending = (Time.now + 1.day).strftime('%Y-%m-%d %H:%M') | |
128 | + ending = (Time.now + 1.day).strftime(format) | |
99 | 129 | |
100 | 130 | post :edit, :profile => profile.identifier, :id => form.id, |
101 | 131 | :form => {:name => 'My Form', :access => 'logged', :begining => begining, :ending => ending, :description => 'Cool form'}, | ... | ... |
plugins/custom_forms/test/functional/custom_forms_plugin_profile_controller_test.rb
0 → 100644
... | ... | @@ -0,0 +1,31 @@ |
1 | +require File.dirname(__FILE__) + '/../../../../test/test_helper' | |
2 | +require File.dirname(__FILE__) + '/../../controllers/custom_forms_plugin_profile_controller' | |
3 | + | |
4 | +# Re-raise errors caught by the controller. | |
5 | +class CustomFormsPluginProfileController; def rescue_action(e) raise e end; end | |
6 | + | |
7 | +class CustomFormsPluginProfileControllerTest < ActionController::TestCase | |
8 | + def setup | |
9 | + @controller = CustomFormsPluginProfileController.new | |
10 | + @request = ActionController::TestRequest.new | |
11 | + @response = ActionController::TestResponse.new | |
12 | + @profile = create_user('profile').person | |
13 | + login_as(@profile.identifier) | |
14 | + environment = Environment.default | |
15 | + environment.enable_plugin(CustomFormsPlugin) | |
16 | + end | |
17 | + | |
18 | + attr_reader :profile | |
19 | + | |
20 | + should 'save submission if fields are ok' do | |
21 | + form = CustomFormsPlugin::Form.create!(:profile => profile, :name => 'Free Software') | |
22 | + field1 = CustomFormsPlugin::TextField.create(:name => 'Name', :form => form, :mandatory => true) | |
23 | + field2 = CustomFormsPlugin::TextField.create(:name => 'License', :form => form) | |
24 | + | |
25 | + assert_difference CustomFormsPlugin::Submission, :count, 1 do | |
26 | + post :show, :profile => profile.identifier, :id => form.id, :submission => {field1.name.to_slug => 'Noosfero', field2.name.to_slug => 'GPL'} | |
27 | + end | |
28 | + assert !session[:notice].include?('not saved') | |
29 | + assert_redirected_to :action => 'show' | |
30 | + end | |
31 | +end | ... | ... |
plugins/custom_forms/test/unit/custom_forms_plugin/field_test.rb
... | ... | @@ -71,5 +71,15 @@ class CustomFormsPlugin::FieldTest < ActiveSupport::TestCase |
71 | 71 | end |
72 | 72 | assert_equal form.fields, [license_field] |
73 | 73 | end |
74 | + | |
75 | + should 'give positions by creation order' do | |
76 | + form = CustomFormsPlugin::Form.create!(:name => 'Free Software', :profile => fast_create(Profile)) | |
77 | + field_0 = CustomFormsPlugin::Field.create!(:name => 'License', :form => form) | |
78 | + field_1 = CustomFormsPlugin::Field.create!(:name => 'URL', :form => form) | |
79 | + field_2 = CustomFormsPlugin::Field.create!(:name => 'Wiki', :form => form) | |
80 | + assert_equal 0, field_0.position | |
81 | + assert_equal 1, field_1.position | |
82 | + assert_equal 2, field_2.position | |
83 | + end | |
74 | 84 | end |
75 | 85 | ... | ... |