Compare View

switch
from
...
to
 
Commits (289)
Showing 1159 changed files   Show diff stats

Too many changes.

To preserve performance only 100 of 1159 files displayed.

.gitlab-ci.yml
@@ -30,14 +30,47 @@ integration: @@ -30,14 +30,47 @@ integration:
30 script: bundle exec rake test:integration 30 script: bundle exec rake test:integration
31 stage: all-tests 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 stage: all-tests 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 stage: all-tests 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 stage: all-tests 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 +
@@ -6,15 +6,19 @@ notifications: @@ -6,15 +6,19 @@ notifications:
6 template: 6 template:
7 - "%{repository_slug} %{branch} %{commit} %{commit_subject} - %{result} %{build_url}" 7 - "%{repository_slug} %{branch} %{commit} %{commit_subject} - %{result} %{build_url}"
8 8
9 -# trusty constainers take more time to start  
10 -#dist: trusty 9 +# Ensure Container-based environment, as others can have some random failures
  10 +# specially with different Firefox versions and selenium tests.
  11 +# E.g. https://travis-ci.org/noosfero/noosfero/jobs/122918772#L1308
  12 +#
  13 +# Also container-based environments have the fatest boot times and
  14 +# are the only one with cache available for public projects.
  15 +# See https://docs.travis-ci.com/user/ci-environment/#Virtualization-environments
  16 +sudo: false
  17 +cache: bundler
11 18
12 language: ruby 19 language: ruby
13 rvm: 20 rvm:
14 - - 2.2  
15 - # ruby 2.3 works but isn't stable on travis  
16 -  
17 -cache: bundler 21 + - 2.3.1
18 22
19 addons: 23 addons:
20 apt: 24 apt:
@@ -25,11 +29,6 @@ addons: @@ -25,11 +29,6 @@ addons:
25 paths: 29 paths:
26 - $(ls tmp/artifact* | tr "\n" ":") 30 - $(ls tmp/artifact* | tr "\n" ":")
27 31
28 -# workaround for https://github.com/travis-ci/travis-ci/issues/4536  
29 -before_install:  
30 - - export GEM_HOME=$PWD/vendor/bundle/ruby/2.2.0  
31 - - gem install bundler  
32 -  
33 before_script: 32 before_script:
34 - mkdir -p tmp/{pids,cache} log cache 33 - mkdir -p tmp/{pids,cache} log cache
35 - script/noosfero-plugins disableall 34 - script/noosfero-plugins disableall
@@ -41,21 +40,36 @@ before_script: @@ -41,21 +40,36 @@ before_script:
41 - bundle exec rake db:migrate &>/dev/null 40 - bundle exec rake db:migrate &>/dev/null
42 41
43 env: 42 env:
44 - - TASK=test:api  
45 - - TASK=test:units  
46 - - TASK=test:functionals  
47 - - TASK=test:integration  
48 - - SLICE=1/2 TASK=cucumber LANG=en  
49 - - SLICE=2/2 TASK=cucumber LANG=en  
50 - - SLICE=1/4 TASK=selenium  
51 - - SLICE=2/4 TASK=selenium  
52 - - SLICE=3/4 TASK=selenium  
53 - - SLICE=4/4 TASK=selenium  
54 - - SLICE=1/5 TASK=test:noosfero_plugins BUNDLE_OPTS=install  
55 - - SLICE=2/5 TASK=test:noosfero_plugins BUNDLE_OPTS=install  
56 - - SLICE=3/5 TASK=test:noosfero_plugins BUNDLE_OPTS=install  
57 - - SLICE=4/5 TASK=test:noosfero_plugins BUNDLE_OPTS=install  
58 - - 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
59 73
60 script: 74 script:
61 - bundle exec rake $TASK 75 - bundle exec rake $TASK
@@ -77,6 +77,7 @@ group :cucumber do @@ -77,6 +77,7 @@ group :cucumber do
77 gem 'cucumber-rails', '~> 1.4.2', :require => false 77 gem 'cucumber-rails', '~> 1.4.2', :require => false
78 gem 'database_cleaner', '~> 1.3' 78 gem 'database_cleaner', '~> 1.3'
79 gem 'selenium-webdriver', '>= 2.50' 79 gem 'selenium-webdriver', '>= 2.50'
  80 + gem 'chromedriver-helper' if ENV['SELENIUM_DRIVER'] == 'chrome'
80 end 81 end
81 82
82 # Requires custom dependencies 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,7 +19,6 @@ Install the RPAF apache module (or skip this step if not using apache):
19 19
20 3a) Edit `/etc/apache2/ports.conf`, and: 20 3a) Edit `/etc/apache2/ports.conf`, and:
21 21
22 - * change `NameVirtualHost *:80` to `NameVirtualHost *:8080`  
23 * change `Listen 80` to `Listen 127.0.0.1:8080` 22 * change `Listen 80` to `Listen 127.0.0.1:8080`
24 23
25 3b) Edit `/etc/apache2/sites-enabled/*`, and change `<VirtualHost *:80>` to `<VirtualHost *:8080>` 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,6 +29,7 @@ Install the RPAF apache module (or skip this step if not using apache):
30 29
31 * change the line that says `START=no` to say `START=yes` 30 * change the line that says `START=no` to say `START=yes`
32 * change `-a :6081` to `-a :80` 31 * change `-a :6081` to `-a :80`
  32 + * add parameter `-p vcc_allow_inline_c=on` on `DAEMON_OPTS`
33 33
34 4b) Edit `/etc/varnish/default.vcl` and add the following lines at the end: 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,7 +99,7 @@ Description of contents
99 Holds controllers that should be named like weblog_controller.rb for automated URL mapping. All controllers should descend from `ActionController::Base`. 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 * `app/models` 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 * `app/views` 104 * `app/views`
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. 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.
app/api/app.rb 0 → 100644
@@ -0,0 +1,89 @@ @@ -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
app/api/entities.rb 0 → 100644
@@ -0,0 +1,269 @@ @@ -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
app/api/entity.rb 0 → 100644
@@ -0,0 +1,27 @@ @@ -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
app/api/helpers.rb 0 → 100644
@@ -0,0 +1,421 @@ @@ -0,0 +1,421 @@
  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_type.split(',').map do |content_type|
  411 + content_type.camelcase
  412 + end
  413 + end
  414 +
  415 + def period(from_date, until_date)
  416 + begin_period = from_date.nil? ? Time.at(0).to_datetime : from_date
  417 + end_period = until_date.nil? ? DateTime.now : until_date
  418 + begin_period..end_period
  419 + end
  420 + end
  421 +end
app/api/v1/activities.rb 0 → 100644
@@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
  1 +module Api
  2 + module V1
  3 + class Activities < Grape::API
  4 + before { authenticate! }
  5 +
  6 + resource :profiles do
  7 +
  8 + get ':id/activities' do
  9 + profile = Profile.find_by id: params[:id]
  10 +
  11 + not_found! if profile.blank? || profile.secret || !profile.visible
  12 + forbidden! if !profile.secret && profile.visible && !profile.display_private_info_to?(current_person)
  13 +
  14 + activities = profile.activities.map(&:activity)
  15 + present activities, :with => Entities::Activity, :current_person => current_person
  16 + end
  17 + end
  18 + end
  19 + end
  20 +end
app/api/v1/articles.rb 0 → 100644
@@ -0,0 +1,303 @@ @@ -0,0 +1,303 @@
  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 + desc 'Report a abuse and/or violent content in a article by id' do
  58 + detail 'Submit a abuse (in general, a content violation) report about a specific article'
  59 + params Entities::Article.documentation
  60 + failure [[400, 'Bad Request']]
  61 + named 'ArticleReportAbuse'
  62 + end
  63 + post ':id/report_abuse' do
  64 + article = find_article(environment.articles, params[:id])
  65 + profile = article.profile
  66 + begin
  67 + abuse_report = AbuseReport.new(:reason => params[:report_abuse])
  68 + if !params[:content_type].blank?
  69 + article = params[:content_type].constantize.find(params[:content_id])
  70 + abuse_report.content = article_reported_version(article)
  71 + end
  72 +
  73 + current_person.register_report(abuse_report, profile)
  74 +
  75 + if !params[:content_type].blank?
  76 + abuse_report = AbuseReport.find_by reporter_id: current_person.id, abuse_complaint_id: profile.opened_abuse_complaint.id
  77 + Delayed::Job.enqueue DownloadReportedImagesJob.new(abuse_report, article)
  78 + end
  79 +
  80 + {
  81 + :success => true,
  82 + :message => _('Your abuse report was registered. The administrators are reviewing your report.'),
  83 + }
  84 + rescue Exception => exception
  85 + #logger.error(exception.to_s)
  86 + render_api_error!(_('Your report couldn\'t be saved due to some problem. Please contact the administrator.'), 400)
  87 + end
  88 +
  89 + end
  90 +
  91 + desc "Returns the articles I voted" do
  92 + detail 'Get the Articles I make a vote'
  93 + failure [[403, 'Forbidden']]
  94 + named 'ArticleFollowers'
  95 + end
  96 + #FIXME refactor this method
  97 + get 'voted_by_me' do
  98 + present_articles(current_person.votes.where(:voteable_type => 'Article').collect(&:voteable))
  99 + end
  100 +
  101 + desc 'Perform a vote on a article by id' do
  102 + detail 'Vote on a specific article with values: 1 (if you like) or -1 (if not)'
  103 + params Entities::UserLogin.documentation
  104 + failure [[401,'Unauthorized']]
  105 + named 'ArticleVote'
  106 + end
  107 + post ':id/vote' do
  108 + authenticate!
  109 + value = (params[:value] || 1).to_i
  110 + # FIXME verify allowed values
  111 + render_api_error!('Vote value not allowed', 400) unless [-1, 1].include?(value)
  112 + article = find_article(environment.articles, params[:id])
  113 + begin
  114 + vote = Vote.new(:voteable => article, :voter => current_person, :vote => value)
  115 + {:vote => vote.save!}
  116 + rescue ActiveRecord::RecordInvalid => e
  117 + render_api_error!(e.message, 400)
  118 + end
  119 + end
  120 +
  121 + desc "Returns the total followers for the article" do
  122 + detail 'Get the followers of a specific article by id'
  123 + failure [[403, 'Forbidden']]
  124 + named 'ArticleFollowers'
  125 + end
  126 + get ':id/followers' do
  127 + article = find_article(environment.articles, params[:id])
  128 + total = article.person_followers.count
  129 + {:total_followers => total}
  130 + end
  131 +
  132 + desc "Return the articles followed by me"
  133 + get 'followed_by_me' do
  134 + present_articles_for_asset(current_person, 'following_articles')
  135 + end
  136 +
  137 + desc "Add a follower for the article" do
  138 + detail 'Add the current user identified by private token, like a follower of a article'
  139 + params Entities::UserLogin.documentation
  140 + failure [[401, 'Unauthorized']]
  141 + named 'ArticleFollow'
  142 + end
  143 + post ':id/follow' do
  144 + authenticate!
  145 + article = find_article(environment.articles, params[:id])
  146 + if article.article_followers.exists?(:person_id => current_person.id)
  147 + {:success => false, :already_follow => true}
  148 + else
  149 + article_follower = ArticleFollower.new
  150 + article_follower.article = article
  151 + article_follower.person = current_person
  152 + article_follower.save!
  153 + {:success => true}
  154 + end
  155 + end
  156 +
  157 + desc 'Return the children of a article identified by id' do
  158 + detail 'Get all children articles of a specific article'
  159 + params Entities::Article.documentation
  160 + failure [[403, 'Forbidden']]
  161 + named 'ArticleChildren'
  162 + end
  163 +
  164 + paginate per_page: MAX_PER_PAGE, max_per_page: MAX_PER_PAGE
  165 + get ':id/children' do
  166 + article = find_article(environment.articles, params[:id])
  167 +
  168 + #TODO make tests for this situation
  169 + votes_order = params.delete(:order) if params[:order]=='votes_score'
  170 + articles = select_filtered_collection_of(article, 'children', params)
  171 + articles = articles.display_filter(current_person, article.profile)
  172 +
  173 + #TODO make tests for this situation
  174 + if votes_order
  175 + articles = articles.joins('left join votes on articles.id=votes.voteable_id').group('articles.id').reorder('sum(coalesce(votes.vote, 0)) DESC')
  176 + end
  177 + Article.hit(articles)
  178 + present_articles(articles)
  179 + end
  180 +
  181 + desc 'Return one child of a article identified by id' do
  182 + detail 'Get a child of a specific article'
  183 + params Entities::Article.documentation
  184 + success Entities::Article
  185 + failure [[403, 'Forbidden']]
  186 + named 'ArticleChild'
  187 + end
  188 + get ':id/children/:child_id' do
  189 + article = find_article(environment.articles, params[:id])
  190 + child = find_article(article.children, params[:child_id])
  191 + child.hit
  192 + present_partial child, :with => Entities::Article
  193 + end
  194 +
  195 + desc 'Suggest a article to another profile' do
  196 + detail 'Suggest a article to another profile (person, community...)'
  197 + params Entities::Article.documentation
  198 + success Entities::Task
  199 + failure [[401,'Unauthorized']]
  200 + named 'ArticleSuggest'
  201 + end
  202 + post ':id/children/suggest' do
  203 + authenticate!
  204 + parent_article = environment.articles.find(params[:id])
  205 +
  206 + suggest_article = SuggestArticle.new
  207 + suggest_article.article = params[:article]
  208 + suggest_article.article[:parent_id] = parent_article.id
  209 + suggest_article.target = parent_article.profile
  210 + suggest_article.requestor = current_person
  211 +
  212 + unless suggest_article.save
  213 + render_api_errors!(suggest_article.article_object.errors.full_messages)
  214 + end
  215 + present_partial suggest_article, :with => Entities::Task
  216 + end
  217 +
  218 + # Example Request:
  219 + # POST api/v1/articles/:id/children?private_token=234298743290432&article[name]=title&article[body]=body
  220 + desc 'Add a child article to a parent identified by id' do
  221 + detail 'Create a new article and associate to a parent'
  222 + params Entities::Article.documentation
  223 + success Entities::Article
  224 + failure [[401,'Unauthorized']]
  225 + named 'ArticleAddChild'
  226 + end
  227 + post ':id/children' do
  228 + parent_article = environment.articles.find(params[:id])
  229 + params[:article][:parent_id] = parent_article.id
  230 + post_article(parent_article.profile, params)
  231 + end
  232 + end
  233 +
  234 + resource :profiles do
  235 + get ':id/home_page' do
  236 + profiles = environment.profiles
  237 + profiles = profiles.visible_for_person(current_person)
  238 + profile = profiles.find_by id: params[:id]
  239 + present_partial profile.home_page, :with => Entities::Article
  240 + end
  241 + end
  242 +
  243 + kinds = %w[profile community person enterprise]
  244 + kinds.each do |kind|
  245 + resource kind.pluralize.to_sym do
  246 + segment "/:#{kind}_id" do
  247 + resource :articles do
  248 +
  249 + desc "Return all articles associate with a profile of type #{kind}" do
  250 + detail 'Get a list of articles of a profile'
  251 + params Entities::Article.documentation
  252 + success Entities::Article
  253 + failure [[403, 'Forbidden']]
  254 + named 'ArticlesOfProfile'
  255 + end
  256 + get do
  257 + profile = environment.send(kind.pluralize).find(params["#{kind}_id"])
  258 +
  259 + if params[:path].present?
  260 + article = profile.articles.find_by path: params[:path]
  261 + if !article || !article.display_to?(current_person)
  262 + article = forbidden!
  263 + end
  264 +
  265 + present_partial article, :with => Entities::Article
  266 + else
  267 +
  268 + present_articles_for_asset(profile)
  269 + end
  270 + end
  271 +
  272 + desc "Return a article associate with a profile of type #{kind}" do
  273 + detail 'Get only one article of a profile'
  274 + params Entities::Article.documentation
  275 + success Entities::Article
  276 + failure [[403, 'Forbidden']]
  277 + named 'ArticleOfProfile'
  278 + end
  279 + get ':id' do
  280 + profile = environment.send(kind.pluralize).find(params["#{kind}_id"])
  281 + present_article(profile)
  282 + end
  283 +
  284 + # Example Request:
  285 + # POST api/v1/{people,communities,enterprises}/:asset_id/articles?private_token=234298743290432&article[name]=title&article[body]=body
  286 + desc "Add a new article associated with a profile of type #{kind}" do
  287 + detail 'Create a new article and associate with a profile'
  288 + params Entities::Article.documentation
  289 + success Entities::Article
  290 + failure [[403, 'Forbidden']]
  291 + named 'ArticleCreateToProfile'
  292 + end
  293 + post do
  294 + profile = environment.send(kind.pluralize).find(params["#{kind}_id"])
  295 + post_article(profile, params)
  296 + end
  297 + end
  298 + end
  299 + end
  300 + end
  301 + end
  302 + end
  303 +end
