Commit c4a8f9bfc6095396090061adbfd34b1601e7bea7

Authored by Larissa Reis
2 parents 28beaa39 14b34706

Merge branch 'master' into federation

Updates branch federation with current master
Showing 1204 changed files with 19570 additions and 18872 deletions   Show diff stats

Too many changes.

To preserve performance only 100 of 1204 files displayed.

.gitlab-ci.yml
@@ -30,14 +30,47 @@ integration: @@ -30,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 +
@@ -18,7 +18,7 @@ cache: bundler @@ -18,7 +18,7 @@ cache: bundler
18 18
19 language: ruby 19 language: ruby
20 rvm: 20 rvm:
21 - - 2.3.0 21 + - 2.3.1
22 22
23 addons: 23 addons:
24 apt: 24 apt:
@@ -29,11 +29,6 @@ addons: @@ -29,11 +29,6 @@ addons:
29 paths: 29 paths:
30 - $(ls tmp/artifact* | tr "\n" ":") 30 - $(ls tmp/artifact* | tr "\n" ":")
31 31
32 -# workaround for https://github.com/travis-ci/travis-ci/issues/4536  
33 -before_install:  
34 - - export GEM_HOME=$PWD/vendor/bundle/ruby/2.2.0  
35 - - gem install bundler  
36 -  
37 before_script: 32 before_script:
38 - mkdir -p tmp/{pids,cache} log cache 33 - mkdir -p tmp/{pids,cache} log cache
39 - script/noosfero-plugins disableall 34 - script/noosfero-plugins disableall
@@ -45,21 +40,36 @@ before_script: @@ -45,21 +40,36 @@ before_script:
45 - bundle exec rake db:migrate &>/dev/null 40 - bundle exec rake db:migrate &>/dev/null
46 41
47 env: 42 env:
48 - - TASK=test:api  
49 - - TASK=test:units  
50 - - TASK=test:functionals  
51 - - TASK=test:integration  
52 - - SLICE=1/2 TASK=cucumber LANG=en  
53 - - SLICE=2/2 TASK=cucumber LANG=en  
54 - - SLICE=1/4 TASK=selenium  
55 - - SLICE=2/4 TASK=selenium  
56 - - SLICE=3/4 TASK=selenium  
57 - - SLICE=4/4 TASK=selenium  
58 - - SLICE=1/5 TASK=test:noosfero_plugins BUNDLE_OPTS=install  
59 - - SLICE=2/5 TASK=test:noosfero_plugins BUNDLE_OPTS=install  
60 - - SLICE=3/5 TASK=test:noosfero_plugins BUNDLE_OPTS=install  
61 - - SLICE=4/5 TASK=test:noosfero_plugins BUNDLE_OPTS=install  
62 - - SLICE=5/5 TASK=test:noosfero_plugins BUNDLE_OPTS=install 43 + global:
  44 + - LANG=en
  45 + matrix:
  46 + - TASK=test:api
  47 + - TASK=test:units
  48 + - TASK=test:functionals
  49 + - TASK=test:integration
  50 + - SLICE=1/2 TASK=cucumber
  51 + - SLICE=2/2 TASK=cucumber
  52 + - SLICE=1/4 TASK=selenium
  53 + - SLICE=2/4 TASK=selenium
  54 + - SLICE=3/4 TASK=selenium
  55 + - SLICE=4/4 TASK=selenium
  56 + - SLICE=1/5 TASK=test:noosfero_plugins NOOSFERO_BUNDLE_OPTS=install
  57 + - SLICE=2/5 TASK=test:noosfero_plugins NOOSFERO_BUNDLE_OPTS=install
  58 + - SLICE=3/5 TASK=test:noosfero_plugins NOOSFERO_BUNDLE_OPTS=install
  59 + - SLICE=4/5 TASK=test:noosfero_plugins NOOSFERO_BUNDLE_OPTS=install
  60 + - SLICE=5/5 TASK=test:noosfero_plugins NOOSFERO_BUNDLE_OPTS=install
  61 + # chrome hanging on travis
  62 + #- SLICE=1/4 TASK=selenium SELENIUM_DRIVER=chrome
  63 + #- SLICE=2/4 TASK=selenium SELENIUM_DRIVER=chrome
  64 + #- SLICE=3/4 TASK=selenium SELENIUM_DRIVER=chrome
  65 + #- SLICE=4/4 TASK=selenium SELENIUM_DRIVER=chrome
  66 +
  67 +matrix:
  68 + allow_failures:
  69 + - env: SLICE=1/4 TASK=selenium SELENIUM_DRIVER=chrome
  70 + - env: SLICE=2/4 TASK=selenium SELENIUM_DRIVER=chrome
  71 + - env: SLICE=3/4 TASK=selenium SELENIUM_DRIVER=chrome
  72 + - env: SLICE=4/4 TASK=selenium SELENIUM_DRIVER=chrome
63 73
64 script: 74 script:
65 - 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,423 @@ @@ -0,0 +1,423 @@
  1 +require 'base64'
  2 +require 'tempfile'
  3 +
  4 +module Api
  5 + module Helpers
  6 + PRIVATE_TOKEN_PARAM = :private_token
  7 + DEFAULT_ALLOWED_PARAMETERS = [:parent_id, :from, :until, :content_type, :author_id, :identifier, :archived]
  8 +
  9 + include SanitizeParams
  10 + include Noosfero::Plugin::HotSpot
  11 + include ForgotPasswordHelper
  12 + include SearchTermHelper
  13 +
  14 + def set_locale
  15 + I18n.locale = (params[:lang] || request.env['HTTP_ACCEPT_LANGUAGE'] || 'en')
  16 + end
  17 +
  18 + def init_noosfero_plugins
  19 + plugins
  20 + end
  21 +
  22 + def current_user
  23 + private_token = (params[PRIVATE_TOKEN_PARAM] || headers['Private-Token']).to_s
  24 + @current_user ||= User.find_by private_token: private_token
  25 + @current_user ||= plugins.dispatch("api_custom_login", request).first
  26 + @current_user
  27 + end
  28 +
  29 + def current_person
  30 + current_user.person unless current_user.nil?
  31 + end
  32 +
  33 + def is_admin?(environment)
  34 + return false unless current_user
  35 + return current_person.is_admin?(environment)
  36 + end
  37 +
  38 + def logout
  39 + @current_user = nil
  40 + end
  41 +
  42 + def environment
  43 + @environment
  44 + end
  45 +
  46 + def present_partial(model, options)
  47 + if(params[:fields].present?)
  48 + begin
  49 + fields = JSON.parse(params[:fields])
  50 + if fields.present?
  51 + options.merge!(fields.symbolize_keys.slice(:only, :except))
  52 + end
  53 + rescue
  54 + fields = params[:fields]
  55 + fields = fields.split(',') if fields.kind_of?(String)
  56 + options[:only] = Array.wrap(fields)
  57 + end
  58 + end
  59 + present model, options
  60 + end
  61 +
  62 + include FindByContents
  63 +
  64 + ####################################################################
  65 + #### SEARCH
  66 + ####################################################################
  67 + def multiple_search?(searches=nil)
  68 + ['index', 'category_index'].include?(params[:action]) || (searches && searches.size > 1)
  69 + end
  70 + ####################################################################
  71 +
  72 + def logger
  73 + logger = Logger.new(File.join(Rails.root, 'log', "#{ENV['RAILS_ENV'] || 'production'}_api.log"))
  74 + logger.formatter = GrapeLogging::Formatters::Default.new
  75 + logger
  76 + end
  77 +
  78 + def limit
  79 + limit = params[:limit].to_i
  80 + limit = default_limit if limit <= 0
  81 + limit
  82 + end
  83 +
  84 + def period(from_date, until_date)
  85 + return nil if from_date.nil? && until_date.nil?
  86 +
  87 + begin_period = from_date.nil? ? Time.at(0).to_datetime : from_date
  88 + end_period = until_date.nil? ? DateTime.now : until_date
  89 +
  90 + begin_period..end_period
  91 + end
  92 +
  93 + def parse_content_type(content_type)
  94 + return nil if content_type.blank?
  95 + content_type.split(',').map do |content_type|
  96 + content_type.camelcase
  97 + end
  98 + end
  99 +
  100 + def find_article(articles, id)
  101 + article = articles.find(id)
  102 + article.display_to?(current_person) ? article : forbidden!
  103 + end
  104 +
  105 + def post_article(asset, params)
  106 + return forbidden! unless current_person.can_post_content?(asset)
  107 +
  108 + klass_type = params[:content_type] || params[:article].delete(:type) || TinyMceArticle.name
  109 + return forbidden! unless klass_type.constantize <= Article
  110 +
  111 + article = klass_type.constantize.new(params[:article])
  112 + article.last_changed_by = current_person
  113 + article.created_by= current_person
  114 + article.profile = asset
  115 +
  116 + if !article.save
  117 + render_api_errors!(article.errors.full_messages)
  118 + end
  119 + present_partial article, :with => Entities::Article
  120 + end
  121 +
  122 + def present_article(asset)
  123 + article = find_article(asset.articles, params[:id])
  124 + present_partial article, :with => Entities::Article, :params => params
  125 + end
  126 +
  127 + def present_articles_for_asset(asset, method = 'articles')
  128 + articles = find_articles(asset, method)
  129 + present_articles(articles)
  130 + end
  131 +
  132 + def present_articles(articles)
  133 + present_partial paginate(articles), :with => Entities::Article, :params => params
  134 + end
  135 +
  136 + def find_articles(asset, method = 'articles')
  137 + articles = select_filtered_collection_of(asset, method, params)
  138 + if current_person.present?
  139 + articles = articles.display_filter(current_person, nil)
  140 + else
  141 + articles = articles.published
  142 + end
  143 + articles
  144 + end
  145 +
  146 + def find_task(asset, id)
  147 + task = asset.tasks.find(id)
  148 + current_person.has_permission?(task.permission, asset) ? task : forbidden!
  149 + end
  150 +
  151 + def post_task(asset, params)
  152 + klass_type= params[:content_type].nil? ? 'Task' : params[:content_type]
  153 + return forbidden! unless klass_type.constantize <= Task
  154 +
  155 + task = klass_type.constantize.new(params[:task])
  156 + task.requestor_id = current_person.id
  157 + task.target_id = asset.id
  158 + task.target_type = 'Profile'
  159 +
  160 + if !task.save
  161 + render_api_errors!(task.errors.full_messages)
  162 + end
  163 + present_partial task, :with => Entities::Task
  164 + end
  165 +
  166 + def present_task(asset)
  167 + task = find_task(asset, params[:id])
  168 + present_partial task, :with => Entities::Task
  169 + end
  170 +
  171 + def present_tasks(asset)
  172 + tasks = select_filtered_collection_of(asset, 'tasks', params)
  173 + tasks = tasks.select {|t| current_person.has_permission?(t.permission, asset)}
  174 + return forbidden! if tasks.empty? && !current_person.has_permission?(:perform_task, asset)
  175 + present_partial tasks, :with => Entities::Task
  176 + end
  177 +
  178 + def make_conditions_with_parameter(params = {})
  179 + parsed_params = parser_params(params)
  180 + conditions = {}
  181 + from_date = DateTime.parse(parsed_params.delete(:from)) if parsed_params[:from]
  182 + until_date = DateTime.parse(parsed_params.delete(:until)) if parsed_params[:until]
  183 +
  184 + conditions[:type] = parse_content_type(parsed_params.delete(:content_type)) unless parsed_params[:content_type].nil?
  185 +
  186 + conditions[:created_at] = period(from_date, until_date) if from_date || until_date
  187 + conditions.merge!(parsed_params)
  188 +
  189 + conditions
  190 + end
  191 +
  192 + # changing make_order_with_parameters to avoid sql injection
  193 + def make_order_with_parameters(object, method, params)
  194 + order = "created_at DESC"
  195 + unless params[:order].blank?
  196 + if params[:order].include? '\'' or params[:order].include? '"'
  197 + order = "created_at DESC"
  198 + elsif ['RANDOM()', 'RANDOM'].include? params[:order].upcase
  199 + order = 'RANDOM()'
  200 + else
  201 + field_name, direction = params[:order].split(' ')
  202 + assoc = object.class.reflect_on_association(method.to_sym)
  203 + if !field_name.blank? and assoc
  204 + if assoc.klass.attribute_names.include? field_name
  205 + if direction.present? and ['ASC','DESC'].include? direction.upcase
  206 + order = "#{field_name} #{direction.upcase}"
  207 + end
  208 + end
  209 + end
  210 + end
  211 + end
  212 + return order
  213 + end
  214 +
  215 + def make_timestamp_with_parameters_and_method(params, method)
  216 + timestamp = nil
  217 + if params[:timestamp]
  218 + datetime = DateTime.parse(params[:timestamp])
  219 + table_name = method.to_s.singularize.camelize.constantize.table_name
  220 + timestamp = "#{table_name}.updated_at >= '#{datetime}'"
  221 + end
  222 +
  223 + timestamp
  224 + end
  225 +
  226 + def by_reference(scope, params)
  227 + reference_id = params[:reference_id].to_i == 0 ? nil : params[:reference_id].to_i
  228 + if reference_id.nil?
  229 + scope
  230 + else
  231 + created_at = scope.find(reference_id).created_at
  232 + scope.send("#{params.key?(:oldest) ? 'older_than' : 'younger_than'}", created_at)
  233 + end
  234 + end
  235 +
  236 + def by_categories(scope, params)
  237 + category_ids = params[:category_ids]
  238 + if category_ids.nil?
  239 + scope
  240 + else
  241 + scope.joins(:categories).where(:categories => {:id => category_ids})
  242 + end
  243 + end
  244 +
  245 + def select_filtered_collection_of(object, method, params)
  246 + conditions = make_conditions_with_parameter(params)
  247 + order = make_order_with_parameters(object,method,params)
  248 + timestamp = make_timestamp_with_parameters_and_method(params, method)
  249 +
  250 + objects = object.send(method)
  251 + objects = by_reference(objects, params)
  252 + objects = by_categories(objects, params)
  253 +
  254 + objects = objects.where(conditions).where(timestamp).reorder(order)
  255 +
  256 + params[:page] ||= 1
  257 + params[:per_page] ||= limit
  258 + paginate(objects)
  259 + end
  260 +
  261 + def authenticate!
  262 + unauthorized! unless current_user
  263 + end
  264 +
  265 + def profiles_for_person(profiles, person)
  266 + if person
  267 + profiles.listed_for_person(person)
  268 + else
  269 + profiles.visible
  270 + end
  271 + end
  272 +
  273 + # Checks the occurrences of uniqueness of attributes, each attribute must be present in the params hash
  274 + # or a Bad Request error is invoked.
  275 + #
  276 + # Parameters:
  277 + # keys (unique) - A hash consisting of keys that must be unique
  278 + def unique_attributes!(obj, keys)
  279 + keys.each do |key|
  280 + cant_be_saved_request!(key) if obj.find_by(key.to_s => params[key])
  281 + end
  282 + end
  283 +
  284 + def attributes_for_keys(keys)
  285 + attrs = {}
  286 + keys.each do |key|
  287 + attrs[key] = params[key] if params[key].present? or (params.has_key?(key) and params[key] == false)
  288 + end
  289 + attrs
  290 + end
  291 +
  292 + ##########################################
  293 + # error helpers #
  294 + ##########################################
  295 +
  296 + def not_found!
  297 + render_api_error!('404 Not found', 404)
  298 + end
  299 +
  300 + def forbidden!
  301 + render_api_error!('403 Forbidden', 403)
  302 + end
  303 +
  304 + def cant_be_saved_request!(attribute)
  305 + message = _("(Invalid request) %s can't be saved") % attribute
  306 + render_api_error!(message, 400)
  307 + end
  308 +
  309 + def bad_request!(attribute)
  310 + message = _("(Invalid request) %s not given") % attribute
  311 + render_api_error!(message, 400)
  312 + end
  313 +
  314 + def something_wrong!
  315 + message = _("Something wrong happened")
  316 + render_api_error!(message, 400)
  317 + end
  318 +
  319 + def unauthorized!
  320 + render_api_error!(_('Unauthorized'), 401)
  321 + end
  322 +
  323 + def not_allowed!
  324 + render_api_error!(_('Method Not Allowed'), 405)
  325 + end
  326 +
  327 + # javascript_console_message is supposed to be executed as console.log()
  328 + def render_api_error!(user_message, status, log_message = nil, javascript_console_message = nil)
  329 + message_hash = {'message' => user_message, :code => status}
  330 + message_hash[:javascript_console_message] = javascript_console_message if javascript_console_message.present?
  331 + log_msg = "#{status}, User message: #{user_message}"
  332 + log_msg = "#{log_message}, #{log_msg}" if log_message.present?
  333 + log_msg = "#{log_msg}, Javascript Console Message: #{javascript_console_message}" if javascript_console_message.present?
  334 + logger.error log_msg unless Rails.env.test?
  335 + error!(message_hash, status)
  336 + end
  337 +
  338 + def render_api_errors!(messages)
  339 + messages = messages.to_a if messages.class == ActiveModel::Errors
  340 + render_api_error!(messages.join(','), 400)
  341 + end
  342 +
  343 + protected
  344 +
  345 + def set_session_cookie
  346 + cookies['_noosfero_api_session'] = { value: @current_user.private_token, httponly: true } if @current_user.present?
  347 + end
  348 +
  349 + def setup_multitenancy
  350 + Noosfero::MultiTenancy.setup!(request.host)
  351 + end
  352 +
  353 + def detect_stuff_by_domain
  354 + @domain = Domain.by_name(request.host)
  355 + if @domain.nil?
  356 + @environment = Environment.default
  357 + if @environment.nil? && Rails.env.development?
  358 + # This should only happen in development ...
  359 + @environment = Environment.create!(:name => "Noosfero", :is_default => true)
  360 + end
  361 + else
  362 + @environment = @domain.environment
  363 + end
  364 + end
  365 +
  366 + def filter_disabled_plugins_endpoints
  367 + not_found! if Api::App.endpoint_unavailable?(self, @environment)
  368 + end
  369 +
  370 + def asset_with_image params
  371 + if params.has_key? :image_builder
  372 + asset_api_params = params
  373 + asset_api_params[:image_builder] = base64_to_uploadedfile(asset_api_params[:image_builder])
  374 + return asset_api_params
  375 + end
  376 + params
  377 + end
  378 +
  379 + def base64_to_uploadedfile(base64_image)
  380 + tempfile = base64_to_tempfile base64_image
  381 + converted_image = base64_image
  382 + converted_image[:tempfile] = tempfile
  383 + return {uploaded_data: ActionDispatch::Http::UploadedFile.new(converted_image)}
  384 + end
  385 +
  386 + def base64_to_tempfile base64_image
  387 + base64_img_str = base64_image[:tempfile]
  388 + decoded_base64_str = Base64.decode64(base64_img_str)
  389 + tempfile = Tempfile.new(base64_image[:filename])
  390 + tempfile.write(decoded_base64_str.encode("ascii-8bit").force_encoding("utf-8"))
  391 + tempfile.rewind
  392 + tempfile
  393 + end
  394 + private
  395 +
  396 + def parser_params(params)
  397 + parsed_params = {}
  398 + params.map do |k,v|
  399 + parsed_params[k.to_sym] = v if DEFAULT_ALLOWED_PARAMETERS.include?(k.to_sym)
  400 + end
  401 + parsed_params
  402 + end
  403 +
  404 + def default_limit
  405 + 20
  406 + end
  407 +
  408 + def parse_content_type(content_type)
  409 + return nil if content_type.blank?
  410 + content_types = content_type.split(',').map do |content_type|
  411 + content_type = content_type.camelcase
  412 + content_type == 'TextArticle' ? Article.text_article_types : content_type
  413 + end
  414 + content_types.flatten.uniq
  415 + end
  416 +
  417 + def period(from_date, until_date)
  418 + begin_period = from_date.nil? ? Time.at(0).to_datetime : from_date
  419 + end_period = until_date.nil? ? DateTime.now : until_date
  420 + begin_period..end_period
  421 + end
  422 + end
  423 +end
