Commit c4a8f9bfc6095396090061adbfd34b1601e7bea7
Exists in
oauth_external_login
and in
4 other branches
Merge branch 'master' into federation
Updates branch federation with current master
Showing
1204 changed files
with
19570 additions
and
18872 deletions
Show diff stats
Too many changes.
To preserve performance only 100 of 1204 files displayed.
.gitlab-ci.yml
... | ... | @@ -30,14 +30,47 @@ integration: |
30 | 30 | script: bundle exec rake test:integration |
31 | 31 | stage: all-tests |
32 | 32 | |
33 | -cucumber: | |
34 | - script: bundle exec rake cucumber | |
33 | +cucumber-1: | |
34 | + script: SLICE=1/2 bundle exec rake cucumber | |
35 | + stage: all-tests | |
36 | +cucumber-2: | |
37 | + script: SLICE=2/2 bundle exec rake cucumber | |
35 | 38 | stage: all-tests |
36 | 39 | |
37 | -selenium: | |
38 | - script: bundle exec rake selenium | |
40 | +selenium-1: | |
41 | + script: SLICE=1/6 bundle exec rake selenium | |
42 | + stage: all-tests | |
43 | +selenium-2: | |
44 | + script: SLICE=2/6 bundle exec rake selenium | |
45 | + stage: all-tests | |
46 | +selenium-3: | |
47 | + script: SLICE=3/6 bundle exec rake selenium | |
48 | + stage: all-tests | |
49 | +selenium-4: | |
50 | + script: SLICE=4/6 bundle exec rake selenium | |
51 | + stage: all-tests | |
52 | +selenium-5: | |
53 | + script: SLICE=5/6 bundle exec rake selenium | |
54 | + stage: all-tests | |
55 | +selenium-6: | |
56 | + script: SLICE=6/6 bundle exec rake selenium | |
39 | 57 | stage: all-tests |
40 | 58 | |
41 | -plugins: | |
42 | - script: bundle exec rake test:noosfero_plugins | |
59 | +# NOOSFERO_BUNDLE_OPTS=install makes migrations fails | |
60 | +# probably because of rubygems-integration | |
61 | +plugins-1: | |
62 | + script: SLICE=1/5 bundle exec rake test:noosfero_plugins | |
43 | 63 | stage: all-tests |
64 | +plugins-2: | |
65 | + script: SLICE=2/5 bundle exec rake test:noosfero_plugins | |
66 | + stage: all-tests | |
67 | +plugins-3: | |
68 | + script: SLICE=3/5 bundle exec rake test:noosfero_plugins | |
69 | + stage: all-tests | |
70 | +plugins-4: | |
71 | + script: SLICE=4/5 bundle exec rake test:noosfero_plugins | |
72 | + stage: all-tests | |
73 | +plugins-5: | |
74 | + script: SLICE=5/5 bundle exec rake test:noosfero_plugins | |
75 | + stage: all-tests | |
76 | + | ... | ... |
.travis.yml
... | ... | @@ -18,7 +18,7 @@ cache: bundler |
18 | 18 | |
19 | 19 | language: ruby |
20 | 20 | rvm: |
21 | - - 2.3.0 | |
21 | + - 2.3.1 | |
22 | 22 | |
23 | 23 | addons: |
24 | 24 | apt: |
... | ... | @@ -29,11 +29,6 @@ addons: |
29 | 29 | paths: |
30 | 30 | - $(ls tmp/artifact* | tr "\n" ":") |
31 | 31 | |
32 | -# workaround for https://github.com/travis-ci/travis-ci/issues/4536 | |
33 | -before_install: | |
34 | - - export GEM_HOME=$PWD/vendor/bundle/ruby/2.2.0 | |
35 | - - gem install bundler | |
36 | - | |
37 | 32 | before_script: |
38 | 33 | - mkdir -p tmp/{pids,cache} log cache |
39 | 34 | - script/noosfero-plugins disableall |
... | ... | @@ -45,21 +40,36 @@ before_script: |
45 | 40 | - bundle exec rake db:migrate &>/dev/null |
46 | 41 | |
47 | 42 | env: |
48 | - - TASK=test:api | |
49 | - - TASK=test:units | |
50 | - - TASK=test:functionals | |
51 | - - TASK=test:integration | |
52 | - - SLICE=1/2 TASK=cucumber LANG=en | |
53 | - - SLICE=2/2 TASK=cucumber LANG=en | |
54 | - - SLICE=1/4 TASK=selenium | |
55 | - - SLICE=2/4 TASK=selenium | |
56 | - - SLICE=3/4 TASK=selenium | |
57 | - - SLICE=4/4 TASK=selenium | |
58 | - - SLICE=1/5 TASK=test:noosfero_plugins BUNDLE_OPTS=install | |
59 | - - SLICE=2/5 TASK=test:noosfero_plugins BUNDLE_OPTS=install | |
60 | - - SLICE=3/5 TASK=test:noosfero_plugins BUNDLE_OPTS=install | |
61 | - - SLICE=4/5 TASK=test:noosfero_plugins BUNDLE_OPTS=install | |
62 | - - SLICE=5/5 TASK=test:noosfero_plugins BUNDLE_OPTS=install | |
43 | + global: | |
44 | + - LANG=en | |
45 | + matrix: | |
46 | + - TASK=test:api | |
47 | + - TASK=test:units | |
48 | + - TASK=test:functionals | |
49 | + - TASK=test:integration | |
50 | + - SLICE=1/2 TASK=cucumber | |
51 | + - SLICE=2/2 TASK=cucumber | |
52 | + - SLICE=1/4 TASK=selenium | |
53 | + - SLICE=2/4 TASK=selenium | |
54 | + - SLICE=3/4 TASK=selenium | |
55 | + - SLICE=4/4 TASK=selenium | |
56 | + - SLICE=1/5 TASK=test:noosfero_plugins NOOSFERO_BUNDLE_OPTS=install | |
57 | + - SLICE=2/5 TASK=test:noosfero_plugins NOOSFERO_BUNDLE_OPTS=install | |
58 | + - SLICE=3/5 TASK=test:noosfero_plugins NOOSFERO_BUNDLE_OPTS=install | |
59 | + - SLICE=4/5 TASK=test:noosfero_plugins NOOSFERO_BUNDLE_OPTS=install | |
60 | + - SLICE=5/5 TASK=test:noosfero_plugins NOOSFERO_BUNDLE_OPTS=install | |
61 | + # chrome hanging on travis | |
62 | + #- SLICE=1/4 TASK=selenium SELENIUM_DRIVER=chrome | |
63 | + #- SLICE=2/4 TASK=selenium SELENIUM_DRIVER=chrome | |
64 | + #- SLICE=3/4 TASK=selenium SELENIUM_DRIVER=chrome | |
65 | + #- SLICE=4/4 TASK=selenium SELENIUM_DRIVER=chrome | |
66 | + | |
67 | +matrix: | |
68 | + allow_failures: | |
69 | + - env: SLICE=1/4 TASK=selenium SELENIUM_DRIVER=chrome | |
70 | + - env: SLICE=2/4 TASK=selenium SELENIUM_DRIVER=chrome | |
71 | + - env: SLICE=3/4 TASK=selenium SELENIUM_DRIVER=chrome | |
72 | + - env: SLICE=4/4 TASK=selenium SELENIUM_DRIVER=chrome | |
63 | 73 | |
64 | 74 | script: |
65 | 75 | - bundle exec rake $TASK | ... | ... |
Gemfile
... | ... | @@ -77,6 +77,7 @@ group :cucumber do |
77 | 77 | gem 'cucumber-rails', '~> 1.4.2', :require => false |
78 | 78 | gem 'database_cleaner', '~> 1.3' |
79 | 79 | gem 'selenium-webdriver', '>= 2.50' |
80 | + gem 'chromedriver-helper' if ENV['SELENIUM_DRIVER'] == 'chrome' | |
80 | 81 | end |
81 | 82 | |
82 | 83 | # Requires custom dependencies | ... | ... |
INSTALL.varnish.md
... | ... | @@ -19,7 +19,6 @@ Install the RPAF apache module (or skip this step if not using apache): |
19 | 19 | |
20 | 20 | 3a) Edit `/etc/apache2/ports.conf`, and: |
21 | 21 | |
22 | - * change `NameVirtualHost *:80` to `NameVirtualHost *:8080` | |
23 | 22 | * change `Listen 80` to `Listen 127.0.0.1:8080` |
24 | 23 | |
25 | 24 | 3b) Edit `/etc/apache2/sites-enabled/*`, and change `<VirtualHost *:80>` to `<VirtualHost *:8080>` |
... | ... | @@ -30,6 +29,7 @@ Install the RPAF apache module (or skip this step if not using apache): |
30 | 29 | |
31 | 30 | * change the line that says `START=no` to say `START=yes` |
32 | 31 | * change `-a :6081` to `-a :80` |
32 | + * add parameter `-p vcc_allow_inline_c=on` on `DAEMON_OPTS` | |
33 | 33 | |
34 | 34 | 4b) Edit `/etc/varnish/default.vcl` and add the following lines at the end: |
35 | 35 | ... | ... |
README.rails.md
... | ... | @@ -99,7 +99,7 @@ Description of contents |
99 | 99 | Holds controllers that should be named like weblog_controller.rb for automated URL mapping. All controllers should descend from `ActionController::Base`. |
100 | 100 | |
101 | 101 | * `app/models` |
102 | - Holds models that should be named like post.rb. Most models will descend from `ActiveRecord::Base`. | |
102 | + Holds models that should be named like post.rb. Most models will descend from `ApplicationRecord`. | |
103 | 103 | |
104 | 104 | * `app/views` |
105 | 105 | Holds the template files for the view that should be named like `weblog/index.rhtml` for the `WeblogController#index` action. All views use eRuby syntax. This directory can also be used to keep stylesheets, images, and so on that can be symlinked to public. | ... | ... |
... | ... | @@ -0,0 +1,89 @@ |
1 | +require_dependency 'api/helpers' | |
2 | + | |
3 | +module Api | |
4 | + class App < Grape::API | |
5 | + use Rack::JSONP | |
6 | + | |
7 | + logger = Logger.new(File.join(Rails.root, 'log', "#{ENV['RAILS_ENV'] || 'production'}_api.log")) | |
8 | + logger.formatter = GrapeLogging::Formatters::Default.new | |
9 | + #use GrapeLogging::Middleware::RequestLogger, { logger: logger } | |
10 | + | |
11 | + rescue_from :all do |e| | |
12 | + logger.error e | |
13 | + error! e.message, 500 | |
14 | + end unless Rails.env.test? | |
15 | + | |
16 | + @@NOOSFERO_CONF = nil | |
17 | + def self.NOOSFERO_CONF | |
18 | + if @@NOOSFERO_CONF | |
19 | + @@NOOSFERO_CONF | |
20 | + else | |
21 | + file = Rails.root.join('config', 'noosfero.yml') | |
22 | + @@NOOSFERO_CONF = File.exists?(file) ? YAML.load_file(file)[Rails.env] || {} : {} | |
23 | + end | |
24 | + end | |
25 | + | |
26 | + before { set_locale } | |
27 | + before { setup_multitenancy } | |
28 | + before { detect_stuff_by_domain } | |
29 | + before { filter_disabled_plugins_endpoints } | |
30 | + before { init_noosfero_plugins } | |
31 | + after { set_session_cookie } | |
32 | + | |
33 | + version 'v1' | |
34 | + prefix [ENV['RAILS_RELATIVE_URL_ROOT'], "api"].compact.join('/') | |
35 | + format :json | |
36 | + content_type :txt, "text/plain" | |
37 | + | |
38 | + helpers Helpers | |
39 | + | |
40 | + mount V1::Session | |
41 | + mount V1::Articles | |
42 | + mount V1::Comments | |
43 | + mount V1::Users | |
44 | + mount V1::Communities | |
45 | + mount V1::People | |
46 | + mount V1::Enterprises | |
47 | + mount V1::Categories | |
48 | + mount V1::Tasks | |
49 | + mount V1::Tags | |
50 | + mount V1::Environments | |
51 | + mount V1::Search | |
52 | + mount V1::Contacts | |
53 | + mount V1::Boxes | |
54 | + mount V1::Blocks | |
55 | + mount V1::Profiles | |
56 | + mount V1::Activities | |
57 | + | |
58 | + # hook point which allow plugins to add Grape::API extensions to Api::App | |
59 | + #finds for plugins which has api mount points classes defined (the class should extends Grape::API) | |
60 | + @plugins = Noosfero::Plugin.all.map { |p| p.constantize } | |
61 | + @plugins.each do |klass| | |
62 | + if klass.public_methods.include? :api_mount_points | |
63 | + klass.api_mount_points.each do |mount_class| | |
64 | + mount mount_class if mount_class && ( mount_class < Grape::API ) | |
65 | + end | |
66 | + end | |
67 | + end | |
68 | + | |
69 | + def self.endpoint_unavailable?(endpoint, environment) | |
70 | + api_class = endpoint.options[:app] || endpoint.options[:for] | |
71 | + if api_class.present? | |
72 | + klass = api_class.name.deconstantize.constantize | |
73 | + return klass < Noosfero::Plugin && !environment.plugin_enabled?(klass) | |
74 | + end | |
75 | + end | |
76 | + | |
77 | + class << self | |
78 | + def endpoints_with_plugins(environment = nil) | |
79 | + if environment.present? | |
80 | + cloned_endpoints = endpoints_without_plugins.dup | |
81 | + cloned_endpoints.delete_if { |endpoint| endpoint_unavailable?(endpoint, environment) } | |
82 | + else | |
83 | + endpoints_without_plugins | |
84 | + end | |
85 | + end | |
86 | + alias_method_chain :endpoints, :plugins | |
87 | + end | |
88 | + end | |
89 | +end | ... | ... |
... | ... | @@ -0,0 +1,269 @@ |
1 | +module Api | |
2 | + module Entities | |
3 | + | |
4 | + Entity.format_with :timestamp do |date| | |
5 | + date.strftime('%Y/%m/%d %H:%M:%S') if date | |
6 | + end | |
7 | + | |
8 | + PERMISSIONS = { | |
9 | + :admin => 0, | |
10 | + :self => 10, | |
11 | + :private_content => 20, | |
12 | + :logged_user => 30, | |
13 | + :anonymous => 40 | |
14 | + } | |
15 | + | |
16 | + def self.can_display_profile_field? profile, options, permission_options={} | |
17 | + permissions={:field => "", :permission => :private_content} | |
18 | + permissions.merge!(permission_options) | |
19 | + field = permissions[:field] | |
20 | + permission = permissions[:permission] | |
21 | + return true if profile.public? && profile.public_fields.map{|f| f.to_sym}.include?(field.to_sym) | |
22 | + | |
23 | + current_person = options[:current_person] | |
24 | + | |
25 | + current_permission = if current_person.present? | |
26 | + if current_person.is_admin? | |
27 | + :admin | |
28 | + elsif current_person == profile | |
29 | + :self | |
30 | + elsif profile.display_private_info_to?(current_person) | |
31 | + :private_content | |
32 | + else | |
33 | + :logged_user | |
34 | + end | |
35 | + else | |
36 | + :anonymous | |
37 | + end | |
38 | + PERMISSIONS[current_permission] <= PERMISSIONS[permission] | |
39 | + end | |
40 | + | |
41 | + class Image < Entity | |
42 | + root 'images', 'image' | |
43 | + | |
44 | + expose :url do |image, options| | |
45 | + image.public_filename | |
46 | + end | |
47 | + | |
48 | + expose :icon_url do |image, options| | |
49 | + image.public_filename(:icon) | |
50 | + end | |
51 | + | |
52 | + expose :minor_url do |image, options| | |
53 | + image.public_filename(:minor) | |
54 | + end | |
55 | + | |
56 | + expose :portrait_url do |image, options| | |
57 | + image.public_filename(:portrait) | |
58 | + end | |
59 | + | |
60 | + expose :thumb_url do |image, options| | |
61 | + image.public_filename(:thumb) | |
62 | + end | |
63 | + end | |
64 | + | |
65 | + class CategoryBase < Entity | |
66 | + root 'categories', 'category' | |
67 | + expose :name, :id, :slug | |
68 | + end | |
69 | + | |
70 | + class Category < CategoryBase | |
71 | + root 'categories', 'category' | |
72 | + expose :full_name do |category, options| | |
73 | + category.full_name | |
74 | + end | |
75 | + expose :parent, :using => CategoryBase, if: { parent: true } | |
76 | + expose :children, :using => CategoryBase, if: { children: true } | |
77 | + expose :image, :using => Image | |
78 | + expose :display_color | |
79 | + end | |
80 | + | |
81 | + class Region < Category | |
82 | + root 'regions', 'region' | |
83 | + expose :parent_id | |
84 | + end | |
85 | + | |
86 | + class Block < Entity | |
87 | + root 'blocks', 'block' | |
88 | + expose :id, :type, :settings, :position, :enabled | |
89 | + expose :mirror, :mirror_block_id, :title | |
90 | + expose :api_content, if: lambda { |object, options| options[:display_api_content] || object.display_api_content_by_default? } | |
91 | + end | |
92 | + | |
93 | + class Box < Entity | |
94 | + root 'boxes', 'box' | |
95 | + expose :id, :position | |
96 | + expose :blocks, :using => Block do |box, options| | |
97 | + box.blocks.select {|block| block.visible_to_user?(options[:current_person]) } | |
98 | + end | |
99 | + end | |
100 | + | |
101 | + class Profile < Entity | |
102 | + expose :identifier, :name, :id | |
103 | + expose :created_at, :format_with => :timestamp | |
104 | + expose :updated_at, :format_with => :timestamp | |
105 | + expose :additional_data do |profile, options| | |
106 | + hash ={} | |
107 | + profile.public_values.each do |value| | |
108 | + hash[value.custom_field.name]=value.value | |
109 | + end | |
110 | + | |
111 | + private_values = profile.custom_field_values - profile.public_values | |
112 | + private_values.each do |value| | |
113 | + if Entities.can_display_profile_field?(profile,options) | |
114 | + hash[value.custom_field.name]=value.value | |
115 | + end | |
116 | + end | |
117 | + hash | |
118 | + end | |
119 | + expose :image, :using => Image | |
120 | + expose :region, :using => Region | |
121 | + expose :type | |
122 | + expose :custom_header | |
123 | + expose :custom_footer | |
124 | + end | |
125 | + | |
126 | + class UserBasic < Entity | |
127 | + expose :id | |
128 | + expose :login | |
129 | + end | |
130 | + | |
131 | + class Person < Profile | |
132 | + root 'people', 'person' | |
133 | + expose :user, :using => UserBasic, documentation: {type: 'User', desc: 'The user data of a person' } | |
134 | + expose :vote_count | |
135 | + expose :comments_count do |person, options| | |
136 | + person.comments.count | |
137 | + end | |
138 | + expose :following_articles_count do |person, options| | |
139 | + person.following_articles.count | |
140 | + end | |
141 | + expose :articles_count do |person, options| | |
142 | + person.articles.count | |
143 | + end | |
144 | + end | |
145 | + | |
146 | + class Enterprise < Profile | |
147 | + root 'enterprises', 'enterprise' | |
148 | + end | |
149 | + | |
150 | + class Community < Profile | |
151 | + root 'communities', 'community' | |
152 | + expose :description | |
153 | + expose :admins, :if => lambda { |community, options| community.display_info_to? options[:current_person]} do |community, options| | |
154 | + community.admins.map{|admin| {"name"=>admin.name, "id"=>admin.id, "username" => admin.identifier}} | |
155 | + end | |
156 | + expose :categories, :using => Category | |
157 | + expose :members, :using => Person , :if => lambda{ |community, options| community.display_info_to? options[:current_person] } | |
158 | + end | |
159 | + | |
160 | + class CommentBase < Entity | |
161 | + expose :body, :title, :id | |
162 | + expose :created_at, :format_with => :timestamp | |
163 | + expose :author, :using => Profile | |
164 | + expose :reply_of, :using => CommentBase | |
165 | + end | |
166 | + | |
167 | + class Comment < CommentBase | |
168 | + root 'comments', 'comment' | |
169 | + expose :children, as: :replies, :using => Comment | |
170 | + end | |
171 | + | |
172 | + class ArticleBase < Entity | |
173 | + root 'articles', 'article' | |
174 | + expose :id | |
175 | + expose :body | |
176 | + expose :abstract, documentation: {type: 'String', desc: 'Teaser of the body'} | |
177 | + expose :created_at, :format_with => :timestamp | |
178 | + expose :updated_at, :format_with => :timestamp | |
179 | + expose :title, :documentation => {:type => "String", :desc => "Title of the article"} | |
180 | + expose :created_by, :as => :author, :using => Profile, :documentation => {type: 'Profile', desc: 'The profile author that create the article'} | |
181 | + expose :profile, :using => Profile, :documentation => {type: 'Profile', desc: 'The profile associated with the article'} | |
182 | + expose :categories, :using => Category | |
183 | + expose :image, :using => Image | |
184 | + expose :votes_for | |
185 | + expose :votes_against | |
186 | + expose :setting | |
187 | + expose :position | |
188 | + expose :hits | |
189 | + expose :start_date | |
190 | + expose :end_date, :documentation => {type: 'DateTime', desc: 'The date of finish of the article'} | |
191 | + expose :tag_list | |
192 | + expose :children_count | |
193 | + expose :slug, :documentation => {:type => "String", :desc => "Trimmed and parsed name of a article"} | |
194 | + expose :path | |
195 | + expose :followers_count | |
196 | + expose :votes_count | |
197 | + expose :comments_count | |
198 | + expose :archived, :documentation => {:type => "Boolean", :desc => "Defines if a article is readonly"} | |
199 | + expose :type | |
200 | + expose :comments, using: CommentBase, :if => lambda{|obj,opt| opt[:params] && ['1','true',true].include?(opt[:params][:show_comments])} | |
201 | + expose :published | |
202 | + expose :accept_comments?, as: :accept_comments | |
203 | + end | |
204 | + | |
205 | + class Article < ArticleBase | |
206 | + root 'articles', 'article' | |
207 | + expose :parent, :using => ArticleBase | |
208 | + expose :children, :using => ArticleBase do |article, options| | |
209 | + article.children.published.limit(V1::Articles::MAX_PER_PAGE) | |
210 | + end | |
211 | + end | |
212 | + | |
213 | + class User < Entity | |
214 | + root 'users', 'user' | |
215 | + | |
216 | + attrs = [:id,:login,:email,:activated?] | |
217 | + aliases = {:activated? => :activated} | |
218 | + | |
219 | + attrs.each do |attribute| | |
220 | + name = aliases.has_key?(attribute) ? aliases[attribute] : attribute | |
221 | + expose attribute, :as => name, :if => lambda{|user,options| Entities.can_display_profile_field?(user.person, options, {:field => attribute})} | |
222 | + end | |
223 | + | |
224 | + expose :person, :using => Person, :if => lambda{|user,options| user.person.display_info_to? options[:current_person]} | |
225 | + expose :permissions, :if => lambda{|user,options| Entities.can_display_profile_field?(user.person, options, {:field => :permissions, :permission => :self})} do |user, options| | |
226 | + output = {} | |
227 | + user.person.role_assignments.map do |role_assigment| | |
228 | + if role_assigment.resource.respond_to?(:identifier) && !role_assigment.role.nil? | |
229 | + output[role_assigment.resource.identifier] = role_assigment.role.permissions | |
230 | + end | |
231 | + end | |
232 | + output | |
233 | + end | |
234 | + end | |
235 | + | |
236 | + class UserLogin < User | |
237 | + root 'users', 'user' | |
238 | + expose :private_token, documentation: {type: 'String', desc: 'A valid authentication code for post/delete api actions'}, if: lambda {|object, options| object.activated? } | |
239 | + end | |
240 | + | |
241 | + class Task < Entity | |
242 | + root 'tasks', 'task' | |
243 | + expose :id | |
244 | + expose :type | |
245 | + end | |
246 | + | |
247 | + class Environment < Entity | |
248 | + expose :name | |
249 | + expose :id | |
250 | + expose :description | |
251 | + expose :settings, if: lambda { |instance, options| options[:is_admin] } | |
252 | + end | |
253 | + | |
254 | + class Tag < Entity | |
255 | + root 'tags', 'tag' | |
256 | + expose :name | |
257 | + end | |
258 | + | |
259 | + class Activity < Entity | |
260 | + root 'activities', 'activity' | |
261 | + expose :id, :params, :verb, :created_at, :updated_at, :comments_count, :visible | |
262 | + expose :user, :using => Profile | |
263 | + expose :target do |activity, opts| | |
264 | + type_map = {Profile => ::Profile, ArticleBase => ::Article}.find {|h| activity.target.kind_of?(h.last)} | |
265 | + type_map.first.represent(activity.target) unless type_map.nil? | |
266 | + end | |
267 | + end | |
268 | + end | |
269 | +end | ... | ... |
... | ... | @@ -0,0 +1,27 @@ |
1 | +module Api | |
2 | + class Entity < Grape::Entity | |
3 | + | |
4 | + def initialize(object, options = {}) | |
5 | + object = nil if object.is_a? Exception | |
6 | + super object, options | |
7 | + end | |
8 | + | |
9 | + def self.represent(objects, options = {}) | |
10 | + if options[:has_exception] | |
11 | + data = super objects, options.merge(is_inner_data: true) | |
12 | + if objects.is_a? Exception | |
13 | + data.merge ok: false, error: { | |
14 | + type: objects.class.name, | |
15 | + message: objects.message | |
16 | + } | |
17 | + else | |
18 | + data = data.serializable_hash if data.is_a? Entity | |
19 | + data.merge ok: true, error: { type: 'Success', message: '' } | |
20 | + end | |
21 | + else | |
22 | + super objects, options | |
23 | + end | |
24 | + end | |
25 | + | |
26 | + end | |
27 | +end | ... | ... |
... | ... | @@ -0,0 +1,423 @@ |
1 | +require 'base64' | |
2 | +require 'tempfile' | |
3 | + | |
4 | +module Api | |
5 | + module Helpers | |
6 | + PRIVATE_TOKEN_PARAM = :private_token | |
7 | + DEFAULT_ALLOWED_PARAMETERS = [:parent_id, :from, :until, :content_type, :author_id, :identifier, :archived] | |
8 | + | |
9 | + include SanitizeParams | |
10 | + include Noosfero::Plugin::HotSpot | |
11 | + include ForgotPasswordHelper | |
12 | + include SearchTermHelper | |
13 | + | |
14 | + def set_locale | |
15 | + I18n.locale = (params[:lang] || request.env['HTTP_ACCEPT_LANGUAGE'] || 'en') | |
16 | + end | |
17 | + | |
18 | + def init_noosfero_plugins | |
19 | + plugins | |
20 | + end | |
21 | + | |
22 | + def current_user | |
23 | + private_token = (params[PRIVATE_TOKEN_PARAM] || headers['Private-Token']).to_s | |
24 | + @current_user ||= User.find_by private_token: private_token | |
25 | + @current_user ||= plugins.dispatch("api_custom_login", request).first | |
26 | + @current_user | |
27 | + end | |
28 | + | |
29 | + def current_person | |
30 | + current_user.person unless current_user.nil? | |
31 | + end | |
32 | + | |
33 | + def is_admin?(environment) | |
34 | + return false unless current_user | |
35 | + return current_person.is_admin?(environment) | |
36 | + end | |
37 | + | |
38 | + def logout | |
39 | + @current_user = nil | |
40 | + end | |
41 | + | |
42 | + def environment | |
43 | + @environment | |
44 | + end | |
45 | + | |
46 | + def present_partial(model, options) | |
47 | + if(params[:fields].present?) | |
48 | + begin | |
49 | + fields = JSON.parse(params[:fields]) | |
50 | + if fields.present? | |
51 | + options.merge!(fields.symbolize_keys.slice(:only, :except)) | |
52 | + end | |
53 | + rescue | |
54 | + fields = params[:fields] | |
55 | + fields = fields.split(',') if fields.kind_of?(String) | |
56 | + options[:only] = Array.wrap(fields) | |
57 | + end | |
58 | + end | |
59 | + present model, options | |
60 | + end | |
61 | + | |
62 | + include FindByContents | |
63 | + | |
64 | + #################################################################### | |
65 | + #### SEARCH | |
66 | + #################################################################### | |
67 | + def multiple_search?(searches=nil) | |
68 | + ['index', 'category_index'].include?(params[:action]) || (searches && searches.size > 1) | |
69 | + end | |
70 | + #################################################################### | |
71 | + | |
72 | + def logger | |
73 | + logger = Logger.new(File.join(Rails.root, 'log', "#{ENV['RAILS_ENV'] || 'production'}_api.log")) | |
74 | + logger.formatter = GrapeLogging::Formatters::Default.new | |
75 | + logger | |
76 | + end | |
77 | + | |
78 | + def limit | |
79 | + limit = params[:limit].to_i | |
80 | + limit = default_limit if limit <= 0 | |
81 | + limit | |
82 | + end | |
83 | + | |
84 | + def period(from_date, until_date) | |
85 | + return nil if from_date.nil? && until_date.nil? | |
86 | + | |
87 | + begin_period = from_date.nil? ? Time.at(0).to_datetime : from_date | |
88 | + end_period = until_date.nil? ? DateTime.now : until_date | |
89 | + | |
90 | + begin_period..end_period | |
91 | + end | |
92 | + | |
93 | + def parse_content_type(content_type) | |
94 | + return nil if content_type.blank? | |
95 | + content_type.split(',').map do |content_type| | |
96 | + content_type.camelcase | |
97 | + end | |
98 | + end | |
99 | + | |
100 | + def find_article(articles, id) | |
101 | + article = articles.find(id) | |
102 | + article.display_to?(current_person) ? article : forbidden! | |
103 | + end | |
104 | + | |
105 | + def post_article(asset, params) | |
106 | + return forbidden! unless current_person.can_post_content?(asset) | |
107 | + | |
108 | + klass_type = params[:content_type] || params[:article].delete(:type) || TinyMceArticle.name | |
109 | + return forbidden! unless klass_type.constantize <= Article | |
110 | + | |
111 | + article = klass_type.constantize.new(params[:article]) | |
112 | + article.last_changed_by = current_person | |
113 | + article.created_by= current_person | |
114 | + article.profile = asset | |
115 | + | |
116 | + if !article.save | |
117 | + render_api_errors!(article.errors.full_messages) | |
118 | + end | |
119 | + present_partial article, :with => Entities::Article | |
120 | + end | |
121 | + | |
122 | + def present_article(asset) | |
123 | + article = find_article(asset.articles, params[:id]) | |
124 | + present_partial article, :with => Entities::Article, :params => params | |
125 | + end | |
126 | + | |
127 | + def present_articles_for_asset(asset, method = 'articles') | |
128 | + articles = find_articles(asset, method) | |
129 | + present_articles(articles) | |
130 | + end | |
131 | + | |
132 | + def present_articles(articles) | |
133 | + present_partial paginate(articles), :with => Entities::Article, :params => params | |
134 | + end | |
135 | + | |
136 | + def find_articles(asset, method = 'articles') | |
137 | + articles = select_filtered_collection_of(asset, method, params) | |
138 | + if current_person.present? | |
139 | + articles = articles.display_filter(current_person, nil) | |
140 | + else | |
141 | + articles = articles.published | |
142 | + end | |
143 | + articles | |
144 | + end | |
145 | + | |
146 | + def find_task(asset, id) | |
147 | + task = asset.tasks.find(id) | |
148 | + current_person.has_permission?(task.permission, asset) ? task : forbidden! | |
149 | + end | |
150 | + | |
151 | + def post_task(asset, params) | |
152 | + klass_type= params[:content_type].nil? ? 'Task' : params[:content_type] | |
153 | + return forbidden! unless klass_type.constantize <= Task | |
154 | + | |
155 | + task = klass_type.constantize.new(params[:task]) | |
156 | + task.requestor_id = current_person.id | |
157 | + task.target_id = asset.id | |
158 | + task.target_type = 'Profile' | |
159 | + | |
160 | + if !task.save | |
161 | + render_api_errors!(task.errors.full_messages) | |
162 | + end | |
163 | + present_partial task, :with => Entities::Task | |
164 | + end | |
165 | + | |
166 | + def present_task(asset) | |
167 | + task = find_task(asset, params[:id]) | |
168 | + present_partial task, :with => Entities::Task | |
169 | + end | |
170 | + | |
171 | + def present_tasks(asset) | |
172 | + tasks = select_filtered_collection_of(asset, 'tasks', params) | |
173 | + tasks = tasks.select {|t| current_person.has_permission?(t.permission, asset)} | |
174 | + return forbidden! if tasks.empty? && !current_person.has_permission?(:perform_task, asset) | |
175 | + present_partial tasks, :with => Entities::Task | |
176 | + end | |
177 | + | |
178 | + def make_conditions_with_parameter(params = {}) | |
179 | + parsed_params = parser_params(params) | |
180 | + conditions = {} | |
181 | + from_date = DateTime.parse(parsed_params.delete(:from)) if parsed_params[:from] | |
182 | + until_date = DateTime.parse(parsed_params.delete(:until)) if parsed_params[:until] | |
183 | + | |
184 | + conditions[:type] = parse_content_type(parsed_params.delete(:content_type)) unless parsed_params[:content_type].nil? | |
185 | + | |
186 | + conditions[:created_at] = period(from_date, until_date) if from_date || until_date | |
187 | + conditions.merge!(parsed_params) | |
188 | + | |
189 | + conditions | |
190 | + end | |
191 | + | |
192 | + # changing make_order_with_parameters to avoid sql injection | |
193 | + def make_order_with_parameters(object, method, params) | |
194 | + order = "created_at DESC" | |
195 | + unless params[:order].blank? | |
196 | + if params[:order].include? '\'' or params[:order].include? '"' | |
197 | + order = "created_at DESC" | |
198 | + elsif ['RANDOM()', 'RANDOM'].include? params[:order].upcase | |
199 | + order = 'RANDOM()' | |
200 | + else | |
201 | + field_name, direction = params[:order].split(' ') | |
202 | + assoc = object.class.reflect_on_association(method.to_sym) | |
203 | + if !field_name.blank? and assoc | |
204 | + if assoc.klass.attribute_names.include? field_name | |
205 | + if direction.present? and ['ASC','DESC'].include? direction.upcase | |
206 | + order = "#{field_name} #{direction.upcase}" | |
207 | + end | |
208 | + end | |
209 | + end | |
210 | + end | |
211 | + end | |
212 | + return order | |
213 | + end | |
214 | + | |
215 | + def make_timestamp_with_parameters_and_method(params, method) | |
216 | + timestamp = nil | |
217 | + if params[:timestamp] | |
218 | + datetime = DateTime.parse(params[:timestamp]) | |
219 | + table_name = method.to_s.singularize.camelize.constantize.table_name | |
220 | + timestamp = "#{table_name}.updated_at >= '#{datetime}'" | |
221 | + end | |
222 | + | |
223 | + timestamp | |
224 | + end | |
225 | + | |
226 | + def by_reference(scope, params) | |
227 | + reference_id = params[:reference_id].to_i == 0 ? nil : params[:reference_id].to_i | |
228 | + if reference_id.nil? | |
229 | + scope | |
230 | + else | |
231 | + created_at = scope.find(reference_id).created_at | |
232 | + scope.send("#{params.key?(:oldest) ? 'older_than' : 'younger_than'}", created_at) | |
233 | + end | |
234 | + end | |
235 | + | |
236 | + def by_categories(scope, params) | |
237 | + category_ids = params[:category_ids] | |
238 | + if category_ids.nil? | |
239 | + scope | |
240 | + else | |
241 | + scope.joins(:categories).where(:categories => {:id => category_ids}) | |
242 | + end | |
243 | + end | |
244 | + | |
245 | + def select_filtered_collection_of(object, method, params) | |
246 | + conditions = make_conditions_with_parameter(params) | |
247 | + order = make_order_with_parameters(object,method,params) | |
248 | + timestamp = make_timestamp_with_parameters_and_method(params, method) | |
249 | + | |
250 | + objects = object.send(method) | |
251 | + objects = by_reference(objects, params) | |
252 | + objects = by_categories(objects, params) | |
253 | + | |
254 | + objects = objects.where(conditions).where(timestamp).reorder(order) | |
255 | + | |
256 | + params[:page] ||= 1 | |
257 | + params[:per_page] ||= limit | |
258 | + paginate(objects) | |
259 | + end | |
260 | + | |
261 | + def authenticate! | |
262 | + unauthorized! unless current_user | |
263 | + end | |
264 | + | |
265 | + def profiles_for_person(profiles, person) | |
266 | + if person | |
267 | + profiles.listed_for_person(person) | |
268 | + else | |
269 | + profiles.visible | |
270 | + end | |
271 | + end | |
272 | + | |
273 | + # Checks the occurrences of uniqueness of attributes, each attribute must be present in the params hash | |
274 | + # or a Bad Request error is invoked. | |
275 | + # | |
276 | + # Parameters: | |
277 | + # keys (unique) - A hash consisting of keys that must be unique | |
278 | + def unique_attributes!(obj, keys) | |
279 | + keys.each do |key| | |
280 | + cant_be_saved_request!(key) if obj.find_by(key.to_s => params[key]) | |
281 | + end | |
282 | + end | |
283 | + | |
284 | + def attributes_for_keys(keys) | |
285 | + attrs = {} | |
286 | + keys.each do |key| | |
287 | + attrs[key] = params[key] if params[key].present? or (params.has_key?(key) and params[key] == false) | |
288 | + end | |
289 | + attrs | |
290 | + end | |
291 | + | |
292 | + ########################################## | |
293 | + # error helpers # | |
294 | + ########################################## | |
295 | + | |
296 | + def not_found! | |
297 | + render_api_error!('404 Not found', 404) | |
298 | + end | |
299 | + | |
300 | + def forbidden! | |
301 | + render_api_error!('403 Forbidden', 403) | |
302 | + end | |
303 | + | |
304 | + def cant_be_saved_request!(attribute) | |
305 | + message = _("(Invalid request) %s can't be saved") % attribute | |
306 | + render_api_error!(message, 400) | |
307 | + end | |
308 | + | |
309 | + def bad_request!(attribute) | |
310 | + message = _("(Invalid request) %s not given") % attribute | |
311 | + render_api_error!(message, 400) | |
312 | + end | |
313 | + | |
314 | + def something_wrong! | |
315 | + message = _("Something wrong happened") | |
316 | + render_api_error!(message, 400) | |
317 | + end | |
318 | + | |
319 | + def unauthorized! | |
320 | + render_api_error!(_('Unauthorized'), 401) | |
321 | + end | |
322 | + | |
323 | + def not_allowed! | |
324 | + render_api_error!(_('Method Not Allowed'), 405) | |
325 | + end | |
326 | + | |
327 | + # javascript_console_message is supposed to be executed as console.log() | |
328 | + def render_api_error!(user_message, status, log_message = nil, javascript_console_message = nil) | |
329 | + message_hash = {'message' => user_message, :code => status} | |
330 | + message_hash[:javascript_console_message] = javascript_console_message if javascript_console_message.present? | |
331 | + log_msg = "#{status}, User message: #{user_message}" | |
332 | + log_msg = "#{log_message}, #{log_msg}" if log_message.present? | |
333 | + log_msg = "#{log_msg}, Javascript Console Message: #{javascript_console_message}" if javascript_console_message.present? | |
334 | + logger.error log_msg unless Rails.env.test? | |
335 | + error!(message_hash, status) | |
336 | + end | |
337 | + | |
338 | + def render_api_errors!(messages) | |
339 | + messages = messages.to_a if messages.class == ActiveModel::Errors | |
340 | + render_api_error!(messages.join(','), 400) | |
341 | + end | |
342 | + | |
343 | + protected | |
344 | + | |
345 | + def set_session_cookie | |
346 | + cookies['_noosfero_api_session'] = { value: @current_user.private_token, httponly: true } if @current_user.present? | |
347 | + end | |
348 | + | |
349 | + def setup_multitenancy | |
350 | + Noosfero::MultiTenancy.setup!(request.host) | |
351 | + end | |
352 | + | |
353 | + def detect_stuff_by_domain | |
354 | + @domain = Domain.by_name(request.host) | |
355 | + if @domain.nil? | |
356 | + @environment = Environment.default | |
357 | + if @environment.nil? && Rails.env.development? | |
358 | + # This should only happen in development ... | |
359 | + @environment = Environment.create!(:name => "Noosfero", :is_default => true) | |
360 | + end | |
361 | + else | |
362 | + @environment = @domain.environment | |
363 | + end | |
364 | + end | |
365 | + | |
366 | + def filter_disabled_plugins_endpoints | |
367 | + not_found! if Api::App.endpoint_unavailable?(self, @environment) | |
368 | + end | |
369 | + | |
370 | + def asset_with_image params | |
371 | + if params.has_key? :image_builder | |
372 | + asset_api_params = params | |
373 | + asset_api_params[:image_builder] = base64_to_uploadedfile(asset_api_params[:image_builder]) | |
374 | + return asset_api_params | |
375 | + end | |
376 | + params | |
377 | + end | |
378 | + | |
379 | + def base64_to_uploadedfile(base64_image) | |
380 | + tempfile = base64_to_tempfile base64_image | |
381 | + converted_image = base64_image | |
382 | + converted_image[:tempfile] = tempfile | |
383 | + return {uploaded_data: ActionDispatch::Http::UploadedFile.new(converted_image)} | |
384 | + end | |
385 | + | |
386 | + def base64_to_tempfile base64_image | |
387 | + base64_img_str = base64_image[:tempfile] | |
388 | + decoded_base64_str = Base64.decode64(base64_img_str) | |
389 | + tempfile = Tempfile.new(base64_image[:filename]) | |
390 | + tempfile.write(decoded_base64_str.encode("ascii-8bit").force_encoding("utf-8")) | |
391 | + tempfile.rewind | |
392 | + tempfile | |
393 | + end | |
394 | + private | |
395 | + | |
396 | + def parser_params(params) | |
397 | + parsed_params = {} | |
398 | + params.map do |k,v| | |
399 | + parsed_params[k.to_sym] = v if DEFAULT_ALLOWED_PARAMETERS.include?(k.to_sym) | |
400 | + end | |
401 | + parsed_params | |
402 | + end | |
403 | + | |
404 | + def default_limit | |
405 | + 20 | |
406 | + end | |
407 | + | |
408 | + def parse_content_type(content_type) | |
409 | + return nil if content_type.blank? | |
410 | + content_types = content_type.split(',').map do |content_type| | |
411 | + content_type = content_type.camelcase | |
412 | + content_type == 'TextArticle' ? Article.text_article_types : content_type | |
413 | + end | |
414 | + content_types.flatten.uniq | |
415 | + end | |
416 | + | |
417 | + def period(from_date, until_date) | |
418 | + begin_period = from_date.nil? ? Time.at(0).to_datetime : from_date | |
419 | + end_period = until_date.nil? ? DateTime.now : until_date | |
420 | + begin_period..end_period | |
421 | + end | |
422 | + end | |
423 | +end | ... | ... |
... | ... | @@ -0,0 +1,19 @@ |
1 | +module Api | |
2 | + module V1 | |
3 | + class Activities < Grape::API | |
4 | + | |
5 | + resource :profiles do | |
6 | + | |
7 | + get ':id/activities' do | |
8 | + profile = Profile.find_by id: params[:id] | |
9 | + | |
10 | + not_found! if profile.blank? || profile.secret || !profile.visible | |
11 | + forbidden! if !profile.display_private_info_to?(current_person) | |
12 | + | |
13 | + activities = profile.activities.map(&:activity) | |
14 | + present activities, :with => Entities::Activity, :current_person => current_person | |
15 | + end | |
16 | + end | |
17 | + end | |
18 | + end | |
19 | +end | ... | ... |
... | ... | @@ -0,0 +1,314 @@ |
1 | +module Api | |
2 | + module V1 | |
3 | + class Articles < Grape::API | |
4 | + | |
5 | + ARTICLE_TYPES = Article.descendants.map{|a| a.to_s} | |
6 | + | |
7 | + MAX_PER_PAGE = 50 | |
8 | + | |
9 | + resource :articles do | |
10 | + | |
11 | + paginate max_per_page: MAX_PER_PAGE | |
12 | + # Collect articles | |
13 | + # | |
14 | + # Parameters: | |
15 | + # from - date where the search will begin. If nothing is passed the default date will be the date of the first article created | |
16 | + # oldest - Collect the oldest articles. If nothing is passed the newest articles are collected | |
17 | + # limit - amount of articles returned. The default value is 20 | |
18 | + # | |
19 | + # Example Request: | |
20 | + # GET host/api/v1/articles?from=2013-04-04-14:41:43&until=2015-04-04-14:41:43&limit=10&private_token=e96fff37c2238fdab074d1dcea8e6317 | |
21 | + | |
22 | + desc 'Return all articles of all kinds' do | |
23 | + detail 'Get all articles filtered by fields in query params' | |
24 | + params Entities::Article.documentation | |
25 | + success Entities::Article | |
26 | + failure [[403, 'Forbidden']] | |
27 | + named 'ArticlesList' | |
28 | + headers [ | |
29 | + 'Per-Page' => { | |
30 | + description: 'Total number of records', | |
31 | + required: false | |
32 | + } | |
33 | + ] | |
34 | + end | |
35 | + get do | |
36 | + present_articles_for_asset(environment) | |
37 | + end | |
38 | + | |
39 | + desc "Return one article by id" do | |
40 | + detail 'Get only one article by id. If not found the "forbidden" http error is showed' | |
41 | + params Entities::Article.documentation | |
42 | + success Entities::Article | |
43 | + failure [[403, 'Forbidden']] | |
44 | + named 'ArticleById' | |
45 | + end | |
46 | + get ':id', requirements: {id: /[0-9]+/} do | |
47 | + present_article(environment) | |
48 | + end | |
49 | + | |
50 | + post ':id' do | |
51 | + article = environment.articles.find(params[:id]) | |
52 | + return forbidden! unless article.allow_edit?(current_person) | |
53 | + article.update_attributes!(asset_with_image(params[:article])) | |
54 | + present_partial article, :with => Entities::Article | |
55 | + end | |
56 | + | |
57 | + delete ':id' do | |
58 | + article = environment.articles.find(params[:id]) | |
59 | + return forbidden! unless article.allow_delete?(current_person) | |
60 | + begin | |
61 | + article.destroy | |
62 | + { :success => true } | |
63 | + rescue Exception => exception | |
64 | + render_api_error!(_('The article couldn\'t be removed due to some problem. Please contact the administrator.'), 400) | |
65 | + end | |
66 | + end | |
67 | + | |
68 | + desc 'Report a abuse and/or violent content in a article by id' do | |
69 | + detail 'Submit a abuse (in general, a content violation) report about a specific article' | |
70 | + params Entities::Article.documentation | |
71 | + failure [[400, 'Bad Request']] | |
72 | + named 'ArticleReportAbuse' | |
73 | + end | |
74 | + post ':id/report_abuse' do | |
75 | + article = find_article(environment.articles, params[:id]) | |
76 | + profile = article.profile | |
77 | + begin | |
78 | + abuse_report = AbuseReport.new(:reason => params[:report_abuse]) | |
79 | + if !params[:content_type].blank? | |
80 | + article = params[:content_type].constantize.find(params[:content_id]) | |
81 | + abuse_report.content = article_reported_version(article) | |
82 | + end | |
83 | + | |
84 | + current_person.register_report(abuse_report, profile) | |
85 | + | |
86 | + if !params[:content_type].blank? | |
87 | + abuse_report = AbuseReport.find_by reporter_id: current_person.id, abuse_complaint_id: profile.opened_abuse_complaint.id | |
88 | + Delayed::Job.enqueue DownloadReportedImagesJob.new(abuse_report, article) | |
89 | + end | |
90 | + | |
91 | + { | |
92 | + :success => true, | |
93 | + :message => _('Your abuse report was registered. The administrators are reviewing your report.'), | |
94 | + } | |
95 | + rescue Exception => exception | |
96 | + #logger.error(exception.to_s) | |
97 | + render_api_error!(_('Your report couldn\'t be saved due to some problem. Please contact the administrator.'), 400) | |
98 | + end | |
99 | + | |
100 | + end | |
101 | + | |
102 | + desc "Returns the articles I voted" do | |
103 | + detail 'Get the Articles I make a vote' | |
104 | + failure [[403, 'Forbidden']] | |
105 | + named 'ArticleFollowers' | |
106 | + end | |
107 | + #FIXME refactor this method | |
108 | + get 'voted_by_me' do | |
109 | + present_articles(current_person.votes.where(:voteable_type => 'Article').collect(&:voteable)) | |
110 | + end | |
111 | + | |
112 | + desc 'Perform a vote on a article by id' do | |
113 | + detail 'Vote on a specific article with values: 1 (if you like) or -1 (if not)' | |
114 | + params Entities::UserLogin.documentation | |
115 | + failure [[401,'Unauthorized']] | |
116 | + named 'ArticleVote' | |
117 | + end | |
118 | + post ':id/vote' do | |
119 | + authenticate! | |
120 | + value = (params[:value] || 1).to_i | |
121 | + # FIXME verify allowed values | |
122 | + render_api_error!('Vote value not allowed', 400) unless [-1, 1].include?(value) | |
123 | + article = find_article(environment.articles, params[:id]) | |
124 | + begin | |
125 | + vote = Vote.new(:voteable => article, :voter => current_person, :vote => value) | |
126 | + {:vote => vote.save!} | |
127 | + rescue ActiveRecord::RecordInvalid => e | |
128 | + render_api_error!(e.message, 400) | |
129 | + end | |
130 | + end | |
131 | + | |
132 | + desc "Returns the total followers for the article" do | |
133 | + detail 'Get the followers of a specific article by id' | |
134 | + failure [[403, 'Forbidden']] | |
135 | + named 'ArticleFollowers' | |
136 | + end | |
137 | + get ':id/followers' do | |
138 | + article = find_article(environment.articles, params[:id]) | |
139 | + total = article.person_followers.count | |
140 | + {:total_followers => total} | |
141 | + end | |
142 | + | |
143 | + desc "Return the articles followed by me" | |
144 | + get 'followed_by_me' do | |
145 | + present_articles_for_asset(current_person, 'following_articles') | |
146 | + end | |
147 | + | |
148 | + desc "Add a follower for the article" do | |
149 | + detail 'Add the current user identified by private token, like a follower of a article' | |
150 | + params Entities::UserLogin.documentation | |
151 | + failure [[401, 'Unauthorized']] | |
152 | + named 'ArticleFollow' | |
153 | + end | |
154 | + post ':id/follow' do | |
155 | + authenticate! | |
156 | + article = find_article(environment.articles, params[:id]) | |
157 | + if article.article_followers.exists?(:person_id => current_person.id) | |
158 | + {:success => false, :already_follow => true} | |
159 | + else | |
160 | + article_follower = ArticleFollower.new | |
161 | + article_follower.article = article | |
162 | + article_follower.person = current_person | |
163 | + article_follower.save! | |
164 | + {:success => true} | |
165 | + end | |
166 | + end | |
167 | + | |
168 | + desc 'Return the children of a article identified by id' do | |
169 | + detail 'Get all children articles of a specific article' | |
170 | + params Entities::Article.documentation | |
171 | + failure [[403, 'Forbidden']] | |
172 | + named 'ArticleChildren' | |
173 | + end | |
174 | + | |
175 | + paginate per_page: MAX_PER_PAGE, max_per_page: MAX_PER_PAGE | |
176 | + get ':id/children' do | |
177 | + article = find_article(environment.articles, params[:id]) | |
178 | + | |
179 | + #TODO make tests for this situation | |
180 | + votes_order = params.delete(:order) if params[:order]=='votes_score' | |
181 | + articles = select_filtered_collection_of(article, 'children', params) | |
182 | + articles = articles.display_filter(current_person, article.profile) | |
183 | + | |
184 | + #TODO make tests for this situation | |
185 | + if votes_order | |
186 | + articles = articles.joins('left join votes on articles.id=votes.voteable_id').group('articles.id').reorder('sum(coalesce(votes.vote, 0)) DESC') | |
187 | + end | |
188 | + Article.hit(articles) | |
189 | + present_articles(articles) | |
190 | + end | |
191 | + | |
192 | + desc 'Return one child of a article identified by id' do | |
193 | + detail 'Get a child of a specific article' | |
194 | + params Entities::Article.documentation | |
195 | + success Entities::Article | |
196 | + failure [[403, 'Forbidden']] | |
197 | + named 'ArticleChild' | |
198 | + end | |
199 | + get ':id/children/:child_id' do | |
200 | + article = find_article(environment.articles, params[:id]) | |
201 | + child = find_article(article.children, params[:child_id]) | |
202 | + child.hit | |
203 | + present_partial child, :with => Entities::Article | |
204 | + end | |
205 | + | |
206 | + desc 'Suggest a article to another profile' do | |
207 | + detail 'Suggest a article to another profile (person, community...)' | |
208 | + params Entities::Article.documentation | |
209 | + success Entities::Task | |
210 | + failure [[401,'Unauthorized']] | |
211 | + named 'ArticleSuggest' | |
212 | + end | |
213 | + post ':id/children/suggest' do | |
214 | + authenticate! | |
215 | + parent_article = environment.articles.find(params[:id]) | |
216 | + | |
217 | + suggest_article = SuggestArticle.new | |
218 | + suggest_article.article = params[:article] | |
219 | + suggest_article.article[:parent_id] = parent_article.id | |
220 | + suggest_article.target = parent_article.profile | |
221 | + suggest_article.requestor = current_person | |
222 | + | |
223 | + unless suggest_article.save | |
224 | + render_api_errors!(suggest_article.article_object.errors.full_messages) | |
225 | + end | |
226 | + present_partial suggest_article, :with => Entities::Task | |
227 | + end | |
228 | + | |
229 | + # Example Request: | |
230 | + # POST api/v1/articles/:id/children?private_token=234298743290432&article[name]=title&article[body]=body | |
231 | + desc 'Add a child article to a parent identified by id' do | |
232 | + detail 'Create a new article and associate to a parent' | |
233 | + params Entities::Article.documentation | |
234 | + success Entities::Article | |
235 | + failure [[401,'Unauthorized']] | |
236 | + named 'ArticleAddChild' | |
237 | + end | |
238 | + post ':id/children' do | |
239 | + parent_article = environment.articles.find(params[:id]) | |
240 | + params[:article][:parent_id] = parent_article.id | |
241 | + post_article(parent_article.profile, params) | |
242 | + end | |
243 | + end | |
244 | + | |
245 | + resource :profiles do | |
246 | + get ':id/home_page' do | |
247 | + profiles = environment.profiles | |
248 | + profiles = profiles.visible_for_person(current_person) | |
249 | + profile = profiles.find_by id: params[:id] | |
250 | + present_partial profile.home_page, :with => Entities::Article | |
251 | + end | |
252 | + end | |
253 | + | |
254 | + kinds = %w[profile community person enterprise] | |
255 | + kinds.each do |kind| | |
256 | + resource kind.pluralize.to_sym do | |
257 | + segment "/:#{kind}_id" do | |
258 | + resource :articles do | |
259 | + | |
260 | + desc "Return all articles associate with a profile of type #{kind}" do | |
261 | + detail 'Get a list of articles of a profile' | |
262 | + params Entities::Article.documentation | |
263 | + success Entities::Article | |
264 | + failure [[403, 'Forbidden']] | |
265 | + named 'ArticlesOfProfile' | |
266 | + end | |
267 | + get do | |
268 | + profile = environment.send(kind.pluralize).find(params["#{kind}_id"]) | |
269 | + | |
270 | + if params[:path].present? | |
271 | + article = profile.articles.find_by path: params[:path] | |
272 | + if !article || !article.display_to?(current_person) | |
273 | + article = forbidden! | |
274 | + end | |
275 | + | |
276 | + present_partial article, :with => Entities::Article | |
277 | + else | |
278 | + | |
279 | + present_articles_for_asset(profile) | |
280 | + end | |
281 | + end | |
282 | + | |
283 | + desc "Return a article associate with a profile of type #{kind}" do | |
284 | + detail 'Get only one article of a profile' | |
285 | + params Entities::Article.documentation | |
286 | + success Entities::Article | |
287 | + failure [[403, 'Forbidden']] | |
288 | + named 'ArticleOfProfile' | |
289 | + end | |
290 | + get ':id' do | |
291 | + profile = environment.send(kind.pluralize).find(params["#{kind}_id"]) | |
292 | + present_article(profile) | |
293 | + end | |
294 | + | |
295 | + # Example Request: | |
296 | + # POST api/v1/{people,communities,enterprises}/:asset_id/articles?private_token=234298743290432&article[name]=title&article[body]=body | |
297 | + desc "Add a new article associated with a profile of type #{kind}" do | |
298 | + detail 'Create a new article and associate with a profile' | |
299 | + params Entities::Article.documentation | |
300 | + success Entities::Article | |
301 | + failure [[403, 'Forbidden']] | |
302 | + named 'ArticleCreateToProfile' | |
303 | + end | |
304 | + post do | |
305 | + profile = environment.send(kind.pluralize).find(params["#{kind}_id"]) | |
306 | + post_article(profile, params) | |
307 | + end | |
308 | + end | |
309 | + end | |
310 | + end | |
311 | + end | |
312 | + end | |
313 | + end | |
314 | +end | ... | ... |
... | ... | @@ -0,0 +1,15 @@ |
1 | +module Api | |
2 | + module V1 | |
3 | + | |
4 | + class Blocks < Grape::API | |
5 | + resource :blocks do | |
6 | + get ':id' do | |
7 | + block = Block.find(params["id"]) | |
8 | + return forbidden! unless block.visible_to_user?(current_person) | |
9 | + present block, :with => Entities::Block, display_api_content: true | |
10 | + end | |
11 | + end | |
12 | + end | |
13 | + | |
14 | + end | |
15 | +end | ... | ... |
... | ... | @@ -0,0 +1,45 @@ |
1 | +module Api | |
2 | + module V1 | |
3 | + | |
4 | + class Boxes < Grape::API | |
5 | + | |
6 | + kinds = %w[profile community person enterprise] | |
7 | + kinds.each do |kind| | |
8 | + | |
9 | + resource kind.pluralize.to_sym do | |
10 | + | |
11 | + segment "/:#{kind}_id" do | |
12 | + resource :boxes do | |
13 | + get do | |
14 | + profile = environment.send(kind.pluralize).find(params["#{kind}_id"]) | |
15 | + return forbidden! unless profile.display_info_to?(current_person) | |
16 | + present profile.boxes, with: Entities::Box, current_person: current_person | |
17 | + end | |
18 | + end | |
19 | + end | |
20 | + end | |
21 | + | |
22 | + end | |
23 | + | |
24 | + resource :environments do | |
25 | + [ '/default', '/context', ':environment_id' ].each do |route| | |
26 | + segment route do | |
27 | + resource :boxes do | |
28 | + get do | |
29 | + if (route.match(/default/)) | |
30 | + env = Environment.default | |
31 | + elsif (route.match(/context/)) | |
32 | + env = environment | |
33 | + else | |
34 | + env = Environment.find(params[:environment_id]) | |
35 | + end | |
36 | + present env.boxes, with: Entities::Box, current_person: current_person | |
37 | + end | |
38 | + end | |
39 | + end | |
40 | + end | |
41 | + end | |
42 | + end | |
43 | + | |
44 | + end | |
45 | +end | ... | ... |
... | ... | @@ -0,0 +1,25 @@ |
1 | +module Api | |
2 | + module V1 | |
3 | + class Categories < Grape::API | |
4 | + | |
5 | + resource :categories do | |
6 | + | |
7 | + get do | |
8 | + type = params[:category_type] | |
9 | + include_parent = params[:include_parent] == 'true' | |
10 | + include_children = params[:include_children] == 'true' | |
11 | + | |
12 | + categories = type.nil? ? environment.categories : environment.categories.where(:type => type) | |
13 | + present categories, :with => Entities::Category, parent: include_parent, children: include_children | |
14 | + end | |
15 | + | |
16 | + desc "Return the category by id" | |
17 | + get ':id' do | |
18 | + present environment.categories.find(params[:id]), :with => Entities::Category, parent: true, children: true | |
19 | + end | |
20 | + | |
21 | + end | |
22 | + | |
23 | + end | |
24 | + end | |
25 | +end | ... | ... |
... | ... | @@ -0,0 +1,63 @@ |
1 | +module Api | |
2 | + module V1 | |
3 | + class Comments < Grape::API | |
4 | + MAX_PER_PAGE = 20 | |
5 | + | |
6 | + | |
7 | + resource :articles do | |
8 | + paginate max_per_page: MAX_PER_PAGE | |
9 | + # Collect comments from articles | |
10 | + # | |
11 | + # Parameters: | |
12 | + # reference_id - comment id used as reference to collect comment | |
13 | + # oldest - Collect the oldest comments from reference_id comment. If nothing is passed the newest comments are collected | |
14 | + # limit - amount of comments returned. The default value is 20 | |
15 | + # | |
16 | + # Example Request: | |
17 | + # GET /articles/12/comments?oldest&limit=10&reference_id=23 | |
18 | + get ":id/comments" do | |
19 | + article = find_article(environment.articles, params[:id]) | |
20 | + comments = select_filtered_collection_of(article, :comments, params) | |
21 | + comments = comments.without_spam | |
22 | + comments = comments.without_reply if(params[:without_reply].present?) | |
23 | + comments = plugins.filter(:unavailable_comments, comments) | |
24 | + present paginate(comments), :with => Entities::Comment, :current_person => current_person | |
25 | + end | |
26 | + | |
27 | + get ":id/comments/:comment_id" do | |
28 | + article = find_article(environment.articles, params[:id]) | |
29 | + present article.comments.find(params[:comment_id]), :with => Entities::Comment, :current_person => current_person | |
30 | + end | |
31 | + | |
32 | + # Example Request: | |
33 | + # POST api/v1/articles/12/comments?private_token=2298743290432&body=new comment&title=New | |
34 | + post ":id/comments" do | |
35 | + authenticate! | |
36 | + article = find_article(environment.articles, params[:id]) | |
37 | + return forbidden! unless article.accept_comments? | |
38 | + options = params.select { |key,v| !['id','private_token'].include?(key) }.merge(:author => current_person, :source => article) | |
39 | + begin | |
40 | + comment = Comment.create!(options) | |
41 | + rescue ActiveRecord::RecordInvalid => e | |
42 | + render_api_error!(e.message, 400) | |
43 | + end | |
44 | + present comment, :with => Entities::Comment, :current_person => current_person | |
45 | + end | |
46 | + | |
47 | + delete ":id/comments/:comment_id" do | |
48 | + article = find_article(environment.articles, params[:id]) | |
49 | + comment = article.comments.find_by_id(params[:comment_id]) | |
50 | + return not_found! if comment.nil? | |
51 | + return forbidden! unless comment.can_be_destroyed_by?(current_person) | |
52 | + begin | |
53 | + comment.destroy | |
54 | + present comment, with: Entities::Comment, :current_person => current_person | |
55 | + rescue => e | |
56 | + render_api_error!(e.message, 500) | |
57 | + end | |
58 | + end | |
59 | + end | |
60 | + | |
61 | + end | |
62 | + end | |
63 | +end | ... | ... |
... | ... | @@ -0,0 +1,82 @@ |
1 | +module Api | |
2 | + module V1 | |
3 | + class Communities < Grape::API | |
4 | + | |
5 | + resource :communities do | |
6 | + | |
7 | + # Collect comments from articles | |
8 | + # | |
9 | + # Parameters: | |
10 | + # from - date where the search will begin. If nothing is passed the default date will be the date of the first article created | |
11 | + # oldest - Collect the oldest comments from reference_id comment. If nothing is passed the newest comments are collected | |
12 | + # limit - amount of comments returned. The default value is 20 | |
13 | + # | |
14 | + # Example Request: | |
15 | + # GET /communities?from=2013-04-04-14:41:43&until=2014-04-04-14:41:43&limit=10 | |
16 | + # GET /communities?reference_id=10&limit=10&oldest | |
17 | + get do | |
18 | + communities = select_filtered_collection_of(environment, 'communities', params) | |
19 | + communities = profiles_for_person(communities, current_person) | |
20 | + communities = communities.by_location(params) # Must be the last. May return Exception obj | |
21 | + present communities, :with => Entities::Community, :current_person => current_person | |
22 | + end | |
23 | + | |
24 | + | |
25 | + # Example Request: | |
26 | + # POST api/v1/communties?private_token=234298743290432&community[name]=some_name | |
27 | + # for each custom field for community, add &community[field_name]=field_value to the request | |
28 | + post do | |
29 | + authenticate! | |
30 | + params[:community] ||= {} | |
31 | + | |
32 | + params[:community][:custom_values]={} | |
33 | + params[:community].keys.each do |key| | |
34 | + params[:community][:custom_values][key]=params[:community].delete(key) if Community.custom_fields(environment).any?{|cf| cf.name==key} | |
35 | + end | |
36 | + | |
37 | + begin | |
38 | + community = Community.create_after_moderation(current_person, params[:community].merge({:environment => environment})) | |
39 | + rescue | |
40 | + community = Community.new(params[:community]) | |
41 | + end | |
42 | + | |
43 | + if !community.save | |
44 | + render_api_errors!(community.errors.full_messages) | |
45 | + end | |
46 | + | |
47 | + present community, :with => Entities::Community, :current_person => current_person | |
48 | + end | |
49 | + | |
50 | + get ':id' do | |
51 | + community = profiles_for_person(environment.communities, current_person).find_by_id(params[:id]) | |
52 | + present community, :with => Entities::Community, :current_person => current_person | |
53 | + end | |
54 | + | |
55 | + end | |
56 | + | |
57 | + resource :people do | |
58 | + | |
59 | + segment '/:person_id' do | |
60 | + | |
61 | + resource :communities do | |
62 | + | |
63 | + get do | |
64 | + person = environment.people.find(params[:person_id]) | |
65 | + | |
66 | + not_found! if person.blank? | |
67 | + forbidden! if !person.display_info_to?(current_person) | |
68 | + | |
69 | + communities = select_filtered_collection_of(person, 'communities', params) | |
70 | + communities = communities.visible | |
71 | + present communities, :with => Entities::Community, :current_person => current_person | |
72 | + end | |
73 | + | |
74 | + end | |
75 | + | |
76 | + end | |
77 | + | |
78 | + end | |
79 | + | |
80 | + end | |
81 | + end | |
82 | +end | ... | ... |
... | ... | @@ -0,0 +1,26 @@ |
1 | +module Api | |
2 | + module V1 | |
3 | + class Contacts < Grape::API | |
4 | + | |
5 | + resource :communities do | |
6 | + | |
7 | + resource ':id/contact' do | |
8 | + #contact => {:name => 'some name', :email => 'test@mail.com', :subject => 'some title', :message => 'some message'} | |
9 | + desc "Send a contact message" | |
10 | + post do | |
11 | + profile = environment.communities.find(params[:id]) | |
12 | + forbidden! unless profile.present? | |
13 | + contact = Contact.new params[:contact].merge(dest: profile) | |
14 | + if contact.deliver | |
15 | + {:success => true} | |
16 | + else | |
17 | + {:success => false} | |
18 | + end | |
19 | + end | |
20 | + | |
21 | + end | |
22 | + end | |
23 | + | |
24 | + end | |
25 | + end | |
26 | +end | ... | ... |
... | ... | @@ -0,0 +1,55 @@ |
1 | +module Api | |
2 | + module V1 | |
3 | + class Enterprises < Grape::API | |
4 | + | |
5 | + resource :enterprises do | |
6 | + | |
7 | + # Collect enterprises from environment | |
8 | + # | |
9 | + # Parameters: | |
10 | + # from - date where the search will begin. If nothing is passed the default date will be the date of the first article created | |
11 | + # oldest - Collect the oldest comments from reference_id comment. If nothing is passed the newest comments are collected | |
12 | + # limit - amount of comments returned. The default value is 20 | |
13 | + # georef params - read `Profile.by_location` for more information. | |
14 | + # | |
15 | + # Example Request: | |
16 | + # GET /enterprises?from=2013-04-04-14:41:43&until=2014-04-04-14:41:43&limit=10 | |
17 | + # GET /enterprises?reference_id=10&limit=10&oldest | |
18 | + get do | |
19 | + enterprises = select_filtered_collection_of(environment, 'enterprises', params) | |
20 | + enterprises = enterprises.visible | |
21 | + enterprises = enterprises.by_location(params) # Must be the last. May return Exception obj. | |
22 | + present enterprises, :with => Entities::Enterprise, :current_person => current_person | |
23 | + end | |
24 | + | |
25 | + desc "Return one enterprise by id" | |
26 | + get ':id' do | |
27 | + enterprise = environment.enterprises.visible.find_by(id: params[:id]) | |
28 | + present enterprise, :with => Entities::Enterprise, :current_person => current_person | |
29 | + end | |
30 | + | |
31 | + end | |
32 | + | |
33 | + resource :people do | |
34 | + | |
35 | + segment '/:person_id' do | |
36 | + | |
37 | + resource :enterprises do | |
38 | + | |
39 | + get do | |
40 | + person = environment.people.find(params[:person_id]) | |
41 | + enterprises = select_filtered_collection_of(person, 'enterprises', params) | |
42 | + enterprises = enterprises.visible.by_location(params) | |
43 | + present enterprises, :with => Entities::Enterprise, :current_person => current_person | |
44 | + end | |
45 | + | |
46 | + end | |
47 | + | |
48 | + end | |
49 | + | |
50 | + end | |
51 | + | |
52 | + | |
53 | + end | |
54 | + end | |
55 | +end | ... | ... |
... | ... | @@ -0,0 +1,28 @@ |
1 | +module Api | |
2 | + module V1 | |
3 | + class Environments < Grape::API | |
4 | + | |
5 | + resource :environment do | |
6 | + | |
7 | + desc "Return the person information" | |
8 | + get '/signup_person_fields' do | |
9 | + present environment.signup_person_fields | |
10 | + end | |
11 | + | |
12 | + get ':id' do | |
13 | + local_environment = nil | |
14 | + if (params[:id] == "default") | |
15 | + local_environment = Environment.default | |
16 | + elsif (params[:id] == "context") | |
17 | + local_environment = environment | |
18 | + else | |
19 | + local_environment = Environment.find(params[:id]) | |
20 | + end | |
21 | + present_partial local_environment, :with => Entities::Environment, :is_admin => is_admin?(local_environment) | |
22 | + end | |
23 | + | |
24 | + end | |
25 | + | |
26 | + end | |
27 | + end | |
28 | +end | ... | ... |
... | ... | @@ -0,0 +1,127 @@ |
1 | +module Api | |
2 | + module V1 | |
3 | + class People < Grape::API | |
4 | + | |
5 | + MAX_PER_PAGE = 50 | |
6 | + | |
7 | + desc 'API Root' | |
8 | + | |
9 | + resource :people do | |
10 | + paginate max_per_page: MAX_PER_PAGE | |
11 | + | |
12 | + # -- A note about privacy -- | |
13 | + # We wold find people by location, but we must test if the related | |
14 | + # fields are public. We can't do it now, with SQL, while the location | |
15 | + # data and the fields_privacy are a serialized settings. | |
16 | + # We must build a new table for profile data, where we can set meta-data | |
17 | + # like: | |
18 | + # | id | profile_id | key | value | privacy_level | source | | |
19 | + # | 1 | 99 | city | Salvador | friends | user | | |
20 | + # | 2 | 99 | lng | -38.521 | me only | automatic | | |
21 | + | |
22 | + # Collect people from environment | |
23 | + # | |
24 | + # Parameters: | |
25 | + # from - date where the search will begin. If nothing is passed the default date will be the date of the first article created | |
26 | + # oldest - Collect the oldest comments from reference_id comment. If nothing is passed the newest comments are collected | |
27 | + # limit - amount of comments returned. The default value is 20 | |
28 | + # | |
29 | + # Example Request: | |
30 | + # GET /people?from=2013-04-04-14:41:43&until=2014-04-04-14:41:43&limit=10 | |
31 | + # GET /people?reference_id=10&limit=10&oldest | |
32 | + | |
33 | + desc "Find environment's people" | |
34 | + get do | |
35 | + people = select_filtered_collection_of(environment, 'people', params) | |
36 | + people = people.visible | |
37 | + present_partial people, :with => Entities::Person, :current_person => current_person | |
38 | + end | |
39 | + | |
40 | + desc "Return the logged user information" | |
41 | + get "/me" do | |
42 | + authenticate! | |
43 | + present_partial current_person, :with => Entities::Person, :current_person => current_person | |
44 | + end | |
45 | + | |
46 | + desc "Return the person information" | |
47 | + get ':id' do | |
48 | + person = environment.people.visible.find_by(id: params[:id]) | |
49 | + return not_found! if person.blank? | |
50 | + present person, :with => Entities::Person, :current_person => current_person | |
51 | + end | |
52 | + | |
53 | + desc "Update person information" | |
54 | + post ':id' do | |
55 | + authenticate! | |
56 | + return forbidden! if current_person.id.to_s != params[:id] | |
57 | + current_person.update_attributes!(asset_with_image(params[:person])) | |
58 | + present current_person, :with => Entities::Person, :current_person => current_person | |
59 | + end | |
60 | + | |
61 | + # POST api/v1/people?person[login]=some_login&person[password]=some_password&person[name]=Jack | |
62 | + # for each custom field for person, add &person[field_name]=field_value to the request | |
63 | + desc "Create person" | |
64 | + post do | |
65 | + authenticate! | |
66 | + user_data = {} | |
67 | + user_data[:login] = params[:person].delete(:login) || params[:person][:identifier] | |
68 | + user_data[:email] = params[:person].delete(:email) | |
69 | + user_data[:password] = params[:person].delete(:password) | |
70 | + user_data[:password_confirmation] = params[:person].delete(:password_confirmation) | |
71 | + | |
72 | + params[:person][:custom_values]={} | |
73 | + params[:person].keys.each do |key| | |
74 | + params[:person][:custom_values][key]=params[:person].delete(key) if Person.custom_fields(environment).any?{|cf| cf.name==key} | |
75 | + end | |
76 | + | |
77 | + user = User.build(user_data, asset_with_image(params[:person]), environment) | |
78 | + | |
79 | + begin | |
80 | + user.signup! | |
81 | + rescue ActiveRecord::RecordInvalid | |
82 | + render_api_errors!(user.errors.full_messages) | |
83 | + end | |
84 | + | |
85 | + present user.person, :with => Entities::Person, :current_person => user.person | |
86 | + end | |
87 | + | |
88 | + desc "Return the person friends" | |
89 | + get ':id/friends' do | |
90 | + person = environment.people.visible.find_by(id: params[:id]) | |
91 | + return not_found! if person.blank? | |
92 | + friends = person.friends.visible | |
93 | + present friends, :with => Entities::Person, :current_person => current_person | |
94 | + end | |
95 | + | |
96 | + desc "Return the person permissions on other profiles" | |
97 | + get ":id/permissions" do | |
98 | + authenticate! | |
99 | + person = environment.people.find(params[:id]) | |
100 | + return not_found! if person.blank? | |
101 | + return forbidden! unless current_person == person || environment.admins.include?(current_person) | |
102 | + | |
103 | + output = {} | |
104 | + person.role_assignments.map do |role_assigment| | |
105 | + if role_assigment.resource.respond_to?(:identifier) | |
106 | + output[role_assigment.resource.identifier] = role_assigment.role.permissions | |
107 | + end | |
108 | + end | |
109 | + present output | |
110 | + end | |
111 | + end | |
112 | + | |
113 | + resource :profiles do | |
114 | + segment '/:profile_id' do | |
115 | + resource :members do | |
116 | + paginate max_per_page: MAX_PER_PAGE | |
117 | + get do | |
118 | + profile = environment.profiles.find_by id: params[:profile_id] | |
119 | + members = select_filtered_collection_of(profile, 'members', params) | |
120 | + present members, :with => Entities::Person, :current_person => current_person | |
121 | + end | |
122 | + end | |
123 | + end | |
124 | + end | |
125 | + end | |
126 | + end | |
127 | +end | ... | ... |
... | ... | @@ -0,0 +1,51 @@ |
1 | +module Api | |
2 | + module V1 | |
3 | + class Profiles < Grape::API | |
4 | + | |
5 | + resource :profiles do | |
6 | + | |
7 | + get do | |
8 | + profiles = select_filtered_collection_of(environment, 'profiles', params) | |
9 | + profiles = profiles.visible | |
10 | + profiles = profiles.by_location(params) # Must be the last. May return Exception obj. | |
11 | + present profiles, :with => Entities::Profile, :current_person => current_person | |
12 | + end | |
13 | + | |
14 | + get ':id' do | |
15 | + profiles = environment.profiles | |
16 | + profiles = profiles.visible | |
17 | + profile = profiles.find_by id: params[:id] | |
18 | + | |
19 | + if profile | |
20 | + present profile, :with => Entities::Profile, :current_person => current_person | |
21 | + else | |
22 | + not_found! | |
23 | + end | |
24 | + end | |
25 | + | |
26 | + desc "Update profile information" | |
27 | + post ':id' do | |
28 | + authenticate! | |
29 | + profile = environment.profiles.find_by(id: params[:id]) | |
30 | + return forbidden! unless current_person.has_permission?(:edit_profile, profile) | |
31 | + profile.update_attributes!(params[:profile]) | |
32 | + present profile, :with => Entities::Profile, :current_person => current_person | |
33 | + end | |
34 | + | |
35 | + delete ':id' do | |
36 | + authenticate! | |
37 | + profiles = environment.profiles | |
38 | + profile = profiles.find_by id: params[:id] | |
39 | + | |
40 | + not_found! if profile.blank? | |
41 | + | |
42 | + if current_person.has_permission?(:destroy_profile, profile) | |
43 | + profile.destroy | |
44 | + else | |
45 | + forbidden! | |
46 | + end | |
47 | + end | |
48 | + end | |
49 | + end | |
50 | + end | |
51 | +end | ... | ... |
... | ... | @@ -0,0 +1,37 @@ |
1 | +module Api | |
2 | + module V1 | |
3 | + class Search < Grape::API | |
4 | + | |
5 | + resource :search do | |
6 | + resource :article do | |
7 | + paginate max_per_page: 200 | |
8 | + get do | |
9 | + # Security checks | |
10 | + sanitize_params_hash(params) | |
11 | + # Api::Helpers | |
12 | + asset = :articles | |
13 | + context = environment | |
14 | + | |
15 | + profile = environment.profiles.find(params[:profile_id]) if params[:profile_id] | |
16 | + scope = profile.nil? ? environment.articles.is_public : profile.articles.is_public | |
17 | + scope = scope.where(:type => params[:type]) if params[:type] && !(params[:type] == 'Article') | |
18 | + scope = scope.where(make_conditions_with_parameter(params)) | |
19 | + scope = scope.joins(:categories).where(:categories => {:id => params[:category_ids]}) if params[:category_ids].present? | |
20 | + scope = scope.where('articles.children_count > 0') if params[:has_children].present? | |
21 | + query = params[:query] || "" | |
22 | + order = "more_recent" | |
23 | + | |
24 | + options = {:filter => order, :template_id => params[:template_id]} | |
25 | + | |
26 | + search_result = find_by_contents(asset, context, scope, query, {:page => 1}, options) | |
27 | + | |
28 | + articles = search_result[:results] | |
29 | + | |
30 | + present_articles(articles) | |
31 | + end | |
32 | + end | |
33 | + end | |
34 | + | |
35 | + end | |
36 | + end | |
37 | +end | ... | ... |
... | ... | @@ -0,0 +1,157 @@ |
1 | +require "uri" | |
2 | + | |
3 | +module Api | |
4 | + module V1 | |
5 | + class Session < Grape::API | |
6 | + | |
7 | + # Login to get token | |
8 | + # | |
9 | + # Parameters: | |
10 | + # login (*required) - user login or email | |
11 | + # password (required) - user password | |
12 | + # | |
13 | + # Example Request: | |
14 | + # POST http://localhost:3000/api/v1/login?login=adminuser&password=admin | |
15 | + post "/login" do | |
16 | + begin | |
17 | + user ||= User.authenticate(params[:login], params[:password], environment) | |
18 | + rescue User::UserNotActivated => e | |
19 | + render_api_error!(e.message, 401) | |
20 | + end | |
21 | + | |
22 | + return unauthorized! unless user | |
23 | + @current_user = user | |
24 | + present user, :with => Entities::UserLogin, :current_person => current_person | |
25 | + end | |
26 | + | |
27 | + post "/login_from_cookie" do | |
28 | + return unauthorized! if cookies[:auth_token].blank? | |
29 | + user = User.where(remember_token: cookies[:auth_token]).first | |
30 | + return unauthorized! unless user && user.activated? | |
31 | + @current_user = user | |
32 | + present user, :with => Entities::UserLogin, :current_person => current_person | |
33 | + end | |
34 | + | |
35 | + # Create user. | |
36 | + # | |
37 | + # Parameters: | |
38 | + # email (required) - Email | |
39 | + # password (required) - Password | |
40 | + # login - login | |
41 | + # Example Request: | |
42 | + # POST /register?email=some@mail.com&password=pas&password_confirmation=pas&login=some | |
43 | + params do | |
44 | + requires :email, type: String, desc: _("Email") | |
45 | + requires :login, type: String, desc: _("Login") | |
46 | + requires :password, type: String, desc: _("Password") | |
47 | + end | |
48 | + | |
49 | + post "/register" do | |
50 | + attrs = attributes_for_keys [:email, :login, :password, :password_confirmation] + environment.signup_person_fields | |
51 | + name = params[:name].present? ? params[:name] : attrs[:email] | |
52 | + attrs[:password_confirmation] = attrs[:password] if !attrs.has_key?(:password_confirmation) | |
53 | + user = User.new(attrs.merge(:name => name)) | |
54 | + | |
55 | + begin | |
56 | + user.signup! | |
57 | + user.generate_private_token! if user.activated? | |
58 | + present user, :with => Entities::UserLogin, :current_person => user.person | |
59 | + rescue ActiveRecord::RecordInvalid | |
60 | + message = user.errors.as_json.merge((user.person.present? ? user.person.errors : {}).as_json).to_json | |
61 | + render_api_error!(message, 400) | |
62 | + end | |
63 | + end | |
64 | + | |
65 | + params do | |
66 | + requires :activation_code, type: String, desc: _("Activation token") | |
67 | + end | |
68 | + | |
69 | + # Activate a user. | |
70 | + # | |
71 | + # Parameter: | |
72 | + # activation_code (required) - Activation token | |
73 | + # Example Request: | |
74 | + # PATCH /activate?activation_code=28259abd12cc6a64ef9399cf3286cb998b96aeaf | |
75 | + patch "/activate" do | |
76 | + user = User.find_by activation_code: params[:activation_code] | |
77 | + if user | |
78 | + unless user.environment.enabled?('admin_must_approve_new_users') | |
79 | + if user.activate | |
80 | + user.generate_private_token! | |
81 | + present user, :with => Entities::UserLogin, :current_person => current_person | |
82 | + end | |
83 | + else | |
84 | + if user.create_moderate_task | |
85 | + user.activation_code = nil | |
86 | + user.save! | |
87 | + | |
88 | + # Waiting for admin moderate user registration | |
89 | + status 202 | |
90 | + body({ | |
91 | + :message => 'Waiting for admin moderate user registration' | |
92 | + }) | |
93 | + end | |
94 | + end | |
95 | + else | |
96 | + # Token not found in database | |
97 | + render_api_error!(_('Token is invalid'), 412) | |
98 | + end | |
99 | + end | |
100 | + | |
101 | + # Request a new password. | |
102 | + # | |
103 | + # Parameters: | |
104 | + # value (required) - Email or login | |
105 | + # Example Request: | |
106 | + # POST /forgot_password?value=some@mail.com | |
107 | + post "/forgot_password" do | |
108 | + requestors = fetch_requestors(params[:value]) | |
109 | + not_found! if requestors.blank? | |
110 | + remote_ip = (request.respond_to?(:remote_ip) && request.remote_ip) || (env && env['REMOTE_ADDR']) | |
111 | + requestors.each do |requestor| | |
112 | + ChangePassword.create!(:requestor => requestor) | |
113 | + end | |
114 | + end | |
115 | + | |
116 | + # Resend activation code. | |
117 | + # | |
118 | + # Parameters: | |
119 | + # value (required) - Email or login | |
120 | + # Example Request: | |
121 | + # POST /resend_activation_code?value=some@mail.com | |
122 | + post "/resend_activation_code" do | |
123 | + requestors = fetch_requestors(params[:value]) | |
124 | + not_found! if requestors.blank? | |
125 | + remote_ip = (request.respond_to?(:remote_ip) && request.remote_ip) || (env && env['REMOTE_ADDR']) | |
126 | + requestors.each do |requestor| | |
127 | + requestor.user.resend_activation_code | |
128 | + end | |
129 | + present requestors.map(&:user), :with => Entities::UserLogin | |
130 | + end | |
131 | + | |
132 | + params do | |
133 | + requires :code, type: String, desc: _("Forgot password code") | |
134 | + end | |
135 | + # Change password | |
136 | + # | |
137 | + # Parameters: | |
138 | + # code (required) - Change password code | |
139 | + # password (required) | |
140 | + # password_confirmation (required) | |
141 | + # Example Request: | |
142 | + # PATCH /new_password?code=xxxx&password=secret&password_confirmation=secret | |
143 | + patch "/new_password" do | |
144 | + change_password = ChangePassword.find_by code: params[:code] | |
145 | + not_found! if change_password.nil? | |
146 | + | |
147 | + if change_password.update_attributes(:password => params[:password], :password_confirmation => params[:password_confirmation]) | |
148 | + change_password.finish | |
149 | + present change_password.requestor.user, :with => Entities::UserLogin, :current_person => current_person | |
150 | + else | |
151 | + something_wrong! | |
152 | + end | |
153 | + end | |
154 | + | |
155 | + end | |
156 | + end | |
157 | +end | ... | ... |
... | ... | @@ -0,0 +1,30 @@ |
1 | +module Api | |
2 | + module V1 | |
3 | + class Tags < Grape::API | |
4 | + resource :articles do | |
5 | + resource ':id/tags' do | |
6 | + get do | |
7 | + article = find_article(environment.articles, params[:id]) | |
8 | + present article.tag_list | |
9 | + end | |
10 | + | |
11 | + desc "Add a tag to an article" | |
12 | + post do | |
13 | + authenticate! | |
14 | + article = find_article(environment.articles, params[:id]) | |
15 | + article.tag_list=params[:tags] | |
16 | + article.save | |
17 | + present article.tag_list | |
18 | + end | |
19 | + end | |
20 | + end | |
21 | + | |
22 | + resource :environment do | |
23 | + desc 'Return the tag counts for this environment' | |
24 | + get '/tags' do | |
25 | + present environment.tag_counts | |
26 | + end | |
27 | + end | |
28 | + end | |
29 | + end | |
30 | +end | ... | ... |
... | ... | @@ -0,0 +1,57 @@ |
1 | +module Api | |
2 | + module V1 | |
3 | + class Tasks < Grape::API | |
4 | +# before { authenticate! } | |
5 | + | |
6 | +# ARTICLE_TYPES = Article.descendants.map{|a| a.to_s} | |
7 | + | |
8 | + resource :tasks do | |
9 | + | |
10 | + # Collect tasks | |
11 | + # | |
12 | + # Parameters: | |
13 | + # from - date where the search will begin. If nothing is passed the default date will be the date of the first article created | |
14 | + # oldest - Collect the oldest articles. If nothing is passed the newest articles are collected | |
15 | + # limit - amount of articles returned. The default value is 20 | |
16 | + # | |
17 | + # Example Request: | |
18 | + # GET host/api/v1/tasks?from=2013-04-04-14:41:43&until=2015-04-04-14:41:43&limit=10&private_token=e96fff37c2238fdab074d1dcea8e6317 | |
19 | + get do | |
20 | + tasks = select_filtered_collection_of(environment, 'tasks', params) | |
21 | + tasks = tasks.select {|t| current_person.has_permission?(t.permission, environment)} | |
22 | + present_partial tasks, :with => Entities::Task | |
23 | + end | |
24 | + | |
25 | + desc "Return the task id" | |
26 | + get ':id' do | |
27 | + task = find_task(environment, params[:id]) | |
28 | + present_partial task, :with => Entities::Task | |
29 | + end | |
30 | + end | |
31 | + | |
32 | + kinds = %w[community person enterprise] | |
33 | + kinds.each do |kind| | |
34 | + resource kind.pluralize.to_sym do | |
35 | + segment "/:#{kind}_id" do | |
36 | + resource :tasks do | |
37 | + get do | |
38 | + profile = environment.send(kind.pluralize).find(params["#{kind}_id"]) | |
39 | + present_tasks(profile) | |
40 | + end | |
41 | + | |
42 | + get ':id' do | |
43 | + profile = environment.send(kind.pluralize).find(params["#{kind}_id"]) | |
44 | + present_task(profile) | |
45 | + end | |
46 | + | |
47 | + post do | |
48 | + profile = environment.send(kind.pluralize).find(params["#{kind}_id"]) | |
49 | + post_task(profile, params) | |
50 | + end | |
51 | + end | |
52 | + end | |
53 | + end | |
54 | + end | |
55 | + end | |
56 | + end | |
57 | +end | ... | ... |
... | ... | @@ -0,0 +1,43 @@ |
1 | +module Api | |
2 | + module V1 | |
3 | + class Users < Grape::API | |
4 | + | |
5 | + resource :users do | |
6 | + | |
7 | + get do | |
8 | + users = select_filtered_collection_of(environment, 'users', params) | |
9 | + users = users.select{|u| u.person.display_info_to? current_person} | |
10 | + present users, :with => Entities::User, :current_person => current_person | |
11 | + end | |
12 | + | |
13 | + get "/me" do | |
14 | + authenticate! | |
15 | + present current_user, :with => Entities::User, :current_person => current_person | |
16 | + end | |
17 | + | |
18 | + get ":id" do | |
19 | + user = environment.users.find_by id: params[:id] | |
20 | + if user | |
21 | + present user, :with => Entities::User, :current_person => current_person | |
22 | + else | |
23 | + not_found! | |
24 | + end | |
25 | + end | |
26 | + | |
27 | + get ":id/permissions" do | |
28 | + authenticate! | |
29 | + user = environment.users.find(params[:id]) | |
30 | + output = {} | |
31 | + user.person.role_assignments.map do |role_assigment| | |
32 | + if role_assigment.resource.respond_to?(:identifier) && role_assigment.resource.identifier == params[:profile] | |
33 | + output[:permissions] = role_assigment.role.permissions | |
34 | + end | |
35 | + end | |
36 | + present output | |
37 | + end | |
38 | + | |
39 | + end | |
40 | + | |
41 | + end | |
42 | + end | |
43 | +end | ... | ... |
... | ... | @@ -0,0 +1,169 @@ |
1 | +module AuthenticatedSystem | |
2 | + | |
3 | + protected | |
4 | + | |
5 | + extend ActiveSupport::Concern | |
6 | + | |
7 | + included do | |
8 | + if self < ActionController::Base | |
9 | + around_filter :user_set_current | |
10 | + before_filter :override_user | |
11 | + before_filter :login_from_cookie | |
12 | + end | |
13 | + | |
14 | + # Inclusion hook to make #current_user and #logged_in? | |
15 | + # available as ActionView helper methods. | |
16 | + helper_method :current_user, :logged_in? | |
17 | + end | |
18 | + | |
19 | + # Returns true or false if the user is logged in. | |
20 | + # Preloads @current_user with the user model if they're logged in. | |
21 | + def logged_in? | |
22 | + current_user != nil | |
23 | + end | |
24 | + | |
25 | + # Accesses the current user from the session. | |
26 | + def current_user user_id = session[:user] | |
27 | + @current_user ||= begin | |
28 | + user = User.find_by id: user_id if user_id | |
29 | + user.session = session if user | |
30 | + User.current = user | |
31 | + user | |
32 | + end | |
33 | + end | |
34 | + | |
35 | + # Store the given user in the session. | |
36 | + def current_user=(new_user) | |
37 | + if new_user.nil? | |
38 | + session.delete(:user) | |
39 | + else | |
40 | + session[:user] = new_user.id | |
41 | + new_user.session = session | |
42 | + new_user.register_login | |
43 | + end | |
44 | + @current_user = User.current = new_user | |
45 | + end | |
46 | + | |
47 | + # See impl. from http://stackoverflow.com/a/2513456/670229 | |
48 | + def user_set_current | |
49 | + User.current = current_user | |
50 | + yield | |
51 | + ensure | |
52 | + # to address the thread variable leak issues in Puma/Thin webserver | |
53 | + User.current = nil | |
54 | + end | |
55 | + | |
56 | + # Check if the user is authorized. | |
57 | + # | |
58 | + # Override this method in your controllers if you want to restrict access | |
59 | + # to only a few actions or if you want to check if the user | |
60 | + # has the correct rights. | |
61 | + # | |
62 | + # Example: | |
63 | + # | |
64 | + # # only allow nonbobs | |
65 | + # def authorize? | |
66 | + # current_user.login != "bob" | |
67 | + # end | |
68 | + def authorized? | |
69 | + true | |
70 | + end | |
71 | + | |
72 | + # Filter method to enforce a login requirement. | |
73 | + # | |
74 | + # To require logins for all actions, use this in your controllers: | |
75 | + # | |
76 | + # before_filter :login_required | |
77 | + # | |
78 | + # To require logins for specific actions, use this in your controllers: | |
79 | + # | |
80 | + # before_filter :login_required, :only => [ :edit, :update ] | |
81 | + # | |
82 | + # To skip this in a subclassed controller: | |
83 | + # | |
84 | + # skip_before_filter :login_required | |
85 | + # | |
86 | + def login_required | |
87 | + username, passwd = get_auth_data | |
88 | + if username && passwd | |
89 | + self.current_user ||= User.authenticate(username, passwd) || nil | |
90 | + end | |
91 | + if logged_in? && authorized? | |
92 | + true | |
93 | + else | |
94 | + if params[:require_login_popup] | |
95 | + render :json => { :require_login_popup => true } | |
96 | + else | |
97 | + access_denied | |
98 | + end | |
99 | + end | |
100 | + end | |
101 | + | |
102 | + # Redirect as appropriate when an access request fails. | |
103 | + # | |
104 | + # The default action is to redirect to the login screen. | |
105 | + # | |
106 | + # Override this method in your controllers if you want to have special | |
107 | + # behavior in case the user is not authorized | |
108 | + # to access the requested action. For example, a popup window might | |
109 | + # simply close itself. | |
110 | + def access_denied | |
111 | + respond_to do |accepts| | |
112 | + accepts.html do | |
113 | + if request.xhr? | |
114 | + render :text => _('Access denied'), :status => 401 | |
115 | + else | |
116 | + store_location | |
117 | + redirect_to :controller => '/account', :action => 'login' | |
118 | + end | |
119 | + end | |
120 | + accepts.xml do | |
121 | + headers["Status"] = "Unauthorized" | |
122 | + headers["WWW-Authenticate"] = %(Basic realm="Web Password") | |
123 | + render :text => "Could't authenticate you", :status => '401 Unauthorized' | |
124 | + end | |
125 | + end | |
126 | + false | |
127 | + end | |
128 | + | |
129 | + # Store the URI of the current request in the session. | |
130 | + # | |
131 | + # We can return to this location by calling #redirect_back_or_default. | |
132 | + def store_location(location = request.url) | |
133 | + session[:return_to] = location | |
134 | + end | |
135 | + | |
136 | + # Redirect to the URI stored by the most recent store_location call or | |
137 | + # to the passed default. | |
138 | + def redirect_back_or_default(default) | |
139 | + if session[:return_to] | |
140 | + redirect_to(session.delete(:return_to)) | |
141 | + else | |
142 | + redirect_to(default) | |
143 | + end | |
144 | + end | |
145 | + | |
146 | + def override_user | |
147 | + return if params[:override_user].blank? | |
148 | + return unless logged_in? and user.is_admin? environment | |
149 | + @current_user = nil | |
150 | + current_user params[:override_user] | |
151 | + end | |
152 | + | |
153 | + # When called with before_filter :login_from_cookie will check for an :auth_token | |
154 | + # cookie and log the user back in if apropriate | |
155 | + def login_from_cookie | |
156 | + return if cookies[:auth_token].blank? or logged_in? | |
157 | + user = User.where(remember_token: cookies[:auth_token]).first | |
158 | + self.current_user = user if user and user.remember_token? | |
159 | + end | |
160 | + | |
161 | + private | |
162 | + @@http_auth_headers = %w(X-HTTP_AUTHORIZATION HTTP_AUTHORIZATION Authorization) | |
163 | + # gets BASIC auth info | |
164 | + def get_auth_data | |
165 | + auth_key = @@http_auth_headers.detect { |h| request.env.has_key?(h) } | |
166 | + auth_data = request.env[auth_key].to_s.split unless auth_key.blank? | |
167 | + return auth_data && auth_data[0] == 'Basic' ? Base64.decode64(auth_data[1]).split(':')[0..1] : [nil, nil] | |
168 | + end | |
169 | +end | ... | ... |
app/controllers/admin/categories_controller.rb
... | ... | @@ -7,7 +7,6 @@ class CategoriesController < AdminController |
7 | 7 | def index |
8 | 8 | @categories = environment.categories.where("parent_id is null AND type is null") |
9 | 9 | @regions = environment.regions.where(:parent_id => nil) |
10 | - @product_categories = environment.product_categories.where(:parent_id => nil) | |
11 | 10 | end |
12 | 11 | |
13 | 12 | def get_children | ... | ... |
app/controllers/admin/edit_template_controller.rb
1 | 1 | class EditTemplateController < AdminController |
2 | - | |
2 | + | |
3 | 3 | protect 'edit_environment_design', :environment |
4 | - | |
4 | + | |
5 | 5 | #FIXME |
6 | 6 | #design_editor :holder => 'environment', :autosave => true, :block_types => :block_types |
7 | 7 | |
... | ... | @@ -9,7 +9,6 @@ class EditTemplateController < AdminController |
9 | 9 | %w[ |
10 | 10 | FavoriteLinks |
11 | 11 | ListBlock |
12 | - SellersSearchBlock | |
13 | 12 | ] |
14 | 13 | end |
15 | 14 | ... | ... |
app/controllers/admin/environment_design_controller.rb
1 | 1 | class EnvironmentDesignController < BoxOrganizerController |
2 | - | |
2 | + | |
3 | 3 | protect 'edit_environment_design', :environment |
4 | 4 | |
5 | 5 | def available_blocks |
6 | - @available_blocks ||= [ ArticleBlock, LoginBlock, RecentDocumentsBlock, EnterprisesBlock, CommunitiesBlock, SellersSearchBlock, LinkListBlock, FeedReaderBlock, SlideshowBlock, HighlightsBlock, FeaturedProductsBlock, CategoriesBlock, RawHTMLBlock, TagsBlock ] | |
6 | + @available_blocks ||= [ ArticleBlock, LoginBlock, RecentDocumentsBlock, EnterprisesBlock, CommunitiesBlock, LinkListBlock, FeedReaderBlock, SlideshowBlock, HighlightsBlock, CategoriesBlock, RawHTMLBlock, TagsBlock ] | |
7 | 7 | @available_blocks += plugins.dispatch(:extra_blocks, :type => Environment) |
8 | 8 | end |
9 | 9 | ... | ... |
app/controllers/admin/users_controller.rb
... | ... | @@ -45,7 +45,6 @@ class UsersController < AdminController |
45 | 45 | redirect_to :action => :index, :q => params[:q], :filter => params[:filter] |
46 | 46 | end |
47 | 47 | |
48 | - | |
49 | 48 | def destroy_user |
50 | 49 | if request.post? |
51 | 50 | person = environment.people.find_by id: params[:id] |
... | ... | @@ -58,7 +57,6 @@ class UsersController < AdminController |
58 | 57 | redirect_to :action => :index, :q => params[:q], :filter => params[:filter] |
59 | 58 | end |
60 | 59 | |
61 | - | |
62 | 60 | def download |
63 | 61 | respond_to do |format| |
64 | 62 | format.html |
... | ... | @@ -87,8 +85,11 @@ class UsersController < AdminController |
87 | 85 | end |
88 | 86 | |
89 | 87 | def send_mail |
90 | - @mailing = environment.mailings.build(params[:mailing]) | |
91 | 88 | if request.post? |
89 | + @mailing = environment.mailings.build(params[:mailing]) | |
90 | + @mailing.recipients_roles = [] | |
91 | + @mailing.recipients_roles << "profile_admin" if params[:recipients][:profile_admins].include?("true") | |
92 | + @mailing.recipients_roles << "environment_administrator" if params[:recipients][:env_admins].include?("true") | |
92 | 93 | @mailing.locale = locale |
93 | 94 | @mailing.person = user |
94 | 95 | if @mailing.save | ... | ... |
app/controllers/application_controller.rb
... | ... | @@ -13,6 +13,13 @@ class ApplicationController < ActionController::Base |
13 | 13 | before_filter :verify_members_whitelist, :if => [:private_environment?, :user] |
14 | 14 | before_filter :redirect_to_current_user |
15 | 15 | |
16 | + before_filter :set_session_theme | |
17 | + def set_session_theme | |
18 | + if params[:theme] | |
19 | + session[:theme] = environment.theme_ids.include?(params[:theme]) ? params[:theme] : nil | |
20 | + end | |
21 | + end | |
22 | + | |
16 | 23 | def require_login_for_environment |
17 | 24 | login_required |
18 | 25 | end | ... | ... |
app/controllers/my_profile/cms_controller.rb
... | ... | @@ -103,12 +103,10 @@ class CmsController < MyProfileController |
103 | 103 | end |
104 | 104 | end |
105 | 105 | end |
106 | - | |
107 | - escape_fields @article | |
108 | 106 | end |
109 | 107 | |
110 | 108 | def new |
111 | - # FIXME this method should share some logic wirh edit !!! | |
109 | + # FIXME this method should share some logic with edit !!! | |
112 | 110 | |
113 | 111 | @success_back_to = params[:success_back_to] |
114 | 112 | # user must choose an article type first |
... | ... | @@ -174,9 +172,6 @@ class CmsController < MyProfileController |
174 | 172 | return |
175 | 173 | end |
176 | 174 | end |
177 | - | |
178 | - escape_fields @article | |
179 | - | |
180 | 175 | render :action => 'edit' |
181 | 176 | end |
182 | 177 | |
... | ... | @@ -365,7 +360,7 @@ class CmsController < MyProfileController |
365 | 360 | def search |
366 | 361 | query = params[:q] |
367 | 362 | results = find_by_contents(:uploaded_files, profile, profile.files.published, query)[:results] |
368 | - render :text => article_list_to_json(results), :content_type => 'application/json' | |
363 | + render :text => article_list_to_json(results).html_safe, :content_type => 'application/json' | |
369 | 364 | end |
370 | 365 | |
371 | 366 | def search_article_privacy_exceptions |
... | ... | @@ -409,9 +404,6 @@ class CmsController < MyProfileController |
409 | 404 | ] |
410 | 405 | articles += special_article_types if params && params[:cms] |
411 | 406 | parent_id = params ? params[:parent_id] : nil |
412 | - if profile.enterprise? | |
413 | - articles << EnterpriseHomepage | |
414 | - end | |
415 | 407 | if @parent && @parent.blog? |
416 | 408 | articles -= Article.folder_types.map(&:constantize) |
417 | 409 | end |
... | ... | @@ -451,9 +443,7 @@ class CmsController < MyProfileController |
451 | 443 | end |
452 | 444 | |
453 | 445 | def refuse_blocks |
454 | - if ['TinyMceArticle', 'TextileArticle', 'Event', 'EnterpriseHomepage'].include?(@type) | |
455 | - @no_design_blocks = true | |
456 | - end | |
446 | + @no_design_blocks = @type.present? && valid_article_type?(@type) ? !@type.constantize.can_display_blocks? : false | |
457 | 447 | end |
458 | 448 | |
459 | 449 | def per_page |
... | ... | @@ -521,10 +511,4 @@ class CmsController < MyProfileController |
521 | 511 | end |
522 | 512 | end |
523 | 513 | |
524 | - def escape_fields article | |
525 | - unless article.kind_of?(RssFeed) | |
526 | - @escaped_body = CGI::escapeHTML(article.body || '') | |
527 | - @escaped_abstract = CGI::escapeHTML(article.abstract || '') | |
528 | - end | |
529 | - end | |
530 | 514 | end | ... | ... |
app/controllers/my_profile/manage_products_controller.rb
... | ... | @@ -1,229 +0,0 @@ |
1 | -class ManageProductsController < ApplicationController | |
2 | - needs_profile | |
3 | - | |
4 | - protect 'manage_products', :profile, :except => [:show] | |
5 | - before_filter :check_environment_feature | |
6 | - before_filter :login_required, :except => [:show] | |
7 | - before_filter :create_product?, :only => [:new] | |
8 | - | |
9 | - protected | |
10 | - | |
11 | - def check_environment_feature | |
12 | - unless profile.environment.enabled?('products_for_enterprises') | |
13 | - render_not_found | |
14 | - return | |
15 | - end | |
16 | - end | |
17 | - | |
18 | - def create_product? | |
19 | - if !profile.create_product? | |
20 | - render_access_denied | |
21 | - return | |
22 | - end | |
23 | - end | |
24 | - | |
25 | - public | |
26 | - | |
27 | - def index | |
28 | - @products = @profile.products.paginate(:per_page => 10, :page => params[:page]) | |
29 | - end | |
30 | - | |
31 | - def show | |
32 | - @product = @profile.products.find(params[:id]) | |
33 | - @inputs = @product.inputs | |
34 | - @allowed_user = user && user.has_permission?('manage_products', profile) | |
35 | - end | |
36 | - | |
37 | - def categories_for_selection | |
38 | - @category = environment.categories.find_by id: params[:category_id] | |
39 | - @object_name = params[:object_name] | |
40 | - if @category | |
41 | - @categories = @category.children | |
42 | - @level = @category.leaf? ? @category.level : @categories.first.level | |
43 | - else | |
44 | - @categories = ProductCategory.top_level_for(environment) | |
45 | - @level = 0 | |
46 | - end | |
47 | - render :partial => 'categories_for_selection', :locals => { :categories => @categories, :level => @level } | |
48 | - end | |
49 | - | |
50 | - def new | |
51 | - @no_design_blocks = true | |
52 | - @category = params[:selected_category_id] ? Category.find(params[:selected_category_id]) : nil | |
53 | - @product = @profile.products.build(:product_category => @category) | |
54 | - @categories = ProductCategory.top_level_for(environment) | |
55 | - @level = 0 | |
56 | - if request.post? | |
57 | - if @product.save | |
58 | - session[:notice] = _('Product succesfully created') | |
59 | - render :partial => 'shared/redirect_via_javascript', | |
60 | - :locals => { :url => url_for(:controller => 'manage_products', :action => 'show', :id => @product) } | |
61 | - else | |
62 | - render_dialog_error_messages 'product' | |
63 | - end | |
64 | - end | |
65 | - end | |
66 | - | |
67 | - def edit | |
68 | - @product = @profile.products.find(params[:id]) | |
69 | - field = params[:field] | |
70 | - if request.post? | |
71 | - begin | |
72 | - @product.update!(params[:product]) | |
73 | - render :partial => "display_#{field}", :locals => {:product => @product} | |
74 | - rescue Exception => e | |
75 | - render :partial => "edit_#{field}", :locals => {:product => @product, :errors => true} | |
76 | - end | |
77 | - else | |
78 | - render :partial => "edit_#{field}", :locals => {:product => @product, :errors => false} | |
79 | - end | |
80 | - end | |
81 | - | |
82 | - def edit_category | |
83 | - @product = @profile.products.find(params[:id]) | |
84 | - @category = @product.product_category || ProductCategory.first | |
85 | - @categories = ProductCategory.top_level_for(environment) | |
86 | - @edit = true | |
87 | - @level = @category.level | |
88 | - if request.post? | |
89 | - if @product.update({:product_category_id => params[:selected_category_id]}, :without_protection => true) | |
90 | - render :partial => 'shared/redirect_via_javascript', | |
91 | - :locals => { :url => url_for(:controller => 'manage_products', :action => 'show', :id => @product) } | |
92 | - else | |
93 | - render_dialog_error_messages 'product' | |
94 | - end | |
95 | - end | |
96 | - end | |
97 | - | |
98 | - def show_category_tree | |
99 | - @category = environment.categories.find params[:category_id] | |
100 | - render :partial => 'selected_category_tree' | |
101 | - end | |
102 | - | |
103 | - def search_categories | |
104 | - @term = params[:term].downcase | |
105 | - conditions = ['LOWER(name) LIKE ? OR LOWER(name) LIKE ?', "#{@term}%", "% #{@term}%"] | |
106 | - @categories = ProductCategory.where(conditions).limit(10) | |
107 | - render :json => (@categories.map do |category| | |
108 | - {:label => category.name, :value => category.id} | |
109 | - end) | |
110 | - end | |
111 | - | |
112 | - def add_input | |
113 | - @product = @profile.products.find(params[:id]) | |
114 | - @input = @product.inputs.build | |
115 | - @categories = ProductCategory.top_level_for(environment) | |
116 | - @level = 0 | |
117 | - if request.post? | |
118 | - if @input.update(:product_category_id => params[:selected_category_id]) | |
119 | - @inputs = @product.inputs | |
120 | - render :partial => 'display_inputs' | |
121 | - else | |
122 | - render_dialog_error_messages 'product' | |
123 | - end | |
124 | - else | |
125 | - render :partial => 'add_input' | |
126 | - end | |
127 | - end | |
128 | - | |
129 | - def manage_product_details | |
130 | - @product = @profile.products.find(params[:id]) | |
131 | - if request.post? | |
132 | - @product.update_price_details(params[:price_details]) if params[:price_details] | |
133 | - render :partial => 'display_price_details' | |
134 | - else | |
135 | - render :partial => 'manage_product_details' | |
136 | - end | |
137 | - end | |
138 | - | |
139 | - def remove_price_detail | |
140 | - @product = @profile.products.find(params[:product]) | |
141 | - @price_detail = @product.price_details.find(params[:id]) | |
142 | - @product = @price_detail.product | |
143 | - if request.post? | |
144 | - @price_detail.destroy | |
145 | - render :nothing => true | |
146 | - end | |
147 | - end | |
148 | - | |
149 | - def display_price_composition_bar | |
150 | - @product = @profile.products.find(params[:id]) | |
151 | - render :partial => 'price_composition_bar' | |
152 | - end | |
153 | - | |
154 | - def display_inputs_cost | |
155 | - @product = @profile.products.find(params[:id]) | |
156 | - render :inline => "<%= float_to_currency(@product.inputs_cost) %>" | |
157 | - end | |
158 | - | |
159 | - def destroy | |
160 | - @product = @profile.products.find(params[:id]) | |
161 | - if @product.destroy | |
162 | - session[:notice] = _('Product succesfully removed') | |
163 | - redirect_back_or_default :action => 'index' | |
164 | - else | |
165 | - session[:notice] = _('Could not remove the product') | |
166 | - redirect_back_or_default :action => 'show', :id => @product | |
167 | - end | |
168 | - end | |
169 | - | |
170 | - def edit_input | |
171 | - if request.xhr? | |
172 | - @input = @profile.inputs.find_by id: params[:id] | |
173 | - if @input | |
174 | - if request.post? | |
175 | - if @input.update(params[:input]) | |
176 | - render :partial => 'display_input', :locals => {:input => @input} | |
177 | - else | |
178 | - render :partial => 'edit_input' | |
179 | - end | |
180 | - else | |
181 | - render :partial => 'edit_input' | |
182 | - end | |
183 | - else | |
184 | - render :text => _('The input was not found') | |
185 | - end | |
186 | - end | |
187 | - end | |
188 | - | |
189 | - def order_inputs | |
190 | - @product = @profile.products.find(params[:id]) | |
191 | - @product.order_inputs!(params[:input]) if params[:input] | |
192 | - render :nothing => true | |
193 | - end | |
194 | - | |
195 | - def remove_input | |
196 | - @input = @profile.inputs.find(params[:id]) | |
197 | - @product = @input.product | |
198 | - if request.post? | |
199 | - if @input.destroy | |
200 | - @inputs = @product.inputs | |
201 | - render :partial => 'display_inputs' | |
202 | - else | |
203 | - render_dialog_error_messages 'input' | |
204 | - end | |
205 | - end | |
206 | - end | |
207 | - | |
208 | - def certifiers_for_selection | |
209 | - @qualifier = Qualifier.exists?(:id => params[:id]) ? Qualifier.find(params[:id]) : nil | |
210 | - render :update do |page| | |
211 | - page.replace_html params[:certifier_area], :partial => 'certifiers_for_selection' | |
212 | - end | |
213 | - end | |
214 | - | |
215 | - def create_production_cost | |
216 | - cost = @profile.production_costs.create(:name => params[:id]) | |
217 | - if cost.valid? | |
218 | - cost.save | |
219 | - render :text => {:name => cost.name, | |
220 | - :id => cost.id, | |
221 | - :ok => true | |
222 | - }.to_json | |
223 | - else | |
224 | - render :text => {:ok => false, | |
225 | - :error_msg => _(cost.errors['name'].join('\n')) % {:fn => _('Name')} | |
226 | - }.to_json | |
227 | - end | |
228 | - end | |
229 | -end |
app/controllers/my_profile/profile_design_controller.rb
... | ... | @@ -45,17 +45,10 @@ class ProfileDesignController < BoxOrganizerController |
45 | 45 | if profile.enterprise? |
46 | 46 | blocks << DisabledEnterpriseMessageBlock |
47 | 47 | blocks << HighlightsBlock |
48 | - blocks << ProductCategoriesBlock | |
49 | - blocks << FeaturedProductsBlock | |
50 | 48 | blocks << FansBlock |
51 | 49 | blocks += plugins.dispatch(:extra_blocks, :type => Enterprise) |
52 | 50 | end |
53 | 51 | |
54 | - # product block exclusive for enterprises in environments that permits it | |
55 | - if profile.enterprise? && profile.environment.enabled?('products_for_enterprises') | |
56 | - blocks << ProductsBlock | |
57 | - end | |
58 | - | |
59 | 52 | # block exclusive to profiles that have blog |
60 | 53 | if profile.has_blog? |
61 | 54 | blocks << BlogArchivesBlock | ... | ... |
app/controllers/my_profile/profile_editor_controller.rb
... | ... | @@ -29,6 +29,7 @@ class ProfileEditorController < MyProfileController |
29 | 29 | Image.transaction do |
30 | 30 | begin |
31 | 31 | @plugins.dispatch(:profile_editor_transaction_extras) |
32 | + # TODO: This is unsafe! Add sanitizer | |
32 | 33 | @profile_data.update!(params[:profile_data]) |
33 | 34 | redirect_to :action => 'index', :profile => profile.identifier |
34 | 35 | rescue Exception => ex | ... | ... |
app/controllers/my_profile/profile_themes_controller.rb
... | ... | @@ -63,12 +63,12 @@ class ProfileThemesController < ThemesController |
63 | 63 | end |
64 | 64 | |
65 | 65 | def start_test |
66 | - session[:theme] = params[:id] | |
66 | + session[:user_theme] = params[:id] | |
67 | 67 | redirect_to :controller => 'content_viewer', :profile => profile.identifier, :action => 'view_page' |
68 | 68 | end |
69 | 69 | |
70 | 70 | def stop_test |
71 | - session[:theme] = nil | |
71 | + session[:user_theme] = nil | |
72 | 72 | redirect_to :action => 'index' |
73 | 73 | end |
74 | 74 | ... | ... |
app/controllers/my_profile/tasks_controller.rb
... | ... | @@ -26,7 +26,11 @@ class TasksController < MyProfileController |
26 | 26 | end |
27 | 27 | |
28 | 28 | def processed |
29 | - @tasks = Task.to(profile).without_spam.closed.sort_by(&:created_at) | |
29 | + @tasks = Task.to(profile).without_spam.closed.order('tasks.created_at DESC') | |
30 | + @filter = params[:filter] || {} | |
31 | + @tasks = filter_tasks(@filter, @tasks) | |
32 | + @tasks = @tasks.paginate(:per_page => Task.per_page, :page => params[:page]) | |
33 | + @task_types = Task.closed_types_for(profile) | |
30 | 34 | end |
31 | 35 | |
32 | 36 | def change_responsible |
... | ... | @@ -102,4 +106,28 @@ class TasksController < MyProfileController |
102 | 106 | render :json => result.map { |task| {:label => task.data[:name], :value => task.data[:name]} } |
103 | 107 | end |
104 | 108 | |
109 | + protected | |
110 | + | |
111 | + def filter_tasks(filter, tasks) | |
112 | + tasks = tasks.eager_load(:requestor, :closed_by) | |
113 | + tasks = tasks.of(filter[:type].presence) | |
114 | + tasks = tasks.where(:status => filter[:status]) unless filter[:status].blank? | |
115 | + | |
116 | + filter[:created_from] = Date.parse(filter[:created_from]) unless filter[:created_from].blank? | |
117 | + filter[:created_until] = Date.parse(filter[:created_until]) unless filter[:created_until].blank? | |
118 | + filter[:closed_from] = Date.parse(filter[:closed_from]) unless filter[:closed_from].blank? | |
119 | + filter[:closed_until] = Date.parse(filter[:closed_until]) unless filter[:closed_until].blank? | |
120 | + | |
121 | + tasks = tasks.from_creation_date filter[:created_from] unless filter[:created_from].blank? | |
122 | + tasks = tasks.until_creation_date filter[:created_until] unless filter[:created_until].blank? | |
123 | + | |
124 | + tasks = tasks.from_closed_date filter[:closed_from] unless filter[:closed_from].blank? | |
125 | + tasks = tasks.until_closed_date filter[:closed_until] unless filter[:closed_until].blank? | |
126 | + | |
127 | + tasks = tasks.where('profiles.name LIKE ?', filter[:requestor]) unless filter[:requestor].blank? | |
128 | + tasks = tasks.where('closed_bies_tasks.name LIKE ?', filter[:closed_by]) unless filter[:closed_by].blank? | |
129 | + tasks = tasks.where('tasks.data LIKE ?', "%#{filter[:text]}%") unless filter[:text].blank? | |
130 | + tasks | |
131 | + end | |
132 | + | |
105 | 133 | end | ... | ... |
app/controllers/public/api_controller.rb
app/controllers/public/catalog_controller.rb
... | ... | @@ -1,19 +0,0 @@ |
1 | -class CatalogController < PublicController | |
2 | - needs_profile | |
3 | - | |
4 | - before_filter :check_enterprise_and_environment | |
5 | - | |
6 | - def index | |
7 | - extend CatalogHelper | |
8 | - catalog_load_index | |
9 | - end | |
10 | - | |
11 | - protected | |
12 | - | |
13 | - def check_enterprise_and_environment | |
14 | - unless profile.enterprise? && @profile.environment.enabled?('products_for_enterprises') | |
15 | - redirect_to :controller => 'profile', :profile => profile.identifier, :action => 'index' | |
16 | - end | |
17 | - end | |
18 | - | |
19 | -end |
app/controllers/public/content_viewer_controller.rb
... | ... | @@ -209,7 +209,7 @@ class ContentViewerController < ApplicationController |
209 | 209 | end |
210 | 210 | |
211 | 211 | if @page.published && @page.uploaded_file? |
212 | - redirect_to @page.public_filename | |
212 | + redirect_to "#{Noosfero.root}#{@page.public_filename}" | |
213 | 213 | else |
214 | 214 | send_data data, @page.download_headers |
215 | 215 | end | ... | ... |
app/controllers/public/profile_controller.rb
... | ... | @@ -86,8 +86,16 @@ class ProfileController < PublicController |
86 | 86 | @articles = profile.top_level_articles.includes([:profile, :parent]) |
87 | 87 | end |
88 | 88 | |
89 | + def join_modal | |
90 | + profile.add_member(user) | |
91 | + session[:notice] = _('%s administrator still needs to accept you as member.') % profile.name | |
92 | + redirect_to :action => :index | |
93 | + end | |
94 | + | |
89 | 95 | def join |
90 | 96 | if !user.memberships.include?(profile) |
97 | + return if profile.community? && show_confirmation_modal?(profile) | |
98 | + | |
91 | 99 | profile.add_member(user) |
92 | 100 | if !profile.members.include?(user) |
93 | 101 | render :text => {:message => _('%s administrator still needs to accept you as member.') % profile.name}.to_json | ... | ... |
app/controllers/public/search_controller.rb
... | ... | @@ -52,7 +52,6 @@ class SearchController < PublicController |
52 | 52 | [ |
53 | 53 | [ :people, _('People'), :recent_people ], |
54 | 54 | [ :enterprises, _('Enterprises'), :recent_enterprises ], |
55 | - [ :products, _('Products'), :recent_products ], | |
56 | 55 | [ :events, _('Upcoming events'), :upcoming_events ], |
57 | 56 | [ :communities, _('Communities'), :recent_communities ], |
58 | 57 | [ :articles, _('Contents'), :recent_articles ] |
... | ... | @@ -78,16 +77,17 @@ class SearchController < PublicController |
78 | 77 | full_text_search |
79 | 78 | end |
80 | 79 | |
81 | - def products | |
82 | - @scope = @environment.products | |
83 | - full_text_search | |
84 | - end | |
85 | - | |
86 | 80 | def enterprises |
87 | 81 | @scope = visible_profiles(Enterprise) |
88 | 82 | full_text_search |
89 | 83 | end |
90 | 84 | |
85 | + # keep URL compatibility | |
86 | + def products | |
87 | + return render_not_found unless defined? ProductsPlugin | |
88 | + redirect_to url_for(params.merge controller: 'products_plugin/search', action: :products) | |
89 | + end | |
90 | + | |
91 | 91 | def communities |
92 | 92 | @scope = visible_profiles(Community) |
93 | 93 | full_text_search |
... | ... | @@ -186,7 +186,6 @@ class SearchController < PublicController |
186 | 186 | people: _('People'), |
187 | 187 | communities: _('Communities'), |
188 | 188 | enterprises: _('Enterprises'), |
189 | - products: _('Products and Services'), | |
190 | 189 | events: _('Events'), |
191 | 190 | } |
192 | 191 | end |
... | ... | @@ -260,12 +259,11 @@ class SearchController < PublicController |
260 | 259 | end |
261 | 260 | |
262 | 261 | def available_assets |
263 | - assets = { | |
262 | + { | |
264 | 263 | articles: _('Contents'), |
265 | 264 | enterprises: _('Enterprises'), |
266 | 265 | people: _('People'), |
267 | 266 | communities: _('Communities'), |
268 | - products: _('Products and Services'), | |
269 | 267 | } |
270 | 268 | end |
271 | 269 | ... | ... |
app/helpers/action_tracker_helper.rb
... | ... | @@ -5,22 +5,22 @@ module ActionTrackerHelper |
5 | 5 | end |
6 | 6 | |
7 | 7 | def new_friendship_description ta |
8 | - n_('has made 1 new friend:<br />%{name}', 'has made %{num} new friends:<br />%{name}', ta.get_friend_name.size) % { | |
8 | + n_('has made 1 new friend:<br />%{name}', 'has made %{num} new friends:<br />%{name}', ta.get_friend_name.size).html_safe % { | |
9 | 9 | num: ta.get_friend_name.size, |
10 | - name: ta.collect_group_with_index(:friend_name) do |n,i| | |
10 | + name: safe_join(ta.collect_group_with_index(:friend_name) do |n,i| | |
11 | 11 | link_to image_tag(ta.get_friend_profile_custom_icon[i] || default_or_themed_icon("/images/icons-app/person-icon.png")), |
12 | 12 | ta.get_friend_url[i], title: n |
13 | - end.join | |
13 | + end) | |
14 | 14 | } |
15 | 15 | end |
16 | 16 | |
17 | 17 | def join_community_description ta |
18 | - n_('has joined 1 community:<br />%{name}', 'has joined %{num} communities:<br />%{name}', ta.get_resource_name.size) % { | |
18 | + n_('has joined 1 community:<br />%{name}'.html_safe, 'has joined %{num} communities:<br />%{name}'.html_safe, ta.get_resource_name.size) % { | |
19 | 19 | num: ta.get_resource_name.size, |
20 | 20 | name: ta.collect_group_with_index(:resource_name) do |n,i| |
21 | - link_to image_tag(ta.get_resource_profile_custom_icon[i] || default_or_themed_icon("/images/icons-app/community-icon.png")), | |
21 | + link = link_to image_tag(ta.get_resource_profile_custom_icon[i] || default_or_themed_icon("/images/icons-app/community-icon.png")), | |
22 | 22 | ta.get_resource_url[i], title: n |
23 | - end.join | |
23 | + end.join.html_safe | |
24 | 24 | } |
25 | 25 | end |
26 | 26 | |
... | ... | @@ -67,24 +67,6 @@ module ActionTrackerHelper |
67 | 67 | } |
68 | 68 | end |
69 | 69 | |
70 | - def create_product_description ta | |
71 | - _('created the product %{title}') % { | |
72 | - title: link_to(truncate(ta.get_name), ta.get_url), | |
73 | - } | |
74 | - end | |
75 | - | |
76 | - def update_product_description ta | |
77 | - _('updated the product %{title}') % { | |
78 | - title: link_to(truncate(ta.get_name), ta.get_url), | |
79 | - } | |
80 | - end | |
81 | - | |
82 | - def remove_product_description ta | |
83 | - _('removed the product %{title}') % { | |
84 | - title: truncate(ta.get_name), | |
85 | - } | |
86 | - end | |
87 | - | |
88 | 70 | def favorite_enterprise_description ta |
89 | 71 | _('favorited enterprise %{title}') % { |
90 | 72 | title: link_to(truncate(ta.get_enterprise_name), ta.get_enterprise_url), | ... | ... |
app/helpers/application_helper.rb
... | ... | @@ -44,8 +44,6 @@ module ApplicationHelper |
44 | 44 | |
45 | 45 | include TokenHelper |
46 | 46 | |
47 | - include CatalogHelper | |
48 | - | |
49 | 47 | include PluginsHelper |
50 | 48 | |
51 | 49 | include ButtonsHelper |
... | ... | @@ -56,6 +54,8 @@ module ApplicationHelper |
56 | 54 | |
57 | 55 | include TaskHelper |
58 | 56 | |
57 | + include MembershipsHelper | |
58 | + | |
59 | 59 | def locale |
60 | 60 | (@page && !@page.language.blank?) ? @page.language : FastGettext.locale |
61 | 61 | end |
... | ... | @@ -99,7 +99,6 @@ module ApplicationHelper |
99 | 99 | # |
100 | 100 | # TODO: implement correcly the 'Help' button click |
101 | 101 | def help(content = nil, link_name = nil, options = {}, &block) |
102 | - | |
103 | 102 | link_name ||= _('Help') |
104 | 103 | |
105 | 104 | @help_message_id ||= 1 |
... | ... | @@ -122,7 +121,7 @@ module ApplicationHelper |
122 | 121 | button = link_to_function(content_tag('span', link_name), "Element.show('#{help_id}')", options ) |
123 | 122 | close_button = content_tag("div", link_to_function(_("Close"), "Element.hide('#{help_id}')", :class => 'close_help_button')) |
124 | 123 | |
125 | - text = content_tag('div', button + content_tag('div', content_tag('div', content) + close_button, :class => 'help_message', :id => help_id, :style => 'display: none;'), :class => 'help_box') | |
124 | + text = content_tag('div', button + content_tag('div', content_tag('div', content.html_safe) + close_button, :class => 'help_message', :id => help_id, :style => 'display: none;'), :class => 'help_box') | |
126 | 125 | |
127 | 126 | unless block.nil? |
128 | 127 | concat(text) |
... | ... | @@ -234,13 +233,6 @@ module ApplicationHelper |
234 | 233 | link_to(content_tag('span', text), url, html_options.merge(:class => the_class, :title => text)) |
235 | 234 | end |
236 | 235 | |
237 | - def button_bar(options = {}, &block) | |
238 | - options[:class].nil? ? | |
239 | - options[:class]='button-bar' : | |
240 | - options[:class]+=' button-bar' | |
241 | - concat(content_tag('div', capture(&block).to_s + tag('br', :style => 'clear: left;'), options)) | |
242 | - end | |
243 | - | |
244 | 236 | def render_profile_actions klass |
245 | 237 | name = klass.to_s.underscore |
246 | 238 | begin |
... | ... | @@ -352,7 +344,7 @@ module ApplicationHelper |
352 | 344 | end |
353 | 345 | |
354 | 346 | def is_testing_theme |
355 | - !controller.session[:theme].nil? | |
347 | + !controller.session[:user_theme].nil? | |
356 | 348 | end |
357 | 349 | |
358 | 350 | def theme_owner |
... | ... | @@ -362,8 +354,8 @@ module ApplicationHelper |
362 | 354 | def popover_menu(title,menu_title,links,html_options={}) |
363 | 355 | html_options[:class] = "" unless html_options[:class] |
364 | 356 | html_options[:class] << " menu-submenu-trigger" |
365 | - html_options[:onclick] = "toggleSubmenu(this, '#{menu_title}', #{CGI::escapeHTML(links.to_json)}); return false" | |
366 | 357 | |
358 | + html_options[:onclick] = "toggleSubmenu(this, '#{menu_title}', #{CGI::escapeHTML(links.to_json)}); return false".html_safe | |
367 | 359 | link_to(content_tag(:span, title), '#', html_options) |
368 | 360 | end |
369 | 361 | |
... | ... | @@ -473,9 +465,9 @@ module ApplicationHelper |
473 | 465 | map(&:role) |
474 | 466 | names = [] |
475 | 467 | roles.each do |role| |
476 | - names << content_tag('span', role.name, :style => "color: #{role_color(role, resource.environment.id)}") | |
468 | + names << content_tag('span', role.name, :style => "color: #{role_color(role, resource.environment.id)}").html_safe | |
477 | 469 | end |
478 | - names.join(', ') | |
470 | + safe_join(names, ', ') | |
479 | 471 | end |
480 | 472 | |
481 | 473 | def role_color(role, env_id) |
... | ... | @@ -601,8 +593,8 @@ module ApplicationHelper |
601 | 593 | end |
602 | 594 | |
603 | 595 | if block |
604 | - field_html ||= '' | |
605 | - field_html += capture(&block) | |
596 | + field_html ||= ''.html_safe | |
597 | + field_html = [field_html, capture(&block)].safe_join | |
606 | 598 | end |
607 | 599 | |
608 | 600 | if controller.action_name == 'signup' || controller.action_name == 'new_community' || (controller.controller_name == "enterprise_registration" && controller.action_name == 'index') || (controller.controller_name == 'home' && controller.action_name == 'index' && user.nil?) |
... | ... | @@ -611,7 +603,9 @@ module ApplicationHelper |
611 | 603 | end |
612 | 604 | else |
613 | 605 | if profile.active_fields.include?(name) |
614 | - result = content_tag('div', field_html + profile_field_privacy_selector(profile, name), :class => 'field-with-privacy-selector') | |
606 | + result = content_tag :div, class: 'field-with-privacy-selector' do | |
607 | + [field_html, profile_field_privacy_selector(profile, name)].safe_join | |
608 | + end | |
615 | 609 | end |
616 | 610 | end |
617 | 611 | |
... | ... | @@ -619,10 +613,6 @@ module ApplicationHelper |
619 | 613 | result = required(result) |
620 | 614 | end |
621 | 615 | |
622 | - if block | |
623 | - concat(result) | |
624 | - end | |
625 | - | |
626 | 616 | result |
627 | 617 | end |
628 | 618 | |
... | ... | @@ -788,7 +778,7 @@ module ApplicationHelper |
788 | 778 | return "" if categories.blank? |
789 | 779 | content_tag(:ul) do |
790 | 780 | categories.map do |category| |
791 | - category_path = category.kind_of?(ProductCategory) ? {:controller => 'search', :action => 'assets', :asset => 'products', :product_category => category.id} : { :controller => 'search', :action => 'category_index', :category_path => category.explode_path } | |
781 | + category_path = { :controller => 'search', :action => 'category_index', :category_path => category.explode_path } | |
792 | 782 | if category.display_in_menu? |
793 | 783 | content_tag(:li) do |
794 | 784 | if !category.is_leaf_displayable_in_menu? |
... | ... | @@ -867,7 +857,7 @@ module ApplicationHelper |
867 | 857 | alias :browse_communities_menu :search_communities_menu |
868 | 858 | |
869 | 859 | def pagination_links(collection, options={}) |
870 | - options = {:previous_label => content_tag(:span, '« ', :class => 'previous-arrow') + _('Previous'), :next_label => _('Next') + content_tag(:span, ' »', :class => 'next-arrow'), :inner_window => 1, :outer_window => 0 }.merge(options) | |
860 | + options = {:previous_label => content_tag(:span, '« '.html_safe, :class => 'previous-arrow') + _('Previous'), :next_label => _('Next') + content_tag(:span, ' »'.html_safe, :class => 'next-arrow'), :inner_window => 1, :outer_window => 0 }.merge(options) | |
871 | 861 | will_paginate(collection, options) |
872 | 862 | end |
873 | 863 | |
... | ... | @@ -911,7 +901,8 @@ module ApplicationHelper |
911 | 901 | end |
912 | 902 | |
913 | 903 | def admin_link |
914 | - user.is_admin?(environment) ? link_to('<i class="icon-menu-admin"></i><strong>' + _('Administration') + '</strong>', environment.admin_url, :title => _("Configure the environment"), :class => 'admin-link') : '' | |
904 | + admin_icon = '<i class="icon-menu-admin"></i><strong>' + _('Administration') + '</strong>' | |
905 | + user.is_admin?(environment) ? link_to(admin_icon.html_safe, environment.admin_url, :title => _("Configure the environment"), :class => 'admin-link') : '' | |
915 | 906 | end |
916 | 907 | |
917 | 908 | def usermenu_logged_in |
... | ... | @@ -920,23 +911,39 @@ module ApplicationHelper |
920 | 911 | if count > 0 |
921 | 912 | pending_tasks_count = link_to(count.to_s, user.tasks_url, :id => 'pending-tasks-count', :title => _("Manage your pending tasks")) |
922 | 913 | end |
914 | + user_identifier = "<i style='background-image:url(#{user.profile_custom_icon(gravatar_default)})'></i><strong>#{user.identifier}</strong>" | |
915 | + welcome_link = link_to(user_identifier.html_safe, user.public_profile_url, :id => "homepage-link", :title => _('Go to your homepage')) | |
916 | + welcome_span = _("<span class='welcome'>Welcome,</span> %s") % welcome_link.html_safe | |
917 | + ctrl_panel_icon = '<i class="icon-menu-ctrl-panel"></i>' | |
918 | + ctrl_panel_section = '<strong>' + ctrl_panel_icon + _('Control panel') + '</strong>' | |
919 | + ctrl_panel_link = link_to(ctrl_panel_section.html_safe, user.admin_url, :class => 'ctrl-panel', :title => _("Configure your personal account and content")) | |
920 | + logout_icon = '<i class="icon-menu-logout"></i><strong>' + _('Logout') + '</strong>' | |
921 | + logout_link = link_to(logout_icon.html_safe, { :controller => 'account', :action => 'logout'} , :id => "logout", :title => _("Leave the system")) | |
922 | + join_result = safe_join( | |
923 | + [welcome_span.html_safe, render_environment_features(:usermenu).html_safe, admin_link.html_safe, | |
924 | + manage_enterprises.html_safe, manage_communities.html_safe, ctrl_panel_link.html_safe, | |
925 | + pending_tasks_count.html_safe, logout_link.html_safe], "") | |
926 | + join_result | |
927 | + end | |
923 | 928 | |
924 | - (_("<span class='welcome'>Welcome,</span> %s") % link_to("<i style='background-image:url(#{user.profile_custom_icon(gravatar_default)})'></i><strong>#{user.identifier}</strong>", user.url, :id => "homepage-link", :title => _('Go to your homepage'))) + | |
925 | - render_environment_features(:usermenu) + | |
926 | - admin_link + | |
927 | - manage_enterprises + | |
928 | - manage_communities + | |
929 | - link_to('<i class="icon-menu-ctrl-panel"></i><strong>' + _('Control panel') + '</strong>', user.admin_url, :class => 'ctrl-panel', :title => _("Configure your personal account and content")) + | |
930 | - pending_tasks_count + | |
931 | - link_to('<i class="icon-menu-logout"></i><strong>' + _('Logout') + '</strong>', { :controller => 'account', :action => 'logout'} , :id => "logout", :title => _("Leave the system")) | |
929 | + def usermenu_notlogged_in | |
930 | + login_str = '<i class="icon-menu-login"></i><strong>' + _('Login') + '</strong>' | |
931 | + ret = _("<span class='login'>%s</span>") % modal_inline_link_to(login_str.html_safe, login_url, '#inlineLoginBox', :id => 'link_login') | |
932 | + return ret.html_safe | |
932 | 933 | end |
933 | 934 | |
935 | + def usermenu_signup | |
936 | + signup_str = '<strong>' + _('Sign up') + '</strong>' | |
937 | + ret = _("<span class='or'>or</span> <span class='signup'>%s</span>") % link_to(signup_str.html_safe, :controller => 'account', :action => 'signup') | |
938 | + return ret.html_safe | |
939 | + | |
940 | + end | |
934 | 941 | def limited_text_area(object_name, method, limit, text_area_id, options = {}) |
935 | - content_tag(:div, [ | |
942 | + content_tag(:div, safe_join([ | |
936 | 943 | text_area(object_name, method, { :id => text_area_id, :onkeyup => "limited_text_area('#{text_area_id}', #{limit})" }.merge(options)), |
937 | 944 | content_tag(:p, content_tag(:span, limit) + ' ' + _(' characters left'), :id => text_area_id + '_left'), |
938 | 945 | content_tag(:p, _('Limit of characters reached'), :id => text_area_id + '_limit', :style => 'display: none') |
939 | - ].join, :class => 'limited-text-area') | |
946 | + ]), :class => 'limited-text-area') | |
940 | 947 | end |
941 | 948 | |
942 | 949 | def expandable_text_area(object_name, method, text_area_id, options = {}) |
... | ... | @@ -970,11 +977,12 @@ module ApplicationHelper |
970 | 977 | |
971 | 978 | def task_information(task) |
972 | 979 | values = {} |
980 | + values.merge!(task.information[:variables]) if task.information[:variables] | |
973 | 981 | values.merge!({:requestor => link_to(task.requestor.name, task.requestor.url)}) if task.requestor |
982 | + values.merge!({:target => link_to(task.target.name, task.target.url)}) if (task.target && task.target.respond_to?(:url)) | |
974 | 983 | values.merge!({:subject => content_tag('span', task.subject, :class=>'task_target')}) if task.subject |
975 | 984 | values.merge!({:linked_subject => link_to(content_tag('span', task.linked_subject[:text], :class => 'task_target'), task.linked_subject[:url])}) if task.linked_subject |
976 | - values.merge!(task.information[:variables]) if task.information[:variables] | |
977 | - task.information[:message] % values | |
985 | + (task.information[:message] % values).html_safe | |
978 | 986 | end |
979 | 987 | |
980 | 988 | def add_zoom_to_article_images |
... | ... | @@ -1032,26 +1040,24 @@ module ApplicationHelper |
1032 | 1040 | end |
1033 | 1041 | |
1034 | 1042 | def render_tabs(tabs) |
1035 | - titles = tabs.inject(''){ |result, tab| result << content_tag(:li, link_to(tab[:title], '#'+tab[:id]), :class => 'tab') } | |
1036 | - contents = tabs.inject(''){ |result, tab| result << content_tag(:div, tab[:content], :id => tab[:id]) } | |
1043 | + titles = tabs.inject(''.html_safe){ |result, tab| result << content_tag(:li, link_to(tab[:title], '#'+tab[:id]), :class => 'tab') } | |
1044 | + contents = tabs.inject(''.html_safe){ |result, tab| result << content_tag(:div, tab[:content], :id => tab[:id]) } | |
1037 | 1045 | |
1038 | 1046 | content_tag(:div, content_tag(:ul, titles) + raw(contents), :class => 'ui-tabs') |
1039 | 1047 | end |
1040 | 1048 | |
1041 | 1049 | def delete_article_message(article) |
1042 | - CGI.escapeHTML( | |
1043 | - if article.folder? | |
1044 | - _("Are you sure that you want to remove the folder \"%s\"? Note that all the items inside it will also be removed!") % article.name | |
1045 | - else | |
1046 | - _("Are you sure that you want to remove the item \"%s\"?") % article.name | |
1047 | - end | |
1048 | - ) | |
1050 | + if article.folder? | |
1051 | + _("Are you sure that you want to remove the folder \"%s\"? Note that all the items inside it will also be removed!") % article.name | |
1052 | + else | |
1053 | + _("Are you sure that you want to remove the item \"%s\"?") % article.name | |
1054 | + end | |
1049 | 1055 | end |
1050 | 1056 | |
1051 | 1057 | def expirable_link_to(expired, content, url, options = {}) |
1052 | 1058 | if expired |
1053 | 1059 | options[:class] = (options[:class] || '') + ' disabled' |
1054 | - content_tag('a', ' '+content_tag('span', content), options) | |
1060 | + content_tag('a', ' '.html_safe+content_tag('span', content), options) | |
1055 | 1061 | else |
1056 | 1062 | if options[:modal] |
1057 | 1063 | options.delete(:modal) |
... | ... | @@ -1084,7 +1090,7 @@ module ApplicationHelper |
1084 | 1090 | |
1085 | 1091 | radios = templates.map do |template| |
1086 | 1092 | content_tag('li', labelled_radio_button(link_to(template.name, template.url, :target => '_blank'), "#{field_name}[template_id]", template.id, environment.is_default_template?(template))) |
1087 | - end.join("\n") | |
1093 | + end.join("\n").html_safe | |
1088 | 1094 | |
1089 | 1095 | content_tag('div', content_tag('label', _('Profile organization'), :for => 'template-options', :class => 'formlabel') + |
1090 | 1096 | content_tag('p', _('Your profile will be created according to the selected template. Click on the options to view them.'), :style => 'margin: 5px 15px;padding: 0px 10px;') + |
... | ... | @@ -1124,7 +1130,7 @@ module ApplicationHelper |
1124 | 1130 | content_tag(:div, :class => 'errorExplanation', :id => 'errorExplanation') do |
1125 | 1131 | content_tag(:h2, _('Errors while saving')) + |
1126 | 1132 | content_tag(:ul) do |
1127 | - errors.map { |err| content_tag(:li, err) }.join | |
1133 | + safe_join(errors.map { |err| content_tag(:li, err.html_safe) }) | |
1128 | 1134 | end |
1129 | 1135 | end |
1130 | 1136 | end |
... | ... | @@ -1234,6 +1240,7 @@ module ApplicationHelper |
1234 | 1240 | :href=>"#", |
1235 | 1241 | :title=>_("Exit full screen mode") |
1236 | 1242 | }) |
1243 | + content.html_safe | |
1237 | 1244 | end |
1238 | 1245 | |
1239 | 1246 | end | ... | ... |
app/helpers/article_helper.rb
... | ... | @@ -69,14 +69,14 @@ module ArticleHelper |
69 | 69 | content_tag('div', |
70 | 70 | content_tag('div', |
71 | 71 | radio_button(:article, :published, true) + |
72 | - content_tag('span', ' ', :class => 'access-public-icon') + | |
72 | + content_tag('span', ' '.html_safe, :class => 'access-public-icon') + | |
73 | 73 | content_tag('label', _('Public'), :for => 'article_published_true') + |
74 | 74 | content_tag('span', _('Visible to other people'), :class => 'access-note'), |
75 | 75 | :class => 'access-item' |
76 | 76 | ) + |
77 | 77 | content_tag('div', |
78 | 78 | radio_button(:article, :published, false) + |
79 | - content_tag('span', ' ', :class => 'access-private-icon') + | |
79 | + content_tag('span', ' '.html_safe, :class => 'access-private-icon') + | |
80 | 80 | content_tag('label', _('Private'), :for => 'article_published_false', :id => "label_private") + |
81 | 81 | content_tag('span', _('Limit visibility of this article'), :class => 'access-note'), |
82 | 82 | :class => 'access-item' |
... | ... | @@ -187,9 +187,9 @@ module ArticleHelper |
187 | 187 | def following_button(page, user) |
188 | 188 | if !user.blank? and user != page.author |
189 | 189 | if page.is_followed_by? user |
190 | - button :cancel, unfollow_button_text(page), {:controller => 'profile', :action => 'unfollow_article', :article_id => page.id} | |
190 | + button :cancel, unfollow_button_text(page), {controller: :profile, profile: page.profile.identifier, action: :unfollow_article, article_id: page.id} | |
191 | 191 | else |
192 | - button :add, follow_button_text(page), {:controller => 'profile', :action => 'follow_article', :article_id => page.id} | |
192 | + button :add, follow_button_text(page), {controller: :profile, profile: page.profile.identifier, action: :follow_article, article_id: page.id} | |
193 | 193 | end |
194 | 194 | end |
195 | 195 | end | ... | ... |
app/helpers/block_helper.rb
... | ... | @@ -3,13 +3,13 @@ module BlockHelper |
3 | 3 | def block_title(title, subtitle=nil) |
4 | 4 | block_header = block_heading title |
5 | 5 | block_header += block_heading(subtitle, 'h4') if subtitle |
6 | - content_tag 'div', block_header, :class => 'block-header' | |
6 | + content_tag('div', block_header, :class => 'block-header').html_safe | |
7 | 7 | end |
8 | 8 | |
9 | 9 | def block_heading(title, heading='h3') |
10 | 10 | tag_class = 'block-' + (heading == 'h3' ? 'title' : 'subtitle') |
11 | 11 | tag_class += ' empty' if title.empty? |
12 | - content_tag heading, content_tag('span', h(title)), :class => tag_class | |
12 | + content_tag heading, content_tag('span', h(title)), :class => tag_class.html_safe | |
13 | 13 | end |
14 | 14 | |
15 | 15 | def highlights_block_config_image_fields(block, image={}, row_number=nil) |
... | ... | @@ -28,7 +28,7 @@ module BlockHelper |
28 | 28 | }</label></td> |
29 | 29 | <td>#{button_without_text(:delete, _('Remove'), '#', class: 'delete-highlight', data: {confirm: _('Are you sure you want to remove this highlight')})}</td> |
30 | 30 | </tr> |
31 | - " | |
31 | + ".html_safe | |
32 | 32 | end |
33 | 33 | |
34 | 34 | end | ... | ... |
app/helpers/blog_helper.rb
... | ... | @@ -41,12 +41,12 @@ module BlogHelper |
41 | 41 | css_add << position |
42 | 42 | content << (content_tag 'div', id: "post-#{art.id}", class: css_add do |
43 | 43 | content_tag 'div', class: position + '-inner blog-post-inner' do |
44 | - display_post(art, conf[:format]).html_safe + | |
44 | + display_post(art, conf[:format]) + | |
45 | 45 | '<br style="clear:both"/>'.html_safe |
46 | 46 | end |
47 | - end) | |
47 | + end).html_safe | |
48 | 48 | } |
49 | - content.join("\n<hr class='sep-posts'/>\n") + (pagination or '') | |
49 | + safe_join(content, "\n<hr class='sep-posts'/>\n".html_safe) + (pagination or '').html_safe | |
50 | 50 | end |
51 | 51 | |
52 | 52 | def display_post(article, format = 'full') |
... | ... | @@ -61,7 +61,8 @@ module BlogHelper |
61 | 61 | else |
62 | 62 | '<div class="post-pic" style="background-image:url('+img+')"></div>' |
63 | 63 | end |
64 | - end.to_s + title + html | |
64 | + end.to_s.html_safe + | |
65 | + title.html_safe + html | |
65 | 66 | end |
66 | 67 | |
67 | 68 | def display_compact_format(article) | ... | ... |
app/helpers/box_organizer_helper.rb
app/helpers/boxes_helper.rb
... | ... | @@ -34,7 +34,7 @@ module BoxesHelper |
34 | 34 | |
35 | 35 | def display_boxes_editor(holder) |
36 | 36 | with_box_decorator self do |
37 | - content_tag('div', display_boxes(holder, '<' + _('Main content') + '>'), :id => 'box-organizer') | |
37 | + content_tag('div', display_boxes(holder, '<' + _('Main content') + '>'), :id => 'box-organizer') | |
38 | 38 | end |
39 | 39 | end |
40 | 40 | |
... | ... | @@ -44,7 +44,7 @@ module BoxesHelper |
44 | 44 | |
45 | 45 | def display_boxes(holder, main_content) |
46 | 46 | boxes = holder.boxes.with_position.first(boxes_limit(holder)) |
47 | - content = boxes.reverse.map { |item| display_box(item, main_content) }.join("\n") | |
47 | + content = safe_join(boxes.reverse.map { |item| display_box(item, main_content) }, "\n") | |
48 | 48 | content = main_content if (content.blank?) |
49 | 49 | |
50 | 50 | content_tag('div', content, :class => 'boxes', :id => 'boxes' ) |
... | ... | @@ -52,9 +52,9 @@ module BoxesHelper |
52 | 52 | |
53 | 53 | def maybe_display_custom_element(holder, element, options = {}) |
54 | 54 | if holder.respond_to?(element) |
55 | - content_tag('div', holder.send(element), options) | |
55 | + content_tag('div', holder.send(element).to_s.html_safe, options) | |
56 | 56 | else |
57 | - '' | |
57 | + ''.html_safe | |
58 | 58 | end |
59 | 59 | end |
60 | 60 | |
... | ... | @@ -64,15 +64,16 @@ module BoxesHelper |
64 | 64 | |
65 | 65 | def display_updated_box(box) |
66 | 66 | with_box_decorator self do |
67 | - display_box_content(box, '<' + _('Main content') + '>') | |
67 | + display_box_content(box, '<' + _('Main content') + '>') | |
68 | 68 | end |
69 | 69 | end |
70 | 70 | |
71 | 71 | def display_box_content(box, main_content) |
72 | 72 | context = { :article => @page, :request_path => request.path, :locale => locale, :params => request.params, :user => user, :controller => controller } |
73 | - box_decorator.select_blocks(box, box.blocks.includes(:box), context).map do |item| | |
73 | + blocks = box_decorator.select_blocks(box, box.blocks.includes(:box), context).map do |item| | |
74 | 74 | display_block item, main_content |
75 | - end.join("\n") + box_decorator.block_target(box) | |
75 | + end | |
76 | + safe_join(blocks, "\n") + box_decorator.block_target(box) | |
76 | 77 | end |
77 | 78 | |
78 | 79 | def select_blocks box, arr, context |
... | ... | @@ -98,15 +99,10 @@ module BoxesHelper |
98 | 99 | end |
99 | 100 | |
100 | 101 | def render_block_content block |
101 | - # FIXME: this conditional should be removed after all | |
102 | - # block footer from plugins methods get refactored into helpers and views. | |
103 | - # They are a failsafe until all of them are done. | |
104 | - return block.content if block.method(:content).owner != Block | |
105 | 102 | render_block block |
106 | 103 | end |
107 | 104 | |
108 | 105 | def render_block_footer block |
109 | - return block.footer if block.method(:footer).owner != Block | |
110 | 106 | render_block block, 'footers/' |
111 | 107 | end |
112 | 108 | |
... | ... | @@ -136,17 +132,18 @@ module BoxesHelper |
136 | 132 | |
137 | 133 | result = filter_html(result, block) |
138 | 134 | |
139 | - content_tag('div', | |
140 | - box_decorator.block_target(block.box, block) + | |
141 | - content_tag('div', | |
142 | - content_tag('div', | |
143 | - content_tag('div', | |
144 | - result + footer_content + box_decorator.block_edit_buttons(block), | |
145 | - :class => 'block-inner-2'), | |
146 | - :class => 'block-inner-1'), | |
147 | - options), | |
148 | - :class => 'block-outer') + | |
149 | - box_decorator.block_handle(block) | |
135 | + join_result = safe_join([result, footer_content, box_decorator.block_edit_buttons(block)]) | |
136 | + content_tag_inner_1 = content_tag('div', join_result, :class => 'block-inner-2') | |
137 | + | |
138 | + content_tag_inner_2 = content_tag('div', content_tag_inner_1, :class => 'block-inner-1') | |
139 | + content_tag_inner_3 = content_tag('div', content_tag_inner_2, options) | |
140 | + content_tag_inner_4 = box_decorator.block_target(block.box, block) + content_tag_inner_3 | |
141 | + c = content_tag('div', content_tag_inner_4, :class => 'block-outer') | |
142 | + box_decorator_result = box_decorator.block_handle(block) | |
143 | + result_final = safe_join([c, box_decorator_result], "") | |
144 | + | |
145 | + | |
146 | + return result_final | |
150 | 147 | end |
151 | 148 | |
152 | 149 | def wrap_main_content(content) |
... | ... | @@ -156,17 +153,17 @@ module BoxesHelper |
156 | 153 | def extract_block_content(content) |
157 | 154 | case content |
158 | 155 | when Hash |
159 | - content_tag('iframe', '', :src => url_for(content)) | |
156 | + content_tag('iframe', ''.html_safe, :src => url_for(content)) | |
160 | 157 | when String |
161 | 158 | if content.split("\n").size == 1 and content =~ /^https?:\/\// |
162 | - content_tag('iframe', '', :src => content) | |
159 | + content_tag('iframe', ''.html_safe, :src => content) | |
163 | 160 | else |
164 | 161 | content |
165 | 162 | end |
166 | 163 | when Proc |
167 | 164 | self.instance_eval(&content) |
168 | 165 | when NilClass |
169 | - '' | |
166 | + ''.html_safe | |
170 | 167 | else |
171 | 168 | raise "Unsupported content for block (#{content.class})" |
172 | 169 | end |
... | ... | @@ -175,14 +172,14 @@ module BoxesHelper |
175 | 172 | module DontMoveBlocks |
176 | 173 | # does nothing |
177 | 174 | def self.block_target(box, block = nil) |
178 | - '' | |
175 | + ''.html_safe | |
179 | 176 | end |
180 | 177 | # does nothing |
181 | 178 | def self.block_handle(block) |
182 | - '' | |
179 | + ''.html_safe | |
183 | 180 | end |
184 | 181 | def self.block_edit_buttons(block) |
185 | - '' | |
182 | + ''.html_safe | |
186 | 183 | end |
187 | 184 | def self.select_blocks box, arr, context |
188 | 185 | arr = arr.select{ |block| block.visible? context } |
... | ... | @@ -229,9 +226,9 @@ module BoxesHelper |
229 | 226 | # makes the given block draggable so it can be moved away. |
230 | 227 | def block_handle(block) |
231 | 228 | return "" unless movable?(block) |
232 | - icon = "<div><div>#{display_icon(block.class)}</div><span>#{_(block.class.pretty_name)}</span></div>" | |
229 | + icon = "<div><div>#{display_icon(block.class)}</div><span>#{_(block.class.pretty_name)}</span></div>".html_safe | |
233 | 230 | block_draggable("block-#{block.id}", |
234 | - :helper => "function() {return cloneDraggableBlock($(this), '#{icon}')}") | |
231 | + :helper => "function() {return cloneDraggableBlock($(this), '#{icon}')}".html_safe) | |
235 | 232 | end |
236 | 233 | |
237 | 234 | def block_draggable(element_id, options={}) |
... | ... | @@ -302,7 +299,7 @@ module BoxesHelper |
302 | 299 | buttons << modal_inline_icon(:embed, _('Embed code'), {}, "#embed-code-box-#{block.id}") << html |
303 | 300 | end |
304 | 301 | |
305 | - content_tag('div', buttons.join("\n") + tag('br', :style => 'clear: left'), :class => 'button-bar') | |
302 | + content_tag('div', buttons.join("\n").html_safe + tag('br', :style => 'clear: left'), :class => 'button-bar') | |
306 | 303 | end |
307 | 304 | |
308 | 305 | def current_blocks | ... | ... |
app/helpers/buttons_helper.rb
1 | 1 | module ButtonsHelper |
2 | + | |
3 | + def button_bar(options = {}, &block) | |
4 | + options[:class] ||= '' | |
5 | + options[:class] << ' button-bar' | |
6 | + | |
7 | + content_tag :div, options do | |
8 | + [ | |
9 | + capture(&block).to_s, | |
10 | + tag(:br, style: 'clear: left;'), | |
11 | + ].safe_join | |
12 | + end | |
13 | + end | |
14 | + | |
2 | 15 | def button(type, label, url, html_options = {}) |
3 | 16 | html_options ||= {} |
4 | 17 | the_class = 'with-text' |
... | ... | @@ -15,9 +28,9 @@ module ButtonsHelper |
15 | 28 | end |
16 | 29 | the_title = html_options[:title] || label |
17 | 30 | if html_options[:disabled] |
18 | - content_tag('a', ' '+content_tag('span', label), html_options.merge(:class => the_class, :title => the_title)) | |
31 | + content_tag('a', ' '.html_safe+content_tag('span', label), html_options.merge(:class => the_class, :title => the_title)) | |
19 | 32 | else |
20 | - link_to(' '+content_tag('span', label), url, html_options.merge(:class => the_class, :title => the_title)) | |
33 | + link_to(' '.html_safe+content_tag('span', label), url, html_options.merge(:class => the_class, :title => the_title)) | |
21 | 34 | end |
22 | 35 | end |
23 | 36 | ... | ... |
app/helpers/catalog_helper.rb
... | ... | @@ -1,45 +0,0 @@ |
1 | -module CatalogHelper | |
2 | - | |
3 | - include DisplayHelper | |
4 | - include ManageProductsHelper | |
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). | |
13 | - reorder('available desc, highlighted desc, name asc'). | |
14 | - paginate(:per_page => @profile.products_per_catalog_page, :page => options[:page]) | |
15 | - end | |
16 | - | |
17 | - def breadcrumb(category) | |
18 | - start = link_to(_('Start'), {:controller => :catalog, :action => 'index'}) | |
19 | - ancestors = category.ancestors.map { |c| link_to(c.name, {:controller => :catalog, :action => 'index', :level => c.id}) }.reverse | |
20 | - current_level = content_tag('strong', category.name) | |
21 | - all_items = [start] + ancestors + [current_level] | |
22 | - content_tag('div', all_items.join(' → '), :id => 'breadcrumb') | |
23 | - end | |
24 | - | |
25 | - def category_link(category) | |
26 | - count = profile.products.from_category(category).count | |
27 | - name = truncate(category.name, :length => 22 - count.to_s.size) | |
28 | - link = link_to(name, {:controller => 'catalog', :action => 'index', :level => category.id}, :title => category.name) | |
29 | - content_tag('div', "#{link} <span class=\"count\">#{count}</span>") if count > 0 | |
30 | - end | |
31 | - | |
32 | - def category_with_sub_list(category) | |
33 | - content_tag 'li', "#{category_link(category)}\n#{sub_category_list(category)}" | |
34 | - end | |
35 | - | |
36 | - def sub_category_list(category) | |
37 | - sub_categories = [] | |
38 | - category.children.order(:name).each do |sub_category| | |
39 | - cat_link = category_link sub_category | |
40 | - sub_categories << content_tag('li', cat_link) unless cat_link.nil? | |
41 | - end | |
42 | - content_tag('ul', sub_categories.join) if sub_categories.size > 0 | |
43 | - end | |
44 | - | |
45 | -end |
app/helpers/categories_helper.rb
... | ... | @@ -2,7 +2,6 @@ module CategoriesHelper |
2 | 2 | |
3 | 3 | TYPES = [ |
4 | 4 | [ _('General Category'), Category.to_s ], |
5 | - [ _('Product Category'), ProductCategory.to_s ], | |
6 | 5 | [ _('Region'), Region.to_s ], |
7 | 6 | ] |
8 | 7 | |
... | ... | @@ -20,7 +19,7 @@ module CategoriesHelper |
20 | 19 | def selected_category_link(cat) |
21 | 20 | js_remove = "jQuery('#selected-category-#{cat.id}').remove();" |
22 | 21 | content_tag('div', button_to_function_without_text(:remove, _('Remove'), js_remove) + |
23 | - link_to_function(cat.full_name(' → '), js_remove, :id => "remove-selected-category-#{cat.id}-button", :class => 'select-subcategory-link'), | |
22 | + link_to_function(cat.full_name(' → ').html_safe, js_remove, :id => "remove-selected-category-#{cat.id}-button", :class => 'select-subcategory-link'), | |
24 | 23 | :class => 'selected-category' |
25 | 24 | ) |
26 | 25 | end | ... | ... |
app/helpers/content_viewer_helper.rb
... | ... | @@ -7,7 +7,8 @@ module ContentViewerHelper |
7 | 7 | def display_number_of_comments(n) |
8 | 8 | base_str = "<span class='comment-count hide'>#{n}</span>" |
9 | 9 | amount_str = n == 0 ? _('no comments yet') : (n == 1 ? _('One comment') : _('%s comments') % n) |
10 | - base_str + "<span class='comment-count-write-out'>#{amount_str}</span>" | |
10 | + base_str += "<span class='comment-count-write-out'>#{amount_str}</span>" | |
11 | + base_str.html_safe | |
11 | 12 | end |
12 | 13 | |
13 | 14 | def number_of_comments(article) |
... | ... | @@ -19,16 +20,16 @@ module ContentViewerHelper |
19 | 20 | title = content_tag('h1', h(title), :class => 'title') |
20 | 21 | if article.belongs_to_blog? || article.belongs_to_forum? |
21 | 22 | unless args[:no_link] |
22 | - title = content_tag('h1', link_to(article.name, article.url), :class => 'title') | |
23 | + title = content_tag('h1', link_to(article.name, url_for(article.url)), :class => 'title') | |
23 | 24 | end |
24 | 25 | comments = '' |
25 | 26 | unless args[:no_comments] || !article.accept_comments |
26 | - comments = (" - %s") % link_to_comments(article) | |
27 | + comments = (" - %s").html_safe % link_to_comments(article) | |
27 | 28 | end |
28 | 29 | date_format = show_with_right_format_date article |
29 | 30 | title << content_tag('span', |
30 | 31 | date_format + |
31 | - content_tag('span', _(", by %s") % (article.author ? link_to(article.author_name, article.author_url) : article.author_name), :class => 'author') + | |
32 | + content_tag('span', _(", by %s").html_safe % (article.author ? link_to(article.author_name, article.author_url) : article.author_name), :class => 'author') + | |
32 | 33 | content_tag('span', comments, :class => 'comments'), |
33 | 34 | :class => 'publishing-info' |
34 | 35 | ) | ... | ... |
app/helpers/custom_fields_helper.rb
... | ... | @@ -61,6 +61,6 @@ module CustomFieldsHelper |
61 | 61 | |
62 | 62 | def form_for_format(customized_type, format) |
63 | 63 | field = CustomField.new(:format => format, :customized_type => customized_type, :environment => environment) |
64 | - CGI::escapeHTML((render(:partial => 'features/custom_fields/form', :locals => {:field => field}))) | |
64 | + CGI::escapeHTML((render(:partial => 'features/custom_fields/form', :locals => {:field => field}))).html_safe | |
65 | 65 | end |
66 | 66 | end | ... | ... |
app/helpers/display_helper.rb
1 | 1 | module DisplayHelper |
2 | 2 | |
3 | - def link_to_product(product, opts={}) | |
4 | - return _('No product') unless product | |
5 | - target = product_path(product) | |
6 | - link_to content_tag( 'span', product.name ), | |
7 | - target, | |
8 | - opts | |
9 | - end | |
10 | - | |
11 | 3 | def themed_path(file) |
12 | 4 | if File.exists?(File.join(Rails.root, 'public', theme_path, file)) |
13 | 5 | File.join(theme_path, file) |
... | ... | @@ -16,55 +8,35 @@ module DisplayHelper |
16 | 8 | end |
17 | 9 | end |
18 | 10 | |
19 | - def image_link_to_product(product, opts={}) | |
20 | - return _('No product') unless product | |
21 | - target = product_path(product) | |
22 | - link_to image_tag(product.default_image(:big), :alt => product.name), | |
23 | - target, | |
24 | - opts | |
25 | - end | |
26 | - | |
27 | 11 | def price_span(price, options = {}) |
28 | 12 | content_tag 'span', |
29 | 13 | number_to_currency(price, :unit => environment.currency_unit, :delimiter => environment.currency_delimiter, :separator => environment.currency_separator), |
30 | 14 | options |
31 | 15 | end |
32 | 16 | |
33 | - def product_path(product) | |
34 | - product.enterprise.enabled? ? product.enterprise.public_profile_url.merge(:controller => 'manage_products', :action => 'show', :id => product) : product.enterprise.url | |
35 | - end | |
36 | - | |
37 | 17 | def link_to_tag(tag, html_options = {}) |
38 | 18 | link_to tag.name, {:controller => 'search', :action => 'tag', :tag => tag.name}, html_options |
39 | 19 | end |
40 | 20 | |
41 | 21 | def link_to_category(category, full = true, html_options = {}) |
42 | - return _('Uncategorized product') unless category | |
43 | 22 | name = full ? category.full_name(' → ') : category.name |
44 | 23 | link_to name, Noosfero.url_options.merge({:controller => 'search', :action => 'category_index', :category_path => category.path.split('/'),:host => category.environment.default_hostname }), html_options |
45 | 24 | end |
46 | 25 | |
47 | - def link_to_product_category(category) | |
48 | - if category | |
49 | - link_to(category.name, :controller => 'search', :action => 'products', :category_path => category.explode_path) | |
50 | - else | |
51 | - _('Uncategorized product') | |
52 | - end | |
53 | - end | |
54 | - | |
55 | 26 | def txt2html(txt) |
56 | - txt.strip. | |
27 | + ret = txt.strip. | |
57 | 28 | gsub( /\s*\n\s*\n\s*/, "\r<p/>\r" ). |
58 | 29 | gsub( /\s*\n\s*/, "\n<br/>\n" ). |
59 | 30 | gsub( /\r/, "\n" ). |
60 | 31 | gsub( /(^|\s)(www\.[^\s]+|https?:\/\/[^\s]+)/ ) do |
61 | 32 | pre_char, href = $1, $2 |
62 | 33 | href = 'http://'+href if ! href.match /^https?:/ |
63 | - content = href.gsub(/^https?:\/\//, '').scan(/.{1,4}/).join('​') | |
34 | + content = safe_join(href.gsub(/^https?:\/\//, '').scan(/.{1,4}/), '​'.html_safe) | |
64 | 35 | pre_char + |
65 | 36 | content_tag(:a, content, :href => href, :target => '_blank', |
66 | - :rel => 'nofolow', :onclick => "return confirm('%s')" % | |
37 | + :rel => 'nofolow', :onclick => "return confirm('%s')".html_safe % | |
67 | 38 | _('Are you sure you want to visit this web site?')) |
68 | 39 | end |
40 | + ret.html_safe | |
69 | 41 | end |
70 | 42 | end | ... | ... |
app/helpers/enterprise_homepage_helper.rb
... | ... | @@ -1,25 +0,0 @@ |
1 | -module EnterpriseHomepageHelper | |
2 | - | |
3 | - def display_profile_info(profile) | |
4 | - data = '' | |
5 | - [ | |
6 | - [ _('Contact person:'), :contact_person ], | |
7 | - [ _('e-Mail:'), :contact_email ], | |
8 | - [ _('Phone(s):'), :contact_phone ], | |
9 | - [ _('Location:'), :location ], | |
10 | - [ _('Address:'), :address ], | |
11 | - [ _('Economic activity:'), :economic_activity ] | |
12 | - ].each { | name, att | | |
13 | - if profile.send( att ) and not profile.send( att ).blank? | |
14 | - data << content_tag( 'li', content_tag('strong', name) +' '+ profile.send( att ).to_s ) +"\n" | |
15 | - end | |
16 | - } | |
17 | - if profile.respond_to?(:distance) and !profile.distance.nil? | |
18 | - data << content_tag( 'li', | |
19 | - content_tag('strong',_('Distance:')) +' '+ | |
20 | - "%.2f%" % profile.distance | |
21 | - ) + "\n" | |
22 | - end | |
23 | - content_tag('div', content_tag('ul', data), :class => 'enterprise-info') | |
24 | - end | |
25 | -end |
app/helpers/events_helper.rb
1 | 1 | module EventsHelper |
2 | 2 | |
3 | 3 | include DatesHelper |
4 | + include ActionView::Helpers::OutputSafetyHelper | |
5 | + | |
4 | 6 | def list_events(date, events) |
5 | 7 | title = _('Events for %s') % show_date_month(date) |
8 | + user_events = events.select { |item| item.display_to?(user) } | |
9 | + events_for_month = safe_join(user_events.map {|item| display_event_in_listing(item)}, '') | |
6 | 10 | content_tag('h2', title) + |
7 | 11 | content_tag('div', |
8 | 12 | (events.any? ? |
9 | - content_tag('table', events.select { |item| item.display_to?(user) }.map {|item| display_event_in_listing(item)}.join('')) : | |
10 | - content_tag('em', _('No events for this month'), :class => 'no-events') | |
13 | + content_tag('table', events_for_month) : | |
14 | + content_tag('em', _('No events for this month'), :class => 'no-events') | |
11 | 15 | ), :id => 'agenda-items' |
12 | 16 | ) |
13 | 17 | end | ... | ... |
app/helpers/forms_helper.rb
... | ... | @@ -101,7 +101,7 @@ module FormsHelper |
101 | 101 | |
102 | 102 | def required_fields_message |
103 | 103 | content_tag('p', content_tag('span', |
104 | - _("The <label class='pseudoformlabel'>highlighted</label> fields are mandatory."), | |
104 | + _("The <label class='pseudoformlabel'>highlighted</label> fields are mandatory.").html_safe, | |
105 | 105 | :class => 'required-field' |
106 | 106 | )) |
107 | 107 | end |
... | ... | @@ -112,10 +112,11 @@ module FormsHelper |
112 | 112 | options_for_select = container.inject([]) do |options, element| |
113 | 113 | text, value = option_text_and_value(element) |
114 | 114 | selected_attribute = ' selected="selected"' if option_value_selected?(value, selected) |
115 | - options << %(<option title="#{html_escape(text.to_s)}" value="#{html_escape(value.to_s)}"#{selected_attribute}>#{html_escape(text.to_s)}</option>) | |
115 | + opt = %(<option title="#{html_escape(text.to_s)}" value="#{html_escape(value.to_s)}"#{selected_attribute}>#{html_escape(text.to_s)}</option>) | |
116 | + options << opt.html_safe | |
116 | 117 | end |
117 | 118 | |
118 | - options_for_select.join("\n") | |
119 | + safe_join(options_for_select, "\n") | |
119 | 120 | end |
120 | 121 | |
121 | 122 | def balanced_table(items, per_row=3) |
... | ... | @@ -248,8 +249,8 @@ module FormsHelper |
248 | 249 | def date_range_field(from_name, to_name, from_value, to_value, datepicker_options = {}, html_options = {}) |
249 | 250 | from_id = html_options[:from_id] || 'datepicker-from-date' |
250 | 251 | to_id = html_options[:to_id] || 'datepicker-to-date' |
251 | - return _('From') +' '+ date_field(from_name, from_value, datepicker_options, html_options.merge({:id => from_id})) + | |
252 | - ' ' + _('until') +' '+ date_field(to_name, to_value, datepicker_options, html_options.merge({:id => to_id})) | |
252 | + return (_('From') +' '+ date_field(from_name, from_value, datepicker_options, html_options.merge({:id => from_id})) + | |
253 | + ' ' + _('until') +' '+ date_field(to_name, to_value, datepicker_options, html_options.merge({:id => to_id}))).html_safe | |
253 | 254 | end |
254 | 255 | |
255 | 256 | def select_folder(label_text, field_id, collection, default_value=nil, html_options = {}, js_options = {}) | ... | ... |
app/helpers/forum_helper.rb
app/helpers/language_helper.rb
... | ... | @@ -40,7 +40,7 @@ module LanguageHelper |
40 | 40 | else |
41 | 41 | link_to(name, params.merge(:lang => code), :rel => 'nofollow') |
42 | 42 | end |
43 | - end.join(separator) | |
43 | + end.join(separator).html_safe | |
44 | 44 | content_tag('div', languages, :id => 'language-chooser', :help => _('The language you choose here is the language used for options, buttons, etc. It does not affect the language of the content created by other users.')) |
45 | 45 | end |
46 | 46 | end | ... | ... |
app/helpers/layout_helper.rb
... | ... | @@ -40,7 +40,8 @@ module LayoutHelper |
40 | 40 | |
41 | 41 | output += templete_javascript_ng.to_s |
42 | 42 | |
43 | - output | |
43 | + # This output should be safe! | |
44 | + output.html_safe | |
44 | 45 | end |
45 | 46 | |
46 | 47 | def noosfero_stylesheets |
... | ... | @@ -64,7 +65,9 @@ module LayoutHelper |
64 | 65 | output << stylesheet_link_tag(global_css_pub) |
65 | 66 | end |
66 | 67 | output << stylesheet_link_tag(theme_stylesheet_path) |
67 | - output.join "\n" | |
68 | + | |
69 | + # This output should be safe! | |
70 | + output.join("\n").html_safe | |
68 | 71 | end |
69 | 72 | |
70 | 73 | def noosfero_layout_features |
... | ... | @@ -94,9 +97,7 @@ module LayoutHelper |
94 | 97 | end |
95 | 98 | |
96 | 99 | def addthis_javascript |
97 | - if NOOSFERO_CONF['addthis_enabled'] | |
98 | - '<script src="https://s7.addthis.com/js/152/addthis_widget.js"></script>' | |
99 | - end | |
100 | + NOOSFERO_CONF['addthis_enabled'] ? '<script src="https://s7.addthis.com/js/152/addthis_widget.js"></script>' : '' | |
100 | 101 | end |
101 | 102 | |
102 | 103 | end | ... | ... |
app/helpers/macros_helper.rb
app/helpers/manage_products_helper.rb
... | ... | @@ -1,285 +0,0 @@ |
1 | -# encoding: UTF-8 | |
2 | - | |
3 | -module ManageProductsHelper | |
4 | - | |
5 | - def remote_function_to_update_categories_selection(container_id, options = {}) | |
6 | - remote_function({ | |
7 | - :update => container_id, | |
8 | - :url => { :action => "categories_for_selection" }, | |
9 | - :loading => "loading('hierarchy_navigation', '#{ _('loading…') }'); loading('#{container_id}', ' ')", | |
10 | - :complete => "loading_done('hierarchy_navigation'); loading_done('#{container_id}')" | |
11 | - }.merge(options)) | |
12 | - end | |
13 | - | |
14 | - def hierarchy_category_item(category, make_links, title = nil) | |
15 | - title ||= category.name | |
16 | - if make_links | |
17 | - link_to(title, '#', | |
18 | - :title => title, | |
19 | - :onclick => remote_function_to_update_categories_selection("categories_container_level#{ category.level + 1 }", | |
20 | - :with => "'category_id=#{ category.id }'" | |
21 | - ) | |
22 | - ) | |
23 | - else | |
24 | - title | |
25 | - end | |
26 | - end | |
27 | - | |
28 | - def hierarchy_category_navigation(current_category, options = {}) | |
29 | - hierarchy = [] | |
30 | - if current_category | |
31 | - hierarchy << current_category.name unless options[:hide_current_category] | |
32 | - ancestors = current_category.ancestors | |
33 | - ancestors.each do |category| | |
34 | - hierarchy << hierarchy_category_item(category, options[:make_links]) | |
35 | - end | |
36 | - end | |
37 | - hierarchy.reverse.join(options[:separator] || ' → ') | |
38 | - end | |
39 | - | |
40 | - def options_for_select_categories(categories, selected = nil) | |
41 | - categories.sort_by{|cat| cat.name.transliterate}.map do |category| | |
42 | - selected_attribute = selected.nil? ? '' : (category == selected ? "selected='selected'" : '') | |
43 | - "<option value='#{category.id}' title='#{category.name}' #{selected_attribute}>#{category.name + (category.leaf? ? '': ' »')}</option>" | |
44 | - end.join("\n") | |
45 | - end | |
46 | - | |
47 | - def build_selects_for_ancestors(ancestors, current_category) | |
48 | - current_ancestor = ancestors.shift | |
49 | - if current_ancestor.nil? | |
50 | - select_for_new_category(current_category.children, current_category.level + 1) | |
51 | - else | |
52 | - content_tag('div', | |
53 | - select_tag('category_id', | |
54 | - options_for_select_categories(current_ancestor.siblings + [current_ancestor], current_ancestor), | |
55 | - :size => 10, | |
56 | - :onchange => remote_function_to_update_categories_selection("categories_container_level#{ current_ancestor.level + 1 }", :with => "'category_id=' + this.value") | |
57 | - ) + | |
58 | - build_selects_for_ancestors(ancestors, current_category), | |
59 | - :class => 'categories_container', | |
60 | - :id => "categories_container_level#{ current_ancestor.level }" | |
61 | - ) | |
62 | - end | |
63 | - end | |
64 | - | |
65 | - def selects_for_all_ancestors(current_category) | |
66 | - build_selects_for_ancestors(current_category.ancestors.reverse + [current_category], current_category) | |
67 | - end | |
68 | - | |
69 | - def select_for_new_category(categories, level) | |
70 | - content_tag('div', | |
71 | - render(:partial => 'categories_for_selection', :locals => { :categories => categories, :level => level }), | |
72 | - :class => 'categories_container', | |
73 | - :id => "categories_container_level#{ level }" | |
74 | - ) | |
75 | - end | |
76 | - | |
77 | - def categories_container(categories_selection_html, hierarchy_html = '') | |
78 | - content_tag 'div', | |
79 | - render('categories_autocomplete') + | |
80 | - hidden_field_tag('selected_category_id') + | |
81 | - content_tag('div', hierarchy_html, :id => 'hierarchy_navigation') + | |
82 | - content_tag('div', categories_selection_html, :id => 'categories_container_wrapper'), | |
83 | - :id => 'categories-container' | |
84 | - end | |
85 | - | |
86 | - def select_for_categories(categories, level = 0) | |
87 | - if categories.empty? | |
88 | - content_tag('div', '', :id => 'no_subcategories') | |
89 | - else | |
90 | - select_tag('category_id', | |
91 | - options_for_select_categories(categories), | |
92 | - :size => 10, | |
93 | - :onchange => remote_function_to_update_categories_selection("categories_container_level#{ level + 1 }", :with => "'category_id=' + this.value") | |
94 | - ) + | |
95 | - content_tag('div', '', :class => 'categories_container', :id => "categories_container_level#{ level + 1 }") | |
96 | - end | |
97 | - end | |
98 | - | |
99 | - def edit_link(label, url, html_options = {}) | |
100 | - return '' unless (user && user.has_permission?('manage_products', profile)) | |
101 | - link_to(label, url, html_options) | |
102 | - end | |
103 | - | |
104 | - def edit_product_link_to_remote(product, field, label, html_options = {}) | |
105 | - return '' unless (user && user.has_permission?('manage_products', profile)) | |
106 | - options = html_options.merge(:id => 'link-edit-product-' + field) | |
107 | - options[:class] = options[:class] ? options[:class] + ' link-to-remote' : 'link-to-remote' | |
108 | - | |
109 | - link_to_remote(label, | |
110 | - {:update => "product-#{field}", | |
111 | - :url => { :controller => 'manage_products', :action => "edit", :id => product.id, :field => field }, | |
112 | - :method => :get, | |
113 | - :loading => "loading_for_button('#link-edit-product-#{field}')"}, | |
114 | - options) | |
115 | - end | |
116 | - | |
117 | - def edit_button(type, label, url, html_options = {}) | |
118 | - return '' unless (user && user.has_permission?('manage_products', profile)) | |
119 | - button(type, label, url, html_options) | |
120 | - end | |
121 | - | |
122 | - def edit_product_button_to_remote(product, field, label, html_options = {}) | |
123 | - the_class = 'button with-text icon-edit' | |
124 | - if html_options.has_key?(:class) | |
125 | - the_class << ' ' << html_options[:class] | |
126 | - end | |
127 | - edit_product_link_to_remote(product, field, label, html_options.merge(:class => the_class)) | |
128 | - end | |
129 | - | |
130 | - def edit_ui_button(label, url, html_options = {}) | |
131 | - return '' unless (user && user.has_permission?('manage_products', profile)) | |
132 | - ui_button(label, url, html_options) | |
133 | - end | |
134 | - | |
135 | - def edit_product_ui_button_to_remote(product, field, label, html_options = {}) | |
136 | - return '' unless (user && user.has_permission?('manage_products', profile)) | |
137 | - id = 'edit-product-remote-button-ui-' + field | |
138 | - options = html_options.merge(:id => id) | |
139 | - | |
140 | - ui_button_to_remote(label, | |
141 | - {:update => "product-#{field}", | |
142 | - :url => { :controller => 'manage_products', :action => "edit", :id => product.id, :field => field }, | |
143 | - :complete => "jQuery('#edit-product-button-ui-#{field}').hide()", | |
144 | - :method => :get, | |
145 | - :loading => "loading_for_button('##{id}')"}, | |
146 | - options) | |
147 | - end | |
148 | - | |
149 | - def cancel_edit_product_link(product, field, html_options = {}) | |
150 | - return '' unless (user && user.has_permission?('manage_products', profile)) | |
151 | - button_to_function(:cancel, _('Cancel'), nil, html_options) do |page| | |
152 | - page.replace_html "product-#{field}", CGI::escapeHTML(render :partial => "display_#{field}", :locals => {:product => product}) | |
153 | - end | |
154 | - end | |
155 | - | |
156 | - def edit_product_category_link(product, html_options = {}) | |
157 | - return '' unless (user && user.has_permission?('manage_products', profile)) | |
158 | - options = html_options.merge(:id => 'link-edit-product-category') | |
159 | - link_to(_('Change category'), { :action => 'edit_category', :id => product.id}, options) | |
160 | - end | |
161 | - | |
162 | - def display_value(product) | |
163 | - price = product.price | |
164 | - return '' if price.blank? || price.zero? | |
165 | - discount = product.discount | |
166 | - if discount.blank? || discount.zero? | |
167 | - result = display_price(_('Price: '), price) | |
168 | - else | |
169 | - result = display_price_with_discount(price, product.price_with_discount) | |
170 | - end | |
171 | - content_tag('span', content_tag('span', result, :class => 'product-price'), :class => "#{product.available? ? '' : 'un'}available-product") | |
172 | - end | |
173 | - | |
174 | - def display_availability(product) | |
175 | - if !product.available? | |
176 | - ui_highlight(_('Product not available!')) | |
177 | - end | |
178 | - end | |
179 | - | |
180 | - def display_price(label, price) | |
181 | - content_tag('span', label, :class => 'field-name') + | |
182 | - content_tag('span', float_to_currency(price), :class => 'field-value') | |
183 | - end | |
184 | - | |
185 | - def display_price_with_discount(price, price_with_discount) | |
186 | - original_value = content_tag('span', display_price(_('List price: '), price), :class => 'list-price') | |
187 | - discount_value = content_tag('span', display_price(_('On sale: '), price_with_discount), :class => 'on-sale-price') | |
188 | - original_value + tag('br') + discount_value | |
189 | - end | |
190 | - | |
191 | - def display_qualifiers(product) | |
192 | - data = '' | |
193 | - product.product_qualifiers.each do |pq| | |
194 | - certified_by = '' | |
195 | - certifier = pq.certifier | |
196 | - if certifier | |
197 | - certifier_name = certifier.link.blank? ? certifier.name : link_to(certifier.name, certifier.link) | |
198 | - certified_by = _('certified by %s') % certifier_name | |
199 | - else | |
200 | - certified_by = _('(Self declared)') | |
201 | - end | |
202 | - data << content_tag('li', "✔ #{pq.qualifier.name} #{certified_by}", :class => 'product-qualifiers-item') | |
203 | - end | |
204 | - content_tag('ul', data, :id => 'product-qualifiers') | |
205 | - end | |
206 | - | |
207 | - def qualifiers_for_select | |
208 | - [[_('Select...'), nil]] + environment.qualifiers.sort.map{ |c| [c.name, c.id] } | |
209 | - end | |
210 | - def certifiers_for_select(qualifier) | |
211 | - [[_('Self declared'), nil]] + qualifier.certifiers.sort.map{ |c| [c.name, c.id] } | |
212 | - end | |
213 | - def select_qualifiers(product, selected = nil) | |
214 | - select_tag('selected_qualifier', options_for_select(qualifiers_for_select, selected), | |
215 | - :onchange => remote_function( | |
216 | - :url => {:action => 'certifiers_for_selection'}, | |
217 | - :with => "'id=' + value + '&certifier_area=' + jQuery(this).parent().next().attr('id')", | |
218 | - :before => "small_loading(jQuery(this).parent().next().attr('id'), ' ')" | |
219 | - ), | |
220 | - :id => nil | |
221 | - ) | |
222 | - end | |
223 | - def select_certifiers(qualifier, product = nil) | |
224 | - if qualifier | |
225 | - selected = product ? product.product_qualifiers.find_by(qualifier_id: qualifier.id).certifier_id : nil | |
226 | - select_tag("product[qualifiers_list][#{qualifier.id}]", options_for_select(certifiers_for_select(qualifier), selected)) | |
227 | - else | |
228 | - select_tag("product[qualifiers_list][nil]") | |
229 | - end | |
230 | - end | |
231 | - | |
232 | - def remove_qualifier_button | |
233 | - button_to_function(:delete, content_tag('span', _('Delete qualifier')), "jQuery(this).parents('tr').remove()") | |
234 | - end | |
235 | - | |
236 | - def select_unit(object) | |
237 | - collection_select(object.class.name.downcase, :unit_id, environment.units, :id, :singular, {:include_blank => _('Select the unit')}) | |
238 | - end | |
239 | - | |
240 | - def input_icon(input) | |
241 | - if input.is_from_solidarity_economy? | |
242 | - hint = _('Product from solidarity economy') | |
243 | - image_tag("/images/solidarity-economy.png", :class => 'solidatiry-economy-icon', :alt => hint, :title => hint) | |
244 | - end | |
245 | - end | |
246 | - | |
247 | - def display_price_by(unit) | |
248 | - selected_unit = content_tag('span', unit, :class => 'selected-unit') | |
249 | - content_tag('span', _('by') + ' ' + selected_unit, :class => 'price-by-unit') | |
250 | - end | |
251 | - | |
252 | - def label_amount_used(input) | |
253 | - product_unit = input.product.unit | |
254 | - if product_unit.blank? | |
255 | - _('Amount used in this product or service') | |
256 | - else | |
257 | - _('Amount used by %s of this product or service') % product_unit.singular.downcase | |
258 | - end | |
259 | - end | |
260 | - | |
261 | - def display_unit(input) | |
262 | - input_amount_used = content_tag('span', input.formatted_amount, :class => 'input-amount-used') | |
263 | - return input_amount_used if input.unit.blank? | |
264 | - n_('1 %{singular_unit}', '%{num} %{plural_unit}', input.amount_used.to_f) % { :num => input_amount_used, :singular_unit => content_tag('span', input.unit.singular, :class => 'input-unit'), :plural_unit => content_tag('span', input.unit.plural, :class => 'input-unit') } | |
265 | - end | |
266 | - | |
267 | - def select_production_cost(product,selected=nil) | |
268 | - url = url_for( :controller => 'manage_products', :action => 'create_production_cost' ) | |
269 | - prompt_msg = _('Insert the name of the new cost:') | |
270 | - error_msg = _('Something went wrong. Please, try again') | |
271 | - select_tag('price_details[][production_cost_id]', | |
272 | - '<option value="" disabled="disabled">' + _('Select...') + '</option>' + | |
273 | - options_for_select(product.available_production_costs.map {|item| [truncate(item.name, {:length => 10, :omission => '...'}), item.id]} + [[_('Other cost'), '']], selected), | |
274 | - {:class => 'production-cost-selection', | |
275 | - :onchange => "productionCostTypeChange(this, '#{url}', '#{prompt_msg}', '#{error_msg}')"}) | |
276 | - end | |
277 | - | |
278 | - def price_composition_progressbar_text(product, args = {}) | |
279 | - currency = environment.currency_unit | |
280 | - production_cost = args[:production_cost_value] || product.formatted_value(:total_production_cost) | |
281 | - product_price = args[:product_price] || product.formatted_value(:price) | |
282 | - | |
283 | - _("%{currency} %{production_cost} of %{currency} %{product_price}") % {:currency => currency, :production_cost => content_tag('span', production_cost, :class => 'production_cost'), :product_price => content_tag('span', product_price, :class => 'product_price')} | |
284 | - end | |
285 | -end |
app/helpers/memberships_helper.rb
1 | 1 | module MembershipsHelper |
2 | + | |
3 | + def join_community_button options={:logged => false} | |
4 | + url = options[:logged] ? profile.join_url : profile.join_not_logged_url | |
5 | + | |
6 | + if show_confirmation_modal? profile | |
7 | + modal_button :add, _('Join this community'), url, class: 'join-community' | |
8 | + else | |
9 | + button :add, _('Join this community'), url, class: 'join-community' | |
10 | + end | |
11 | + end | |
12 | + | |
13 | + def show_confirmation_modal? profile | |
14 | + profile.requires_email? && current_person && !current_person.public_fields.include?("email") | |
15 | + end | |
16 | + | |
2 | 17 | end | ... | ... |
app/helpers/partials_helper.rb
... | ... | @@ -29,14 +29,49 @@ module PartialsHelper |
29 | 29 | raise ArgumentError, 'No partial for object. Is there a partial for any class in the inheritance hierarchy?' |
30 | 30 | end |
31 | 31 | |
32 | - def render_partial_for_class klass, *args | |
32 | + | |
33 | + def partial_for_class(klass, prefix=nil, suffix=nil) | |
33 | 34 | raise ArgumentError, 'No partial for object. Is there a partial for any class in the inheritance hierarchy?' if klass.nil? |
35 | + name = klass.name.underscore | |
36 | + controller.view_paths.each do |view_path| | |
37 | + partial = partial_for_class_in_view_path(klass, view_path, prefix, suffix) | |
38 | + return partial if partial | |
39 | + end | |
40 | + | |
41 | + raise ArgumentError, 'No partial for object. Is there a partial for any class in the inheritance hierarchy?' | |
42 | + end | |
43 | + | |
44 | + ## | |
45 | + # Calculate partial name with prefix and suffix | |
46 | + # Togheter with render_partial_for_class, | |
47 | + # it should replace #partial_for_class_in_view_path in the future | |
48 | + # | |
49 | + def partial_name_for underscore, prefix = nil, suffix = nil | |
50 | + parts = underscore.split '/' | |
51 | + if prefix or suffix | |
52 | + partial = [prefix, parts.last, suffix].compact.map(&:to_s).join '_' | |
53 | + else | |
54 | + partial = parts.last | |
55 | + end | |
56 | + if parts.size > 1 | |
57 | + "#{params[:controller]}/#{parts.first}/#{partial}" | |
58 | + else | |
59 | + partial | |
60 | + end | |
61 | + end | |
62 | + | |
63 | + def render_for_class klass, *args, &block | |
64 | + raise ArgumentError, 'No partial for object. Is there a partial for any class in the inheritance hierarchy?' unless klass | |
34 | 65 | begin |
35 | - partial = klass.name.underscore | |
36 | - partial = "#{params[:controller]}/#{partial}" if params[:controller] and partial.index '/' | |
37 | - return render partial, *args | |
66 | + capture klass, &block | |
38 | 67 | rescue ActionView::MissingTemplate |
39 | - return render_partial_for_class klass.superclass, *args | |
68 | + render_for_class klass.superclass, *args, &block | |
69 | + end | |
70 | + end | |
71 | + | |
72 | + def render_partial_for_class klass, *args | |
73 | + render_for_class klass do |klass| | |
74 | + render partial_name_for(klass.name.underscore), *args | |
40 | 75 | end |
41 | 76 | end |
42 | 77 | ... | ... |
app/helpers/profile_editor_helper.rb
... | ... | @@ -129,7 +129,11 @@ module ProfileEditorHelper |
129 | 129 | else |
130 | 130 | domains = environment.domains |
131 | 131 | end |
132 | - labelled_form_field(_('Preferred domain name:'), select(object, :preferred_domain_id, domains.map {|item| [item.name, item.id]}, :prompt => '<' + _('Select domain') + '>')) | |
132 | + select_domain_prompt = '<'.html_safe + _('Select domain').html_safe + '>'.html_safe | |
133 | + select_field = select(object, :preferred_domain_id, domains.map { | |
134 | + |item| [item.name, item.id]}, :prompt => select_domain_prompt.html_safe) | |
135 | + | |
136 | + labelled_form_field(_('Preferred domain name:'), select_field) | |
133 | 137 | end |
134 | 138 | |
135 | 139 | def control_panel(&block) | ... | ... |
app/helpers/profile_helper.rb
... | ... | @@ -84,7 +84,7 @@ module ProfileHelper |
84 | 84 | entries.map do |entry| |
85 | 85 | content = self.send("treat_#{field}", entry) |
86 | 86 | unless content.blank? |
87 | - content_tag('tr', content_tag('td', title(field, entry), :class => 'field-name') + content_tag('td', content)) | |
87 | + content_tag('tr', content_tag('td', title(field, entry), :class => 'field-name') + content_tag('td', content.to_s.html_safe)) | |
88 | 88 | end |
89 | 89 | end.join("\n") |
90 | 90 | end | ... | ... |
app/helpers/profile_image_helper.rb
... | ... | @@ -61,6 +61,8 @@ module ProfileImageHelper |
61 | 61 | image_tag(profile_icon(profile, size), opt ) |
62 | 62 | end |
63 | 63 | |
64 | + include MembershipsHelper | |
65 | + | |
64 | 66 | def links_for_balloon(profile) |
65 | 67 | if environment.enabled?(:show_balloon_with_profile_links_when_clicked) |
66 | 68 | if profile.kind_of?(Person) |
... | ... | @@ -76,13 +78,12 @@ module ProfileImageHelper |
76 | 78 | {_('Wall') => {:href => url_for(profile.public_profile_url)}}, |
77 | 79 | {_('Members') => {:href => url_for(:controller => :profile, :action => :members, :profile => profile.identifier)}}, |
78 | 80 | {_('Agenda') => {:href => url_for(:controller => :profile, :action => :events, :profile => profile.identifier)}}, |
79 | - {_('Join') => {:href => url_for(profile.join_url), :class => 'join-community', :style => 'display: none'}}, | |
81 | + {_('Join') => {:href => url_for(profile.join_url), :class => 'join-community'+ (show_confirmation_modal?(profile) ? ' modal-toggle' : '') , :style => 'display: none'}}, | |
80 | 82 | {_('Leave community') => {:href => url_for(profile.leave_url), :class => 'leave-community', :style => 'display: none'}}, |
81 | 83 | {_('Send an e-mail') => {:href => url_for(:profile => profile.identifier, :controller => 'contact', :action => 'new'), :class => 'send-an-email', :style => 'display: none'}} |
82 | 84 | ] |
83 | 85 | elsif profile.kind_of?(Enterprise) |
84 | 86 | [ |
85 | - {_('Products') => {:href => catalog_path(profile.identifier)}}, | |
86 | 87 | {_('Members') => {:href => url_for(:controller => :profile, :action => :members, :profile => profile.identifier)}}, |
87 | 88 | {_('Agenda') => {:href => url_for(:controller => :profile, :action => :events, :profile => profile.identifier)}}, |
88 | 89 | {_('Send an e-mail') => {:href => url_for(:profile => profile.identifier, :controller => 'contact', :action => 'new'), :class => 'send-an-email', :style => 'display: none'}}, |
... | ... | @@ -131,7 +132,7 @@ module ProfileImageHelper |
131 | 132 | links = links_for_balloon(profile) |
132 | 133 | content_tag('div', content_tag(tag, |
133 | 134 | (environment.enabled?(:show_balloon_with_profile_links_when_clicked) ? |
134 | - popover_menu(_('Profile links'),profile.short_name,links,{:class => trigger_class, :url => url}) : "") + | |
135 | + popover_menu(_('Profile links'),profile.short_name,links,{:class => trigger_class, :url => url}) : "").html_safe + | |
135 | 136 | link_to( |
136 | 137 | content_tag( 'span', profile_image( profile, size ), :class => img_class ) + |
137 | 138 | content_tag( 'span', h(name), :class => ( profile.class == Person ? 'fn' : 'org' ) ) + |
... | ... | @@ -139,7 +140,7 @@ module ProfileImageHelper |
139 | 140 | profile.url, |
140 | 141 | :class => 'profile_link url', |
141 | 142 | :help => _('Click on this icon to go to the <b>%s</b>\'s home page') % profile.name, |
142 | - :title => profile.name ), | |
143 | + :title => profile.name ).html_safe, | |
143 | 144 | :class => 'vcard'), :class => 'common-profile-list-block') |
144 | 145 | end |
145 | 146 | end | ... | ... |
app/helpers/search_helper.rb
... | ... | @@ -124,10 +124,10 @@ module SearchHelper |
124 | 124 | def filters(asset) |
125 | 125 | return if !asset |
126 | 126 | klass = asset_class(asset) |
127 | - content_tag('div', klass::SEARCH_FILTERS.map do |name, options| | |
127 | + content_tag('div', safe_join(klass::SEARCH_FILTERS.map do |name, options| | |
128 | 128 | default = klass.respond_to?("default_search_#{name}") ? klass.send("default_search_#{name}".to_s) : nil |
129 | 129 | select_filter(name, options, default) |
130 | - end.join("\n"), :id => 'search-filters') | |
130 | + end, "\n"), :id => 'search-filters') | |
131 | 131 | end |
132 | 132 | |
133 | 133 | def assets_menu(selected) |
... | ... | @@ -137,11 +137,11 @@ module SearchHelper |
137 | 137 | # menu. |
138 | 138 | assets.delete(:events) |
139 | 139 | content_tag('ul', |
140 | - assets.map do |asset| | |
140 | + safe_join(assets.map do |asset| | |
141 | 141 | options = {} |
142 | 142 | options.merge!(:class => 'selected') if selected.to_s == asset.to_s |
143 | 143 | content_tag('li', asset_link(asset), options) |
144 | - end.join("\n"), | |
144 | + end, "\n"), | |
145 | 145 | :id => 'assets-menu') |
146 | 146 | end |
147 | 147 | ... | ... |
app/helpers/tags_helper.rb
... | ... | @@ -58,7 +58,7 @@ module TagsHelper |
58 | 58 | |
59 | 59 | if options[:show_count] |
60 | 60 | display_count = options[:show_count] ? "<small><sup>(#{count})</sup></small>" : "" |
61 | - link_to tag + display_count, destination, :style => style | |
61 | + link_to (tag + display_count).html_safe, destination, :style => style | |
62 | 62 | else |
63 | 63 | link_to h(tag) , destination, :style => style, |
64 | 64 | :title => n_( 'one item', '%d items', count ) % count | ... | ... |
app/helpers/theme_loader_helper.rb
... | ... | @@ -2,8 +2,8 @@ module ThemeLoaderHelper |
2 | 2 | def current_theme |
3 | 3 | @current_theme ||= |
4 | 4 | begin |
5 | - if session[:theme] | |
6 | - session[:theme] | |
5 | + if session[:user_theme] | |
6 | + session[:user_theme] | |
7 | 7 | else |
8 | 8 | # utility for developers: set the theme to 'random' in development mode and |
9 | 9 | # you will get a different theme every request. This is interesting for |
... | ... | @@ -11,8 +11,8 @@ module ThemeLoaderHelper |
11 | 11 | if Rails.env.development? && environment.theme == 'random' |
12 | 12 | @random_theme ||= Dir.glob('public/designs/themes/*').map { |f| File.basename(f) }.rand |
13 | 13 | @random_theme |
14 | - elsif Rails.env.development? && respond_to?(:params) && params[:theme] && File.exists?(Rails.root.join('public/designs/themes', params[:theme])) | |
15 | - params[:theme] | |
14 | + elsif Rails.env.development? && respond_to?(:params) && params[:user_theme] && File.exists?(Rails.root.join('public/designs/themes', params[:user_theme])) | |
15 | + params[:user_theme] | |
16 | 16 | else |
17 | 17 | if profile && !profile.theme.nil? |
18 | 18 | profile.theme |
... | ... | @@ -34,8 +34,10 @@ module ThemeLoaderHelper |
34 | 34 | end |
35 | 35 | |
36 | 36 | def theme_path |
37 | - if session[:theme] | |
37 | + if session[:user_theme] | |
38 | 38 | '/user_themes/' + current_theme |
39 | + elsif session[:theme] | |
40 | + '/designs/themes/' + session[:theme] | |
39 | 41 | else |
40 | 42 | '/designs/themes/' + current_theme |
41 | 43 | end | ... | ... |
app/helpers/tinymce_helper.rb
... | ... | @@ -7,7 +7,7 @@ module TinymceHelper |
7 | 7 | output += javascript_include_tag 'tinymce/js/tinymce/jquery.tinymce.min.js' |
8 | 8 | output += javascript_include_tag 'tinymce.js' |
9 | 9 | output += include_macro_js_files.to_s |
10 | - output | |
10 | + output.html_safe | |
11 | 11 | end |
12 | 12 | |
13 | 13 | def tinymce_init_js options = {} |
... | ... | @@ -37,7 +37,7 @@ module TinymceHelper |
37 | 37 | #cleanup non tinymce options |
38 | 38 | options = options.except :mode |
39 | 39 | |
40 | - "noosfero.tinymce.init(#{options.to_json})" | |
40 | + "noosfero.tinymce.init(#{options.to_json})".html_safe | |
41 | 41 | end |
42 | 42 | |
43 | 43 | def menubar mode | ... | ... |
app/helpers/url_helper.rb
app/helpers/users_helper.rb
1 | 1 | module UsersHelper |
2 | 2 | |
3 | - FILTER_TRANSLATION = { | |
3 | + def filter_translation | |
4 | + { | |
4 | 5 | 'all_users' => _('All users'), |
5 | 6 | 'admin_users' => _('Admin users'), |
6 | 7 | 'activated_users' => _('Activated users'), |
7 | 8 | 'deactivated_users' => _('Deativated users'), |
8 | - } | |
9 | + } | |
10 | + end | |
9 | 11 | |
10 | 12 | def filter_selector(filter, float = 'right') |
11 | - options = options_for_select(FILTER_TRANSLATION.map {|key, name| [name, key]}, :selected => filter) | |
13 | + options = options_for_select(filter_translation.map {|key, name| [name, key]}, :selected => filter) | |
12 | 14 | url_params = url_for(params.merge(:filter => 'FILTER')) |
13 | 15 | onchange = "document.location.href = '#{url_params}'.replace('FILTER', this.value)" |
14 | 16 | select_field = select_tag(:filter, options, :onchange => onchange) |
... | ... | @@ -19,7 +21,7 @@ module UsersHelper |
19 | 21 | end |
20 | 22 | |
21 | 23 | def users_filter_title(filter) |
22 | - FILTER_TRANSLATION[filter] | |
24 | + filter_translation[filter] | |
23 | 25 | end |
24 | 26 | |
25 | 27 | end | ... | ... |
... | ... | @@ -0,0 +1,14 @@ |
1 | +class ActivitiesCounterCacheJob | |
2 | + | |
3 | + def perform | |
4 | + person_activities_counts = ApplicationRecord.connection.execute("SELECT profiles.id, count(action_tracker.id) as count FROM profiles LEFT OUTER JOIN action_tracker ON profiles.id = action_tracker.user_id WHERE (action_tracker.created_at >= #{ApplicationRecord.connection.quote(ActionTracker::Record::RECENT_DELAY.days.ago.to_s(:db))}) AND ( (profiles.type = 'Person' ) ) GROUP BY profiles.id;") | |
5 | + organization_activities_counts = ApplicationRecord.connection.execute("SELECT profiles.id, count(action_tracker.id) as count FROM profiles LEFT OUTER JOIN action_tracker ON profiles.id = action_tracker.target_id WHERE (action_tracker.created_at >= #{ApplicationRecord.connection.quote(ActionTracker::Record::RECENT_DELAY.days.ago.to_s(:db))}) AND ( (profiles.type = 'Community' OR profiles.type = 'Enterprise' OR profiles.type = 'Organization' ) ) GROUP BY profiles.id;") | |
6 | + activities_counts = person_activities_counts.entries + organization_activities_counts.entries | |
7 | + activities_counts.each do |count| | |
8 | + update_sql = ApplicationRecord.__send__(:sanitize_sql, ["UPDATE profiles SET activities_count=? WHERE profiles.id=?;", count['count'].to_i, count['id'] ], '') | |
9 | + ApplicationRecord.connection.execute(update_sql) | |
10 | + end | |
11 | + Delayed::Job.enqueue(ActivitiesCounterCacheJob.new, {:priority => -3, :run_at => 1.day.from_now}) | |
12 | + end | |
13 | + | |
14 | +end | ... | ... |
... | ... | @@ -0,0 +1,11 @@ |
1 | +class CreateThumbnailsJob < Struct.new(:class_name, :file_id) | |
2 | + def perform | |
3 | + return unless class_name.constantize.exists?(file_id) | |
4 | + file = class_name.constantize.find(file_id) | |
5 | + file.create_thumbnails | |
6 | + article = Article.where(:image_id => file_id).first | |
7 | + if article | |
8 | + article.touch | |
9 | + end | |
10 | + end | |
11 | +end | ... | ... |
... | ... | @@ -0,0 +1,27 @@ |
1 | +class DownloadReportedImagesJob < Struct.new(:abuse_report, :article) | |
2 | + def perform | |
3 | + images_paths = article.image? ? [File.join(article.profile.environment.top_url, article.public_filename(:display))] : article.body_images_paths | |
4 | + images_paths.each do |image_path| | |
5 | + image = get_image(image_path) | |
6 | + reported_image = ReportedImage.create!( :abuse_report => abuse_report, | |
7 | + :uploaded_data => image, | |
8 | + :filename => File.basename(image_path), | |
9 | + :size => image.size ) | |
10 | + abuse_report.content = parse_content(abuse_report, image_path, reported_image) | |
11 | + end | |
12 | + abuse_report.save! | |
13 | + end | |
14 | + | |
15 | + def get_image(image_path) | |
16 | + image = ActionController::UploadedTempfile.new('reported_image') | |
17 | + image.write(Net::HTTP.get(URI.parse(image_path))) | |
18 | + image.original_path = 'tmp/' + File.basename(image_path) | |
19 | + image.content_type = 'image/' + File.extname(image_path).gsub('.','') | |
20 | + image | |
21 | + end | |
22 | + | |
23 | + def parse_content(report, old_path, image) | |
24 | + old_path = old_path.gsub(report.reporter.environment.top_url, '') | |
25 | + report.content.gsub(/#{old_path}/, image.public_filename) | |
26 | + end | |
27 | +end | ... | ... |
... | ... | @@ -0,0 +1,11 @@ |
1 | +class GetEmailContactsJob < Struct.new(:import_from, :login, :password, :contact_list_id) | |
2 | + def perform | |
3 | + begin | |
4 | + Invitation.get_contacts(import_from, login, password, contact_list_id) | |
5 | + rescue Contacts::AuthenticationError => ex | |
6 | + ContactList.exists?(contact_list_id) && ContactList.find(contact_list_id).register_auth_error | |
7 | + rescue Exception => ex | |
8 | + ContactList.exists?(contact_list_id) && ContactList.find(contact_list_id).register_error | |
9 | + end | |
10 | + end | |
11 | +end | ... | ... |
... | ... | @@ -0,0 +1,10 @@ |
1 | +class InvitationJob < Struct.new(:person_id, :contacts_to_invite, :message, :profile_id, :contact_list_id, :locale) | |
2 | + def perform | |
3 | + Noosfero.with_locale(locale) do | |
4 | + person = Person.find(person_id) | |
5 | + profile = Profile.find(profile_id) | |
6 | + Invitation.invite(person, contacts_to_invite, message, profile) | |
7 | + ContactList.exists?(contact_list_id) && ContactList.find(contact_list_id).destroy | |
8 | + end | |
9 | + end | |
10 | +end | ... | ... |
... | ... | @@ -0,0 +1,22 @@ |
1 | +class LogMemoryConsumptionJob < Struct.new(:last_stat) | |
2 | + # Number of entries do display | |
3 | + N = 20 | |
4 | + | |
5 | + def perform | |
6 | + logpath = File.join(Rails.root, 'log', "#{ENV['RAILS_ENV']}_memory_consumption.log") | |
7 | + logger = Logger.new(logpath) | |
8 | + stats = Hash.new(0) | |
9 | + ObjectSpace.each_object {|o| stats[o.class.to_s] += 1} | |
10 | + i = 1 | |
11 | + | |
12 | + logger << "[#{Time.now.strftime('%F %T %z')}]\n" | |
13 | + stats.sort {|(k1,v1),(k2,v2)| v2 <=> v1}.each do |k,v| | |
14 | + logger << (sprintf "%-60s %10d", k, v) | |
15 | + logger << (sprintf " | delta %10d", (v - last_stat[k])) if last_stat && last_stat[k] | |
16 | + logger << "\n" | |
17 | + break if i > N | |
18 | + i += 1 | |
19 | + end | |
20 | + logger << "\n" | |
21 | + end | |
22 | +end | ... | ... |
... | ... | @@ -0,0 +1,44 @@ |
1 | +class NotifyActivityToProfilesJob < Struct.new(:tracked_action_id) | |
2 | + NOTIFY_ONLY_COMMUNITY = [ | |
3 | + 'add_member_in_community' | |
4 | + ] | |
5 | + | |
6 | + NOT_NOTIFY_COMMUNITY = [ | |
7 | + 'join_community' | |
8 | + ] | |
9 | + def perform | |
10 | + return unless ActionTracker::Record.exists?(tracked_action_id) | |
11 | + tracked_action = ActionTracker::Record.find(tracked_action_id) | |
12 | + return unless tracked_action.user.present? | |
13 | + target = tracked_action.target | |
14 | + if target.is_a?(Community) && ( NOTIFY_ONLY_COMMUNITY.include?(tracked_action.verb) || ! target.public_profile ) | |
15 | + ActionTrackerNotification.create(:profile_id => target.id, :action_tracker_id => tracked_action.id) | |
16 | + return | |
17 | + end | |
18 | + | |
19 | + # Notify the user | |
20 | + ActionTrackerNotification.create(:profile_id => tracked_action.user.id, :action_tracker_id => tracked_action.id) | |
21 | + | |
22 | + # Notify all friends | |
23 | + ActionTrackerNotification.connection.execute("insert into action_tracker_notifications(profile_id, action_tracker_id) select f.friend_id, #{tracked_action.id} from friendships as f where person_id=#{tracked_action.user.id} and f.friend_id not in (select atn.profile_id from action_tracker_notifications as atn where atn.action_tracker_id = #{tracked_action.id})") | |
24 | + | |
25 | + if tracked_action.user.is_a? Organization | |
26 | + ActionTrackerNotification.connection.execute "insert into action_tracker_notifications(profile_id, action_tracker_id) " + | |
27 | + "select distinct accessor_id, #{tracked_action.id} from role_assignments where resource_id = #{tracked_action.user.id} and resource_type='Profile' " + | |
28 | + if tracked_action.user.is_a? Enterprise then "union select distinct person_id, #{tracked_action.id} from favorite_enterprise_people where enterprise_id = #{tracked_action.user.id}" else "" end | |
29 | + end | |
30 | + | |
31 | + if target.is_a?(Community) | |
32 | + ActionTrackerNotification.create(:profile_id => target.id, :action_tracker_id => tracked_action.id) unless NOT_NOTIFY_COMMUNITY.include?(tracked_action.verb) | |
33 | + | |
34 | + ActionTrackerNotification.connection.execute("insert into action_tracker_notifications(profile_id, action_tracker_id) select distinct profiles.id, #{tracked_action.id} from role_assignments, profiles where profiles.type = 'Person' and profiles.id = role_assignments.accessor_id and profiles.id != #{tracked_action.user.id} and profiles.id not in (select atn.profile_id from action_tracker_notifications as atn where atn.action_tracker_id = #{tracked_action.id}) and role_assignments.resource_type = 'Profile' and role_assignments.resource_id = #{target.id}") | |
35 | + end | |
36 | + | |
37 | + if target.is_a?(Article) && target.profile.is_a?(Community) | |
38 | + ActionTrackerNotification.create(:profile_id => target.profile.id, :action_tracker_id => tracked_action.id) unless NOT_NOTIFY_COMMUNITY.include?(tracked_action.verb) | |
39 | + | |
40 | + ActionTrackerNotification.connection.execute("insert into action_tracker_notifications(profile_id, action_tracker_id) select distinct profiles.id, #{tracked_action.id} from role_assignments, profiles where profiles.type = 'Person' and profiles.id = role_assignments.accessor_id and profiles.id != #{tracked_action.user.id} and profiles.id not in (select atn.profile_id from action_tracker_notifications as atn where atn.action_tracker_id = #{tracked_action.id}) and role_assignments.resource_type = 'Profile' and role_assignments.resource_id = #{target.profile.id}") | |
41 | + end | |
42 | + | |
43 | + end | |
44 | +end | ... | ... |
... | ... | @@ -0,0 +1,22 @@ |
1 | +class ProfileSuggestionsJob < Struct.new(:person_id) | |
2 | + | |
3 | + def self.exists?(person_id) | |
4 | + !find(person_id).empty? | |
5 | + end | |
6 | + | |
7 | + def self.find(person_id) | |
8 | + Delayed::Job.by_handler("--- !ruby/struct:ProfileSuggestionsJob\nperson_id: #{person_id}\n") | |
9 | + end | |
10 | + | |
11 | + def perform | |
12 | + logger = Delayed::Worker.logger | |
13 | + begin | |
14 | + person = Person.find(person_id) | |
15 | + ProfileSuggestion.calculate_suggestions(person) | |
16 | + UserMailer.profiles_suggestions_email(person).deliver if person.email_suggestions | |
17 | + rescue Exception => exception | |
18 | + logger.error("Error with suggestions for person ID %d: %s" % [person_id, exception.to_s]) | |
19 | + end | |
20 | + end | |
21 | + | |
22 | +end | ... | ... |
app/mailers/environment_mailing.rb
1 | 1 | class EnvironmentMailing < Mailing |
2 | 2 | |
3 | + settings_items :recipients_roles, :type => :array | |
4 | + attr_accessible :recipients_roles | |
5 | + | |
3 | 6 | def recipients(offset=0, limit=100) |
4 | - source.people.order(:id).offset(offset).limit(limit) | |
7 | + recipients_by_role.order(:id).offset(offset).limit(limit) | |
5 | 8 | .joins("LEFT OUTER JOIN mailing_sents m ON (m.mailing_id = #{id} AND m.person_id = profiles.id)") |
6 | 9 | .where("m.person_id" => nil) |
7 | 10 | end |
8 | 11 | |
12 | + def recipients_by_role | |
13 | + if recipients_roles.blank? | |
14 | + source.people | |
15 | + else | |
16 | + roles = Environment::Role.where("key in (?)", self.recipients_roles) | |
17 | + Person.by_role(roles).where(environment_id: self.source_id) | |
18 | + end | |
19 | + end | |
20 | + | |
9 | 21 | def each_recipient |
10 | 22 | offset = 0 |
11 | 23 | limit = 100 | ... | ... |
app/mailers/mailing.rb
app/models/abuse_report.rb
app/models/action_tracker_notification.rb
app/models/add_member.rb
... | ... | @@ -29,16 +29,22 @@ class AddMember < Task |
29 | 29 | end |
30 | 30 | |
31 | 31 | def information |
32 | - requestor_email = " (#{requestor.email})" if requestor.may_display_field_to?("email") | |
33 | - | |
34 | - {:message => _("%{requestor}%{requestor_email} wants to be a member of '%{organization}'."), | |
35 | - variables: {requestor: requestor.name, requestor_email: requestor_email, organization: organization.name}} | |
32 | + {:message => _("%{requestor} wants to be a member of '%{target}'."), | |
33 | + variables: {requestor: requestor.name, target: target.name}} | |
36 | 34 | end |
37 | 35 | |
38 | 36 | def accept_details |
39 | 37 | true |
40 | 38 | end |
41 | 39 | |
40 | + def reject_details | |
41 | + true | |
42 | + end | |
43 | + | |
44 | + def footer | |
45 | + true | |
46 | + end | |
47 | + | |
42 | 48 | def icon |
43 | 49 | {:type => :profile_image, :profile => requestor, :url => requestor.url} |
44 | 50 | end |
... | ... | @@ -63,4 +69,16 @@ class AddMember < Task |
63 | 69 | suggestion.disable if suggestion |
64 | 70 | end |
65 | 71 | |
72 | + def task_finished_message | |
73 | + _("You have been accepted at \"%{target}\" with the profile \"%{requestor}\"") % | |
74 | + {:target => self.target.name, | |
75 | + :requestor => self.requestor.name} | |
76 | + end | |
77 | + | |
78 | + def task_cancelled_message | |
79 | + _("Your request to enter community \"%{target}\" with the profile \"%{requestor}\" was not accepted. Please contact any profile admin from %{target} for more information. The following explanation was given: \n\n\"%{explanation}\"") % | |
80 | + {:target => self.target.name, | |
81 | + :requestor => self.requestor.name, | |
82 | + :explanation => self.reject_explanation} | |
83 | + end | |
66 | 84 | end | ... | ... |
... | ... | @@ -0,0 +1,61 @@ |
1 | +class ApplicationRecord < ActiveRecord::Base | |
2 | + | |
3 | + self.abstract_class = true | |
4 | + self.store_full_sti_class = true | |
5 | + | |
6 | + # an ActionView instance for rendering views on models | |
7 | + def self.action_view | |
8 | + @action_view ||= begin | |
9 | + view_paths = ::ActionController::Base.view_paths | |
10 | + action_view = ::ActionView::Base.new view_paths | |
11 | + # for using Noosfero helpers inside render calls | |
12 | + action_view.extend ::ApplicationHelper | |
13 | + action_view | |
14 | + end | |
15 | + end | |
16 | + | |
17 | + # default value needed for the above ActionView | |
18 | + def to_partial_path | |
19 | + self.class.name.underscore | |
20 | + end | |
21 | + | |
22 | + alias :meta_cache_key :cache_key | |
23 | + def cache_key | |
24 | + key = [Noosfero::VERSION, meta_cache_key] | |
25 | + key.unshift ApplicationRecord.connection.schema_search_path | |
26 | + key.join('/') | |
27 | + end | |
28 | + | |
29 | + def self.like_search(query, options={}) | |
30 | + if defined?(self::SEARCHABLE_FIELDS) || options[:fields].present? | |
31 | + fields_per_table = {} | |
32 | + fields_per_table[table_name] = (options[:fields].present? ? options[:fields] : self::SEARCHABLE_FIELDS.keys.map(&:to_s)) & column_names | |
33 | + | |
34 | + if options[:joins].present? | |
35 | + join_asset = options[:joins].to_s.classify.constantize | |
36 | + if defined?(join_asset::SEARCHABLE_FIELDS) || options[:fields].present? | |
37 | + fields_per_table[join_asset.table_name] = (options[:fields].present? ? options[:fields] : join_asset::SEARCHABLE_FIELDS.keys.map(&:to_s)) & join_asset.column_names | |
38 | + end | |
39 | + end | |
40 | + | |
41 | + query = query.downcase.strip | |
42 | + fields_per_table.delete_if { |table,fields| fields.blank? } | |
43 | + conditions = fields_per_table.map do |table,fields| | |
44 | + fields.map do |field| | |
45 | + "lower(#{table}.#{field}) LIKE '%#{query}%'" | |
46 | + end.join(' OR ') | |
47 | + end.join(' OR ') | |
48 | + | |
49 | + if options[:joins].present? | |
50 | + joins(options[:joins]).where(conditions) | |
51 | + else | |
52 | + where(conditions) | |
53 | + end | |
54 | + | |
55 | + else | |
56 | + raise "No searchable fields defined for #{self.name}" | |
57 | + end | |
58 | + end | |
59 | + | |
60 | +end | |
61 | + | ... | ... |
app/models/approve_article.rb
... | ... | @@ -86,7 +86,7 @@ class ApproveArticle < Task |
86 | 86 | |
87 | 87 | def information |
88 | 88 | if article |
89 | - {:message => _('%{requestor} wants to publish the article: %{linked_subject}.')} | |
89 | + {:message => _('%{requestor} wants to publish the article: %{linked_subject}.').html_safe} | |
90 | 90 | else |
91 | 91 | {:message => _("The article was removed.")} |
92 | 92 | end | ... | ... |
app/models/article.rb
1 | 1 | |
2 | -class Article < ActiveRecord::Base | |
2 | +class Article < ApplicationRecord | |
3 | 3 | |
4 | 4 | include SanitizeHelper |
5 | 5 | |
... | ... | @@ -29,6 +29,8 @@ class Article < ActiveRecord::Base |
29 | 29 | :display => %w[full] |
30 | 30 | } |
31 | 31 | |
32 | + N_('article') | |
33 | + | |
32 | 34 | def initialize(*params) |
33 | 35 | super |
34 | 36 | if params.present? && params.first.present? |
... | ... | @@ -604,7 +606,7 @@ class Article < ActiveRecord::Base |
604 | 606 | end |
605 | 607 | |
606 | 608 | def accept_category?(cat) |
607 | - !cat.is_a?(ProductCategory) | |
609 | + true | |
608 | 610 | end |
609 | 611 | |
610 | 612 | def public? |
... | ... | @@ -806,11 +808,13 @@ class Article < ActiveRecord::Base |
806 | 808 | end |
807 | 809 | |
808 | 810 | def body_images_paths |
809 | - Nokogiri::HTML.fragment(self.body.to_s).css('img[src]').collect do |i| | |
811 | + paths = Nokogiri::HTML.fragment(self.body.to_s).css('img[src]').collect do |i| | |
810 | 812 | src = i['src'] |
811 | 813 | src = URI.escape src if self.new_record? # xss_terminate runs on save |
812 | 814 | (self.profile && self.profile.environment) ? URI.join(self.profile.environment.top_url, src).to_s : src |
813 | 815 | end |
816 | + paths.unshift(URI.join(self.profile.environment.top_url, self.image.public_filename).to_s) if self.image.present? | |
817 | + paths | |
814 | 818 | end |
815 | 819 | |
816 | 820 | def more_comments_label |
... | ... | @@ -862,6 +866,10 @@ class Article < ActiveRecord::Base |
862 | 866 | HashWithIndifferentAccess.new :name => name, :abstract => abstract, :body => body, :id => id, :parent_id => parent_id, :author => author |
863 | 867 | end |
864 | 868 | |
869 | + def self.can_display_blocks? | |
870 | + true | |
871 | + end | |
872 | + | |
865 | 873 | private |
866 | 874 | |
867 | 875 | def sanitize_tag_list | ... | ... |
app/models/article_categorization.rb
app/models/article_follower.rb
app/models/block.rb
1 | -class Block < ActiveRecord::Base | |
1 | +class Block < ApplicationRecord | |
2 | 2 | |
3 | 3 | attr_accessible :title, :subtitle, :display, :limit, :box_id, :posts_per_page, |
4 | 4 | :visualization_format, :language, :display_user, |
... | ... | @@ -76,6 +76,17 @@ class Block < ActiveRecord::Base |
76 | 76 | true |
77 | 77 | end |
78 | 78 | |
79 | + def visible_to_user?(user) | |
80 | + visible = self.display_to_user?(user) | |
81 | + if self.owner.kind_of?(Profile) | |
82 | + visible &= self.owner.display_info_to?(user) | |
83 | + visible &= (self.visible? || user && user.has_permission?(:edit_profile_design, self.owner)) | |
84 | + elsif self.owner.kind_of?(Environment) | |
85 | + visible &= (self.visible? || user && user.has_permission?(:edit_environment_design, self.owner)) | |
86 | + end | |
87 | + visible | |
88 | + end | |
89 | + | |
79 | 90 | def display_to_user?(user) |
80 | 91 | display_user == 'all' || (user.nil? && display_user == 'not_logged') || (user && display_user == 'logged') || (user && display_user == 'followers' && user.follows?(owner)) |
81 | 92 | end |
... | ... | @@ -170,30 +181,6 @@ class Block < ActiveRecord::Base |
170 | 181 | "/images/block_preview.png" |
171 | 182 | end |
172 | 183 | |
173 | - # Returns the content to be used for this block. | |
174 | - # | |
175 | - # This method can return several types of objects: | |
176 | - # | |
177 | - # * <tt>String</tt>: if the string starts with <tt>http://</tt> or <tt>https://</tt>, then it is assumed to be address of an IFRAME. Otherwise it's is used as regular HTML. | |
178 | - # * <tt>Hash</tt>: the hash is used to build an URL that is used as the address for a IFRAME. | |
179 | - # * <tt>Proc</tt>: the Proc is evaluated in the scope of BoxesHelper. The | |
180 | - # block can then use <tt>render</tt>, <tt>link_to</tt>, etc. | |
181 | - # | |
182 | - # The method can also return <tt>nil</tt>, which means "no content". | |
183 | - # | |
184 | - # See BoxesHelper#extract_block_content for implementation details. | |
185 | - def content(args={}) | |
186 | - "This is block number %d" % self.id | |
187 | - end | |
188 | - | |
189 | - # A footer to be appended to the end of the block. Returns <tt>nil</tt>. | |
190 | - # | |
191 | - # Override in your subclasses. You can return the same types supported by | |
192 | - # #content. | |
193 | - def footer | |
194 | - nil | |
195 | - end | |
196 | - | |
197 | 184 | # Is this block editable? (Default to <tt>true</tt>) |
198 | 185 | def editable?(user=nil) |
199 | 186 | self.edit_modes == "all" |
... | ... | @@ -314,6 +301,14 @@ class Block < ActiveRecord::Base |
314 | 301 | self.observers << block |
315 | 302 | end |
316 | 303 | |
304 | + def api_content | |
305 | + nil | |
306 | + end | |
307 | + | |
308 | + def display_api_content_by_default? | |
309 | + false | |
310 | + end | |
311 | + | |
317 | 312 | private |
318 | 313 | |
319 | 314 | def home_page_path | ... | ... |
app/models/box.rb
1 | -class Box < ActiveRecord::Base | |
1 | +class Box < ApplicationRecord | |
2 | 2 | |
3 | 3 | acts_as_list scope: -> box { where owner_id: box.owner_id, owner_type: box.owner_type } |
4 | 4 | |
... | ... | @@ -41,7 +41,6 @@ class Box < ActiveRecord::Base |
41 | 41 | ProfileImageBlock, |
42 | 42 | RawHTMLBlock, |
43 | 43 | RecentDocumentsBlock, |
44 | - SellersSearchBlock, | |
45 | 44 | TagsBlock ] |
46 | 45 | end |
47 | 46 | |
... | ... | @@ -54,21 +53,17 @@ class Box < ActiveRecord::Base |
54 | 53 | EnterprisesBlock, |
55 | 54 | FansBlock, |
56 | 55 | FavoriteEnterprisesBlock, |
57 | - FeaturedProductsBlock, | |
58 | 56 | FeedReaderBlock, |
59 | 57 | HighlightsBlock, |
60 | 58 | LinkListBlock, |
61 | 59 | LocationBlock, |
62 | 60 | LoginBlock, |
63 | 61 | MyNetworkBlock, |
64 | - ProductsBlock, | |
65 | - ProductCategoriesBlock, | |
66 | 62 | ProfileImageBlock, |
67 | 63 | ProfileInfoBlock, |
68 | 64 | ProfileSearchBlock, |
69 | 65 | RawHTMLBlock, |
70 | 66 | RecentDocumentsBlock, |
71 | - SellersSearchBlock, | |
72 | 67 | SlideshowBlock, |
73 | 68 | TagsBlock |
74 | 69 | ] | ... | ... |
app/models/category.rb
1 | -class Category < ActiveRecord::Base | |
1 | +class Category < ApplicationRecord | |
2 | 2 | |
3 | 3 | attr_accessible :name, :parent_id, :display_color, :display_in_menu, :image_builder, :environment, :parent |
4 | 4 | |
... | ... | @@ -35,8 +35,6 @@ class Category < ActiveRecord::Base |
35 | 35 | has_many :people, :through => :profile_categorizations, :source => :profile, :class_name => 'Person' |
36 | 36 | has_many :communities, :through => :profile_categorizations, :source => :profile, :class_name => 'Community' |
37 | 37 | |
38 | - has_many :products, :through => :enterprises | |
39 | - | |
40 | 38 | acts_as_having_image |
41 | 39 | |
42 | 40 | before_save :normalize_display_color |
... | ... | @@ -64,10 +62,6 @@ class Category < ActiveRecord::Base |
64 | 62 | self.communities.reorder('created_at DESC, id DESC').paginate(page: 1, per_page: limit) |
65 | 63 | end |
66 | 64 | |
67 | - def recent_products(limit = 10) | |
68 | - self.products.reorder('created_at DESC, id DESC').paginate(page: 1, per_page: limit) | |
69 | - end | |
70 | - | |
71 | 65 | def recent_articles(limit = 10) |
72 | 66 | self.articles.recent(limit) |
73 | 67 | end | ... | ... |
app/models/certifier.rb
... | ... | @@ -1,35 +0,0 @@ |
1 | -class Certifier < ActiveRecord::Base | |
2 | - | |
3 | - attr_accessible :name, :environment | |
4 | - | |
5 | - SEARCHABLE_FIELDS = { | |
6 | - :name => {:label => _('Name'), :weight => 10}, | |
7 | - :description => {:label => _('Description'), :weight => 3}, | |
8 | - :link => {:label => _('Link'), :weight => 1}, | |
9 | - } | |
10 | - | |
11 | - belongs_to :environment | |
12 | - | |
13 | - has_many :qualifier_certifiers, :dependent => :destroy | |
14 | - has_many :qualifiers, :through => :qualifier_certifiers | |
15 | - | |
16 | - has_many :product_qualifiers | |
17 | - has_many :products, :through => :product_qualifiers, :source => :product | |
18 | - | |
19 | - validates_presence_of :environment_id | |
20 | - validates_presence_of :name | |
21 | - | |
22 | - def destroy | |
23 | - product_qualifiers.each { |pq| pq.update! :certifier => nil } | |
24 | - super | |
25 | - end | |
26 | - | |
27 | - def link | |
28 | - self[:link] || '' | |
29 | - end | |
30 | - | |
31 | - def <=>(b) | |
32 | - self.name.downcase.transliterate <=> b.name.downcase.transliterate | |
33 | - end | |
34 | - | |
35 | -end |