app/api/v1/blocks.rb 0 → 100644
@@ -0,0 +1,15 @@ @@ -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
app/api/v1/boxes.rb 0 → 100644
@@ -0,0 +1,45 @@ @@ -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
  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
  37 + end
  38 + end
  39 + end
  40 + end
  41 + end
  42 + end
  43 +
  44 + end
  45 +end
app/api/v1/categories.rb 0 → 100644
@@ -0,0 +1,25 @@ @@ -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
app/api/v1/comments.rb 0 → 100644
@@ -0,0 +1,49 @@ @@ -0,0 +1,49 @@
  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 + options = params.select { |key,v| !['id','private_token'].include?(key) }.merge(:author => current_person, :source => article)
  38 + begin
  39 + comment = Comment.create!(options)
  40 + rescue ActiveRecord::RecordInvalid => e
  41 + render_api_error!(e.message, 400)
  42 + end
  43 + present comment, :with => Entities::Comment, :current_person => current_person
  44 + end
  45 + end
  46 +
  47 + end
  48 + end
  49 +end
app/api/v1/communities.rb 0 → 100644
@@ -0,0 +1,82 @@ @@ -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
app/api/v1/contacts.rb 0 → 100644
@@ -0,0 +1,26 @@ @@ -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
app/api/v1/enterprises.rb 0 → 100644
@@ -0,0 +1,55 @@ @@ -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
app/api/v1/environments.rb 0 → 100644
@@ -0,0 +1,28 @@ @@ -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
app/api/v1/people.rb 0 → 100644
@@ -0,0 +1,127 @@ @@ -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
app/api/v1/profiles.rb 0 → 100644
@@ -0,0 +1,42 @@ @@ -0,0 +1,42 @@
  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 + delete ':id' do
  27 + authenticate!
  28 + profiles = environment.profiles
  29 + profile = profiles.find_by id: params[:id]
  30 +
  31 + not_found! if profile.blank?
  32 +
  33 + if current_person.has_permission?(:destroy_profile, profile)
  34 + profile.destroy
  35 + else
  36 + forbidden!
  37 + end
  38 + end
  39 + end
  40 + end
  41 + end
  42 +end
app/api/v1/search.rb 0 → 100644
@@ -0,0 +1,37 @@ @@ -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
app/api/v1/session.rb 0 → 100644
@@ -0,0 +1,157 @@ @@ -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
app/api/v1/tags.rb 0 → 100644
@@ -0,0 +1,30 @@ @@ -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
app/api/v1/tasks.rb 0 → 100644
@@ -0,0 +1,57 @@ @@ -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
app/api/v1/users.rb 0 → 100644
@@ -0,0 +1,43 @@ @@ -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
app/controllers/admin/categories_controller.rb
@@ -7,7 +7,6 @@ class CategoriesController &lt; AdminController @@ -7,7 +7,6 @@ class CategoriesController &lt; AdminController
7 def index 7 def index
8 @categories = environment.categories.where("parent_id is null AND type is null") 8 @categories = environment.categories.where("parent_id is null AND type is null")
9 @regions = environment.regions.where(:parent_id => nil) 9 @regions = environment.regions.where(:parent_id => nil)
10 - @product_categories = environment.product_categories.where(:parent_id => nil)  
11 end 10 end
12 11
13 def get_children 12 def get_children
app/controllers/admin/edit_template_controller.rb
1 class EditTemplateController < AdminController 1 class EditTemplateController < AdminController
2 - 2 +
3 protect 'edit_environment_design', :environment 3 protect 'edit_environment_design', :environment
4 - 4 +
5 #FIXME 5 #FIXME
6 #design_editor :holder => 'environment', :autosave => true, :block_types => :block_types 6 #design_editor :holder => 'environment', :autosave => true, :block_types => :block_types
7 7
@@ -9,7 +9,6 @@ class EditTemplateController &lt; AdminController @@ -9,7 +9,6 @@ class EditTemplateController &lt; AdminController
9 %w[ 9 %w[
10 FavoriteLinks 10 FavoriteLinks
11 ListBlock 11 ListBlock
12 - SellersSearchBlock  
13 ] 12 ]
14 end 13 end
15 14
app/controllers/admin/environment_design_controller.rb
1 class EnvironmentDesignController < BoxOrganizerController 1 class EnvironmentDesignController < BoxOrganizerController
2 - 2 +
3 protect 'edit_environment_design', :environment 3 protect 'edit_environment_design', :environment
4 4
5 def available_blocks 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 @available_blocks += plugins.dispatch(:extra_blocks, :type => Environment) 7 @available_blocks += plugins.dispatch(:extra_blocks, :type => Environment)
8 end 8 end
9 9
app/controllers/admin/users_controller.rb
@@ -45,7 +45,6 @@ class UsersController &lt; AdminController @@ -45,7 +45,6 @@ class UsersController &lt; AdminController
45 redirect_to :action => :index, :q => params[:q], :filter => params[:filter] 45 redirect_to :action => :index, :q => params[:q], :filter => params[:filter]
46 end 46 end
47 47
48 -  
49 def destroy_user 48 def destroy_user
50 if request.post? 49 if request.post?
51 person = environment.people.find_by id: params[:id] 50 person = environment.people.find_by id: params[:id]
@@ -58,7 +57,6 @@ class UsersController &lt; AdminController @@ -58,7 +57,6 @@ class UsersController &lt; AdminController
58 redirect_to :action => :index, :q => params[:q], :filter => params[:filter] 57 redirect_to :action => :index, :q => params[:q], :filter => params[:filter]
59 end 58 end
60 59
61 -  
62 def download 60 def download
63 respond_to do |format| 61 respond_to do |format|
64 format.html 62 format.html
@@ -87,8 +85,11 @@ class UsersController &lt; AdminController @@ -87,8 +85,11 @@ class UsersController &lt; AdminController
87 end 85 end
88 86
89 def send_mail 87 def send_mail
90 - @mailing = environment.mailings.build(params[:mailing])  
91 if request.post? 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 @mailing.locale = locale 93 @mailing.locale = locale
93 @mailing.person = user 94 @mailing.person = user
94 if @mailing.save 95 if @mailing.save
app/controllers/my_profile/cms_controller.rb
@@ -103,12 +103,10 @@ class CmsController &lt; MyProfileController @@ -103,12 +103,10 @@ class CmsController &lt; MyProfileController
103 end 103 end
104 end 104 end
105 end 105 end
106 -  
107 - escape_fields @article  
108 end 106 end
109 107
110 def new 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 @success_back_to = params[:success_back_to] 111 @success_back_to = params[:success_back_to]
114 # user must choose an article type first 112 # user must choose an article type first
@@ -174,9 +172,6 @@ class CmsController &lt; MyProfileController @@ -174,9 +172,6 @@ class CmsController &lt; MyProfileController
174 return 172 return
175 end 173 end
176 end 174 end
177 -  
178 - escape_fields @article  
179 -  
180 render :action => 'edit' 175 render :action => 'edit'
181 end 176 end
182 177
@@ -365,7 +360,7 @@ class CmsController &lt; MyProfileController @@ -365,7 +360,7 @@ class CmsController &lt; MyProfileController
365 def search 360 def search
366 query = params[:q] 361 query = params[:q]
367 results = find_by_contents(:uploaded_files, profile, profile.files.published, query)[:results] 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 end 364 end
370 365
371 def search_article_privacy_exceptions 366 def search_article_privacy_exceptions
@@ -409,9 +404,6 @@ class CmsController &lt; MyProfileController @@ -409,9 +404,6 @@ class CmsController &lt; MyProfileController
409 ] 404 ]
410 articles += special_article_types if params && params[:cms] 405 articles += special_article_types if params && params[:cms]
411 parent_id = params ? params[:parent_id] : nil 406 parent_id = params ? params[:parent_id] : nil
412 - if profile.enterprise?  
413 - articles << EnterpriseHomepage  
414 - end  
415 if @parent && @parent.blog? 407 if @parent && @parent.blog?
416 articles -= Article.folder_types.map(&:constantize) 408 articles -= Article.folder_types.map(&:constantize)
417 end 409 end
@@ -451,9 +443,7 @@ class CmsController &lt; MyProfileController @@ -451,9 +443,7 @@ class CmsController &lt; MyProfileController
451 end 443 end
452 444
453 def refuse_blocks 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 end 447 end
458 448
459 def per_page 449 def per_page
@@ -521,10 +511,4 @@ class CmsController &lt; MyProfileController @@ -521,10 +511,4 @@ class CmsController &lt; MyProfileController
521 end 511 end
522 end 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 end 514 end
app/controllers/my_profile/profile_design_controller.rb
@@ -45,17 +45,10 @@ class ProfileDesignController &lt; BoxOrganizerController @@ -45,17 +45,10 @@ class ProfileDesignController &lt; BoxOrganizerController
45 if profile.enterprise? 45 if profile.enterprise?
46 blocks << DisabledEnterpriseMessageBlock 46 blocks << DisabledEnterpriseMessageBlock
47 blocks << HighlightsBlock 47 blocks << HighlightsBlock
48 - blocks << ProductCategoriesBlock  
49 - blocks << FeaturedProductsBlock  
50 blocks << FansBlock 48 blocks << FansBlock
51 blocks += plugins.dispatch(:extra_blocks, :type => Enterprise) 49 blocks += plugins.dispatch(:extra_blocks, :type => Enterprise)
52 end 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 # block exclusive to profiles that have blog 52 # block exclusive to profiles that have blog
60 if profile.has_blog? 53 if profile.has_blog?
61 blocks << BlogArchivesBlock 54 blocks << BlogArchivesBlock
app/controllers/my_profile/profile_editor_controller.rb
@@ -29,6 +29,7 @@ class ProfileEditorController &lt; MyProfileController @@ -29,6 +29,7 @@ class ProfileEditorController &lt; MyProfileController
29 Image.transaction do 29 Image.transaction do
30 begin 30 begin
31 @plugins.dispatch(:profile_editor_transaction_extras) 31 @plugins.dispatch(:profile_editor_transaction_extras)
  32 + # TODO: This is unsafe! Add sanitizer
32 @profile_data.update!(params[:profile_data]) 33 @profile_data.update!(params[:profile_data])
33 redirect_to :action => 'index', :profile => profile.identifier 34 redirect_to :action => 'index', :profile => profile.identifier
34 rescue Exception => ex 35 rescue Exception => ex
app/controllers/my_profile/tasks_controller.rb
@@ -14,20 +14,23 @@ class TasksController &lt; MyProfileController @@ -14,20 +14,23 @@ class TasksController &lt; MyProfileController
14 @filter_text = params[:filter_text].presence 14 @filter_text = params[:filter_text].presence
15 @filter_responsible = params[:filter_responsible] 15 @filter_responsible = params[:filter_responsible]
16 @task_types = Task.pending_types_for(profile) 16 @task_types = Task.pending_types_for(profile)
17 -  
18 - @tasks = Task.pending_all(profile, @filter_type, @filter_text).order_by('created_at', 'asc') 17 + @tasks = Task.pending_all(profile, @filter_type, @filter_text).order_by('created_at', 'asc').paginate(:per_page => Task.per_page, :page => params[:page])
19 @tasks = @tasks.where(:responsible_id => @filter_responsible.to_i != -1 ? @filter_responsible : nil) if @filter_responsible.present? 18 @tasks = @tasks.where(:responsible_id => @filter_responsible.to_i != -1 ? @filter_responsible : nil) if @filter_responsible.present?
20 @tasks = @tasks.paginate(:per_page => Task.per_page, :page => params[:page]) 19 @tasks = @tasks.paginate(:per_page => Task.per_page, :page => params[:page])
21 -  
22 @failed = params ? params[:failed] : {} 20 @failed = params ? params[:failed] : {}
23 21
24 @responsible_candidates = profile.members.by_role(profile.roles.reject {|r| !r.has_permission?('perform_task')}) if profile.organization? 22 @responsible_candidates = profile.members.by_role(profile.roles.reject {|r| !r.has_permission?('perform_task')}) if profile.organization?
25 23
26 @view_only = !current_person.has_permission?(:perform_task, profile) 24 @view_only = !current_person.has_permission?(:perform_task, profile)
  25 +