app/api/v1/activities.rb 0 → 100644
@@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
  1 +module Api
  2 + module V1
  3 + class Activities < Grape::API
  4 +
  5 + resource :profiles do
  6 +
  7 + get ':id/activities' do
  8 + profile = Profile.find_by id: params[:id]
  9 +
  10 + not_found! if profile.blank? || profile.secret || !profile.visible
  11 + forbidden! if !profile.display_private_info_to?(current_person)
  12 +
  13 + activities = profile.activities.map(&:activity)
  14 + present activities, :with => Entities::Activity, :current_person => current_person
  15 + end
  16 + end
  17 + end
  18 + end
  19 +end
app/api/v1/articles.rb 0 → 100644
@@ -0,0 +1,314 @@ @@ -0,0 +1,314 @@
  1 +module Api
  2 + module V1
  3 + class Articles < Grape::API
  4 +
  5 + ARTICLE_TYPES = Article.descendants.map{|a| a.to_s}
  6 +
  7 + MAX_PER_PAGE = 50
  8 +
  9 + resource :articles do
  10 +
  11 + paginate max_per_page: MAX_PER_PAGE
  12 + # Collect articles
  13 + #
  14 + # Parameters:
  15 + # from - date where the search will begin. If nothing is passed the default date will be the date of the first article created
  16 + # oldest - Collect the oldest articles. If nothing is passed the newest articles are collected
  17 + # limit - amount of articles returned. The default value is 20
  18 + #
  19 + # Example Request:
  20 + # GET host/api/v1/articles?from=2013-04-04-14:41:43&until=2015-04-04-14:41:43&limit=10&private_token=e96fff37c2238fdab074d1dcea8e6317
  21 +
  22 + desc 'Return all articles of all kinds' do
  23 + detail 'Get all articles filtered by fields in query params'
  24 + params Entities::Article.documentation
  25 + success Entities::Article
  26 + failure [[403, 'Forbidden']]
  27 + named 'ArticlesList'
  28 + headers [
  29 + 'Per-Page' => {
  30 + description: 'Total number of records',
  31 + required: false
  32 + }
  33 + ]
  34 + end
  35 + get do
  36 + present_articles_for_asset(environment)
  37 + end
  38 +
  39 + desc "Return one article by id" do
  40 + detail 'Get only one article by id. If not found the "forbidden" http error is showed'
  41 + params Entities::Article.documentation
  42 + success Entities::Article
  43 + failure [[403, 'Forbidden']]
  44 + named 'ArticleById'
  45 + end
  46 + get ':id', requirements: {id: /[0-9]+/} do
  47 + present_article(environment)
  48 + end
  49 +
  50 + post ':id' do
  51 + article = environment.articles.find(params[:id])
  52 + return forbidden! unless article.allow_edit?(current_person)
  53 + article.update_attributes!(asset_with_image(params[:article]))
  54 + present_partial article, :with => Entities::Article
  55 + end
  56 +
  57 + delete ':id' do
  58 + article = environment.articles.find(params[:id])
  59 + return forbidden! unless article.allow_delete?(current_person)
  60 + begin
  61 + article.destroy
  62 + { :success => true }
  63 + rescue Exception => exception
  64 + render_api_error!(_('The article couldn\'t be removed due to some problem. Please contact the administrator.'), 400)
  65 + end
  66 + end
  67 +
  68 + desc 'Report a abuse and/or violent content in a article by id' do
  69 + detail 'Submit a abuse (in general, a content violation) report about a specific article'
  70 + params Entities::Article.documentation
  71 + failure [[400, 'Bad Request']]
  72 + named 'ArticleReportAbuse'
  73 + end
  74 + post ':id/report_abuse' do
  75 + article = find_article(environment.articles, params[:id])
  76 + profile = article.profile
  77 + begin
  78 + abuse_report = AbuseReport.new(:reason => params[:report_abuse])
  79 + if !params[:content_type].blank?
  80 + article = params[:content_type].constantize.find(params[:content_id])
  81 + abuse_report.content = article_reported_version(article)
  82 + end
  83 +
  84 + current_person.register_report(abuse_report, profile)
  85 +
  86 + if !params[:content_type].blank?
  87 + abuse_report = AbuseReport.find_by reporter_id: current_person.id, abuse_complaint_id: profile.opened_abuse_complaint.id
  88 + Delayed::Job.enqueue DownloadReportedImagesJob.new(abuse_report, article)
  89 + end
  90 +
  91 + {
  92 + :success => true,
  93 + :message => _('Your abuse report was registered. The administrators are reviewing your report.'),
  94 + }
  95 + rescue Exception => exception
  96 + #logger.error(exception.to_s)
  97 + render_api_error!(_('Your report couldn\'t be saved due to some problem. Please contact the administrator.'), 400)
  98 + end
  99 +
  100 + end
  101 +
  102 + desc "Returns the articles I voted" do
  103 + detail 'Get the Articles I make a vote'
  104 + failure [[403, 'Forbidden']]
  105 + named 'ArticleFollowers'
  106 + end
  107 + #FIXME refactor this method
  108 + get 'voted_by_me' do
  109 + present_articles(current_person.votes.where(:voteable_type => 'Article').collect(&:voteable))
  110 + end
  111 +
  112 + desc 'Perform a vote on a article by id' do
  113 + detail 'Vote on a specific article with values: 1 (if you like) or -1 (if not)'
  114 + params Entities::UserLogin.documentation
  115 + failure [[401,'Unauthorized']]
  116 + named 'ArticleVote'
  117 + end
  118 + post ':id/vote' do
  119 + authenticate!
  120 + value = (params[:value] || 1).to_i
  121 + # FIXME verify allowed values
  122 + render_api_error!('Vote value not allowed', 400) unless [-1, 1].include?(value)
  123 + article = find_article(environment.articles, params[:id])
  124 + begin
  125 + vote = Vote.new(:voteable => article, :voter => current_person, :vote => value)
  126 + {:vote => vote.save!}
  127 + rescue ActiveRecord::RecordInvalid => e
  128 + render_api_error!(e.message, 400)
  129 + end
  130 + end
  131 +
  132 + desc "Returns the total followers for the article" do
  133 + detail 'Get the followers of a specific article by id'
  134 + failure [[403, 'Forbidden']]
  135 + named 'ArticleFollowers'
  136 + end
  137 + get ':id/followers' do
  138 + article = find_article(environment.articles, params[:id])
  139 + total = article.person_followers.count
  140 + {:total_followers => total}
  141 + end
  142 +
  143 + desc "Return the articles followed by me"
  144 + get 'followed_by_me' do
  145 + present_articles_for_asset(current_person, 'following_articles')
  146 + end
  147 +
  148 + desc "Add a follower for the article" do
  149 + detail 'Add the current user identified by private token, like a follower of a article'
  150 + params Entities::UserLogin.documentation
  151 + failure [[401, 'Unauthorized']]
  152 + named 'ArticleFollow'
  153 + end
  154 + post ':id/follow' do
  155 + authenticate!
  156 + article = find_article(environment.articles, params[:id])
  157 + if article.article_followers.exists?(:person_id => current_person.id)
  158 + {:success => false, :already_follow => true}
  159 + else
  160 + article_follower = ArticleFollower.new
  161 + article_follower.article = article
  162 + article_follower.person = current_person
  163 + article_follower.save!
  164 + {:success => true}
  165 + end
  166 + end
  167 +
  168 + desc 'Return the children of a article identified by id' do
  169 + detail 'Get all children articles of a specific article'
  170 + params Entities::Article.documentation
  171 + failure [[403, 'Forbidden']]
  172 + named 'ArticleChildren'
  173 + end
  174 +
  175 + paginate per_page: MAX_PER_PAGE, max_per_page: MAX_PER_PAGE
  176 + get ':id/children' do
  177 + article = find_article(environment.articles, params[:id])
  178 +
  179 + #TODO make tests for this situation
  180 + votes_order = params.delete(:order) if params[:order]=='votes_score'
  181 + articles = select_filtered_collection_of(article, 'children', params)
  182 + articles = articles.display_filter(current_person, article.profile)
  183 +
  184 + #TODO make tests for this situation
  185 + if votes_order
  186 + articles = articles.joins('left join votes on articles.id=votes.voteable_id').group('articles.id').reorder('sum(coalesce(votes.vote, 0)) DESC')
  187 + end
  188 + Article.hit(articles)
  189 + present_articles(articles)
  190 + end
  191 +
  192 + desc 'Return one child of a article identified by id' do
  193 + detail 'Get a child of a specific article'
  194 + params Entities::Article.documentation
  195 + success Entities::Article
  196 + failure [[403, 'Forbidden']]
  197 + named 'ArticleChild'
  198 + end
  199 + get ':id/children/:child_id' do
  200 + article = find_article(environment.articles, params[:id])
  201 + child = find_article(article.children, params[:child_id])
  202 + child.hit
  203 + present_partial child, :with => Entities::Article
  204 + end
  205 +
  206 + desc 'Suggest a article to another profile' do
  207 + detail 'Suggest a article to another profile (person, community...)'
  208 + params Entities::Article.documentation
  209 + success Entities::Task
  210 + failure [[401,'Unauthorized']]
  211 + named 'ArticleSuggest'
  212 + end
  213 + post ':id/children/suggest' do
  214 + authenticate!
  215 + parent_article = environment.articles.find(params[:id])
  216 +
  217 + suggest_article = SuggestArticle.new
  218 + suggest_article.article = params[:article]
  219 + suggest_article.article[:parent_id] = parent_article.id
  220 + suggest_article.target = parent_article.profile
  221 + suggest_article.requestor = current_person
  222 +
  223 + unless suggest_article.save
  224 + render_api_errors!(suggest_article.article_object.errors.full_messages)
  225 + end
  226 + present_partial suggest_article, :with => Entities::Task
  227 + end
  228 +
  229 + # Example Request:
  230 + # POST api/v1/articles/:id/children?private_token=234298743290432&article[name]=title&article[body]=body
  231 + desc 'Add a child article to a parent identified by id' do
  232 + detail 'Create a new article and associate to a parent'
  233 + params Entities::Article.documentation
  234 + success Entities::Article
  235 + failure [[401,'Unauthorized']]
  236 + named 'ArticleAddChild'
  237 + end
  238 + post ':id/children' do
  239 + parent_article = environment.articles.find(params[:id])
  240 + params[:article][:parent_id] = parent_article.id
  241 + post_article(parent_article.profile, params)
  242 + end
  243 + end
  244 +
  245 + resource :profiles do
  246 + get ':id/home_page' do
  247 + profiles = environment.profiles
  248 + profiles = profiles.visible_for_person(current_person)
  249 + profile = profiles.find_by id: params[:id]
  250 + present_partial profile.home_page, :with => Entities::Article
  251 + end
  252 + end
  253 +
  254 + kinds = %w[profile community person enterprise]
  255 + kinds.each do |kind|
  256 + resource kind.pluralize.to_sym do
  257 + segment "/:#{kind}_id" do
  258 + resource :articles do
  259 +
  260 + desc "Return all articles associate with a profile of type #{kind}" do
  261 + detail 'Get a list of articles of a profile'
  262 + params Entities::Article.documentation
  263 + success Entities::Article
  264 + failure [[403, 'Forbidden']]
  265 + named 'ArticlesOfProfile'
  266 + end
  267 + get do
  268 + profile = environment.send(kind.pluralize).find(params["#{kind}_id"])
  269 +
  270 + if params[:path].present?
  271 + article = profile.articles.find_by path: params[:path]
  272 + if !article || !article.display_to?(current_person)
  273 + article = forbidden!
  274 + end
  275 +
  276 + present_partial article, :with => Entities::Article
  277 + else
  278 +
  279 + present_articles_for_asset(profile)
  280 + end
  281 + end
  282 +
  283 + desc "Return a article associate with a profile of type #{kind}" do
  284 + detail 'Get only one article of a profile'
  285 + params Entities::Article.documentation
  286 + success Entities::Article
  287 + failure [[403, 'Forbidden']]
  288 + named 'ArticleOfProfile'
  289 + end
  290 + get ':id' do
  291 + profile = environment.send(kind.pluralize).find(params["#{kind}_id"])
  292 + present_article(profile)
  293 + end
  294 +
  295 + # Example Request:
  296 + # POST api/v1/{people,communities,enterprises}/:asset_id/articles?private_token=234298743290432&article[name]=title&article[body]=body
  297 + desc "Add a new article associated with a profile of type #{kind}" do
  298 + detail 'Create a new article and associate with a profile'
  299 + params Entities::Article.documentation
  300 + success Entities::Article
  301 + failure [[403, 'Forbidden']]
  302 + named 'ArticleCreateToProfile'
  303 + end
  304 + post do
  305 + profile = environment.send(kind.pluralize).find(params["#{kind}_id"])
  306 + post_article(profile, params)
  307 + end
  308 + end
  309 + end
  310 + end
  311 + end
  312 + end
  313 + end
  314 +end
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, current_person: current_person
  17 + end
  18 + end
  19 + end
  20 + end
  21 +
  22 + end
  23 +
  24 + resource :environments do
  25 + [ '/default', '/context', ':environment_id' ].each do |route|
  26 + segment route do
  27 + resource :boxes do
  28 + get do
  29 + if (route.match(/default/))
  30 + env = Environment.default
  31 + elsif (route.match(/context/))
  32 + env = environment
  33 + else
  34 + env = Environment.find(params[:environment_id])
  35 + end
  36 + present env.boxes, with: Entities::Box, current_person: current_person
  37 + end
  38 + end
  39 + end
  40 + end
  41 + end
  42 + end
  43 +
  44 + end
  45 +end
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,63 @@ @@ -0,0 +1,63 @@
  1 +module Api
  2 + module V1
  3 + class Comments < Grape::API
  4 + MAX_PER_PAGE = 20
  5 +
  6 +
  7 + resource :articles do
  8 + paginate max_per_page: MAX_PER_PAGE
  9 + # Collect comments from articles
  10 + #
  11 + # Parameters:
  12 + # reference_id - comment id used as reference to collect comment
  13 + # oldest - Collect the oldest comments from reference_id comment. If nothing is passed the newest comments are collected
  14 + # limit - amount of comments returned. The default value is 20
  15 + #
  16 + # Example Request:
  17 + # GET /articles/12/comments?oldest&limit=10&reference_id=23
  18 + get ":id/comments" do
  19 + article = find_article(environment.articles, params[:id])
  20 + comments = select_filtered_collection_of(article, :comments, params)
  21 + comments = comments.without_spam
  22 + comments = comments.without_reply if(params[:without_reply].present?)
  23 + comments = plugins.filter(:unavailable_comments, comments)
  24 + present paginate(comments), :with => Entities::Comment, :current_person => current_person
  25 + end
  26 +
  27 + get ":id/comments/:comment_id" do
  28 + article = find_article(environment.articles, params[:id])
  29 + present article.comments.find(params[:comment_id]), :with => Entities::Comment, :current_person => current_person
  30 + end
  31 +
  32 + # Example Request:
  33 + # POST api/v1/articles/12/comments?private_token=2298743290432&body=new comment&title=New
  34 + post ":id/comments" do
  35 + authenticate!
  36 + article = find_article(environment.articles, params[:id])
  37 + return forbidden! unless article.accept_comments?
  38 + options = params.select { |key,v| !['id','private_token'].include?(key) }.merge(:author => current_person, :source => article)
  39 + begin
  40 + comment = Comment.create!(options)
  41 + rescue ActiveRecord::RecordInvalid => e
  42 + render_api_error!(e.message, 400)
  43 + end
  44 + present comment, :with => Entities::Comment, :current_person => current_person
  45 + end
  46 +
  47 + delete ":id/comments/:comment_id" do
  48 + article = find_article(environment.articles, params[:id])
  49 + comment = article.comments.find_by_id(params[:comment_id])
  50 + return not_found! if comment.nil?
  51 + return forbidden! unless comment.can_be_destroyed_by?(current_person)
  52 + begin
  53 + comment.destroy
  54 + present comment, with: Entities::Comment, :current_person => current_person
  55 + rescue => e
  56 + render_api_error!(e.message, 500)
  57 + end
  58 + end
  59 + end
  60 +
  61 + end
  62 + end
  63 +end
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,51 @@ @@ -0,0 +1,51 @@
  1 +module Api
  2 + module V1
  3 + class Profiles < Grape::API
  4 +
  5 + resource :profiles do
  6 +
  7 + get do
  8 + profiles = select_filtered_collection_of(environment, 'profiles', params)
  9 + profiles = profiles.visible
  10 + profiles = profiles.by_location(params) # Must be the last. May return Exception obj.
  11 + present profiles, :with => Entities::Profile, :current_person => current_person
  12 + end
  13 +
  14 + get ':id' do
  15 + profiles = environment.profiles
  16 + profiles = profiles.visible
  17 + profile = profiles.find_by id: params[:id]
  18 +
  19 + if profile
  20 + present profile, :with => Entities::Profile, :current_person => current_person
  21 + else
  22 + not_found!
  23 + end
  24 + end
  25 +
  26 + desc "Update profile information"
  27 + post ':id' do
  28 + authenticate!
  29 + profile = environment.profiles.find_by(id: params[:id])
  30 + return forbidden! unless current_person.has_permission?(:edit_profile, profile)
  31 + profile.update_attributes!(params[:profile])
  32 + present profile, :with => Entities::Profile, :current_person => current_person
  33 + end
  34 +
  35 + delete ':id' do
  36 + authenticate!
  37 + profiles = environment.profiles
  38 + profile = profiles.find_by id: params[:id]
  39 +
  40 + not_found! if profile.blank?
  41 +
  42 + if current_person.has_permission?(:destroy_profile, profile)
  43 + profile.destroy
  44 + else
  45 + forbidden!
  46 + end
  47 + end
  48 + end
  49 + end
  50 + end
  51 +end
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/concerns/authenticated_system.rb 0 → 100644
@@ -0,0 +1,169 @@ @@ -0,0 +1,169 @@
  1 +module AuthenticatedSystem
  2 +
  3 + protected
  4 +
  5 + extend ActiveSupport::Concern
  6 +
  7 + included do
  8 + if self < ActionController::Base
  9 + around_filter :user_set_current
  10 + before_filter :override_user
  11 + before_filter :login_from_cookie
  12 + end
  13 +
  14 + # Inclusion hook to make #current_user and #logged_in?
  15 + # available as ActionView helper methods.
  16 + helper_method :current_user, :logged_in?
  17 + end
  18 +
  19 + # Returns true or false if the user is logged in.
  20 + # Preloads @current_user with the user model if they're logged in.
  21 + def logged_in?
  22 + current_user != nil
  23 + end
  24 +
  25 + # Accesses the current user from the session.
  26 + def current_user user_id = session[:user]
  27 + @current_user ||= begin
  28 + user = User.find_by id: user_id if user_id
  29 + user.session = session if user
  30 + User.current = user
  31 + user
  32 + end
  33 + end
  34 +
  35 + # Store the given user in the session.
  36 + def current_user=(new_user)
  37 + if new_user.nil?
  38 + session.delete(:user)
  39 + else
  40 + session[:user] = new_user.id
  41 + new_user.session = session
  42 + new_user.register_login
  43 + end
  44 + @current_user = User.current = new_user
  45 + end
  46 +
  47 + # See impl. from http://stackoverflow.com/a/2513456/670229
  48 + def user_set_current
  49 + User.current = current_user
  50 + yield
  51 + ensure
  52 + # to address the thread variable leak issues in Puma/Thin webserver
  53 + User.current = nil
  54 + end
  55 +
  56 + # Check if the user is authorized.
  57 + #
  58 + # Override this method in your controllers if you want to restrict access
  59 + # to only a few actions or if you want to check if the user
  60 + # has the correct rights.
  61 + #
  62 + # Example:
  63 + #
  64 + # # only allow nonbobs
  65 + # def authorize?
  66 + # current_user.login != "bob"
  67 + # end
  68 + def authorized?
  69 + true
  70 + end
  71 +
  72 + # Filter method to enforce a login requirement.
  73 + #
  74 + # To require logins for all actions, use this in your controllers:
  75 + #
  76 + # before_filter :login_required
  77 + #
  78 + # To require logins for specific actions, use this in your controllers:
  79 + #
  80 + # before_filter :login_required, :only => [ :edit, :update ]
  81 + #
  82 + # To skip this in a subclassed controller:
  83 + #
  84 + # skip_before_filter :login_required
  85 + #
  86 + def login_required
  87 + username, passwd = get_auth_data
  88 + if username && passwd
  89 + self.current_user ||= User.authenticate(username, passwd) || nil
  90 + end
  91 + if logged_in? && authorized?
  92 + true
  93 + else
  94 + if params[:require_login_popup]
  95 + render :json => { :require_login_popup => true }
  96 + else
  97 + access_denied
  98 + end
  99 + end
  100 + end
  101 +
  102 + # Redirect as appropriate when an access request fails.
  103 + #
  104 + # The default action is to redirect to the login screen.
  105 + #
  106 + # Override this method in your controllers if you want to have special
  107 + # behavior in case the user is not authorized
  108 + # to access the requested action. For example, a popup window might
  109 + # simply close itself.
  110 + def access_denied
  111 + respond_to do |accepts|
  112 + accepts.html do
  113 + if request.xhr?
  114 + render :text => _('Access denied'), :status => 401
  115 + else
  116 + store_location
  117 + redirect_to :controller => '/account', :action => 'login'
  118 + end
  119 + end
  120 + accepts.xml do
  121 + headers["Status"] = "Unauthorized"
  122 + headers["WWW-Authenticate"] = %(Basic realm="Web Password")
  123 + render :text => "Could't authenticate you", :status => '401 Unauthorized'
  124 + end
  125 + end
  126 + false
  127 + end
  128 +
  129 + # Store the URI of the current request in the session.
  130 + #
  131 + # We can return to this location by calling #redirect_back_or_default.
  132 + def store_location(location = request.url)
  133 + session[:return_to] = location
  134 + end
  135 +
  136 + # Redirect to the URI stored by the most recent store_location call or
  137 + # to the passed default.
  138 + def redirect_back_or_default(default)
  139 + if session[:return_to]
  140 + redirect_to(session.delete(:return_to))
  141 + else
  142 + redirect_to(default)
  143 + end
  144 + end
  145 +
  146 + def override_user
  147 + return if params[:override_user].blank?
  148 + return unless logged_in? and user.is_admin? environment
  149 + @current_user = nil
  150 + current_user params[:override_user]
  151 + end
  152 +
  153 + # When called with before_filter :login_from_cookie will check for an :auth_token
  154 + # cookie and log the user back in if apropriate
  155 + def login_from_cookie
  156 + return if cookies[:auth_token].blank? or logged_in?
  157 + user = User.where(remember_token: cookies[:auth_token]).first
  158 + self.current_user = user if user and user.remember_token?
  159 + end
  160 +
  161 + private
  162 + @@http_auth_headers = %w(X-HTTP_AUTHORIZATION HTTP_AUTHORIZATION Authorization)
  163 + # gets BASIC auth info
  164 + def get_auth_data
  165 + auth_key = @@http_auth_headers.detect { |h| request.env.has_key?(h) }
  166 + auth_data = request.env[auth_key].to_s.split unless auth_key.blank?
  167 + return auth_data && auth_data[0] == 'Basic' ? Base64.decode64(auth_data[1]).split(':')[0..1] : [nil, nil]
  168 + end
  169 +end