27 end 26 end
28 27
29 def processed 28 def processed
30 - @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)
31 end 34 end
32 35
33 def change_responsible 36 def change_responsible
@@ -95,4 +98,36 @@ class TasksController &lt; MyProfileController @@ -95,4 +98,36 @@ class TasksController &lt; MyProfileController
95 @ticket = Ticket.where('(requestor_id = ? or target_id = ?) and id = ?', profile.id, profile.id, params[:id]).first 98 @ticket = Ticket.where('(requestor_id = ? or target_id = ?) and id = ?', profile.id, profile.id, params[:id]).first
96 end 99 end
97 100
  101 + def search_tasks
  102 + filter_type = params[:filter_type].presence
  103 + filter_text = params[:filter_text].presence
  104 + result = Task.pending_all(profile,filter_type, filter_text)
  105 +
  106 + render :json => result.map { |task| {:label => task.data[:name], :value => task.data[:name]} }
  107 + end
  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 +
98 end 133 end
app/controllers/public/api_controller.rb
@@ -13,7 +13,7 @@ class ApiController &lt; PublicController @@ -13,7 +13,7 @@ class ApiController &lt; PublicController
13 private 13 private
14 14
15 def endpoints 15 def endpoints
16 - Noosfero::API::API.endpoints(environment) 16 + Api::App.endpoints(environment)
17 end 17 end
18 18
19 end 19 end
app/controllers/public/profile_controller.rb
@@ -86,8 +86,16 @@ class ProfileController &lt; PublicController @@ -86,8 +86,16 @@ class ProfileController &lt; PublicController
86 @articles = profile.top_level_articles.includes([:profile, :parent]) 86 @articles = profile.top_level_articles.includes([:profile, :parent])
87 end 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 def join 95 def join
90 if !user.memberships.include?(profile) 96 if !user.memberships.include?(profile)
  97 + return if profile.community? && show_confirmation_modal?(profile)
  98 +
91 profile.add_member(user) 99 profile.add_member(user)
92 if !profile.members.include?(user) 100 if !profile.members.include?(user)
93 render :text => {:message => _('%s administrator still needs to accept you as member.') % profile.name}.to_json 101 render :text => {:message => _('%s administrator still needs to accept you as member.') % profile.name}.to_json
app/controllers/public/search_controller.rb
@@ -3,7 +3,10 @@ class SearchController &lt; PublicController @@ -3,7 +3,10 @@ class SearchController &lt; PublicController
3 helper TagsHelper 3 helper TagsHelper
4 include SearchHelper 4 include SearchHelper
5 include ActionView::Helpers::NumberHelper 5 include ActionView::Helpers::NumberHelper
  6 + include SanitizeParams
6 7
  8 +
  9 + before_filter :sanitize_params
7 before_filter :redirect_asset_param, :except => [:assets, :suggestions] 10 before_filter :redirect_asset_param, :except => [:assets, :suggestions]
8 before_filter :load_category, :except => :suggestions 11 before_filter :load_category, :except => :suggestions
9 before_filter :load_search_assets, :except => :suggestions 12 before_filter :load_search_assets, :except => :suggestions
@@ -49,7 +52,6 @@ class SearchController &lt; PublicController @@ -49,7 +52,6 @@ class SearchController &lt; PublicController
49 [ 52 [
50 [ :people, _('People'), :recent_people ], 53 [ :people, _('People'), :recent_people ],
51 [ :enterprises, _('Enterprises'), :recent_enterprises ], 54 [ :enterprises, _('Enterprises'), :recent_enterprises ],
52 - [ :products, _('Products'), :recent_products ],  
53 [ :events, _('Upcoming events'), :upcoming_events ], 55 [ :events, _('Upcoming events'), :upcoming_events ],
54 [ :communities, _('Communities'), :recent_communities ], 56 [ :communities, _('Communities'), :recent_communities ],
55 [ :articles, _('Contents'), :recent_articles ] 57 [ :articles, _('Contents'), :recent_articles ]
@@ -75,16 +77,17 @@ class SearchController &lt; PublicController @@ -75,16 +77,17 @@ class SearchController &lt; PublicController
75 full_text_search 77 full_text_search
76 end 78 end
77 79
78 - def products  
79 - @scope = @environment.products  
80 - full_text_search  
81 - end  
82 -  
83 def enterprises 80 def enterprises
84 @scope = visible_profiles(Enterprise) 81 @scope = visible_profiles(Enterprise)
85 full_text_search 82 full_text_search
86 end 83 end
87 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 +
88 def communities 91 def communities
89 @scope = visible_profiles(Community) 92 @scope = visible_profiles(Community)
90 full_text_search 93 full_text_search
@@ -134,7 +137,8 @@ class SearchController &lt; PublicController @@ -134,7 +137,8 @@ class SearchController &lt; PublicController
134 137
135 def tag 138 def tag
136 @tag = params[:tag] 139 @tag = params[:tag]
137 - @tag_cache_key = "tag_#{CGI.escape(@tag.to_s)}_env_#{environment.id.to_s}_page_#{params[:npage]}" 140 + tag_str = @tag.kind_of?(Array) ? @tag.join(" ") : @tag.to_str
  141 + @tag_cache_key = "tag_#{CGI.escape(tag_str)}_env_#{environment.id.to_s}_page_#{params[:npage]}"
138 if is_cache_expired?(@tag_cache_key) 142 if is_cache_expired?(@tag_cache_key)
139 @searches[@asset] = {:results => environment.articles.tagged_with(@tag).paginate(paginate_options)} 143 @searches[@asset] = {:results => environment.articles.tagged_with(@tag).paginate(paginate_options)}
140 end 144 end
@@ -182,7 +186,6 @@ class SearchController &lt; PublicController @@ -182,7 +186,6 @@ class SearchController &lt; PublicController
182 people: _('People'), 186 people: _('People'),
183 communities: _('Communities'), 187 communities: _('Communities'),
184 enterprises: _('Enterprises'), 188 enterprises: _('Enterprises'),
185 - products: _('Products and Services'),  
186 events: _('Events'), 189 events: _('Events'),
187 } 190 }
188 end 191 end
@@ -256,12 +259,11 @@ class SearchController &lt; PublicController @@ -256,12 +259,11 @@ class SearchController &lt; PublicController
256 end 259 end
257 260
258 def available_assets 261 def available_assets
259 - assets = { 262 + {
260 articles: _('Contents'), 263 articles: _('Contents'),
261 enterprises: _('Enterprises'), 264 enterprises: _('Enterprises'),
262 people: _('People'), 265 people: _('People'),
263 communities: _('Communities'), 266 communities: _('Communities'),
264 - products: _('Products and Services'),  
265 } 267 }
266 end 268 end
267 269
app/helpers/action_tracker_helper.rb
@@ -5,22 +5,22 @@ module ActionTrackerHelper @@ -5,22 +5,22 @@ module ActionTrackerHelper
5 end 5 end
6 6
7 def new_friendship_description ta 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 num: ta.get_friend_name.size, 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 link_to image_tag(ta.get_friend_profile_custom_icon[i] || default_or_themed_icon("/images/icons-app/person-icon.png")), 11 link_to image_tag(ta.get_friend_profile_custom_icon[i] || default_or_themed_icon("/images/icons-app/person-icon.png")),
12 ta.get_friend_url[i], title: n 12 ta.get_friend_url[i], title: n
13 - end.join 13 + end)
14 } 14 }
15 end 15 end
16 16
17 def join_community_description ta 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 num: ta.get_resource_name.size, 19 num: ta.get_resource_name.size,
20 name: ta.collect_group_with_index(:resource_name) do |n,i| 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 ta.get_resource_url[i], title: n 22 ta.get_resource_url[i], title: n
23 - end.join 23 + end.join.html_safe
24 } 24 }
25 end 25 end
26 26
@@ -67,24 +67,6 @@ module ActionTrackerHelper @@ -67,24 +67,6 @@ module ActionTrackerHelper
67 } 67 }
68 end 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 def favorite_enterprise_description ta 70 def favorite_enterprise_description ta
89 _('favorited enterprise %{title}') % { 71 _('favorited enterprise %{title}') % {
90 title: link_to(truncate(ta.get_enterprise_name), ta.get_enterprise_url), 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,8 +44,6 @@ module ApplicationHelper
44 44
45 include TokenHelper 45 include TokenHelper
46 46
47 - include CatalogHelper  
48 -  
49 include PluginsHelper 47 include PluginsHelper
50 48
51 include ButtonsHelper 49 include ButtonsHelper
@@ -56,6 +54,8 @@ module ApplicationHelper @@ -56,6 +54,8 @@ module ApplicationHelper
56 54
57 include TaskHelper 55 include TaskHelper
58 56
  57 + include MembershipsHelper
  58 +
59 def locale 59 def locale
60 (@page && !@page.language.blank?) ? @page.language : FastGettext.locale 60 (@page && !@page.language.blank?) ? @page.language : FastGettext.locale
61 end 61 end
@@ -99,7 +99,6 @@ module ApplicationHelper @@ -99,7 +99,6 @@ module ApplicationHelper
99 # 99 #
100 # TODO: implement correcly the 'Help' button click 100 # TODO: implement correcly the 'Help' button click
101 def help(content = nil, link_name = nil, options = {}, &block) 101 def help(content = nil, link_name = nil, options = {}, &block)
102 -  
103 link_name ||= _('Help') 102 link_name ||= _('Help')
104 103
105 @help_message_id ||= 1 104 @help_message_id ||= 1
@@ -122,7 +121,7 @@ module ApplicationHelper @@ -122,7 +121,7 @@ module ApplicationHelper
122 button = link_to_function(content_tag('span', link_name), "Element.show('#{help_id}')", options ) 121 button = link_to_function(content_tag('span', link_name), "Element.show('#{help_id}')", options )
123 close_button = content_tag("div", link_to_function(_("Close"), "Element.hide('#{help_id}')", :class => 'close_help_button')) 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 unless block.nil? 126 unless block.nil?
128 concat(text) 127 concat(text)
@@ -234,13 +233,6 @@ module ApplicationHelper @@ -234,13 +233,6 @@ module ApplicationHelper
234 link_to(content_tag('span', text), url, html_options.merge(:class => the_class, :title => text)) 233 link_to(content_tag('span', text), url, html_options.merge(:class => the_class, :title => text))
235 end 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 def render_profile_actions klass 236 def render_profile_actions klass
245 name = klass.to_s.underscore 237 name = klass.to_s.underscore
246 begin 238 begin
@@ -362,8 +354,8 @@ module ApplicationHelper @@ -362,8 +354,8 @@ module ApplicationHelper
362 def popover_menu(title,menu_title,links,html_options={}) 354 def popover_menu(title,menu_title,links,html_options={})
363 html_options[:class] = "" unless html_options[:class] 355 html_options[:class] = "" unless html_options[:class]
364 html_options[:class] << " menu-submenu-trigger" 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 link_to(content_tag(:span, title), '#', html_options) 359 link_to(content_tag(:span, title), '#', html_options)
368 end 360 end
369 361
@@ -473,9 +465,9 @@ module ApplicationHelper @@ -473,9 +465,9 @@ module ApplicationHelper
473 map(&:role) 465 map(&:role)
474 names = [] 466 names = []
475 roles.each do |role| 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 end 469 end
478 - names.join(', ') 470 + safe_join(names, ', ')
479 end 471 end
480 472
481 def role_color(role, env_id) 473 def role_color(role, env_id)
@@ -788,7 +780,7 @@ module ApplicationHelper @@ -788,7 +780,7 @@ module ApplicationHelper
788 return "" if categories.blank? 780 return "" if categories.blank?
789 content_tag(:ul) do 781 content_tag(:ul) do
790 categories.map do |category| 782 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 } 783 + category_path = { :controller => 'search', :action => 'category_index', :category_path => category.explode_path }
792 if category.display_in_menu? 784 if category.display_in_menu?
793 content_tag(:li) do 785 content_tag(:li) do
794 if !category.is_leaf_displayable_in_menu? 786 if !category.is_leaf_displayable_in_menu?
@@ -911,7 +903,8 @@ module ApplicationHelper @@ -911,7 +903,8 @@ module ApplicationHelper
911 end 903 end
912 904
913 def admin_link 905 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') : '' 906 + admin_icon = '<i class="icon-menu-admin"></i><strong>' + _('Administration') + '</strong>'
  907 + user.is_admin?(environment) ? link_to(admin_icon.html_safe, environment.admin_url, :title => _("Configure the environment"), :class => 'admin-link') : ''
915 end 908 end
916 909
917 def usermenu_logged_in 910 def usermenu_logged_in
@@ -920,23 +913,39 @@ module ApplicationHelper @@ -920,23 +913,39 @@ module ApplicationHelper
920 if count > 0 913 if count > 0
921 pending_tasks_count = link_to(count.to_s, user.tasks_url, :id => 'pending-tasks-count', :title => _("Manage your pending tasks")) 914 pending_tasks_count = link_to(count.to_s, user.tasks_url, :id => 'pending-tasks-count', :title => _("Manage your pending tasks"))
922 end 915 end
  916 + user_identifier = "<i style='background-image:url(#{user.profile_custom_icon(gravatar_default)})'></i><strong>#{user.identifier}</strong>"
  917 + welcome_link = link_to(user_identifier.html_safe, user.public_profile_url, :id => "homepage-link", :title => _('Go to your homepage'))
  918 + welcome_span = _("<span class='welcome'>Welcome,</span> %s") % welcome_link.html_safe
  919 + ctrl_panel_icon = '<i class="icon-menu-ctrl-panel"></i>'
  920 + ctrl_panel_section = '<strong>' + ctrl_panel_icon + _('Control panel') + '</strong>'
  921 + ctrl_panel_link = link_to(ctrl_panel_section.html_safe, user.admin_url, :class => 'ctrl-panel', :title => _("Configure your personal account and content"))
  922 + logout_icon = '<i class="icon-menu-logout"></i><strong>' + _('Logout') + '</strong>'
  923 + logout_link = link_to(logout_icon.html_safe, { :controller => 'account', :action => 'logout'} , :id => "logout", :title => _("Leave the system"))
  924 + join_result = safe_join(
  925 + [welcome_span.html_safe, render_environment_features(:usermenu).html_safe, admin_link.html_safe,
  926 + manage_enterprises.html_safe, manage_communities.html_safe, ctrl_panel_link.html_safe,
  927 + pending_tasks_count.html_safe, logout_link.html_safe], "")
  928 + join_result
  929 + end
923 930
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")) 931 + def usermenu_notlogged_in
  932 + login_str = '<i class="icon-menu-login"></i><strong>' + _('Login') + '</strong>'
  933 + ret = _("<span class='login'>%s</span>") % modal_inline_link_to(login_str.html_safe, login_url, '#inlineLoginBox', :id => 'link_login')
  934 + return ret.html_safe
932 end 935 end
933 936
  937 + def usermenu_signup
  938 + signup_str = '<strong>' + _('Sign up') + '</strong>'
  939 + ret = _("<span class='or'>or</span> <span class='signup'>%s</span>") % link_to(signup_str.html_safe, :controller => 'account', :action => 'signup')
  940 + return ret.html_safe
  941 +
  942 + end
934 def limited_text_area(object_name, method, limit, text_area_id, options = {}) 943 def limited_text_area(object_name, method, limit, text_area_id, options = {})
935 - content_tag(:div, [ 944 + content_tag(:div, safe_join([
936 text_area(object_name, method, { :id => text_area_id, :onkeyup => "limited_text_area('#{text_area_id}', #{limit})" }.merge(options)), 945 text_area(object_name, method, { :id => text_area_id, :onkeyup => "limited_text_area('#{text_area_id}', #{limit})" }.merge(options)),
937 content_tag(:p, content_tag(:span, limit) + ' ' + _(' characters left'), :id => text_area_id + '_left'), 946 content_tag(:p, content_tag(:span, limit) + ' ' + _(' characters left'), :id => text_area_id + '_left'),
938 content_tag(:p, _('Limit of characters reached'), :id => text_area_id + '_limit', :style => 'display: none') 947 content_tag(:p, _('Limit of characters reached'), :id => text_area_id + '_limit', :style => 'display: none')
939 - ].join, :class => 'limited-text-area') 948 + ]), :class => 'limited-text-area')
940 end 949 end
941 950
942 def expandable_text_area(object_name, method, text_area_id, options = {}) 951 def expandable_text_area(object_name, method, text_area_id, options = {})
@@ -970,11 +979,11 @@ module ApplicationHelper @@ -970,11 +979,11 @@ module ApplicationHelper
970 979
971 def task_information(task) 980 def task_information(task)
972 values = {} 981 values = {}
  982 + values.merge!(task.information[:variables]) if task.information[:variables]
973 values.merge!({:requestor => link_to(task.requestor.name, task.requestor.url)}) if task.requestor 983 values.merge!({:requestor => link_to(task.requestor.name, task.requestor.url)}) if task.requestor
974 values.merge!({:subject => content_tag('span', task.subject, :class=>'task_target')}) if task.subject 984 values.merge!({:subject => content_tag('span', task.subject, :class=>'task_target')}) if task.subject
975 values.merge!({:linked_subject => link_to(content_tag('span', task.linked_subject[:text], :class => 'task_target'), task.linked_subject[:url])}) if task.linked_subject 985 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 986 + (task.information[:message] % values).html_safe
978 end 987 end
979 988
980 def add_zoom_to_article_images 989 def add_zoom_to_article_images
@@ -1032,26 +1041,24 @@ module ApplicationHelper @@ -1032,26 +1041,24 @@ module ApplicationHelper
1032 end 1041 end
1033 1042
1034 def render_tabs(tabs) 1043 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]) } 1044 + titles = tabs.inject(''.html_safe){ |result, tab| result << content_tag(:li, link_to(tab[:title], '#'+tab[:id]), :class => 'tab') }
  1045 + contents = tabs.inject(''.html_safe){ |result, tab| result << content_tag(:div, tab[:content], :id => tab[:id]) }
1037 1046
1038 content_tag(:div, content_tag(:ul, titles) + raw(contents), :class => 'ui-tabs') 1047 content_tag(:div, content_tag(:ul, titles) + raw(contents), :class => 'ui-tabs')
1039 end 1048 end
1040 1049
1041 def delete_article_message(article) 1050 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 - ) 1051 + if article.folder?
  1052 + _("Are you sure that you want to remove the folder \"%s\"? Note that all the items inside it will also be removed!") % article.name
  1053 + else
  1054 + _("Are you sure that you want to remove the item \"%s\"?") % article.name
  1055 + end
1049 end 1056 end
1050 1057
1051 def expirable_link_to(expired, content, url, options = {}) 1058 def expirable_link_to(expired, content, url, options = {})
1052 if expired 1059 if expired
1053 options[:class] = (options[:class] || '') + ' disabled' 1060 options[:class] = (options[:class] || '') + ' disabled'
1054 - content_tag('a', '&nbsp;'+content_tag('span', content), options) 1061 + content_tag('a', '&nbsp;'.html_safe+content_tag('span', content), options)
1055 else 1062 else
1056 if options[:modal] 1063 if options[:modal]
1057 options.delete(:modal) 1064 options.delete(:modal)
@@ -1084,7 +1091,7 @@ module ApplicationHelper @@ -1084,7 +1091,7 @@ module ApplicationHelper
1084 1091
1085 radios = templates.map do |template| 1092 radios = templates.map do |template|
1086 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))) 1093 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") 1094 + end.join("\n").html_safe
1088 1095
1089 content_tag('div', content_tag('label', _('Profile organization'), :for => 'template-options', :class => 'formlabel') + 1096 content_tag('div', content_tag('label', _('Profile organization'), :for => 'template-options', :class => 'formlabel') +
1090 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;') + 1097 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 +1131,7 @@ module ApplicationHelper @@ -1124,7 +1131,7 @@ module ApplicationHelper
1124 content_tag(:div, :class => 'errorExplanation', :id => 'errorExplanation') do 1131 content_tag(:div, :class => 'errorExplanation', :id => 'errorExplanation') do
1125 content_tag(:h2, _('Errors while saving')) + 1132 content_tag(:h2, _('Errors while saving')) +
1126 content_tag(:ul) do 1133 content_tag(:ul) do
1127 - errors.map { |err| content_tag(:li, err) }.join 1134 + safe_join(errors.map { |err| content_tag(:li, err) })
1128 end 1135 end
1129 end 1136 end
1130 end 1137 end
@@ -1234,6 +1241,7 @@ module ApplicationHelper @@ -1234,6 +1241,7 @@ module ApplicationHelper
1234 :href=>"#", 1241 :href=>"#",
1235 :title=>_("Exit full screen mode") 1242 :title=>_("Exit full screen mode")
1236 }) 1243 })
  1244 + content.html_safe