app/controllers/admin/categories_controller.rb
@@ -7,7 +7,6 @@ class CategoriesController &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/application_controller.rb
@@ -13,6 +13,13 @@ class ApplicationController &lt; ActionController::Base @@ -13,6 +13,13 @@ class ApplicationController &lt; ActionController::Base
13 before_filter :verify_members_whitelist, :if => [:private_environment?, :user] 13 before_filter :verify_members_whitelist, :if => [:private_environment?, :user]
14 before_filter :redirect_to_current_user 14 before_filter :redirect_to_current_user
15 15
  16 + before_filter :set_session_theme
  17 + def set_session_theme
  18 + if params[:theme]
  19 + session[:theme] = environment.theme_ids.include?(params[:theme]) ? params[:theme] : nil
  20 + end
  21 + end
  22 +
16 def require_login_for_environment 23 def require_login_for_environment
17 login_required 24 login_required
18 end 25 end
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/manage_products_controller.rb
@@ -1,229 +0,0 @@ @@ -1,229 +0,0 @@
1 -class ManageProductsController < ApplicationController  
2 - needs_profile  
3 -  
4 - protect 'manage_products', :profile, :except => [:show]  
5 - before_filter :check_environment_feature  
6 - before_filter :login_required, :except => [:show]  
7 - before_filter :create_product?, :only => [:new]  
8 -  
9 - protected  
10 -  
11 - def check_environment_feature  
12 - unless profile.environment.enabled?('products_for_enterprises')  
13 - render_not_found  
14 - return  
15 - end  
16 - end  
17 -  
18 - def create_product?  
19 - if !profile.create_product?  
20 - render_access_denied  
21 - return  
22 - end  
23 - end  
24 -  
25 - public  
26 -  
27 - def index  
28 - @products = @profile.products.paginate(:per_page => 10, :page => params[:page])  
29 - end  
30 -  
31 - def show  
32 - @product = @profile.products.find(params[:id])  
33 - @inputs = @product.inputs  
34 - @allowed_user = user && user.has_permission?('manage_products', profile)  
35 - end  
36 -  
37 - def categories_for_selection  
38 - @category = environment.categories.find_by id: params[:category_id]  
39 - @object_name = params[:object_name]  
40 - if @category  
41 - @categories = @category.children  
42 - @level = @category.leaf? ? @category.level : @categories.first.level  
43 - else  
44 - @categories = ProductCategory.top_level_for(environment)  
45 - @level = 0  
46 - end  
47 - render :partial => 'categories_for_selection', :locals => { :categories => @categories, :level => @level }  
48 - end  
49 -  
50 - def new  
51 - @no_design_blocks = true  
52 - @category = params[:selected_category_id] ? Category.find(params[:selected_category_id]) : nil  
53 - @product = @profile.products.build(:product_category => @category)  
54 - @categories = ProductCategory.top_level_for(environment)  
55 - @level = 0  
56 - if request.post?  
57 - if @product.save  
58 - session[:notice] = _('Product succesfully created')  
59 - render :partial => 'shared/redirect_via_javascript',  
60 - :locals => { :url => url_for(:controller => 'manage_products', :action => 'show', :id => @product) }  
61 - else  
62 - render_dialog_error_messages 'product'  
63 - end  
64 - end  
65 - end  
66 -  
67 - def edit  
68 - @product = @profile.products.find(params[:id])  
69 - field = params[:field]  
70 - if request.post?  
71 - begin  
72 - @product.update!(params[:product])  
73 - render :partial => "display_#{field}", :locals => {:product => @product}  
74 - rescue Exception => e  
75 - render :partial => "edit_#{field}", :locals => {:product => @product, :errors => true}  
76 - end  
77 - else  
78 - render :partial => "edit_#{field}", :locals => {:product => @product, :errors => false}  
79 - end  
80 - end  
81 -  
82 - def edit_category  
83 - @product = @profile.products.find(params[:id])  
84 - @category = @product.product_category || ProductCategory.first  
85 - @categories = ProductCategory.top_level_for(environment)  
86 - @edit = true  
87 - @level = @category.level  
88 - if request.post?  
89 - if @product.update({:product_category_id => params[:selected_category_id]}, :without_protection => true)  
90 - render :partial => 'shared/redirect_via_javascript',  
91 - :locals => { :url => url_for(:controller => 'manage_products', :action => 'show', :id => @product) }  
92 - else  
93 - render_dialog_error_messages 'product'  
94 - end  
95 - end  
96 - end  
97 -  
98 - def show_category_tree  
99 - @category = environment.categories.find params[:category_id]  
100 - render :partial => 'selected_category_tree'  
101 - end  
102 -  
103 - def search_categories  
104 - @term = params[:term].downcase  
105 - conditions = ['LOWER(name) LIKE ? OR LOWER(name) LIKE ?', "#{@term}%", "% #{@term}%"]  
106 - @categories = ProductCategory.where(conditions).limit(10)  
107 - render :json => (@categories.map do |category|  
108 - {:label => category.name, :value => category.id}  
109 - end)  
110 - end  
111 -  
112 - def add_input  
113 - @product = @profile.products.find(params[:id])  
114 - @input = @product.inputs.build  
115 - @categories = ProductCategory.top_level_for(environment)  
116 - @level = 0  
117 - if request.post?  
118 - if @input.update(:product_category_id => params[:selected_category_id])  
119 - @inputs = @product.inputs  
120 - render :partial => 'display_inputs'  
121 - else  
122 - render_dialog_error_messages 'product'  
123 - end  
124 - else  
125 - render :partial => 'add_input'  
126 - end  
127 - end  
128 -  
129 - def manage_product_details  
130 - @product = @profile.products.find(params[:id])  
131 - if request.post?  
132 - @product.update_price_details(params[:price_details]) if params[:price_details]  
133 - render :partial => 'display_price_details'  
134 - else  
135 - render :partial => 'manage_product_details'  
136 - end  
137 - end  
138 -  
139 - def remove_price_detail  
140 - @product = @profile.products.find(params[:product])  
141 - @price_detail = @product.price_details.find(params[:id])  
142 - @product = @price_detail.product  
143 - if request.post?  
144 - @price_detail.destroy  
145 - render :nothing => true  
146 - end  
147 - end  
148 -  
149 - def display_price_composition_bar  
150 - @product = @profile.products.find(params[:id])  
151 - render :partial => 'price_composition_bar'  
152 - end  
153 -  
154 - def display_inputs_cost  
155 - @product = @profile.products.find(params[:id])  
156 - render :inline => "<%= float_to_currency(@product.inputs_cost) %>"  
157 - end  
158 -  
159 - def destroy  
160 - @product = @profile.products.find(params[:id])  
161 - if @product.destroy  
162 - session[:notice] = _('Product succesfully removed')  
163 - redirect_back_or_default :action => 'index'  
164 - else  
165 - session[:notice] = _('Could not remove the product')  
166 - redirect_back_or_default :action => 'show', :id => @product  
167 - end  
168 - end  
169 -  
170 - def edit_input  
171 - if request.xhr?  
172 - @input = @profile.inputs.find_by id: params[:id]  
173 - if @input  
174 - if request.post?  
175 - if @input.update(params[:input])  
176 - render :partial => 'display_input', :locals => {:input => @input}  
177 - else  
178 - render :partial => 'edit_input'  
179 - end  
180 - else  
181 - render :partial => 'edit_input'  
182 - end  
183 - else  
184 - render :text => _('The input was not found')  
185 - end  
186 - end  
187 - end  
188 -  
189 - def order_inputs  
190 - @product = @profile.products.find(params[:id])  
191 - @product.order_inputs!(params[:input]) if params[:input]  
192 - render :nothing => true  
193 - end  
194 -  
195 - def remove_input  
196 - @input = @profile.inputs.find(params[:id])  
197 - @product = @input.product  
198 - if request.post?  
199 - if @input.destroy  
200 - @inputs = @product.inputs  
201 - render :partial => 'display_inputs'  
202 - else  
203 - render_dialog_error_messages 'input'  
204 - end  
205 - end  
206 - end  
207 -  
208 - def certifiers_for_selection  
209 - @qualifier = Qualifier.exists?(:id => params[:id]) ? Qualifier.find(params[:id]) : nil  
210 - render :update do |page|  
211 - page.replace_html params[:certifier_area], :partial => 'certifiers_for_selection'  
212 - end  
213 - end  
214 -  
215 - def create_production_cost  
216 - cost = @profile.production_costs.create(:name => params[:id])  
217 - if cost.valid?  
218 - cost.save  
219 - render :text => {:name => cost.name,  
220 - :id => cost.id,  
221 - :ok => true  
222 - }.to_json  
223 - else  
224 - render :text => {:ok => false,  
225 - :error_msg => _(cost.errors['name'].join('\n')) % {:fn => _('Name')}  
226 - }.to_json  
227 - end  
228 - end  
229 -end  
app/controllers/my_profile/profile_design_controller.rb
@@ -45,17 +45,10 @@ class ProfileDesignController &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/profile_themes_controller.rb
@@ -63,12 +63,12 @@ class ProfileThemesController &lt; ThemesController @@ -63,12 +63,12 @@ class ProfileThemesController &lt; ThemesController
63 end 63 end
64 64
65 def start_test 65 def start_test
66 - session[:theme] = params[:id] 66 + session[:user_theme] = params[:id]
67 redirect_to :controller => 'content_viewer', :profile => profile.identifier, :action => 'view_page' 67 redirect_to :controller => 'content_viewer', :profile => profile.identifier, :action => 'view_page'
68 end 68 end
69 69
70 def stop_test 70 def stop_test
71 - session[:theme] = nil 71 + session[:user_theme] = nil
72 redirect_to :action => 'index' 72 redirect_to :action => 'index'
73 end 73 end
74 74
app/controllers/my_profile/tasks_controller.rb
@@ -26,7 +26,11 @@ class TasksController &lt; MyProfileController @@ -26,7 +26,11 @@ class TasksController &lt; MyProfileController
26 end 26 end
27 27
28 def processed 28 def processed
29 - @tasks = Task.to(profile).without_spam.closed.sort_by(&:created_at) 29 + @tasks = Task.to(profile).without_spam.closed.order('tasks.created_at DESC')
  30 + @filter = params[:filter] || {}
  31 + @tasks = filter_tasks(@filter, @tasks)
  32 + @tasks = @tasks.paginate(:per_page => Task.per_page, :page => params[:page])
  33 + @task_types = Task.closed_types_for(profile)