1237 end 1245 end
1238 1246
1239 end 1247 end
app/helpers/article_helper.rb
@@ -69,14 +69,14 @@ module ArticleHelper @@ -69,14 +69,14 @@ module ArticleHelper
69 content_tag('div', 69 content_tag('div',
70 content_tag('div', 70 content_tag('div',
71 radio_button(:article, :published, true) + 71 radio_button(:article, :published, true) +
72 - content_tag('span', '&nbsp;', :class => 'access-public-icon') + 72 + content_tag('span', '&nbsp;'.html_safe, :class => 'access-public-icon') +
73 content_tag('label', _('Public'), :for => 'article_published_true') + 73 content_tag('label', _('Public'), :for => 'article_published_true') +
74 content_tag('span', _('Visible to other people'), :class => 'access-note'), 74 content_tag('span', _('Visible to other people'), :class => 'access-note'),
75 :class => 'access-item' 75 :class => 'access-item'
76 ) + 76 ) +
77 content_tag('div', 77 content_tag('div',
78 radio_button(:article, :published, false) + 78 radio_button(:article, :published, false) +
79 - content_tag('span', '&nbsp;', :class => 'access-private-icon') + 79 + content_tag('span', '&nbsp;'.html_safe, :class => 'access-private-icon') +
80 content_tag('label', _('Private'), :for => 'article_published_false', :id => "label_private") + 80 content_tag('label', _('Private'), :for => 'article_published_false', :id => "label_private") +
81 content_tag('span', _('Limit visibility of this article'), :class => 'access-note'), 81 content_tag('span', _('Limit visibility of this article'), :class => 'access-note'),
82 :class => 'access-item' 82 :class => 'access-item'
@@ -187,9 +187,9 @@ module ArticleHelper @@ -187,9 +187,9 @@ module ArticleHelper
187 def following_button(page, user) 187 def following_button(page, user)
188 if !user.blank? and user != page.author 188 if !user.blank? and user != page.author
189 if page.is_followed_by? user 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', :action => 'unfollow_article', :article_id => page.id, :profile => page.profile.identifier}
191 else 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', :action => 'follow_article', :article_id => page.id, :profile => page.profile.identifier}
193 end 193 end
194 end 194 end
195 end 195 end
app/helpers/block_helper.rb
@@ -3,13 +3,13 @@ module BlockHelper @@ -3,13 +3,13 @@ module BlockHelper
3 def block_title(title, subtitle=nil) 3 def block_title(title, subtitle=nil)
4 block_header = block_heading title 4 block_header = block_heading title
5 block_header += block_heading(subtitle, 'h4') if subtitle 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 end 7 end
8 8
9 def block_heading(title, heading='h3') 9 def block_heading(title, heading='h3')
10 tag_class = 'block-' + (heading == 'h3' ? 'title' : 'subtitle') 10 tag_class = 'block-' + (heading == 'h3' ? 'title' : 'subtitle')
11 tag_class += ' empty' if title.empty? 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 end 13 end
14 14
15 def highlights_block_config_image_fields(block, image={}, row_number=nil) 15 def highlights_block_config_image_fields(block, image={}, row_number=nil)
@@ -28,7 +28,7 @@ module BlockHelper @@ -28,7 +28,7 @@ module BlockHelper
28 }</label></td> 28 }</label></td>
29 <td>#{button_without_text(:delete, _('Remove'), '#', class: 'delete-highlight', data: {confirm: _('Are you sure you want to remove this highlight')})}</td> 29 <td>#{button_without_text(:delete, _('Remove'), '#', class: 'delete-highlight', data: {confirm: _('Are you sure you want to remove this highlight')})}</td>
30 </tr> 30 </tr>
31 - " 31 + ".html_safe
32 end 32 end
33 33
34 end 34 end
app/helpers/blog_helper.rb
@@ -41,12 +41,12 @@ module BlogHelper @@ -41,12 +41,12 @@ module BlogHelper
41 css_add << position 41 css_add << position
42 content << (content_tag 'div', id: "post-#{art.id}", class: css_add do 42 content << (content_tag 'div', id: "post-#{art.id}", class: css_add do
43 content_tag 'div', class: position + '-inner blog-post-inner' do 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 '<br style="clear:both"/>'.html_safe 45 '<br style="clear:both"/>'.html_safe
46 end 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 end 50 end
51 51
52 def display_post(article, format = 'full') 52 def display_post(article, format = 'full')
@@ -61,7 +61,8 @@ module BlogHelper @@ -61,7 +61,8 @@ module BlogHelper
61 else 61 else
62 '<div class="post-pic" style="background-image:url('+img+')"></div>' 62 '<div class="post-pic" style="background-image:url('+img+')"></div>'
63 end 63 end
64 - end.to_s + title + html 64 + end.to_s.html_safe +
  65 + title.html_safe + html
65 end 66 end
66 67
67 def display_compact_format(article) 68 def display_compact_format(article)
app/helpers/box_organizer_helper.rb
@@ -38,7 +38,7 @@ module BoxOrganizerHelper @@ -38,7 +38,7 @@ module BoxOrganizerHelper
38 content_tag(:ul, 38 content_tag(:ul,
39 images_path.map do |preview| 39 images_path.map do |preview|
40 content_tag(:li, image_tag(preview, height: '240', alt: '')) 40 content_tag(:li, image_tag(preview, height: '240', alt: ''))
41 - end.join("\n") 41 + end.join("\n").html_safe
42 ) 42 )
43 end 43 end
44 44
app/helpers/boxes_helper.rb
@@ -34,7 +34,7 @@ module BoxesHelper @@ -34,7 +34,7 @@ module BoxesHelper
34 34
35 def display_boxes_editor(holder) 35 def display_boxes_editor(holder)
36 with_box_decorator self do 36 with_box_decorator self do
37 - content_tag('div', display_boxes(holder, '&lt;' + _('Main content') + '&gt;'), :id => 'box-organizer') 37 + content_tag('div', display_boxes(holder, '<' + _('Main content') + '>'), :id => 'box-organizer')
38 end 38 end
39 end 39 end
40 40
@@ -44,7 +44,7 @@ module BoxesHelper @@ -44,7 +44,7 @@ module BoxesHelper
44 44
45 def display_boxes(holder, main_content) 45 def display_boxes(holder, main_content)
46 boxes = holder.boxes.with_position.first(boxes_limit(holder)) 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 content = main_content if (content.blank?) 48 content = main_content if (content.blank?)
49 49
50 content_tag('div', content, :class => 'boxes', :id => 'boxes' ) 50 content_tag('div', content, :class => 'boxes', :id => 'boxes' )
@@ -52,9 +52,9 @@ module BoxesHelper @@ -52,9 +52,9 @@ module BoxesHelper
52 52
53 def maybe_display_custom_element(holder, element, options = {}) 53 def maybe_display_custom_element(holder, element, options = {})
54 if holder.respond_to?(element) 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 else 56 else
57 - '' 57 + ''.html_safe
58 end 58 end
59 end 59 end
60 60
@@ -64,15 +64,16 @@ module BoxesHelper @@ -64,15 +64,16 @@ module BoxesHelper
64 64
65 def display_updated_box(box) 65 def display_updated_box(box)
66 with_box_decorator self do 66 with_box_decorator self do
67 - display_box_content(box, '&lt;' + _('Main content') + '&gt;') 67 + display_box_content(box, '<' + _('Main content') + '>')
68 end 68 end
69 end 69 end
70 70
71 def display_box_content(box, main_content) 71 def display_box_content(box, main_content)
72 context = { :article => @page, :request_path => request.path, :locale => locale, :params => request.params, :user => user, :controller => controller } 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 display_block item, main_content 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 end 77 end
77 78
78 def select_blocks box, arr, context 79 def select_blocks box, arr, context
@@ -136,17 +137,18 @@ module BoxesHelper @@ -136,17 +137,18 @@ module BoxesHelper
136 137
137 result = filter_html(result, block) 138 result = filter_html(result, block)
138 139
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) 140 + join_result = safe_join([result, footer_content, box_decorator.block_edit_buttons(block)])
  141 + content_tag_inner_1 = content_tag('div', join_result, :class => 'block-inner-2')
  142 +
  143 + content_tag_inner_2 = content_tag('div', content_tag_inner_1, :class => 'block-inner-1')
  144 + content_tag_inner_3 = content_tag('div', content_tag_inner_2, options)
  145 + content_tag_inner_4 = box_decorator.block_target(block.box, block) + content_tag_inner_3
  146 + c = content_tag('div', content_tag_inner_4, :class => 'block-outer')
  147 + box_decorator_result = box_decorator.block_handle(block)
  148 + result_final = safe_join([c, box_decorator_result], "")
  149 +
  150 +
  151 + return result_final
150 end 152 end
151 153
152 def wrap_main_content(content) 154 def wrap_main_content(content)
@@ -156,17 +158,17 @@ module BoxesHelper @@ -156,17 +158,17 @@ module BoxesHelper
156 def extract_block_content(content) 158 def extract_block_content(content)
157 case content 159 case content
158 when Hash 160 when Hash
159 - content_tag('iframe', '', :src => url_for(content)) 161 + content_tag('iframe', ''.html_safe, :src => url_for(content))
160 when String 162 when String
161 if content.split("\n").size == 1 and content =~ /^https?:\/\// 163 if content.split("\n").size == 1 and content =~ /^https?:\/\//
162 - content_tag('iframe', '', :src => content) 164 + content_tag('iframe', ''.html_safe, :src => content)
163 else 165 else
164 content 166 content
165 end 167 end
166 when Proc 168 when Proc
167 self.instance_eval(&content) 169 self.instance_eval(&content)
168 when NilClass 170 when NilClass
169 - '' 171 + ''.html_safe
170 else 172 else
171 raise "Unsupported content for block (#{content.class})" 173 raise "Unsupported content for block (#{content.class})"
172 end 174 end
@@ -175,14 +177,14 @@ module BoxesHelper @@ -175,14 +177,14 @@ module BoxesHelper
175 module DontMoveBlocks 177 module DontMoveBlocks
176 # does nothing 178 # does nothing
177 def self.block_target(box, block = nil) 179 def self.block_target(box, block = nil)
178 - '' 180 + ''.html_safe
179 end 181 end
180 # does nothing 182 # does nothing
181 def self.block_handle(block) 183 def self.block_handle(block)
182 - '' 184 + ''.html_safe
183 end 185 end
184 def self.block_edit_buttons(block) 186 def self.block_edit_buttons(block)
185 - '' 187 + ''.html_safe
186 end 188 end
187 def self.select_blocks box, arr, context 189 def self.select_blocks box, arr, context
188 arr = arr.select{ |block| block.visible? context } 190 arr = arr.select{ |block| block.visible? context }
@@ -229,9 +231,9 @@ module BoxesHelper @@ -229,9 +231,9 @@ module BoxesHelper
229 # makes the given block draggable so it can be moved away. 231 # makes the given block draggable so it can be moved away.
230 def block_handle(block) 232 def block_handle(block)
231 return "" unless movable?(block) 233 return "" unless movable?(block)
232 - icon = "<div><div>#{display_icon(block.class)}</div><span>#{_(block.class.pretty_name)}</span></div>" 234 + icon = "<div><div>#{display_icon(block.class)}</div><span>#{_(block.class.pretty_name)}</span></div>".html_safe
233 block_draggable("block-#{block.id}", 235 block_draggable("block-#{block.id}",
234 - :helper => "function() {return cloneDraggableBlock($(this), '#{icon}')}") 236 + :helper => "function() {return cloneDraggableBlock($(this), '#{icon}')}".html_safe)
235 end 237 end
236 238
237 def block_draggable(element_id, options={}) 239 def block_draggable(element_id, options={})
@@ -302,7 +304,7 @@ module BoxesHelper @@ -302,7 +304,7 @@ module BoxesHelper
302 buttons << modal_inline_icon(:embed, _('Embed code'), {}, "#embed-code-box-#{block.id}") << html 304 buttons << modal_inline_icon(:embed, _('Embed code'), {}, "#embed-code-box-#{block.id}") << html
303 end 305 end
304 306
305 - content_tag('div', buttons.join("\n") + tag('br', :style => 'clear: left'), :class => 'button-bar') 307 + content_tag('div', buttons.join("\n").html_safe + tag('br', :style => 'clear: left'), :class => 'button-bar')
306 end 308 end
307 309
308 def current_blocks 310 def current_blocks
app/helpers/buttons_helper.rb
1 module ButtonsHelper 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 def button(type, label, url, html_options = {}) 15 def button(type, label, url, html_options = {})
3 html_options ||= {} 16 html_options ||= {}
4 the_class = 'with-text' 17 the_class = 'with-text'
@@ -15,9 +28,9 @@ module ButtonsHelper @@ -15,9 +28,9 @@ module ButtonsHelper
15 end 28 end
16 the_title = html_options[:title] || label 29 the_title = html_options[:title] || label
17 if html_options[:disabled] 30 if html_options[:disabled]
18 - content_tag('a', '&nbsp;'+content_tag('span', label), html_options.merge(:class => the_class, :title => the_title)) 31 + content_tag('a', '&nbsp;'.html_safe+content_tag('span', label), html_options.merge(:class => the_class, :title => the_title))
19 else 32 else
20 - link_to('&nbsp;'+content_tag('span', label), url, html_options.merge(:class => the_class, :title => the_title)) 33 + link_to('&nbsp;'.html_safe+content_tag('span', label), url, html_options.merge(:class => the_class, :title => the_title))
21 end 34 end
22 end 35 end
23 36
app/helpers/categories_helper.rb
@@ -2,7 +2,6 @@ module CategoriesHelper @@ -2,7 +2,6 @@ module CategoriesHelper
2 2
3 TYPES = [ 3 TYPES = [
4 [ _('General Category'), Category.to_s ], 4 [ _('General Category'), Category.to_s ],
5 - [ _('Product Category'), ProductCategory.to_s ],  
6 [ _('Region'), Region.to_s ], 5 [ _('Region'), Region.to_s ],
7 ] 6 ]
8 7
@@ -20,7 +19,7 @@ module CategoriesHelper @@ -20,7 +19,7 @@ module CategoriesHelper
20 def selected_category_link(cat) 19 def selected_category_link(cat)
21 js_remove = "jQuery('#selected-category-#{cat.id}').remove();" 20 js_remove = "jQuery('#selected-category-#{cat.id}').remove();"
22 content_tag('div', button_to_function_without_text(:remove, _('Remove'), js_remove) + 21 content_tag('div', button_to_function_without_text(:remove, _('Remove'), js_remove) +
23 - link_to_function(cat.full_name(' &rarr; '), js_remove, :id => "remove-selected-category-#{cat.id}-button", :class => 'select-subcategory-link'), 22 + link_to_function(cat.full_name(' &rarr; ').html_safe, js_remove, :id => "remove-selected-category-#{cat.id}-button", :class => 'select-subcategory-link'),
24 :class => 'selected-category' 23 :class => 'selected-category'
25 ) 24 )
26 end 25 end
app/helpers/comment_helper.rb
@@ -66,7 +66,7 @@ module CommentHelper @@ -66,7 +66,7 @@ module CommentHelper
66 66
67 def link_for_edit(comment) 67 def link_for_edit(comment)
68 if comment.can_be_updated_by?(user) 68 if comment.can_be_updated_by?(user)
69 - {:link => expirable_comment_link(comment, :edit, _('Edit'), url_for(:profile => profile.identifier, :controller => :comment, :action => :edit, :id => comment.id),:class => 'modal')} 69 + {:link => expirable_comment_link(comment, :edit, _('Edit'), url_for(:profile => profile.identifier, :controller => :comment, :action => :edit, :id => comment.id), :modal => true)}
70 end 70 end
71 end 71 end
72 72
app/helpers/content_viewer_helper.rb
@@ -7,7 +7,8 @@ module ContentViewerHelper @@ -7,7 +7,8 @@ module ContentViewerHelper
7 def display_number_of_comments(n) 7 def display_number_of_comments(n)
8 base_str = "<span class='comment-count hide'>#{n}</span>" 8 base_str = "<span class='comment-count hide'>#{n}</span>"
9 amount_str = n == 0 ? _('no comments yet') : (n == 1 ? _('One comment') : _('%s comments') % n) 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 end 12 end
12 13
13 def number_of_comments(article) 14 def number_of_comments(article)
@@ -19,16 +20,16 @@ module ContentViewerHelper @@ -19,16 +20,16 @@ module ContentViewerHelper
19 title = content_tag('h1', h(title), :class => 'title') 20 title = content_tag('h1', h(title), :class => 'title')
20 if article.belongs_to_blog? || article.belongs_to_forum? 21 if article.belongs_to_blog? || article.belongs_to_forum?
21 unless args[:no_link] 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 end 24 end
24 comments = '' 25 comments = ''
25 unless args[:no_comments] || !article.accept_comments 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 end 28 end
28 date_format = show_with_right_format_date article 29 date_format = show_with_right_format_date article
29 title << content_tag('span', 30 title << content_tag('span',
30 date_format + 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 content_tag('span', comments, :class => 'comments'), 33 content_tag('span', comments, :class => 'comments'),
33 :class => 'publishing-info' 34 :class => 'publishing-info'
34 ) 35 )
app/helpers/display_helper.rb
1 module DisplayHelper 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 def themed_path(file) 3 def themed_path(file)
12 if File.exists?(File.join(Rails.root, 'public', theme_path, file)) 4 if File.exists?(File.join(Rails.root, 'public', theme_path, file))
13 File.join(theme_path, file) 5 File.join(theme_path, file)
@@ -16,55 +8,35 @@ module DisplayHelper @@ -16,55 +8,35 @@ module DisplayHelper
16 end 8 end
17 end 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 def price_span(price, options = {}) 11 def price_span(price, options = {})
28 content_tag 'span', 12 content_tag 'span',
29 number_to_currency(price, :unit => environment.currency_unit, :delimiter => environment.currency_delimiter, :separator => environment.currency_separator), 13 number_to_currency(price, :unit => environment.currency_unit, :delimiter => environment.currency_delimiter, :separator => environment.currency_separator),
30 options 14 options
31 end 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 def link_to_tag(tag, html_options = {}) 17 def link_to_tag(tag, html_options = {})
38 link_to tag.name, {:controller => 'search', :action => 'tag', :tag => tag.name}, html_options 18 link_to tag.name, {:controller => 'search', :action => 'tag', :tag => tag.name}, html_options
39 end 19 end
40 20
41 def link_to_category(category, full = true, html_options = {}) 21 def link_to_category(category, full = true, html_options = {})
42 - return _('Uncategorized product') unless category  
43 name = full ? category.full_name(' &rarr; ') : category.name 22 name = full ? category.full_name(' &rarr; ') : category.name
44 link_to name, Noosfero.url_options.merge({:controller => 'search', :action => 'category_index', :category_path => category.path.split('/'),:host => category.environment.default_hostname }), html_options 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 end 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 def txt2html(txt) 26 def txt2html(txt)
56 - txt.strip. 27 + ret = txt.strip.
57 gsub( /\s*\n\s*\n\s*/, "\r<p/>\r" ). 28 gsub( /\s*\n\s*\n\s*/, "\r<p/>\r" ).
58 gsub( /\s*\n\s*/, "\n<br/>\n" ). 29 gsub( /\s*\n\s*/, "\n<br/>\n" ).
59 gsub( /\r/, "\n" ). 30 gsub( /\r/, "\n" ).
60 gsub( /(^|\s)(www\.[^\s]+|https?:\/\/[^\s]+)/ ) do 31 gsub( /(^|\s)(www\.[^\s]+|https?:\/\/[^\s]+)/ ) do
61 pre_char, href = $1, $2 32 pre_char, href = $1, $2
62 href = 'http://'+href if ! href.match /^https?:/ 33 href = 'http://'+href if ! href.match /^https?:/
63 - content = href.gsub(/^https?:\/\//, '').scan(/.{1,4}/).join('&#x200B;') 34 + content = safe_join(href.gsub(/^https?:\/\//, '').scan(/.{1,4}/), '&#x200B;'.html_safe)
64 pre_char + 35 pre_char +
65 content_tag(:a, content, :href => href, :target => '_blank', 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 _('Are you sure you want to visit this web site?')) 38 _('Are you sure you want to visit this web site?'))
68 end 39 end
  40 + ret.html_safe
69 end 41 end
70 end 42 end
app/helpers/events_helper.rb
1 module EventsHelper 1 module EventsHelper
2 2
3 include DatesHelper 3 include DatesHelper
  4 + include ActionView::Helpers::OutputSafetyHelper
  5 +
4 def list_events(date, events) 6 def list_events(date, events)
5 title = _('Events for %s') % show_date_month(date) 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 content_tag('h2', title) + 10 content_tag('h2', title) +
7 content_tag('div', 11 content_tag('div',
8 (events.any? ? 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 ), :id => 'agenda-items' 15 ), :id => 'agenda-items'
12 ) 16 )
13 end 17 end
app/helpers/forms_helper.rb
@@ -101,7 +101,7 @@ module FormsHelper @@ -101,7 +101,7 @@ module FormsHelper
101 101
102 def required_fields_message 102 def required_fields_message
103 content_tag('p', content_tag('span', 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 :class => 'required-field' 105 :class => 'required-field'
106 )) 106 ))
107 end 107 end
@@ -112,10 +112,11 @@ module FormsHelper @@ -112,10 +112,11 @@ module FormsHelper
112 options_for_select = container.inject([]) do |options, element| 112 options_for_select = container.inject([]) do |options, element|
113 text, value = option_text_and_value(element) 113 text, value = option_text_and_value(element)
114 selected_attribute = ' selected="selected"' if option_value_selected?(value, selected) 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 end 117 end
117 118
118 - options_for_select.join("\n") 119 + safe_join(options_for_select, "\n")
119 end 120 end
120 121
121 def balanced_table(items, per_row=3) 122 def balanced_table(items, per_row=3)
@@ -248,8 +249,8 @@ module FormsHelper @@ -248,8 +249,8 @@ module FormsHelper
248 def date_range_field(from_name, to_name, from_value, to_value, datepicker_options = {}, html_options = {}) 249 def date_range_field(from_name, to_name, from_value, to_value, datepicker_options = {}, html_options = {})
249 from_id = html_options[:from_id] || 'datepicker-from-date' 250 from_id = html_options[:from_id] || 'datepicker-from-date'
250 to_id = html_options[:to_id] || 'datepicker-to-date' 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 end 254 end
254 255
255 def select_folder(label_text, field_id, collection, default_value=nil, html_options = {}, js_options = {}) 256 def select_folder(label_text, field_id, collection, default_value=nil, html_options = {}, js_options = {})
app/helpers/forum_helper.rb
@@ -35,7 +35,7 @@ module ForumHelper @@ -35,7 +35,7 @@ module ForumHelper
35 :id => "post-#{art.id}" 35 :id => "post-#{art.id}"
36 ) 36 )
37 } 37 }
38 - content_tag('table', content.join) + (pagination or '') 38 + content_tag('table', safe_join(content, "")) + (pagination or '').html_safe
39 end 39 end
40 40
41 def last_topic_update(article) 41 def last_topic_update(article)
app/helpers/language_helper.rb
@@ -40,7 +40,7 @@ module LanguageHelper @@ -40,7 +40,7 @@ module LanguageHelper
40 else 40 else
41 link_to(name, params.merge(:lang => code), :rel => 'nofollow') 41 link_to(name, params.merge(:lang => code), :rel => 'nofollow')
42 end 42 end
43 - end.join(separator) 43 + end.join(separator).html_safe
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.')) 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 end 45 end
46 end 46 end
app/helpers/layout_helper.rb
@@ -40,7 +40,8 @@ module LayoutHelper @@ -40,7 +40,8 @@ module LayoutHelper
40 40
41 output += templete_javascript_ng.to_s 41 output += templete_javascript_ng.to_s
42 42
43 - output 43 + # This output should be safe!
  44 + output.html_safe
44 end 45 end
45 46
46 def noosfero_stylesheets 47 def noosfero_stylesheets
@@ -64,7 +65,9 @@ module LayoutHelper @@ -64,7 +65,9 @@ module LayoutHelper
64 output << stylesheet_link_tag(global_css_pub) 65 output << stylesheet_link_tag(global_css_pub)
65 end 66 end
66 output << stylesheet_link_tag(theme_stylesheet_path) 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 end 71 end
69 72
70 def noosfero_layout_features 73 def noosfero_layout_features
@@ -94,9 +97,7 @@ module LayoutHelper @@ -94,9 +97,7 @@ module LayoutHelper
94 end 97 end
95 98
96 def addthis_javascript 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 end 101 end
101 102
102 end 103 end
app/helpers/macros_helper.rb
@@ -32,7 +32,7 @@ module MacrosHelper @@ -32,7 +32,7 @@ module MacrosHelper
32 } 32 }
33 }); 33 });
34 }" 34 }"
35 - end 35 + end.html_safe
36 end 36 end
37 37
38 def include_macro_js_files 38 def include_macro_js_files
app/helpers/memberships_helper.rb
1 module MembershipsHelper 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 end 17 end
app/helpers/partials_helper.rb
@@ -29,14 +29,49 @@ module PartialsHelper @@ -29,14 +29,49 @@ module PartialsHelper
29 raise ArgumentError, 'No partial for object. Is there a partial for any class in the inheritance hierarchy?' 29 raise ArgumentError, 'No partial for object. Is there a partial for any class in the inheritance hierarchy?'
30 end 30 end
31 31
32 - def render_partial_for_class klass, *args 32 +
  33 + def partial_for_class(klass, prefix=nil, suffix=nil)
33 raise ArgumentError, 'No partial for object. Is there a partial for any class in the inheritance hierarchy?' if klass.nil? 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 begin 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 rescue ActionView::MissingTemplate 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 end 75 end
41 end 76 end
42 77
app/helpers/profile_editor_helper.rb
@@ -129,7 +129,11 @@ module ProfileEditorHelper @@ -129,7 +129,11 @@ module ProfileEditorHelper
129 else 129 else
130 domains = environment.domains 130 domains = environment.domains
131 end 131 end
132 - labelled_form_field(_('Preferred domain name:'), select(object, :preferred_domain_id, domains.map {|item| [item.name, item.id]}, :prompt => '&lt;' + _('Select domain') + '&gt;')) 132 + select_domain_prompt = '&lt;'.html_safe + _('Select domain').html_safe + '&gt;'.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 end 137 end
134 138
135 def control_panel(&block) 139 def control_panel(&block)
app/helpers/profile_helper.rb
@@ -84,7 +84,7 @@ module ProfileHelper @@ -84,7 +84,7 @@ module ProfileHelper
84 entries.map do |entry| 84 entries.map do |entry|
85 content = self.send("treat_#{field}", entry) 85 content = self.send("treat_#{field}", entry)
86 unless content.blank? 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 end 88 end
89 end.join("\n") 89 end.join("\n")
90 end 90 end
app/helpers/profile_image_helper.rb
@@ -61,6 +61,8 @@ module ProfileImageHelper @@ -61,6 +61,8 @@ module ProfileImageHelper
61 image_tag(profile_icon(profile, size), opt ) 61 image_tag(profile_icon(profile, size), opt )
62 end 62 end
63 63
  64 + include MembershipsHelper
  65 +