30 end 34 end
31 35
32 def change_responsible 36 def change_responsible
@@ -102,4 +106,28 @@ class TasksController &lt; MyProfileController @@ -102,4 +106,28 @@ class TasksController &lt; MyProfileController
102 render :json => result.map { |task| {:label => task.data[:name], :value => task.data[:name]} } 106 render :json => result.map { |task| {:label => task.data[:name], :value => task.data[:name]} }
103 end 107 end
104 108
  109 + protected
  110 +
  111 + def filter_tasks(filter, tasks)
  112 + tasks = tasks.eager_load(:requestor, :closed_by)
  113 + tasks = tasks.of(filter[:type].presence)
  114 + tasks = tasks.where(:status => filter[:status]) unless filter[:status].blank?
  115 +
  116 + filter[:created_from] = Date.parse(filter[:created_from]) unless filter[:created_from].blank?
  117 + filter[:created_until] = Date.parse(filter[:created_until]) unless filter[:created_until].blank?
  118 + filter[:closed_from] = Date.parse(filter[:closed_from]) unless filter[:closed_from].blank?
  119 + filter[:closed_until] = Date.parse(filter[:closed_until]) unless filter[:closed_until].blank?
  120 +
  121 + tasks = tasks.from_creation_date filter[:created_from] unless filter[:created_from].blank?
  122 + tasks = tasks.until_creation_date filter[:created_until] unless filter[:created_until].blank?
  123 +
  124 + tasks = tasks.from_closed_date filter[:closed_from] unless filter[:closed_from].blank?
  125 + tasks = tasks.until_closed_date filter[:closed_until] unless filter[:closed_until].blank?
  126 +
  127 + tasks = tasks.where('profiles.name LIKE ?', filter[:requestor]) unless filter[:requestor].blank?
  128 + tasks = tasks.where('closed_bies_tasks.name LIKE ?', filter[:closed_by]) unless filter[:closed_by].blank?
  129 + tasks = tasks.where('tasks.data LIKE ?', "%#{filter[:text]}%") unless filter[:text].blank?
  130 + tasks
  131 + end
  132 +
105 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/catalog_controller.rb
@@ -1,19 +0,0 @@ @@ -1,19 +0,0 @@
1 -class CatalogController < PublicController  
2 - needs_profile  
3 -  
4 - before_filter :check_enterprise_and_environment  
5 -  
6 - def index  
7 - extend CatalogHelper  
8 - catalog_load_index  
9 - end  
10 -  
11 - protected  
12 -  
13 - def check_enterprise_and_environment  
14 - unless profile.enterprise? && @profile.environment.enabled?('products_for_enterprises')  
15 - redirect_to :controller => 'profile', :profile => profile.identifier, :action => 'index'  
16 - end  
17 - end  
18 -  
19 -end  
app/controllers/public/content_viewer_controller.rb
@@ -209,7 +209,7 @@ class ContentViewerController &lt; ApplicationController @@ -209,7 +209,7 @@ class ContentViewerController &lt; ApplicationController
209 end 209 end
210 210
211 if @page.published && @page.uploaded_file? 211 if @page.published && @page.uploaded_file?
212 - redirect_to @page.public_filename 212 + redirect_to "#{Noosfero.root}#{@page.public_filename}"
213 else 213 else
214 send_data data, @page.download_headers 214 send_data data, @page.download_headers
215 end 215 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
@@ -52,7 +52,6 @@ class SearchController &lt; PublicController @@ -52,7 +52,6 @@ class SearchController &lt; PublicController
52 [ 52 [
53 [ :people, _('People'), :recent_people ], 53 [ :people, _('People'), :recent_people ],
54 [ :enterprises, _('Enterprises'), :recent_enterprises ], 54 [ :enterprises, _('Enterprises'), :recent_enterprises ],
55 - [ :products, _('Products'), :recent_products ],  
56 [ :events, _('Upcoming events'), :upcoming_events ], 55 [ :events, _('Upcoming events'), :upcoming_events ],
57 [ :communities, _('Communities'), :recent_communities ], 56 [ :communities, _('Communities'), :recent_communities ],
58 [ :articles, _('Contents'), :recent_articles ] 57 [ :articles, _('Contents'), :recent_articles ]
@@ -78,16 +77,17 @@ class SearchController &lt; PublicController @@ -78,16 +77,17 @@ class SearchController &lt; PublicController
78 full_text_search 77 full_text_search
79 end 78 end
80 79
81 - def products  
82 - @scope = @environment.products  
83 - full_text_search  
84 - end  
85 -  
86 def enterprises 80 def enterprises
87 @scope = visible_profiles(Enterprise) 81 @scope = visible_profiles(Enterprise)
88 full_text_search 82 full_text_search
89 end 83 end
90 84
  85 + # keep URL compatibility
  86 + def products
  87 + return render_not_found unless defined? ProductsPlugin
  88 + redirect_to url_for(params.merge controller: 'products_plugin/search', action: :products)
  89 + end
  90 +
91 def communities 91 def communities
92 @scope = visible_profiles(Community) 92 @scope = visible_profiles(Community)
93 full_text_search 93 full_text_search
@@ -186,7 +186,6 @@ class SearchController &lt; PublicController @@ -186,7 +186,6 @@ class SearchController &lt; PublicController
186 people: _('People'), 186 people: _('People'),
187 communities: _('Communities'), 187 communities: _('Communities'),
188 enterprises: _('Enterprises'), 188 enterprises: _('Enterprises'),
189 - products: _('Products and Services'),  
190 events: _('Events'), 189 events: _('Events'),
191 } 190 }
192 end 191 end
@@ -260,12 +259,11 @@ class SearchController &lt; PublicController @@ -260,12 +259,11 @@ class SearchController &lt; PublicController
260 end 259 end
261 260
262 def available_assets 261 def available_assets
263 - assets = { 262 + {
264 articles: _('Contents'), 263 articles: _('Contents'),
265 enterprises: _('Enterprises'), 264 enterprises: _('Enterprises'),
266 people: _('People'), 265 people: _('People'),
267 communities: _('Communities'), 266 communities: _('Communities'),
268 - products: _('Products and Services'),  
269 } 267 }
270 end 268 end
271 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
@@ -352,7 +344,7 @@ module ApplicationHelper @@ -352,7 +344,7 @@ module ApplicationHelper
352 end 344 end
353 345
354 def is_testing_theme 346 def is_testing_theme
355 - !controller.session[:theme].nil? 347 + !controller.session[:user_theme].nil?
356 end 348 end
357 349
358 def theme_owner 350 def theme_owner
@@ -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)
@@ -601,8 +593,8 @@ module ApplicationHelper @@ -601,8 +593,8 @@ module ApplicationHelper
601 end 593 end
602 594
603 if block 595 if block
604 - field_html ||= ''  
605 - field_html += capture(&block) 596 + field_html ||= ''.html_safe
  597 + field_html = [field_html, capture(&block)].safe_join