64 def links_for_balloon(profile) 66 def links_for_balloon(profile)
65 if environment.enabled?(:show_balloon_with_profile_links_when_clicked) 67 if environment.enabled?(:show_balloon_with_profile_links_when_clicked)
66 if profile.kind_of?(Person) 68 if profile.kind_of?(Person)
@@ -76,13 +78,12 @@ module ProfileImageHelper @@ -76,13 +78,12 @@ module ProfileImageHelper
76 {_('Wall') => {:href => url_for(profile.public_profile_url)}}, 78 {_('Wall') => {:href => url_for(profile.public_profile_url)}},
77 {_('Members') => {:href => url_for(:controller => :profile, :action => :members, :profile => profile.identifier)}}, 79 {_('Members') => {:href => url_for(:controller => :profile, :action => :members, :profile => profile.identifier)}},
78 {_('Agenda') => {:href => url_for(:controller => :profile, :action => :events, :profile => profile.identifier)}}, 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 {_('Leave community') => {:href => url_for(profile.leave_url), :class => 'leave-community', :style => 'display: none'}}, 82 {_('Leave community') => {:href => url_for(profile.leave_url), :class => 'leave-community', :style => 'display: none'}},
81 {_('Send an e-mail') => {:href => url_for(:profile => profile.identifier, :controller => 'contact', :action => 'new'), :class => 'send-an-email', :style => 'display: none'}} 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 elsif profile.kind_of?(Enterprise) 85 elsif profile.kind_of?(Enterprise)
84 [ 86 [
85 - {_('Products') => {:href => catalog_path(profile.identifier)}},  
86 {_('Members') => {:href => url_for(:controller => :profile, :action => :members, :profile => profile.identifier)}}, 87 {_('Members') => {:href => url_for(:controller => :profile, :action => :members, :profile => profile.identifier)}},
87 {_('Agenda') => {:href => url_for(:controller => :profile, :action => :events, :profile => profile.identifier)}}, 88 {_('Agenda') => {:href => url_for(:controller => :profile, :action => :events, :profile => profile.identifier)}},
88 {_('Send an e-mail') => {:href => url_for(:profile => profile.identifier, :controller => 'contact', :action => 'new'), :class => 'send-an-email', :style => 'display: none'}}, 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,7 +132,7 @@ module ProfileImageHelper
131 links = links_for_balloon(profile) 132 links = links_for_balloon(profile)
132 content_tag('div', content_tag(tag, 133 content_tag('div', content_tag(tag,
133 (environment.enabled?(:show_balloon_with_profile_links_when_clicked) ? 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 link_to( 136 link_to(
136 content_tag( 'span', profile_image( profile, size ), :class => img_class ) + 137 content_tag( 'span', profile_image( profile, size ), :class => img_class ) +
137 content_tag( 'span', h(name), :class => ( profile.class == Person ? 'fn' : 'org' ) ) + 138 content_tag( 'span', h(name), :class => ( profile.class == Person ? 'fn' : 'org' ) ) +
@@ -139,7 +140,7 @@ module ProfileImageHelper @@ -139,7 +140,7 @@ module ProfileImageHelper
139 profile.url, 140 profile.url,
140 :class => 'profile_link url', 141 :class => 'profile_link url',
141 :help => _('Click on this icon to go to the <b>%s</b>\'s home page') % profile.name, 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 :class => 'vcard'), :class => 'common-profile-list-block') 144 :class => 'vcard'), :class => 'common-profile-list-block')
144 end 145 end
145 end 146 end
app/helpers/sanitize_helper.rb 0 → 100644
@@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
  1 +module SanitizeHelper
  2 +
  3 + def sanitize_html(text, type= :full_sanitize)
  4 + sanitizer(type).sanitize(text, scrubber: permit_scrubber)
  5 + end
  6 +
  7 + def sanitize_link(text)
  8 + sanitizer(:white_list).sanitize(text, scrubber:permit_scrubber)
  9 + end
  10 +
  11 +protected
  12 +
  13 + def permit_scrubber
  14 + scrubber = Rails::Html::PermitScrubber.new
  15 + scrubber.tags = Rails.application.config.action_view.sanitized_allowed_tags
  16 + scrubber.attributes = Rails.application.config.action_view.sanitized_allowed_attributes
  17 + scrubber
  18 + end
  19 +
  20 + def sanitizer type = :full_sanitize
  21 + return HTML::WhiteListSanitizer.new if type == :white_list
  22 + HTML::FullSanitizer.new
  23 + end
  24 +
  25 +end
app/helpers/search_helper.rb
@@ -124,10 +124,10 @@ module SearchHelper @@ -124,10 +124,10 @@ module SearchHelper
124 def filters(asset) 124 def filters(asset)
125 return if !asset 125 return if !asset
126 klass = asset_class(asset) 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 default = klass.respond_to?("default_search_#{name}") ? klass.send("default_search_#{name}".to_s) : nil 128 default = klass.respond_to?("default_search_#{name}") ? klass.send("default_search_#{name}".to_s) : nil
129 select_filter(name, options, default) 129 select_filter(name, options, default)
130 - end.join("\n"), :id => 'search-filters') 130 + end, "\n"), :id => 'search-filters')
131 end 131 end
132 132
133 def assets_menu(selected) 133 def assets_menu(selected)
@@ -137,11 +137,11 @@ module SearchHelper @@ -137,11 +137,11 @@ module SearchHelper
137 # menu. 137 # menu.
138 assets.delete(:events) 138 assets.delete(:events)
139 content_tag('ul', 139 content_tag('ul',
140 - assets.map do |asset| 140 + safe_join(assets.map do |asset|
141 options = {} 141 options = {}
142 options.merge!(:class => 'selected') if selected.to_s == asset.to_s 142 options.merge!(:class => 'selected') if selected.to_s == asset.to_s
143 content_tag('li', asset_link(asset), options) 143 content_tag('li', asset_link(asset), options)
144 - end.join("\n"), 144 + end, "\n"),
145 :id => 'assets-menu') 145 :id => 'assets-menu')
146 end 146 end
147 147
app/helpers/tags_helper.rb
@@ -58,7 +58,7 @@ module TagsHelper @@ -58,7 +58,7 @@ module TagsHelper
58 58
59 if options[:show_count] 59 if options[:show_count]
60 display_count = options[:show_count] ? "<small><sup>(#{count})</sup></small>" : "" 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 else 62 else
63 link_to h(tag) , destination, :style => style, 63 link_to h(tag) , destination, :style => style,
64 :title => n_( 'one item', '%d items', count ) % count 64 :title => n_( 'one item', '%d items', count ) % count
app/helpers/tinymce_helper.rb
@@ -7,7 +7,7 @@ module TinymceHelper @@ -7,7 +7,7 @@ module TinymceHelper
7 output += javascript_include_tag 'tinymce/js/tinymce/jquery.tinymce.min.js' 7 output += javascript_include_tag 'tinymce/js/tinymce/jquery.tinymce.min.js'
8 output += javascript_include_tag 'tinymce.js' 8 output += javascript_include_tag 'tinymce.js'
9 output += include_macro_js_files.to_s 9 output += include_macro_js_files.to_s
10 - output 10 + output.html_safe
11 end 11 end
12 12
13 def tinymce_init_js options = {} 13 def tinymce_init_js options = {}
@@ -37,7 +37,7 @@ module TinymceHelper @@ -37,7 +37,7 @@ module TinymceHelper
37 #cleanup non tinymce options 37 #cleanup non tinymce options
38 options = options.except :mode 38 options = options.except :mode
39 39
40 - "noosfero.tinymce.init(#{options.to_json})" 40 + "noosfero.tinymce.init(#{options.to_json})".html_safe
41 end 41 end
42 42
43 def menubar mode 43 def menubar mode
app/helpers/users_helper.rb
1 module UsersHelper 1 module UsersHelper
2 2
3 - FILTER_TRANSLATION = { 3 + def filter_translation
  4 + {
4 'all_users' => _('All users'), 5 'all_users' => _('All users'),
5 'admin_users' => _('Admin users'), 6 'admin_users' => _('Admin users'),
6 'activated_users' => _('Activated users'), 7 'activated_users' => _('Activated users'),
7 'deactivated_users' => _('Deativated users'), 8 'deactivated_users' => _('Deativated users'),
8 - } 9 + }
  10 + end
9 11
10 def filter_selector(filter, float = 'right') 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 url_params = url_for(params.merge(:filter => 'FILTER')) 14 url_params = url_for(params.merge(:filter => 'FILTER'))
13 onchange = "document.location.href = '#{url_params}'.replace('FILTER', this.value)" 15 onchange = "document.location.href = '#{url_params}'.replace('FILTER', this.value)"
14 select_field = select_tag(:filter, options, :onchange => onchange) 16 select_field = select_tag(:filter, options, :onchange => onchange)
@@ -19,7 +21,7 @@ module UsersHelper @@ -19,7 +21,7 @@ module UsersHelper
19 end 21 end
20 22
21 def users_filter_title(filter) 23 def users_filter_title(filter)
22 - FILTER_TRANSLATION[filter] 24 + filter_translation[filter]
23 end 25 end
24 26
25 end 27 end
app/jobs/activities_counter_cache_job.rb 0 → 100644
@@ -0,0 +1,14 @@ @@ -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
app/jobs/create_thumbnails_job.rb 0 → 100644
@@ -0,0 +1,11 @@ @@ -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
app/mailers/environment_mailing.rb
1 class EnvironmentMailing < Mailing 1 class EnvironmentMailing < Mailing
2 2
  3 + settings_items :recipients_roles, :type => :array
  4 + attr_accessible :recipients_roles
  5 +
3 def recipients(offset=0, limit=100) 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 .joins("LEFT OUTER JOIN mailing_sents m ON (m.mailing_id = #{id} AND m.person_id = profiles.id)") 8 .joins("LEFT OUTER JOIN mailing_sents m ON (m.mailing_id = #{id} AND m.person_id = profiles.id)")
6 .where("m.person_id" => nil) 9 .where("m.person_id" => nil)
7 end 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 def each_recipient 21 def each_recipient
10 offset = 0 22 offset = 0
11 limit = 100 23 limit = 100
app/mailers/mailing.rb
1 require_dependency 'mailing_job' 1 require_dependency 'mailing_job'
2 2
3 -class Mailing < ActiveRecord::Base 3 +class Mailing < ApplicationRecord
4 4
5 acts_as_having_settings :field => :data 5 acts_as_having_settings :field => :data
6 6
app/models/abuse_report.rb
1 -class AbuseReport < ActiveRecord::Base 1 +class AbuseReport < ApplicationRecord
2 2
3 attr_accessible :content, :reason 3 attr_accessible :content, :reason
4 4
app/models/action_tracker_notification.rb
1 -class ActionTrackerNotification < ActiveRecord::Base 1 +class ActionTrackerNotification < ApplicationRecord
2 2
3 belongs_to :profile 3 belongs_to :profile
4 belongs_to :action_tracker, :class_name => 'ActionTracker::Record', :foreign_key => 'action_tracker_id' 4 belongs_to :action_tracker, :class_name => 'ActionTracker::Record', :foreign_key => 'action_tracker_id'
app/models/add_member.rb
@@ -29,16 +29,18 @@ class AddMember &lt; Task @@ -29,16 +29,18 @@ class AddMember &lt; Task
29 end 29 end
30 30
31 def information 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 end 34 end
37 35
38 def accept_details 36 def accept_details
39 true 37 true
40 end 38 end
41 39
  40 + def footer
  41 + true
  42 + end
  43 +
42 def icon 44 def icon
43 {:type => :profile_image, :profile => requestor, :url => requestor.url} 45 {:type => :profile_image, :profile => requestor, :url => requestor.url}
44 end 46 end
@@ -63,4 +65,15 @@ class AddMember &lt; Task @@ -63,4 +65,15 @@ class AddMember &lt; Task
63 suggestion.disable if suggestion 65 suggestion.disable if suggestion
64 end 66 end
65 67
  68 + def task_finished_message
  69 + _("You have been accepted at \"%{target}\" with the profile \"%{requestor}\"") %
  70 + {:target => self.target.name,
  71 + :requestor => self.requestor.name}
  72 + end
  73 +
  74 + def task_cancelled_message
  75 + _("Your request to enter community \"%{target} with the profile \"%{requestor}\" was not accepted. Please contact any profile admin from %{url} for more information.") %
  76 + {:target => self.target.name, :url => self.target.url,
  77 + :requestor => self.requestor.name}
  78 + end
66 end 79 end
app/models/application_record.rb 0 → 100644
@@ -0,0 +1,61 @@ @@ -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 &lt; Task @@ -86,7 +86,7 @@ class ApproveArticle &lt; Task
86 86
87 def information 87 def information
88 if article 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 else 90 else
91 {:message => _("The article was removed.")} 91 {:message => _("The article was removed.")}
92 end 92 end
app/models/article.rb
1 1
2 -class Article < ActiveRecord::Base 2 +class Article < ApplicationRecord
  3 +
  4 + include SanitizeHelper
3 5
4 attr_accessible :name, :body, :abstract, :profile, :tag_list, :parent, 6 attr_accessible :name, :body, :abstract, :profile, :tag_list, :parent,
5 :allow_members_to_edit, :translation_of_id, :language, 7 :allow_members_to_edit, :translation_of_id, :language,
@@ -54,6 +56,7 @@ class Article &lt; ActiveRecord::Base @@ -54,6 +56,7 @@ class Article &lt; ActiveRecord::Base
54 track_actions :create_article, :after_create, :keep_params => [:name, :url, :lead, :first_image], :if => Proc.new { |a| a.is_trackable? && !a.image? } 56 track_actions :create_article, :after_create, :keep_params => [:name, :url, :lead, :first_image], :if => Proc.new { |a| a.is_trackable? && !a.image? }
55 57
56 # xss_terminate plugin can't sanitize array fields 58 # xss_terminate plugin can't sanitize array fields
  59 + # sanitize_tag_list is used with SanitizeHelper
57 before_save :sanitize_tag_list 60 before_save :sanitize_tag_list
58 61
59 before_create do |article| 62 before_create do |article|
@@ -601,7 +604,7 @@ class Article &lt; ActiveRecord::Base @@ -601,7 +604,7 @@ class Article &lt; ActiveRecord::Base
601 end 604 end
602 605
603 def accept_category?(cat) 606 def accept_category?(cat)
604 - !cat.is_a?(ProductCategory) 607 + true
605 end 608 end
606 609
607 def public? 610 def public?
@@ -803,11 +806,13 @@ class Article &lt; ActiveRecord::Base @@ -803,11 +806,13 @@ class Article &lt; ActiveRecord::Base
803 end 806 end
804 807
805 def body_images_paths 808 def body_images_paths
806 - Nokogiri::HTML.fragment(self.body.to_s).css('img[src]').collect do |i| 809 + paths = Nokogiri::HTML.fragment(self.body.to_s).css('img[src]').collect do |i|
807 src = i['src'] 810 src = i['src']
808 src = URI.escape src if self.new_record? # xss_terminate runs on save 811 src = URI.escape src if self.new_record? # xss_terminate runs on save
809 (self.profile && self.profile.environment) ? URI.join(self.profile.environment.top_url, src).to_s : src 812 (self.profile && self.profile.environment) ? URI.join(self.profile.environment.top_url, src).to_s : src
810 end 813 end
  814 + paths.unshift(URI.join(self.profile.environment.top_url, self.image.public_filename).to_s) if self.image.present?
  815 + paths
811 end 816 end
812 817
813 def more_comments_label 818 def more_comments_label
@@ -859,6 +864,10 @@ class Article &lt; ActiveRecord::Base @@ -859,6 +864,10 @@ class Article &lt; ActiveRecord::Base
859 HashWithIndifferentAccess.new :name => name, :abstract => abstract, :body => body, :id => id, :parent_id => parent_id, :author => author 864 HashWithIndifferentAccess.new :name => name, :abstract => abstract, :body => body, :id => id, :parent_id => parent_id, :author => author
860 end 865 end
861 866
  867 + def self.can_display_blocks?
  868 + true
  869 + end
  870 +
862 private 871 private
863 872
864 def sanitize_tag_list 873 def sanitize_tag_list
@@ -870,11 +879,6 @@ class Article &lt; ActiveRecord::Base @@ -870,11 +879,6 @@ class Article &lt; ActiveRecord::Base
870 tag_name.gsub(/[<>]/, '') 879 tag_name.gsub(/[<>]/, '')
871 end 880 end
872 881
873 - def sanitize_html(text)  
874 - sanitizer = HTML::FullSanitizer.new  
875 - sanitizer.sanitize(text)  
876 - end  
877 -  
878 def parent_archived? 882 def parent_archived?
879 if self.parent_id_changed? && self.parent && self.parent.archived? 883 if self.parent_id_changed? && self.parent && self.parent.archived?
880 errors.add(:parent_folder, N_('is archived!!')) 884 errors.add(:parent_folder, N_('is archived!!'))
app/models/article_categorization.rb
1 -class ArticleCategorization < ActiveRecord::Base 1 +class ArticleCategorization < ApplicationRecord
2 self.table_name = :articles_categories 2 self.table_name = :articles_categories
3 3
4 belongs_to :article 4 belongs_to :article
app/models/article_follower.rb
1 -class ArticleFollower < ActiveRecord::Base 1 +class ArticleFollower < ApplicationRecord
2 2
3 attr_accessible :article_id, :person_id 3 attr_accessible :article_id, :person_id
4 belongs_to :article, :counter_cache => :followers_count 4 belongs_to :article, :counter_cache => :followers_count
app/models/block.rb
1 -class Block < ActiveRecord::Base 1 +class Block < ApplicationRecord
2 2
3 attr_accessible :title, :subtitle, :display, :limit, :box_id, :posts_per_page, 3 attr_accessible :title, :subtitle, :display, :limit, :box_id, :posts_per_page,
4 :visualization_format, :language, :display_user, 4 :visualization_format, :language, :display_user,
@@ -76,6 +76,17 @@ class Block &lt; ActiveRecord::Base @@ -76,6 +76,17 @@ class Block &lt; ActiveRecord::Base
76 true 76 true
77 end 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 def display_to_user?(user) 90 def display_to_user?(user)
80 display_user == 'all' || (user.nil? && display_user == 'not_logged') || (user && display_user == 'logged') || (user && display_user == 'followers' && user.follows?(owner)) 91 display_user == 'all' || (user.nil? && display_user == 'not_logged') || (user && display_user == 'logged') || (user && display_user == 'followers' && user.follows?(owner))
81 end 92 end
@@ -314,6 +325,14 @@ class Block &lt; ActiveRecord::Base @@ -314,6 +325,14 @@ class Block &lt; ActiveRecord::Base
314 self.observers << block 325 self.observers << block
315 end 326 end
316 327
  328 + def api_content
  329 + nil
  330 + end
  331 +
  332 + def display_api_content_by_default?
  333 + false
  334 + end
  335 +
317 private 336 private
318 337
319 def home_page_path 338 def home_page_path
app/models/box.rb
1 -class Box < ActiveRecord::Base 1 +class Box < ApplicationRecord
2 2
3 acts_as_list scope: -> box { where owner_id: box.owner_id, owner_type: box.owner_type } 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 &lt; ActiveRecord::Base @@ -41,7 +41,6 @@ class Box &lt; ActiveRecord::Base
41 ProfileImageBlock, 41 ProfileImageBlock,
42 RawHTMLBlock, 42 RawHTMLBlock,
43 RecentDocumentsBlock, 43 RecentDocumentsBlock,
44 - SellersSearchBlock,  
45 TagsBlock ] 44 TagsBlock ]
46 end 45 end
47 46
@@ -54,21 +53,17 @@ class Box &lt; ActiveRecord::Base @@ -54,21 +53,17 @@ class Box &lt; ActiveRecord::Base
54 EnterprisesBlock, 53 EnterprisesBlock,
55 FansBlock, 54 FansBlock,
56 FavoriteEnterprisesBlock, 55 FavoriteEnterprisesBlock,
57 - FeaturedProductsBlock,  
58 FeedReaderBlock, 56 FeedReaderBlock,
59 HighlightsBlock, 57 HighlightsBlock,
60 LinkListBlock, 58 LinkListBlock,
61 LocationBlock, 59 LocationBlock,
62 LoginBlock, 60 LoginBlock,
63 MyNetworkBlock, 61 MyNetworkBlock,
64 - ProductsBlock,  
65 - ProductCategoriesBlock,  
66 ProfileImageBlock, 62 ProfileImageBlock,
67 ProfileInfoBlock, 63 ProfileInfoBlock,
68 ProfileSearchBlock, 64 ProfileSearchBlock,
69 RawHTMLBlock, 65 RawHTMLBlock,
70 RecentDocumentsBlock, 66 RecentDocumentsBlock,
71 - SellersSearchBlock,  
72 SlideshowBlock, 67 SlideshowBlock,
73 TagsBlock 68 TagsBlock
74 ] 69 ]
app/models/category.rb
1 -class Category < ActiveRecord::Base 1 +class Category < ApplicationRecord
2 2
3 attr_accessible :name, :parent_id, :display_color, :display_in_menu, :image_builder, :environment, :parent 3 attr_accessible :name, :parent_id, :display_color, :display_in_menu, :image_builder, :environment, :parent
4 4
@@ -35,8 +35,6 @@ class Category &lt; ActiveRecord::Base @@ -35,8 +35,6 @@ class Category &lt; ActiveRecord::Base
35 has_many :people, :through => :profile_categorizations, :source => :profile, :class_name => 'Person' 35 has_many :people, :through => :profile_categorizations, :source => :profile, :class_name => 'Person'
36 has_many :communities, :through => :profile_categorizations, :source => :profile, :class_name => 'Community' 36 has_many :communities, :through => :profile_categorizations, :source => :profile, :class_name => 'Community'
37 37
38 - has_many :products, :through => :enterprises  
39 -  
40 acts_as_having_image 38 acts_as_having_image
41 39
42 before_save :normalize_display_color 40 before_save :normalize_display_color
@@ -64,10 +62,6 @@ class Category &lt; ActiveRecord::Base @@ -64,10 +62,6 @@ class Category &lt; ActiveRecord::Base
64 self.communities.reorder('created_at DESC, id DESC').paginate(page: 1, per_page: limit) 62 self.communities.reorder('created_at DESC, id DESC').paginate(page: 1, per_page: limit)
65 end 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 def recent_articles(limit = 10) 65 def recent_articles(limit = 10)
72 self.articles.recent(limit) 66 self.articles.recent(limit)
73 end 67 end
app/models/chat_message.rb
1 -class ChatMessage < ActiveRecord::Base 1 +class ChatMessage < ApplicationRecord
  2 +
2 attr_accessible :body, :from, :to 3 attr_accessible :body, :from, :to
3 4
4 belongs_to :to, :class_name => 'Profile' 5 belongs_to :to, :class_name => 'Profile'
app/models/comment.rb
1 -class Comment < ActiveRecord::Base 1 +class Comment < ApplicationRecord
2 2
3 SEARCHABLE_FIELDS = { 3 SEARCHABLE_FIELDS = {
4 :title => {:label => _('Title'), :weight => 10}, 4 :title => {:label => _('Title'), :weight => 10},
app/models/community.rb
@@ -2,6 +2,7 @@ class Community &lt; Organization @@ -2,6 +2,7 @@ class Community &lt; Organization
2 2
3 attr_accessible :accessor_id, :accessor_type, :role_id, :resource_id, :resource_type 3 attr_accessible :accessor_id, :accessor_type, :role_id, :resource_id, :resource_type
4 attr_accessible :address_reference, :district, :tag_list, :language, :description 4 attr_accessible :address_reference, :district, :tag_list, :language, :description
  5 + attr_accessible :requires_email
5 after_destroy :check_invite_member_for_destroy 6 after_destroy :check_invite_member_for_destroy
6 7
7 def self.type_name 8 def self.type_name
@@ -12,6 +13,9 @@ class Community &lt; Organization @@ -12,6 +13,9 @@ class Community &lt; Organization
12 N_('Language') 13 N_('Language')
13 14
14 settings_items :language 15 settings_items :language
  16 + settings_items :requires_email, :type => :boolean
  17 +
  18 + alias_method :requires_email?, :requires_email
15 19
16 extend SetProfileRegionFromCityState::ClassMethods 20 extend SetProfileRegionFromCityState::ClassMethods
17 set_profile_region_from_city_state 21 set_profile_region_from_city_state
app/models/contact_list.rb
1 -class ContactList < ActiveRecord::Base 1 +class ContactList < ApplicationRecord
2 2
3 serialize :list, Array 3 serialize :list, Array
4 4
app/models/create_community.rb
@@ -60,9 +60,9 @@ class CreateCommunity &lt; Task @@ -60,9 +60,9 @@ class CreateCommunity &lt; Task
60 60
61 def information 61 def information
62 if description.blank? 62 if description.blank?
63 - { :message => _('%{requestor} wants to create community %{subject} with no description.') } 63 + { :message => _('%{requestor} wants to create community %{subject} with no description.').html_safe }
64 else 64 else
65 - { :message => _('%{requestor} wants to create community %{subject} with this description:<p><em>%{description}</em></p>'), 65 + { :message => _('%{requestor} wants to create community %{subject} with this description:<p><em>%{description}</em></p>').html_safe,
66 :variables => {:description => description} } 66 :variables => {:description => description} }
67 end 67 end
68 end 68 end
app/models/create_enterprise.rb
@@ -163,7 +163,7 @@ class CreateEnterprise &lt; Task @@ -163,7 +163,7 @@ class CreateEnterprise &lt; Task
163 end 163 end
164 164
165 def information 165 def information
166 - {:message => _('%{requestor} wants to create enterprise %{subject}.')} 166 + {:message => _('%{requestor} wants to create enterprise %{subject}.').html_safe}
167 end 167 end
168 168
169 def reject_details 169 def reject_details
app/models/custom_field.rb
1 -class CustomField < ActiveRecord::Base 1 +class CustomField < ApplicationRecord
  2 +
2 attr_accessible :name, :default_value, :format, :extras, :customized_type, :active, :required, :signup, :environment, :moderation_task 3 attr_accessible :name, :default_value, :format, :extras, :customized_type, :active, :required, :signup, :environment, :moderation_task
3 serialize :customized_type 4 serialize :customized_type
4 serialize :extras 5 serialize :extras
app/models/custom_field_value.rb
1 -class CustomFieldValue < ActiveRecord::Base 1 +class CustomFieldValue < ApplicationRecord
  2 +
2 belongs_to :custom_field 3 belongs_to :custom_field
3 belongs_to :customized, :polymorphic => true 4 belongs_to :customized, :polymorphic => true
4 attr_accessible :value, :public, :customized, :custom_field, :customized_type 5 attr_accessible :value, :public, :customized, :custom_field, :customized_type
app/models/doc_item.rb
@@ -17,7 +17,7 @@ class DocItem @@ -17,7 +17,7 @@ class DocItem
17 else 17 else
18 match 18 match
19 end 19 end
20 - end 20 + end.html_safe
21 end 21 end
22 22
23 private 23 private
app/models/domain.rb
1 require 'noosfero/multi_tenancy' 1 require 'noosfero/multi_tenancy'
2 2
3 -class Domain < ActiveRecord::Base 3 +class Domain < ApplicationRecord
4 4
5 attr_accessible :name, :owner, :is_default 5 attr_accessible :name, :owner, :is_default
6 6
app/models/email_template.rb
1 -class EmailTemplate < ActiveRecord::Base 1 +class EmailTemplate < ApplicationRecord
2 2
3 belongs_to :owner, :polymorphic => true 3 belongs_to :owner, :polymorphic => true
4 4
app/models/enterprise.rb
1 -# An enterprise is a kind of organization. According to the system concept,  
2 -# only enterprises can offer products and services.  
3 class Enterprise < Organization 1 class Enterprise < Organization
4 2
5 - attr_accessible :business_name, :address_reference, :district, :tag_list, :organization_website, :historic_and_current_context, :activities_short_description, :products_per_catalog_page 3 + attr_accessible :business_name, :address_reference, :district, :tag_list,
  4 + :organization_website, :historic_and_current_context, :activities_short_description
6 5
7 SEARCH_FILTERS = { 6 SEARCH_FILTERS = {
8 :order => %w[more_recent more_popular more_active], 7 :order => %w[more_recent more_popular more_active],
@@ -17,11 +16,6 @@ class Enterprise &lt; Organization @@ -17,11 +16,6 @@ class Enterprise &lt; Organization
17 16
18 acts_as_trackable after_add: proc{ |p, t| notify_activity t } 17 acts_as_trackable after_add: proc{ |p, t| notify_activity t }
19 18
20 - has_many :products, :foreign_key => :profile_id, :dependent => :destroy  
21 - has_many :product_categories, :through => :products  
22 - has_many :inputs, :through => :products  
23 - has_many :production_costs, :as => :owner  
24 -  
25 has_many :favorite_enterprise_people 19 has_many :favorite_enterprise_people
26 has_many :fans, source: :person, through: :favorite_enterprise_people 20 has_many :fans, source: :person, through: :favorite_enterprise_people
27 21
@@ -29,10 +23,6 @@ class Enterprise &lt; Organization @@ -29,10 +23,6 @@ class Enterprise &lt; Organization
29 23
30 settings_items :organization_website, :historic_and_current_context, :activities_short_description 24 settings_items :organization_website, :historic_and_current_context, :activities_short_description
31 25
32 - settings_items :products_per_catalog_page, :type => :integer, :default => 6  
33 - alias_method :products_per_catalog_page_before_type_cast, :products_per_catalog_page  
34 - validates_numericality_of :products_per_catalog_page, :allow_nil => true, :greater_than => 0  
35 -  
36 extend SetProfileRegionFromCityState::ClassMethods 26 extend SetProfileRegionFromCityState::ClassMethods
37 set_profile_region_from_city_state 27 set_profile_region_from_city_state
38 28
@@ -66,10 +56,6 @@ class Enterprise &lt; Organization @@ -66,10 +56,6 @@ class Enterprise &lt; Organization
66 environment ? environment.active_enterprise_fields : [] 56 environment ? environment.active_enterprise_fields : []
67 end 57 end
68 58
69 - def highlighted_products_with_image(options = {})  
70 - Product.where(:highlighted => true).joins(:image)  
71 - end  
72 -  
73 def required_fields 59 def required_fields
74 environment ? environment.required_enterprise_fields : [] 60 environment ? environment.required_enterprise_fields : []
75 end 61 end
@@ -136,19 +122,14 @@ class Enterprise &lt; Organization @@ -136,19 +122,14 @@ class Enterprise &lt; Organization
136 links = [ 122 links = [
137 {:name => _("Enterprises's profile"), :address => '/profile/{profile}', :icon => 'ok'}, 123 {:name => _("Enterprises's profile"), :address => '/profile/{profile}', :icon => 'ok'},
138 {:name => _('Blog'), :address => '/{profile}/blog', :icon => 'edit'}, 124 {:name => _('Blog'), :address => '/{profile}/blog', :icon => 'edit'},
139 - {:name => _('Products'), :address => '/catalog/{profile}', :icon => 'new'},  
140 ] 125 ]
141 blocks = [ 126 blocks = [
142 [MainBlock.new], 127 [MainBlock.new],
143 [ ProfileImageBlock.new, 128 [ ProfileImageBlock.new,
144 LinkListBlock.new(:links => links), 129 LinkListBlock.new(:links => links),
145 - ProductCategoriesBlock.new  
146 ], 130 ],
147 [LocationBlock.new] 131 [LocationBlock.new]
148 ] 132 ]
149 - if environment.enabled?('products_for_enterprises')  
150 - blocks[2].unshift ProductsBlock.new  
151 - end  
152 blocks 133 blocks
153 end 134 end
154 135
@@ -189,14 +170,6 @@ class Enterprise &lt; Organization @@ -189,14 +170,6 @@ class Enterprise &lt; Organization
189 {:title => _('Enterprise Info and settings'), :icon => 'edit-profile-enterprise'} 170 {:title => _('Enterprise Info and settings'), :icon => 'edit-profile-enterprise'}
190 end 171 end
191 172
192 - def create_product?  
193 - true  
194 - end  
195 -  
196 - def catalog_url  
197 - { :profile => identifier, :controller => 'catalog'}  
198 - end  
199 -  
200 def more_recent_label 173 def more_recent_label
201 '' 174 ''
202 end 175 end
@@ -205,5 +178,4 @@ class Enterprise &lt; Organization @@ -205,5 +178,4 @@ class Enterprise &lt; Organization
205 super or self.fans.where(id: person.id).count > 0 178 super or self.fans.where(id: person.id).count > 0
206 end 179 end
207 180
208 -  
209 end 181 end
app/models/environment.rb
1 # A Environment is like a website to be hosted in the platform. It may 1 # A Environment is like a website to be hosted in the platform. It may
2 # contain multiple Profile's and can be identified by several different 2 # contain multiple Profile's and can be identified by several different
3 # domains. 3 # domains.
4 -class Environment < ActiveRecord::Base 4 +class Environment < ApplicationRecord
5 5
6 attr_accessible :name, :is_default, :signup_welcome_text_subject, 6 attr_accessible :name, :is_default, :signup_welcome_text_subject,
7 :signup_welcome_text_body, :terms_of_use, 7 :signup_welcome_text_body, :terms_of_use,
@@ -13,7 +13,9 @@ class Environment &lt; ActiveRecord::Base @@ -13,7 +13,9 @@ class Environment &lt; ActiveRecord::Base
13 :reports_lower_bound, :noreply_email, 13 :reports_lower_bound, :noreply_email,
14 :signup_welcome_screen_body, :members_whitelist_enabled, 14 :signup_welcome_screen_body, :members_whitelist_enabled,
15 :members_whitelist, :highlighted_news_amount, 15 :members_whitelist, :highlighted_news_amount,
16 - :portal_news_amount, :date_format, :signup_intro 16 + :portal_news_amount, :date_format, :signup_intro,
  17 + :enable_feed_proxy, :http_feed_proxy, :https_feed_proxy,
  18 + :disable_feed_ssl
17 19
18 has_many :users 20 has_many :users
19 21
@@ -126,7 +128,6 @@ class Environment &lt; ActiveRecord::Base @@ -126,7 +128,6 @@ class Environment &lt; ActiveRecord::Base
126 'disable_asset_enterprises' => _('Disable search for enterprises'), 128 'disable_asset_enterprises' => _('Disable search for enterprises'),
127 'disable_asset_people' => _('Disable search for people'), 129 'disable_asset_people' => _('Disable search for people'),
128 'disable_asset_communities' => _('Disable search for communities'), 130 'disable_asset_communities' => _('Disable search for communities'),
129 - 'disable_asset_products' => _('Disable search for products'),  
130 'disable_asset_events' => _('Disable search for events'), 131 'disable_asset_events' => _('Disable search for events'),
131 'disable_categories' => _('Disable categories'), 132 'disable_categories' => _('Disable categories'),
132 'disable_header_and_footer' => _('Disable header/footer editing by users'), 133 'disable_header_and_footer' => _('Disable header/footer editing by users'),
@@ -137,7 +138,6 @@ class Environment &lt; ActiveRecord::Base @@ -137,7 +138,6 @@ class Environment &lt; ActiveRecord::Base
137 'disable_contact_community' => _('Disable contact for groups/communities'), 138 'disable_contact_community' => _('Disable contact for groups/communities'),
138 'forbid_destroy_profile' => _('Forbid users of removing profiles'), 139 'forbid_destroy_profile' => _('Forbid users of removing profiles'),
139 140
140 - 'products_for_enterprises' => _('Enable products for enterprises'),  
141 'enterprise_registration' => _('Enterprise registration'), 141 'enterprise_registration' => _('Enterprise registration'),
142 'enterprise_activation' => _('Enable activation of enterprises'), 142 'enterprise_activation' => _('Enable activation of enterprises'),
143 'enterprises_are_disabled_when_created' => _('Enterprises are disabled when created'), 143 'enterprises_are_disabled_when_created' => _('Enterprises are disabled when created'),
@@ -224,7 +224,6 @@ class Environment &lt; ActiveRecord::Base @@ -224,7 +224,6 @@ class Environment &lt; ActiveRecord::Base
224 224
225 has_many :organizations 225 has_many :organizations
226 has_many :enterprises 226 has_many :enterprises
227 - has_many :products, :through => :enterprises  
228 has_many :people 227 has_many :people
229 has_many :communities 228 has_many :communities
230 has_many :licenses 229 has_many :licenses
@@ -234,23 +233,16 @@ class Environment &lt; ActiveRecord::Base @@ -234,23 +233,16 @@ class Environment &lt; ActiveRecord::Base
234 order('display_color').where('display_color is not null and parent_id is null') 233 order('display_color').where('display_color is not null and parent_id is null')
235 }, class_name: 'Category' 234 }, class_name: 'Category'
236 235
237 - has_many :product_categories, -> { where type: 'ProductCategory'}  
238 has_many :regions 236 has_many :regions
239 has_many :states 237 has_many :states
240 has_many :cities 238 has_many :cities
241 239
242 has_many :roles, :dependent => :destroy 240 has_many :roles, :dependent => :destroy
243 241
244 - has_many :qualifiers  
245 - has_many :certifiers  
246 -  
247 has_many :mailings, :class_name => 'EnvironmentMailing', :foreign_key => :source_id, :as => 'source' 242 has_many :mailings, :class_name => 'EnvironmentMailing', :foreign_key => :source_id, :as => 'source'
248 243
249 acts_as_accessible 244 acts_as_accessible
250 245
251 - has_many :units, -> { order 'position' }  
252 - has_many :production_costs, :as => :owner  
253 -  
254 def superior_intances 246 def superior_intances
255 [self, nil] 247 [self, nil]
256 end 248 end
@@ -439,9 +431,7 @@ class Environment &lt; ActiveRecord::Base @@ -439,9 +431,7 @@ class Environment &lt; ActiveRecord::Base
439 end 431 end
440 432
441 DEFAULT_FEATURES = %w( 433 DEFAULT_FEATURES = %w(
442 - disable_asset_products  
443 disable_gender_icon 434 disable_gender_icon
444 - products_for_enterprises  
445 disable_select_city_for_contact 435 disable_select_city_for_contact
446 enterprise_registration 436 enterprise_registration
447 media_panel 437 media_panel
@@ -729,7 +719,7 @@ class Environment &lt; ActiveRecord::Base @@ -729,7 +719,7 @@ class Environment &lt; ActiveRecord::Base
729 url << (Noosfero.url_options.key?(:host) ? Noosfero.url_options[:host] : default_hostname) 719 url << (Noosfero.url_options.key?(:host) ? Noosfero.url_options[:host] : default_hostname)
730 url << ':' << Noosfero.url_options[:port].to_s if Noosfero.url_options.key?(:port) 720 url << ':' << Noosfero.url_options[:port].to_s if Noosfero.url_options.key?(:port)
731 url << Noosfero.root('') 721 url << Noosfero.root('')
732 - url 722 + url.html_safe
733 end 723 end
734 724
735 def to_s 725 def to_s
@@ -966,10 +956,6 @@ class Environment &lt; ActiveRecord::Base @@ -966,10 +956,6 @@ class Environment &lt; ActiveRecord::Base
966 end 956 end
967 end 957 end
968 958
969 - def highlighted_products_with_image(options = {})  
970 - self.products.where(highlighted: true).joins(:image).order('created_at ASC')  
971 - end  
972 -  
973 settings_items :home_cache_in_minutes, :type => :integer, :default => 5 959 settings_items :home_cache_in_minutes, :type => :integer, :default => 5
974 settings_items :general_cache_in_minutes, :type => :integer, :default => 15 960 settings_items :general_cache_in_minutes, :type => :integer, :default => 15
975 settings_items :profile_cache_in_minutes, :type => :integer, :default => 15 961 settings_items :profile_cache_in_minutes, :type => :integer, :default => 15