606 end 598 end
607 599
608 if controller.action_name == 'signup' || controller.action_name == 'new_community' || (controller.controller_name == "enterprise_registration" && controller.action_name == 'index') || (controller.controller_name == 'home' && controller.action_name == 'index' && user.nil?) 600 if controller.action_name == 'signup' || controller.action_name == 'new_community' || (controller.controller_name == "enterprise_registration" && controller.action_name == 'index') || (controller.controller_name == 'home' && controller.action_name == 'index' && user.nil?)
@@ -611,7 +603,9 @@ module ApplicationHelper @@ -611,7 +603,9 @@ module ApplicationHelper
611 end 603 end
612 else 604 else
613 if profile.active_fields.include?(name) 605 if profile.active_fields.include?(name)
614 - result = content_tag('div', field_html + profile_field_privacy_selector(profile, name), :class => 'field-with-privacy-selector') 606 + result = content_tag :div, class: 'field-with-privacy-selector' do
  607 + [field_html, profile_field_privacy_selector(profile, name)].safe_join
  608 + end
615 end 609 end
616 end 610 end
617 611
@@ -619,10 +613,6 @@ module ApplicationHelper @@ -619,10 +613,6 @@ module ApplicationHelper
619 result = required(result) 613 result = required(result)
620 end 614 end
621 615
622 - if block  
623 - concat(result)  
624 - end  
625 -  
626 result 616 result
627 end 617 end
628 618
@@ -788,7 +778,7 @@ module ApplicationHelper @@ -788,7 +778,7 @@ module ApplicationHelper
788 return "" if categories.blank? 778 return "" if categories.blank?
789 content_tag(:ul) do 779 content_tag(:ul) do
790 categories.map do |category| 780 categories.map do |category|
791 - category_path = category.kind_of?(ProductCategory) ? {:controller => 'search', :action => 'assets', :asset => 'products', :product_category => category.id} : { :controller => 'search', :action => 'category_index', :category_path => category.explode_path } 781 + category_path = { :controller => 'search', :action => 'category_index', :category_path => category.explode_path }
792 if category.display_in_menu? 782 if category.display_in_menu?
793 content_tag(:li) do 783 content_tag(:li) do
794 if !category.is_leaf_displayable_in_menu? 784 if !category.is_leaf_displayable_in_menu?
@@ -867,7 +857,7 @@ module ApplicationHelper @@ -867,7 +857,7 @@ module ApplicationHelper
867 alias :browse_communities_menu :search_communities_menu 857 alias :browse_communities_menu :search_communities_menu
868 858
869 def pagination_links(collection, options={}) 859 def pagination_links(collection, options={})
870 - options = {:previous_label => content_tag(:span, '&laquo; ', :class => 'previous-arrow') + _('Previous'), :next_label => _('Next') + content_tag(:span, ' &raquo;', :class => 'next-arrow'), :inner_window => 1, :outer_window => 0 }.merge(options) 860 + options = {:previous_label => content_tag(:span, '&laquo; '.html_safe, :class => 'previous-arrow') + _('Previous'), :next_label => _('Next') + content_tag(:span, ' &raquo;'.html_safe, :class => 'next-arrow'), :inner_window => 1, :outer_window => 0 }.merge(options)
871 will_paginate(collection, options) 861 will_paginate(collection, options)
872 end 862 end
873 863
@@ -911,7 +901,8 @@ module ApplicationHelper @@ -911,7 +901,8 @@ module ApplicationHelper
911 end 901 end
912 902
913 def admin_link 903 def admin_link
914 - user.is_admin?(environment) ? link_to('<i class="icon-menu-admin"></i><strong>' + _('Administration') + '</strong>', environment.admin_url, :title => _("Configure the environment"), :class => 'admin-link') : '' 904 + admin_icon = '<i class="icon-menu-admin"></i><strong>' + _('Administration') + '</strong>'
  905 + user.is_admin?(environment) ? link_to(admin_icon.html_safe, environment.admin_url, :title => _("Configure the environment"), :class => 'admin-link') : ''
915 end 906 end
916 907
917 def usermenu_logged_in 908 def usermenu_logged_in
@@ -920,23 +911,39 @@ module ApplicationHelper @@ -920,23 +911,39 @@ module ApplicationHelper
920 if count > 0 911 if count > 0
921 pending_tasks_count = link_to(count.to_s, user.tasks_url, :id => 'pending-tasks-count', :title => _("Manage your pending tasks")) 912 pending_tasks_count = link_to(count.to_s, user.tasks_url, :id => 'pending-tasks-count', :title => _("Manage your pending tasks"))
922 end 913 end
  914 + user_identifier = "<i style='background-image:url(#{user.profile_custom_icon(gravatar_default)})'></i><strong>#{user.identifier}</strong>"
  915 + welcome_link = link_to(user_identifier.html_safe, user.public_profile_url, :id => "homepage-link", :title => _('Go to your homepage'))
  916 + welcome_span = _("<span class='welcome'>Welcome,</span> %s") % welcome_link.html_safe
  917 + ctrl_panel_icon = '<i class="icon-menu-ctrl-panel"></i>'
  918 + ctrl_panel_section = '<strong>' + ctrl_panel_icon + _('Control panel') + '</strong>'
  919 + ctrl_panel_link = link_to(ctrl_panel_section.html_safe, user.admin_url, :class => 'ctrl-panel', :title => _("Configure your personal account and content"))
  920 + logout_icon = '<i class="icon-menu-logout"></i><strong>' + _('Logout') + '</strong>'
  921 + logout_link = link_to(logout_icon.html_safe, { :controller => 'account', :action => 'logout'} , :id => "logout", :title => _("Leave the system"))
  922 + join_result = safe_join(
  923 + [welcome_span.html_safe, render_environment_features(:usermenu).html_safe, admin_link.html_safe,
  924 + manage_enterprises.html_safe, manage_communities.html_safe, ctrl_panel_link.html_safe,
  925 + pending_tasks_count.html_safe, logout_link.html_safe], "")
  926 + join_result
  927 + end
923 928
924 - (_("<span class='welcome'>Welcome,</span> %s") % link_to("<i style='background-image:url(#{user.profile_custom_icon(gravatar_default)})'></i><strong>#{user.identifier}</strong>", user.url, :id => "homepage-link", :title => _('Go to your homepage'))) +  
925 - render_environment_features(:usermenu) +  
926 - admin_link +  
927 - manage_enterprises +  
928 - manage_communities +  
929 - link_to('<i class="icon-menu-ctrl-panel"></i><strong>' + _('Control panel') + '</strong>', user.admin_url, :class => 'ctrl-panel', :title => _("Configure your personal account and content")) +  
930 - pending_tasks_count +  
931 - link_to('<i class="icon-menu-logout"></i><strong>' + _('Logout') + '</strong>', { :controller => 'account', :action => 'logout'} , :id => "logout", :title => _("Leave the system")) 929 + def usermenu_notlogged_in
  930 + login_str = '<i class="icon-menu-login"></i><strong>' + _('Login') + '</strong>'
  931 + ret = _("<span class='login'>%s</span>") % modal_inline_link_to(login_str.html_safe, login_url, '#inlineLoginBox', :id => 'link_login')
  932 + return ret.html_safe
932 end 933 end
933 934
  935 + def usermenu_signup
  936 + signup_str = '<strong>' + _('Sign up') + '</strong>'
  937 + ret = _("<span class='or'>or</span> <span class='signup'>%s</span>") % link_to(signup_str.html_safe, :controller => 'account', :action => 'signup')
  938 + return ret.html_safe
  939 +
  940 + end
934 def limited_text_area(object_name, method, limit, text_area_id, options = {}) 941 def limited_text_area(object_name, method, limit, text_area_id, options = {})
935 - content_tag(:div, [ 942 + 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)), 943 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'), 944 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') 945 content_tag(:p, _('Limit of characters reached'), :id => text_area_id + '_limit', :style => 'display: none')
939 - ].join, :class => 'limited-text-area') 946 + ]), :class => 'limited-text-area')
940 end 947 end
941 948
942 def expandable_text_area(object_name, method, text_area_id, options = {}) 949 def expandable_text_area(object_name, method, text_area_id, options = {})
@@ -970,11 +977,12 @@ module ApplicationHelper @@ -970,11 +977,12 @@ module ApplicationHelper
970 977
971 def task_information(task) 978 def task_information(task)
972 values = {} 979 values = {}
  980 + values.merge!(task.information[:variables]) if task.information[:variables]
973 values.merge!({:requestor => link_to(task.requestor.name, task.requestor.url)}) if task.requestor 981 values.merge!({:requestor => link_to(task.requestor.name, task.requestor.url)}) if task.requestor
  982 + values.merge!({:target => link_to(task.target.name, task.target.url)}) if (task.target && task.target.respond_to?(:url))
974 values.merge!({:subject => content_tag('span', task.subject, :class=>'task_target')}) if task.subject 983 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 984 values.merge!({:linked_subject => link_to(content_tag('span', task.linked_subject[:text], :class => 'task_target'), task.linked_subject[:url])}) if task.linked_subject
976 - values.merge!(task.information[:variables]) if task.information[:variables]  
977 - task.information[:message] % values 985 + (task.information[:message] % values).html_safe
978 end 986 end
979 987
980 def add_zoom_to_article_images 988 def add_zoom_to_article_images
@@ -1032,26 +1040,24 @@ module ApplicationHelper @@ -1032,26 +1040,24 @@ module ApplicationHelper
1032 end 1040 end
1033 1041
1034 def render_tabs(tabs) 1042 def render_tabs(tabs)
1035 - titles = tabs.inject(''){ |result, tab| result << content_tag(:li, link_to(tab[:title], '#'+tab[:id]), :class => 'tab') }  
1036 - contents = tabs.inject(''){ |result, tab| result << content_tag(:div, tab[:content], :id => tab[:id]) } 1043 + titles = tabs.inject(''.html_safe){ |result, tab| result << content_tag(:li, link_to(tab[:title], '#'+tab[:id]), :class => 'tab') }
  1044 + contents = tabs.inject(''.html_safe){ |result, tab| result << content_tag(:div, tab[:content], :id => tab[:id]) }
1037 1045
1038 content_tag(:div, content_tag(:ul, titles) + raw(contents), :class => 'ui-tabs') 1046 content_tag(:div, content_tag(:ul, titles) + raw(contents), :class => 'ui-tabs')
1039 end 1047 end
1040 1048
1041 def delete_article_message(article) 1049 def delete_article_message(article)
1042 - CGI.escapeHTML(  
1043 - if article.folder?  
1044 - _("Are you sure that you want to remove the folder \"%s\"? Note that all the items inside it will also be removed!") % article.name  
1045 - else  
1046 - _("Are you sure that you want to remove the item \"%s\"?") % article.name  
1047 - end  
1048 - ) 1050 + if article.folder?
  1051 + _("Are you sure that you want to remove the folder \"%s\"? Note that all the items inside it will also be removed!") % article.name
  1052 + else
  1053 + _("Are you sure that you want to remove the item \"%s\"?") % article.name
  1054 + end
1049 end 1055 end
1050 1056
1051 def expirable_link_to(expired, content, url, options = {}) 1057 def expirable_link_to(expired, content, url, options = {})
1052 if expired 1058 if expired
1053 options[:class] = (options[:class] || '') + ' disabled' 1059 options[:class] = (options[:class] || '') + ' disabled'
1054 - content_tag('a', '&nbsp;'+content_tag('span', content), options) 1060 + content_tag('a', '&nbsp;'.html_safe+content_tag('span', content), options)
1055 else 1061 else
1056 if options[:modal] 1062 if options[:modal]
1057 options.delete(:modal) 1063 options.delete(:modal)
@@ -1084,7 +1090,7 @@ module ApplicationHelper @@ -1084,7 +1090,7 @@ module ApplicationHelper
1084 1090
1085 radios = templates.map do |template| 1091 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))) 1092 content_tag('li', labelled_radio_button(link_to(template.name, template.url, :target => '_blank'), "#{field_name}[template_id]", template.id, environment.is_default_template?(template)))
1087 - end.join("\n") 1093 + end.join("\n").html_safe
1088 1094
1089 content_tag('div', content_tag('label', _('Profile organization'), :for => 'template-options', :class => 'formlabel') + 1095 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;') + 1096 content_tag('p', _('Your profile will be created according to the selected template. Click on the options to view them.'), :style => 'margin: 5px 15px;padding: 0px 10px;') +
@@ -1124,7 +1130,7 @@ module ApplicationHelper @@ -1124,7 +1130,7 @@ module ApplicationHelper
1124 content_tag(:div, :class => 'errorExplanation', :id => 'errorExplanation') do 1130 content_tag(:div, :class => 'errorExplanation', :id => 'errorExplanation') do
1125 content_tag(:h2, _('Errors while saving')) + 1131 content_tag(:h2, _('Errors while saving')) +
1126 content_tag(:ul) do 1132 content_tag(:ul) do
1127 - errors.map { |err| content_tag(:li, err) }.join 1133 + safe_join(errors.map { |err| content_tag(:li, err.html_safe) })
1128 end 1134 end
1129 end 1135 end
1130 end 1136 end
@@ -1234,6 +1240,7 @@ module ApplicationHelper @@ -1234,6 +1240,7 @@ module ApplicationHelper
1234 :href=>"#", 1240 :href=>"#",
1235 :title=>_("Exit full screen mode") 1241 :title=>_("Exit full screen mode")
1236 }) 1242 })
  1243 + content.html_safe
1237 end 1244 end
1238 1245
1239 end 1246 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, profile: page.profile.identifier, action: :unfollow_article, article_id: page.id}
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, profile: page.profile.identifier, action: :follow_article, article_id: page.id}
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
@@ -98,15 +99,10 @@ module BoxesHelper @@ -98,15 +99,10 @@ module BoxesHelper
98 end 99 end
99 100
100 def render_block_content block 101 def render_block_content block
101 - # FIXME: this conditional should be removed after all  
102 - # block footer from plugins methods get refactored into helpers and views.  
103 - # They are a failsafe until all of them are done.  
104 - return block.content if block.method(:content).owner != Block  
105 render_block block 102 render_block block
106 end 103 end
107 104
108 def render_block_footer block 105 def render_block_footer block
109 - return block.footer if block.method(:footer).owner != Block  
110 render_block block, 'footers/' 106 render_block block, 'footers/'
111 end 107 end
112 108
@@ -136,17 +132,18 @@ module BoxesHelper @@ -136,17 +132,18 @@ module BoxesHelper
136 132
137 result = filter_html(result, block) 133 result = filter_html(result, block)
138 134
139 - content_tag('div',  
140 - box_decorator.block_target(block.box, block) +  
141 - content_tag('div',  
142 - content_tag('div',  
143 - content_tag('div',  
144 - result + footer_content + box_decorator.block_edit_buttons(block),  
145 - :class => 'block-inner-2'),  
146 - :class => 'block-inner-1'),  
147 - options),  
148 - :class => 'block-outer') +  
149 - box_decorator.block_handle(block) 135 + join_result = safe_join([result, footer_content, box_decorator.block_edit_buttons(block)])
  136 + content_tag_inner_1 = content_tag('div', join_result, :class => 'block-inner-2')
  137 +
  138 + content_tag_inner_2 = content_tag('div', content_tag_inner_1, :class => 'block-inner-1')
  139 + content_tag_inner_3 = content_tag('div', content_tag_inner_2, options)
  140 + content_tag_inner_4 = box_decorator.block_target(block.box, block) + content_tag_inner_3
  141 + c = content_tag('div', content_tag_inner_4, :class => 'block-outer')
  142 + box_decorator_result = box_decorator.block_handle(block)
  143 + result_final = safe_join([c, box_decorator_result], "")
  144 +
  145 +
  146 + return result_final
150 end 147 end
151 148
152 def wrap_main_content(content) 149 def wrap_main_content(content)
@@ -156,17 +153,17 @@ module BoxesHelper @@ -156,17 +153,17 @@ module BoxesHelper
156 def extract_block_content(content) 153 def extract_block_content(content)
157 case content 154 case content
158 when Hash 155 when Hash
159 - content_tag('iframe', '', :src => url_for(content)) 156 + content_tag('iframe', ''.html_safe, :src => url_for(content))
160 when String 157 when String
161 if content.split("\n").size == 1 and content =~ /^https?:\/\// 158 if content.split("\n").size == 1 and content =~ /^https?:\/\//
162 - content_tag('iframe', '', :src => content) 159 + content_tag('iframe', ''.html_safe, :src => content)
163 else 160 else
164 content 161 content
165 end 162 end
166 when Proc 163 when Proc
167 self.instance_eval(&content) 164 self.instance_eval(&content)
168 when NilClass 165 when NilClass
169 - '' 166 + ''.html_safe
170 else 167 else
171 raise "Unsupported content for block (#{content.class})" 168 raise "Unsupported content for block (#{content.class})"
172 end 169 end
@@ -175,14 +172,14 @@ module BoxesHelper @@ -175,14 +172,14 @@ module BoxesHelper
175 module DontMoveBlocks 172 module DontMoveBlocks
176 # does nothing 173 # does nothing
177 def self.block_target(box, block = nil) 174 def self.block_target(box, block = nil)
178 - '' 175 + ''.html_safe
179 end 176 end
180 # does nothing 177 # does nothing
181 def self.block_handle(block) 178 def self.block_handle(block)
182 - '' 179 + ''.html_safe
183 end 180 end
184 def self.block_edit_buttons(block) 181 def self.block_edit_buttons(block)
185 - '' 182 + ''.html_safe
186 end 183 end
187 def self.select_blocks box, arr, context 184 def self.select_blocks box, arr, context
188 arr = arr.select{ |block| block.visible? context } 185 arr = arr.select{ |block| block.visible? context }
@@ -229,9 +226,9 @@ module BoxesHelper @@ -229,9 +226,9 @@ module BoxesHelper
229 # makes the given block draggable so it can be moved away. 226 # makes the given block draggable so it can be moved away.
230 def block_handle(block) 227 def block_handle(block)
231 return "" unless movable?(block) 228 return "" unless movable?(block)
232 - icon = "<div><div>#{display_icon(block.class)}</div><span>#{_(block.class.pretty_name)}</span></div>" 229 + icon = "<div><div>#{display_icon(block.class)}</div><span>#{_(block.class.pretty_name)}</span></div>".html_safe
233 block_draggable("block-#{block.id}", 230 block_draggable("block-#{block.id}",
234 - :helper => "function() {return cloneDraggableBlock($(this), '#{icon}')}") 231 + :helper => "function() {return cloneDraggableBlock($(this), '#{icon}')}".html_safe)
235 end 232 end
236 233
237 def block_draggable(element_id, options={}) 234 def block_draggable(element_id, options={})
@@ -302,7 +299,7 @@ module BoxesHelper @@ -302,7 +299,7 @@ module BoxesHelper
302 buttons << modal_inline_icon(:embed, _('Embed code'), {}, "#embed-code-box-#{block.id}") << html 299 buttons << modal_inline_icon(:embed, _('Embed code'), {}, "#embed-code-box-#{block.id}") << html
303 end 300 end
304 301
305 - content_tag('div', buttons.join("\n") + tag('br', :style => 'clear: left'), :class => 'button-bar') 302 + content_tag('div', buttons.join("\n").html_safe + tag('br', :style => 'clear: left'), :class => 'button-bar')
306 end 303 end
307 304
308 def current_blocks 305 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/catalog_helper.rb
@@ -1,45 +0,0 @@ @@ -1,45 +0,0 @@
1 -module CatalogHelper  
2 -  
3 - include DisplayHelper  
4 - include ManageProductsHelper  
5 -  
6 - def catalog_load_index options = {:page => params[:page], :show_categories => true}  
7 - if options[:show_categories]  
8 - @category = params[:level] ? ProductCategory.find(params[:level]) : nil  
9 - @categories = ProductCategory.on_level(params[:level]).order(:name)  
10 - end  
11 -  
12 - @products = profile.products.from_category(@category).  
13 - reorder('available desc, highlighted desc, name asc').  
14 - paginate(:per_page => @profile.products_per_catalog_page, :page => options[:page])  
15 - end  
16 -  
17 - def breadcrumb(category)  
18 - start = link_to(_('Start'), {:controller => :catalog, :action => 'index'})  
19 - ancestors = category.ancestors.map { |c| link_to(c.name, {:controller => :catalog, :action => 'index', :level => c.id}) }.reverse  
20 - current_level = content_tag('strong', category.name)  
21 - all_items = [start] + ancestors + [current_level]  
22 - content_tag('div', all_items.join(' &rarr; '), :id => 'breadcrumb')  
23 - end  
24 -  
25 - def category_link(category)  
26 - count = profile.products.from_category(category).count  
27 - name = truncate(category.name, :length => 22 - count.to_s.size)  
28 - link = link_to(name, {:controller => 'catalog', :action => 'index', :level => category.id}, :title => category.name)  
29 - content_tag('div', "#{link} <span class=\"count\">#{count}</span>") if count > 0  
30 - end  
31 -  
32 - def category_with_sub_list(category)  
33 - content_tag 'li', "#{category_link(category)}\n#{sub_category_list(category)}"  
34 - end  
35 -  
36 - def sub_category_list(category)  
37 - sub_categories = []  
38 - category.children.order(:name).each do |sub_category|  
39 - cat_link = category_link sub_category  
40 - sub_categories << content_tag('li', cat_link) unless cat_link.nil?  
41 - end  
42 - content_tag('ul', sub_categories.join) if sub_categories.size > 0  
43 - end  
44 -  
45 -end  
app/helpers/categories_helper.rb
@@ -2,7 +2,6 @@ module CategoriesHelper @@ -2,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/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/custom_fields_helper.rb
@@ -61,6 +61,6 @@ module CustomFieldsHelper @@ -61,6 +61,6 @@ module CustomFieldsHelper
61 61
62 def form_for_format(customized_type, format) 62 def form_for_format(customized_type, format)
63 field = CustomField.new(:format => format, :customized_type => customized_type, :environment => environment) 63 field = CustomField.new(:format => format, :customized_type => customized_type, :environment => environment)
64 - CGI::escapeHTML((render(:partial => 'features/custom_fields/form', :locals => {:field => field}))) 64 + CGI::escapeHTML((render(:partial => 'features/custom_fields/form', :locals => {:field => field}))).html_safe
65 end 65 end
66 end 66 end
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/enterprise_homepage_helper.rb
@@ -1,25 +0,0 @@ @@ -1,25 +0,0 @@
1 -module EnterpriseHomepageHelper  
2 -  
3 - def display_profile_info(profile)  
4 - data = ''  
5 - [  
6 - [ _('Contact person:'), :contact_person ],  
7 - [ _('e-Mail:'), :contact_email ],  
8 - [ _('Phone(s):'), :contact_phone ],  
9 - [ _('Location:'), :location ],  
10 - [ _('Address:'), :address ],  
11 - [ _('Economic activity:'), :economic_activity ]  
12 - ].each { | name, att |  
13 - if profile.send( att ) and not profile.send( att ).blank?  
14 - data << content_tag( 'li', content_tag('strong', name) +' '+ profile.send( att ).to_s ) +"\n"  
15 - end  
16 - }  
17 - if profile.respond_to?(:distance) and !profile.distance.nil?  
18 - data << content_tag( 'li',  
19 - content_tag('strong',_('Distance:')) +' '+  
20 - "%.2f%" % profile.distance  
21 - ) + "\n"  
22 - end  
23 - content_tag('div', content_tag('ul', data), :class => 'enterprise-info')  
24 - end  
25 -end  
app/helpers/events_helper.rb
1 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/manage_products_helper.rb
@@ -1,285 +0,0 @@ @@ -1,285 +0,0 @@
1 -# encoding: UTF-8  
2 -  
3 -module ManageProductsHelper  
4 -  
5 - def remote_function_to_update_categories_selection(container_id, options = {})  
6 - remote_function({  
7 - :update => container_id,  
8 - :url => { :action => "categories_for_selection" },  
9 - :loading => "loading('hierarchy_navigation', '#{ _('loading…') }'); loading('#{container_id}', '&nbsp;')",  
10 - :complete => "loading_done('hierarchy_navigation'); loading_done('#{container_id}')"  
11 - }.merge(options))  
12 - end  
13 -  
14 - def hierarchy_category_item(category, make_links, title = nil)  
15 - title ||= category.name  
16 - if make_links  
17 - link_to(title, '#',  
18 - :title => title,  
19 - :onclick => remote_function_to_update_categories_selection("categories_container_level#{ category.level + 1 }",  
20 - :with => "'category_id=#{ category.id }'"  
21 - )  
22 - )  
23 - else  
24 - title  
25 - end  
26 - end  
27 -  
28 - def hierarchy_category_navigation(current_category, options = {})  
29 - hierarchy = []  
30 - if current_category  
31 - hierarchy << current_category.name unless options[:hide_current_category]  
32 - ancestors = current_category.ancestors  
33 - ancestors.each do |category|  
34 - hierarchy << hierarchy_category_item(category, options[:make_links])  
35 - end  
36 - end  
37 - hierarchy.reverse.join(options[:separator] || ' &rarr; ')  
38 - end  
39 -  
40 - def options_for_select_categories(categories, selected = nil)  
41 - categories.sort_by{|cat| cat.name.transliterate}.map do |category|  
42 - selected_attribute = selected.nil? ? '' : (category == selected ? "selected='selected'" : '')  
43 - "<option value='#{category.id}' title='#{category.name}' #{selected_attribute}>#{category.name + (category.leaf? ? '': ' &raquo;')}</option>"  
44 - end.join("\n")  
45 - end  
46 -  
47 - def build_selects_for_ancestors(ancestors, current_category)  
48 - current_ancestor = ancestors.shift  
49 - if current_ancestor.nil?  
50 - select_for_new_category(current_category.children, current_category.level + 1)  
51 - else  
52 - content_tag('div',  
53 - select_tag('category_id',  
54 - options_for_select_categories(current_ancestor.siblings + [current_ancestor], current_ancestor),  
55 - :size => 10,  
56 - :onchange => remote_function_to_update_categories_selection("categories_container_level#{ current_ancestor.level + 1 }", :with => "'category_id=' + this.value")  
57 - ) +  
58 - build_selects_for_ancestors(ancestors, current_category),  
59 - :class => 'categories_container',  
60 - :id => "categories_container_level#{ current_ancestor.level }"  
61 - )  
62 - end  
63 - end  
64 -  
65 - def selects_for_all_ancestors(current_category)  
66 - build_selects_for_ancestors(current_category.ancestors.reverse + [current_category], current_category)  
67 - end  
68 -  
69 - def select_for_new_category(categories, level)  
70 - content_tag('div',  
71 - render(:partial => 'categories_for_selection', :locals => { :categories => categories, :level => level }),  
72 - :class => 'categories_container',  
73 - :id => "categories_container_level#{ level }"  
74 - )  
75 - end  
76 -  
77 - def categories_container(categories_selection_html, hierarchy_html = '')  
78 - content_tag 'div',  
79 - render('categories_autocomplete') +  
80 - hidden_field_tag('selected_category_id') +  
81 - content_tag('div', hierarchy_html, :id => 'hierarchy_navigation') +  
82 - content_tag('div', categories_selection_html, :id => 'categories_container_wrapper'),  
83 - :id => 'categories-container'  
84 - end  
85 -  
86 - def select_for_categories(categories, level = 0)  
87 - if categories.empty?  
88 - content_tag('div', '', :id => 'no_subcategories')  
89 - else  
90 - select_tag('category_id',  
91 - options_for_select_categories(categories),  
92 - :size => 10,  
93 - :onchange => remote_function_to_update_categories_selection("categories_container_level#{ level + 1 }", :with => "'category_id=' + this.value")  
94 - ) +  
95 - content_tag('div', '', :class => 'categories_container', :id => "categories_container_level#{ level + 1 }")  
96 - end  
97 - end  
98 -  
99 - def edit_link(label, url, html_options = {})  
100 - return '' unless (user && user.has_permission?('manage_products', profile))  
101 - link_to(label, url, html_options)  
102 - end  
103 -  
104 - def edit_product_link_to_remote(product, field, label, html_options = {})  
105 - return '' unless (user && user.has_permission?('manage_products', profile))  
106 - options = html_options.merge(:id => 'link-edit-product-' + field)  
107 - options[:class] = options[:class] ? options[:class] + ' link-to-remote' : 'link-to-remote'  
108 -  
109 - link_to_remote(label,  
110 - {:update => "product-#{field}",  
111 - :url => { :controller => 'manage_products', :action => "edit", :id => product.id, :field => field },  
112 - :method => :get,  
113 - :loading => "loading_for_button('#link-edit-product-#{field}')"},  
114 - options)  
115 - end  
116 -  
117 - def edit_button(type, label, url, html_options = {})  
118 - return '' unless (user && user.has_permission?('manage_products', profile))  
119 - button(type, label, url, html_options)  
120 - end  
121 -  
122 - def edit_product_button_to_remote(product, field, label, html_options = {})  
123 - the_class = 'button with-text icon-edit'  
124 - if html_options.has_key?(:class)  
125 - the_class << ' ' << html_options[:class]  
126 - end  
127 - edit_product_link_to_remote(product, field, label, html_options.merge(:class => the_class))  
128 - end  
129 -  
130 - def edit_ui_button(label, url, html_options = {})  
131 - return '' unless (user && user.has_permission?('manage_products', profile))  
132 - ui_button(label, url, html_options)  
133 - end  
134 -  
135 - def edit_product_ui_button_to_remote(product, field, label, html_options = {})  
136 - return '' unless (user && user.has_permission?('manage_products', profile))  
137 - id = 'edit-product-remote-button-ui-' + field  
138 - options = html_options.merge(:id => id)  
139 -  
140 - ui_button_to_remote(label,  
141 - {:update => "product-#{field}",  
142 - :url => { :controller => 'manage_products', :action => "edit", :id => product.id, :field => field },  
143 - :complete => "jQuery('#edit-product-button-ui-#{field}').hide()",  
144 - :method => :get,  
145 - :loading => "loading_for_button('##{id}')"},  
146 - options)  
147 - end  
148 -  
149 - def cancel_edit_product_link(product, field, html_options = {})  
150 - return '' unless (user && user.has_permission?('manage_products', profile))  
151 - button_to_function(:cancel, _('Cancel'), nil, html_options) do |page|  
152 - page.replace_html "product-#{field}", CGI::escapeHTML(render :partial => "display_#{field}", :locals => {:product => product})  
153 - end  
154 - end  
155 -  
156 - def edit_product_category_link(product, html_options = {})  
157 - return '' unless (user && user.has_permission?('manage_products', profile))  
158 - options = html_options.merge(:id => 'link-edit-product-category')  
159 - link_to(_('Change category'), { :action => 'edit_category', :id => product.id}, options)  
160 - end  
161 -  
162 - def display_value(product)  
163 - price = product.price  
164 - return '' if price.blank? || price.zero?  
165 - discount = product.discount  
166 - if discount.blank? || discount.zero?  
167 - result = display_price(_('Price: '), price)  
168 - else  
169 - result = display_price_with_discount(price, product.price_with_discount)  
170 - end  
171 - content_tag('span', content_tag('span', result, :class => 'product-price'), :class => "#{product.available? ? '' : 'un'}available-product")  
172 - end  
173 -  
174 - def display_availability(product)  
175 - if !product.available?  
176 - ui_highlight(_('Product not available!'))  
177 - end  
178 - end  
179 -  
180 - def display_price(label, price)  
181 - content_tag('span', label, :class => 'field-name') +  
182 - content_tag('span', float_to_currency(price), :class => 'field-value')  
183 - end  
184 -  
185 - def display_price_with_discount(price, price_with_discount)  
186 - original_value = content_tag('span', display_price(_('List price: '), price), :class => 'list-price')  
187 - discount_value = content_tag('span', display_price(_('On sale: '), price_with_discount), :class => 'on-sale-price')  
188 - original_value + tag('br') + discount_value  
189 - end  
190 -  
191 - def display_qualifiers(product)  
192 - data = ''  
193 - product.product_qualifiers.each do |pq|  
194 - certified_by = ''  
195 - certifier = pq.certifier  
196 - if certifier  
197 - certifier_name = certifier.link.blank? ? certifier.name : link_to(certifier.name, certifier.link)  
198 - certified_by = _('certified by %s') % certifier_name  
199 - else  
200 - certified_by = _('(Self declared)')  
201 - end  
202 - data << content_tag('li', "✔ #{pq.qualifier.name} #{certified_by}", :class => 'product-qualifiers-item')  
203 - end  
204 - content_tag('ul', data, :id => 'product-qualifiers')  
205 - end  
206 -  
207 - def qualifiers_for_select  
208 - [[_('Select...'), nil]] + environment.qualifiers.sort.map{ |c| [c.name, c.id] }  
209 - end  
210 - def certifiers_for_select(qualifier)  
211 - [[_('Self declared'), nil]] + qualifier.certifiers.sort.map{ |c| [c.name, c.id] }  
212 - end  
213 - def select_qualifiers(product, selected = nil)  
214 - select_tag('selected_qualifier', options_for_select(qualifiers_for_select, selected),  
215 - :onchange => remote_function(  
216 - :url => {:action => 'certifiers_for_selection'},  
217 - :with => "'id=' + value + '&certifier_area=' + jQuery(this).parent().next().attr('id')",  
218 - :before => "small_loading(jQuery(this).parent().next().attr('id'), '&nbsp;')"  
219 - ),  
220 - :id => nil  
221 - )  
222 - end  
223 - def select_certifiers(qualifier, product = nil)  
224 - if qualifier  
225 - selected = product ? product.product_qualifiers.find_by(qualifier_id: qualifier.id).certifier_id : nil  
226 - select_tag("product[qualifiers_list][#{qualifier.id}]", options_for_select(certifiers_for_select(qualifier), selected))  
227 - else  
228 - select_tag("product[qualifiers_list][nil]")  
229 - end  
230 - end  
231 -  
232 - def remove_qualifier_button  
233 - button_to_function(:delete, content_tag('span', _('Delete qualifier')), "jQuery(this).parents('tr').remove()")  
234 - end  
235 -  
236 - def select_unit(object)  
237 - collection_select(object.class.name.downcase, :unit_id, environment.units, :id, :singular, {:include_blank => _('Select the unit')})  
238 - end  
239 -  
240 - def input_icon(input)  
241 - if input.is_from_solidarity_economy?  
242 - hint = _('Product from solidarity economy')  
243 - image_tag("/images/solidarity-economy.png", :class => 'solidatiry-economy-icon', :alt => hint, :title => hint)  
244 - end  
245 - end  
246 -  
247 - def display_price_by(unit)  
248 - selected_unit = content_tag('span', unit, :class => 'selected-unit')  
249 - content_tag('span', _('by') + ' ' + selected_unit, :class => 'price-by-unit')  
250 - end  
251 -  
252 - def label_amount_used(input)  
253 - product_unit = input.product.unit  
254 - if product_unit.blank?  
255 - _('Amount used in this product or service')  
256 - else  
257 - _('Amount used by %s of this product or service') % product_unit.singular.downcase  
258 - end  
259 - end  
260 -  
261 - def display_unit(input)  
262 - input_amount_used = content_tag('span', input.formatted_amount, :class => 'input-amount-used')  
263 - return input_amount_used if input.unit.blank?  
264 - n_('1 %{singular_unit}', '%{num} %{plural_unit}', input.amount_used.to_f) % { :num => input_amount_used, :singular_unit => content_tag('span', input.unit.singular, :class => 'input-unit'), :plural_unit => content_tag('span', input.unit.plural, :class => 'input-unit') }  
265 - end  
266 -  
267 - def select_production_cost(product,selected=nil)  
268 - url = url_for( :controller => 'manage_products', :action => 'create_production_cost' )  
269 - prompt_msg = _('Insert the name of the new cost:')  
270 - error_msg = _('Something went wrong. Please, try again')  
271 - select_tag('price_details[][production_cost_id]',  
272 - '<option value="" disabled="disabled">' + _('Select...') + '</option>' +  
273 - options_for_select(product.available_production_costs.map {|item| [truncate(item.name, {:length => 10, :omission => '...'}), item.id]} + [[_('Other cost'), '']], selected),  
274 - {:class => 'production-cost-selection',  
275 - :onchange => "productionCostTypeChange(this, '#{url}', '#{prompt_msg}', '#{error_msg}')"})  
276 - end  
277 -  
278 - def price_composition_progressbar_text(product, args = {})  
279 - currency = environment.currency_unit  
280 - production_cost = args[:production_cost_value] || product.formatted_value(:total_production_cost)  
281 - product_price = args[:product_price] || product.formatted_value(:price)  
282 -  
283 - _("%{currency} %{production_cost} of %{currency} %{product_price}") % {:currency => currency, :production_cost => content_tag('span', production_cost, :class => 'production_cost'), :product_price => content_tag('span', product_price, :class => 'product_price')}  
284 - end  
285 -end  
app/helpers/memberships_helper.rb
1 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/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/theme_loader_helper.rb
@@ -2,8 +2,8 @@ module ThemeLoaderHelper @@ -2,8 +2,8 @@ module ThemeLoaderHelper
2 def current_theme 2 def current_theme
3 @current_theme ||= 3 @current_theme ||=
4 begin 4 begin
5 - if session[:theme]  
6 - session[:theme] 5 + if session[:user_theme]
  6 + session[:user_theme]
7 else 7 else
8 # utility for developers: set the theme to 'random' in development mode and 8 # utility for developers: set the theme to 'random' in development mode and
9 # you will get a different theme every request. This is interesting for 9 # you will get a different theme every request. This is interesting for
@@ -11,8 +11,8 @@ module ThemeLoaderHelper @@ -11,8 +11,8 @@ module ThemeLoaderHelper
11 if Rails.env.development? && environment.theme == 'random' 11 if Rails.env.development? && environment.theme == 'random'
12 @random_theme ||= Dir.glob('public/designs/themes/*').map { |f| File.basename(f) }.rand 12 @random_theme ||= Dir.glob('public/designs/themes/*').map { |f| File.basename(f) }.rand
13 @random_theme 13 @random_theme
14 - elsif Rails.env.development? && respond_to?(:params) && params[:theme] && File.exists?(Rails.root.join('public/designs/themes', params[:theme]))  
15 - params[:theme] 14 + elsif Rails.env.development? && respond_to?(:params) && params[:user_theme] && File.exists?(Rails.root.join('public/designs/themes', params[:user_theme]))
  15 + params[:user_theme]
16 else 16 else
17 if profile && !profile.theme.nil? 17 if profile && !profile.theme.nil?
18 profile.theme 18 profile.theme
@@ -34,8 +34,10 @@ module ThemeLoaderHelper @@ -34,8 +34,10 @@ module ThemeLoaderHelper
34 end 34 end
35 35
36 def theme_path 36 def theme_path
37 - if session[:theme] 37 + if session[:user_theme]
38 '/user_themes/' + current_theme 38 '/user_themes/' + current_theme
  39 + elsif session[:theme]
  40 + '/designs/themes/' + session[:theme]
39 else 41 else
40 '/designs/themes/' + current_theme 42 '/designs/themes/' + current_theme
41 end 43 end
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/url_helper.rb
@@ -4,4 +4,12 @@ module UrlHelper @@ -4,4 +4,12 @@ module UrlHelper
4 'javascript:history.back()' 4 'javascript:history.back()'
5 end 5 end
6 6
  7 + def default_url_options
  8 + options = {}
  9 +
  10 + options[:override_user] = params[:override_user] if params[:override_user].present?
  11 +
  12 + options
  13 + end
  14 +
7 end 15 end
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/jobs/download_reported_images_job.rb 0 → 100644
@@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
  1 +class DownloadReportedImagesJob < Struct.new(:abuse_report, :article)
  2 + def perform
  3 + images_paths = article.image? ? [File.join(article.profile.environment.top_url, article.public_filename(:display))] : article.body_images_paths
  4 + images_paths.each do |image_path|
  5 + image = get_image(image_path)
  6 + reported_image = ReportedImage.create!( :abuse_report => abuse_report,
  7 + :uploaded_data => image,
  8 + :filename => File.basename(image_path),
  9 + :size => image.size )
  10 + abuse_report.content = parse_content(abuse_report, image_path, reported_image)
  11 + end
  12 + abuse_report.save!
  13 + end
  14 +
  15 + def get_image(image_path)
  16 + image = ActionController::UploadedTempfile.new('reported_image')
  17 + image.write(Net::HTTP.get(URI.parse(image_path)))
  18 + image.original_path = 'tmp/' + File.basename(image_path)
  19 + image.content_type = 'image/' + File.extname(image_path).gsub('.','')
  20 + image
  21 + end
  22 +
  23 + def parse_content(report, old_path, image)
  24 + old_path = old_path.gsub(report.reporter.environment.top_url, '')
  25 + report.content.gsub(/#{old_path}/, image.public_filename)
  26 + end
  27 +end
app/jobs/get_email_contacts_job.rb 0 → 100644
@@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
  1 +class GetEmailContactsJob < Struct.new(:import_from, :login, :password, :contact_list_id)
  2 + def perform
  3 + begin
  4 + Invitation.get_contacts(import_from, login, password, contact_list_id)
  5 + rescue Contacts::AuthenticationError => ex
  6 + ContactList.exists?(contact_list_id) && ContactList.find(contact_list_id).register_auth_error
  7 + rescue Exception => ex
  8 + ContactList.exists?(contact_list_id) && ContactList.find(contact_list_id).register_error
  9 + end
  10 + end
  11 +end
app/jobs/invitation_job.rb 0 → 100644
@@ -0,0 +1,10 @@ @@ -0,0 +1,10 @@
  1 +class InvitationJob < Struct.new(:person_id, :contacts_to_invite, :message, :profile_id, :contact_list_id, :locale)
  2 + def perform
  3 + Noosfero.with_locale(locale) do
  4 + person = Person.find(person_id)
  5 + profile = Profile.find(profile_id)
  6 + Invitation.invite(person, contacts_to_invite, message, profile)
  7 + ContactList.exists?(contact_list_id) && ContactList.find(contact_list_id).destroy
  8 + end
  9 + end
  10 +end
app/jobs/log_memory_consumption_job.rb 0 → 100644
@@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
  1 +class LogMemoryConsumptionJob < Struct.new(:last_stat)
  2 + # Number of entries do display
  3 + N = 20
  4 +
  5 + def perform
  6 + logpath = File.join(Rails.root, 'log', "#{ENV['RAILS_ENV']}_memory_consumption.log")
  7 + logger = Logger.new(logpath)
  8 + stats = Hash.new(0)
  9 + ObjectSpace.each_object {|o| stats[o.class.to_s] += 1}
  10 + i = 1
  11 +
  12 + logger << "[#{Time.now.strftime('%F %T %z')}]\n"
  13 + stats.sort {|(k1,v1),(k2,v2)| v2 <=> v1}.each do |k,v|
  14 + logger << (sprintf "%-60s %10d", k, v)
  15 + logger << (sprintf " | delta %10d", (v - last_stat[k])) if last_stat && last_stat[k]
  16 + logger << "\n"
  17 + break if i > N
  18 + i += 1
  19 + end
  20 + logger << "\n"
  21 + end
  22 +end
app/jobs/mailing_job.rb 0 → 100644
@@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
  1 +class MailingJob < Struct.new(:mailing_id)
  2 + def perform
  3 + mailing = Mailing.find(mailing_id)
  4 + Noosfero.with_locale(mailing.locale) do
  5 + mailing.deliver
  6 + end
  7 + end
  8 +end
app/jobs/notify_activity_to_profiles_job.rb 0 → 100644
@@ -0,0 +1,44 @@ @@ -0,0 +1,44 @@
  1 +class NotifyActivityToProfilesJob < Struct.new(:tracked_action_id)
  2 + NOTIFY_ONLY_COMMUNITY = [
  3 + 'add_member_in_community'
  4 + ]
  5 +
  6 + NOT_NOTIFY_COMMUNITY = [
  7 + 'join_community'
  8 + ]
  9 + def perform
  10 + return unless ActionTracker::Record.exists?(tracked_action_id)
  11 + tracked_action = ActionTracker::Record.find(tracked_action_id)
  12 + return unless tracked_action.user.present?
  13 + target = tracked_action.target
  14 + if target.is_a?(Community) && ( NOTIFY_ONLY_COMMUNITY.include?(tracked_action.verb) || ! target.public_profile )
  15 + ActionTrackerNotification.create(:profile_id => target.id, :action_tracker_id => tracked_action.id)
  16 + return
  17 + end
  18 +
  19 + # Notify the user
  20 + ActionTrackerNotification.create(:profile_id => tracked_action.user.id, :action_tracker_id => tracked_action.id)
  21 +
  22 + # Notify all friends
  23 + ActionTrackerNotification.connection.execute("insert into action_tracker_notifications(profile_id, action_tracker_id) select f.friend_id, #{tracked_action.id} from friendships as f where person_id=#{tracked_action.user.id} and f.friend_id not in (select atn.profile_id from action_tracker_notifications as atn where atn.action_tracker_id = #{tracked_action.id})")
  24 +
  25 + if tracked_action.user.is_a? Organization
  26 + ActionTrackerNotification.connection.execute "insert into action_tracker_notifications(profile_id, action_tracker_id) " +
  27 + "select distinct accessor_id, #{tracked_action.id} from role_assignments where resource_id = #{tracked_action.user.id} and resource_type='Profile' " +
  28 + if tracked_action.user.is_a? Enterprise then "union select distinct person_id, #{tracked_action.id} from favorite_enterprise_people where enterprise_id = #{tracked_action.user.id}" else "" end
  29 + end
  30 +
  31 + if target.is_a?(Community)
  32 + ActionTrackerNotification.create(:profile_id => target.id, :action_tracker_id => tracked_action.id) unless NOT_NOTIFY_COMMUNITY.include?(tracked_action.verb)
  33 +
  34 + ActionTrackerNotification.connection.execute("insert into action_tracker_notifications(profile_id, action_tracker_id) select distinct profiles.id, #{tracked_action.id} from role_assignments, profiles where profiles.type = 'Person' and profiles.id = role_assignments.accessor_id and profiles.id != #{tracked_action.user.id} and profiles.id not in (select atn.profile_id from action_tracker_notifications as atn where atn.action_tracker_id = #{tracked_action.id}) and role_assignments.resource_type = 'Profile' and role_assignments.resource_id = #{target.id}")
  35 + end
  36 +
  37 + if target.is_a?(Article) && target.profile.is_a?(Community)
  38 + ActionTrackerNotification.create(:profile_id => target.profile.id, :action_tracker_id => tracked_action.id) unless NOT_NOTIFY_COMMUNITY.include?(tracked_action.verb)
  39 +
  40 + ActionTrackerNotification.connection.execute("insert into action_tracker_notifications(profile_id, action_tracker_id) select distinct profiles.id, #{tracked_action.id} from role_assignments, profiles where profiles.type = 'Person' and profiles.id = role_assignments.accessor_id and profiles.id != #{tracked_action.user.id} and profiles.id not in (select atn.profile_id from action_tracker_notifications as atn where atn.action_tracker_id = #{tracked_action.id}) and role_assignments.resource_type = 'Profile' and role_assignments.resource_id = #{target.profile.id}")
  41 + end
  42 +
  43 + end
  44 +end
app/jobs/profile_suggestions_job.rb 0 → 100644
@@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
  1 +class ProfileSuggestionsJob < Struct.new(:person_id)
  2 +
  3 + def self.exists?(person_id)
  4 + !find(person_id).empty?
  5 + end
  6 +
  7 + def self.find(person_id)
  8 + Delayed::Job.by_handler("--- !ruby/struct:ProfileSuggestionsJob\nperson_id: #{person_id}\n")
  9 + end
  10 +
  11 + def perform
  12 + logger = Delayed::Worker.logger
  13 + begin
  14 + person = Person.find(person_id)
  15 + ProfileSuggestion.calculate_suggestions(person)
  16 + UserMailer.profiles_suggestions_email(person).deliver if person.email_suggestions
  17 + rescue Exception => exception
  18 + logger.error("Error with suggestions for person ID %d: %s" % [person_id, exception.to_s])
  19 + end
  20 + end
  21 +
  22 +end
app/jobs/user_activation_job.rb 0 → 100644
@@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
  1 +class UserActivationJob < Struct.new(:user_id)
  2 + def perform
  3 + user = User.find(user_id)
  4 + user.destroy unless user.activated? || user.person.is_template? || user.moderate_registration_pending?
  5 + end
  6 +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,22 @@ class AddMember &lt; Task @@ -29,16 +29,22 @@ 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 reject_details
  41 + true
  42 + end
  43 +
  44 + def footer
  45 + true
  46 + end
  47 +
42 def icon 48 def icon
43 {:type => :profile_image, :profile => requestor, :url => requestor.url} 49 {:type => :profile_image, :profile => requestor, :url => requestor.url}
44 end 50 end
@@ -63,4 +69,16 @@ class AddMember &lt; Task @@ -63,4 +69,16 @@ class AddMember &lt; Task
63 suggestion.disable if suggestion 69 suggestion.disable if suggestion
64 end 70 end
65 71
  72 + def task_finished_message
  73 + _("You have been accepted at \"%{target}\" with the profile \"%{requestor}\"") %
  74 + {:target => self.target.name,
  75 + :requestor => self.requestor.name}
  76 + end
  77 +
  78 + def task_cancelled_message
  79 + _("Your request to enter community \"%{target}\" with the profile \"%{requestor}\" was not accepted. Please contact any profile admin from %{target} for more information. The following explanation was given: \n\n\"%{explanation}\"") %
  80 + {:target => self.target.name,
  81 + :requestor => self.requestor.name,
  82 + :explanation => self.reject_explanation}
  83 + end
66 end 84 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 3
4 include SanitizeHelper 4 include SanitizeHelper
5 5
@@ -29,6 +29,8 @@ class Article &lt; ActiveRecord::Base @@ -29,6 +29,8 @@ class Article &lt; ActiveRecord::Base
29 :display => %w[full] 29 :display => %w[full]
30 } 30 }
31 31
  32 + N_('article')
  33 +
32 def initialize(*params) 34 def initialize(*params)
33 super 35 super
34 if params.present? && params.first.present? 36 if params.present? && params.first.present?
@@ -604,7 +606,7 @@ class Article &lt; ActiveRecord::Base @@ -604,7 +606,7 @@ class Article &lt; ActiveRecord::Base
604 end 606 end
605 607
606 def accept_category?(cat) 608 def accept_category?(cat)
607 - !cat.is_a?(ProductCategory) 609 + true
608 end 610 end
609 611
610 def public? 612 def public?
@@ -806,11 +808,13 @@ class Article &lt; ActiveRecord::Base @@ -806,11 +808,13 @@ class Article &lt; ActiveRecord::Base
806 end 808 end
807 809
808 def body_images_paths 810 def body_images_paths
809 - Nokogiri::HTML.fragment(self.body.to_s).css('img[src]').collect do |i| 811 + paths = Nokogiri::HTML.fragment(self.body.to_s).css('img[src]').collect do |i|
810 src = i['src'] 812 src = i['src']
811 src = URI.escape src if self.new_record? # xss_terminate runs on save 813 src = URI.escape src if self.new_record? # xss_terminate runs on save
812 (self.profile && self.profile.environment) ? URI.join(self.profile.environment.top_url, src).to_s : src 814 (self.profile && self.profile.environment) ? URI.join(self.profile.environment.top_url, src).to_s : src
813 end 815 end
  816 + paths.unshift(URI.join(self.profile.environment.top_url, self.image.public_filename).to_s) if self.image.present?
  817 + paths
814 end 818 end
815 819
816 def more_comments_label 820 def more_comments_label
@@ -862,6 +866,10 @@ class Article &lt; ActiveRecord::Base @@ -862,6 +866,10 @@ class Article &lt; ActiveRecord::Base
862 HashWithIndifferentAccess.new :name => name, :abstract => abstract, :body => body, :id => id, :parent_id => parent_id, :author => author 866 HashWithIndifferentAccess.new :name => name, :abstract => abstract, :body => body, :id => id, :parent_id => parent_id, :author => author
863 end 867 end
864 868
  869 + def self.can_display_blocks?
  870 + true
  871 + end
  872 +
865 private 873 private
866 874
867 def sanitize_tag_list 875 def sanitize_tag_list
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
@@ -170,30 +181,6 @@ class Block &lt; ActiveRecord::Base @@ -170,30 +181,6 @@ class Block &lt; ActiveRecord::Base
170 "/images/block_preview.png" 181 "/images/block_preview.png"
171 end 182 end
172 183
173 - # Returns the content to be used for this block.  
174 - #  
175 - # This method can return several types of objects:  
176 - #  
177 - # * <tt>String</tt>: if the string starts with <tt>http://</tt> or <tt>https://</tt>, then it is assumed to be address of an IFRAME. Otherwise it's is used as regular HTML.  
178 - # * <tt>Hash</tt>: the hash is used to build an URL that is used as the address for a IFRAME.  
179 - # * <tt>Proc</tt>: the Proc is evaluated in the scope of BoxesHelper. The  
180 - # block can then use <tt>render</tt>, <tt>link_to</tt>, etc.  
181 - #  
182 - # The method can also return <tt>nil</tt>, which means "no content".  
183 - #  
184 - # See BoxesHelper#extract_block_content for implementation details.  
185 - def content(args={})  
186 - "This is block number %d" % self.id  
187 - end  
188 -  
189 - # A footer to be appended to the end of the block. Returns <tt>nil</tt>.  
190 - #  
191 - # Override in your subclasses. You can return the same types supported by  
192 - # #content.  
193 - def footer  
194 - nil  
195 - end  
196 -  
197 # Is this block editable? (Default to <tt>true</tt>) 184 # Is this block editable? (Default to <tt>true</tt>)
198 def editable?(user=nil) 185 def editable?(user=nil)
199 self.edit_modes == "all" 186 self.edit_modes == "all"
@@ -314,6 +301,14 @@ class Block &lt; ActiveRecord::Base @@ -314,6 +301,14 @@ class Block &lt; ActiveRecord::Base
314 self.observers << block 301 self.observers << block
315 end 302 end
316 303
  304 + def api_content
  305 + nil
  306 + end
  307 +
  308 + def display_api_content_by_default?
  309 + false
  310 + end
  311 +
317 private 312 private
318 313
319 def home_page_path 314 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/certifier.rb
@@ -1,35 +0,0 @@ @@ -1,35 +0,0 @@
1 -class Certifier < ActiveRecord::Base  
2 -  
3 - attr_accessible :name, :environment  
4 -  
5 - SEARCHABLE_FIELDS = {  
6 - :name => {:label => _('Name'), :weight => 10},  
7 - :description => {:label => _('Description'), :weight => 3},  
8 - :link => {:label => _('Link'), :weight => 1},  
9 - }  
10 -  
11 - belongs_to :environment  
12 -  
13 - has_many :qualifier_certifiers, :dependent => :destroy  
14 - has_many :qualifiers, :through => :qualifier_certifiers  
15 -  
16 - has_many :product_qualifiers  
17 - has_many :products, :through => :product_qualifiers, :source => :product  
18 -  
19 - validates_presence_of :environment_id  
20 - validates_presence_of :name  
21 -  
22 - def destroy  
23 - product_qualifiers.each { |pq| pq.update! :certifier => nil }  
24 - super  
25 - end  
26 -  
27 - def link  
28 - self[:link] || ''  
29 - end  
30 -  
31 - def <=>(b)  
32 - self.name.downcase.transliterate <=> b.name.downcase.transliterate  
33 - end  
34 -  
35 -end  
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'