Commit c22f58b9286d8d9e92cedcdb014dfec6e31c06a8

Authored by Leandro Santos
2 parents 69562568 41fa795f
Exists in staging and in 1 other branch production

Merge branch 'staging' of softwarepublico.gov.br:noosferogov/noosfero into staging

Showing 497 changed files with 4932 additions and 7914 deletions   Show diff stats

Too many changes.

To preserve performance only 100 of 497 files displayed.

app/api/app.rb 0 → 100644
... ... @@ -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 @@
  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 @@
  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,484 @@
  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_tmp_user
  23 + private_token = (params[PRIVATE_TOKEN_PARAM] || headers['Private-Token']).to_s
  24 + ## Get the "captcha" session store
  25 + @current_tmp_user = Noosfero::API::SessionStore.get("captcha##{private_token}")
  26 + @current_tmp_user
  27 + end
  28 +
  29 + def logout_tmp_user
  30 + @current_tmp_user = nil
  31 + end
  32 +
  33 + def current_user
  34 + private_token = (params[PRIVATE_TOKEN_PARAM] || headers['Private-Token']).to_s
  35 + @current_user ||= User.find_by private_token: private_token
  36 + @current_user ||= plugins.dispatch("api_custom_login", request).first
  37 + @current_user
  38 + end
  39 +
  40 + def current_person
  41 + current_user.person unless current_user.nil?
  42 + end
  43 +
  44 + def is_admin?(environment)
  45 + return false unless current_user
  46 + return current_person.is_admin?(environment)
  47 + end
  48 +
  49 + def logout
  50 + @current_user = nil
  51 + end
  52 +
  53 + def environment
  54 + @environment
  55 + end
  56 +
  57 + def present_partial(model, options)
  58 + if(params[:fields].present?)
  59 + begin
  60 + fields = JSON.parse(params[:fields])
  61 + if fields.present?
  62 + options.merge!(fields.symbolize_keys.slice(:only, :except))
  63 + end
  64 + rescue
  65 + fields = params[:fields]
  66 + fields = fields.split(',') if fields.kind_of?(String)
  67 + options[:only] = Array.wrap(fields)
  68 + end
  69 + end
  70 + present model, options
  71 + end
  72 +
  73 + include FindByContents
  74 +
  75 + ####################################################################
  76 + #### SEARCH
  77 + ####################################################################
  78 + def multiple_search?(searches=nil)
  79 + ['index', 'category_index'].include?(params[:action]) || (searches && searches.size > 1)
  80 + end
  81 + ####################################################################
  82 +
  83 + def logger
  84 + logger = Logger.new(File.join(Rails.root, 'log', "#{ENV['RAILS_ENV'] || 'production'}_api.log"))
  85 + logger.formatter = GrapeLogging::Formatters::Default.new
  86 + logger
  87 + end
  88 +
  89 + def limit
  90 + limit = params[:limit].to_i
  91 + limit = default_limit if limit <= 0
  92 + limit
  93 + end
  94 +
  95 + def period(from_date, until_date)
  96 + return nil if from_date.nil? && until_date.nil?
  97 +
  98 + begin_period = from_date.nil? ? Time.at(0).to_datetime : from_date
  99 + end_period = until_date.nil? ? DateTime.now : until_date
  100 +
  101 + begin_period..end_period
  102 + end
  103 +
  104 + def parse_content_type(content_type)
  105 + return nil if content_type.blank?
  106 + content_type.split(',').map do |content_type|
  107 + content_type.camelcase
  108 + end
  109 + end
  110 +
  111 + def find_article(articles, id)
  112 + article = articles.find(id)
  113 + article.display_to?(current_person) ? article : forbidden!
  114 + end
  115 +
  116 + def post_article(asset, params)
  117 + return forbidden! unless current_person.can_post_content?(asset)
  118 +
  119 + klass_type = params[:content_type] || params[:article].delete(:type) || TinyMceArticle.name
  120 + return forbidden! unless klass_type.constantize <= Article
  121 +
  122 + article = klass_type.constantize.new(params[:article])
  123 + article.last_changed_by = current_person
  124 + article.created_by= current_person
  125 + article.profile = asset
  126 +
  127 + if !article.save
  128 + render_api_errors!(article.errors.full_messages)
  129 + end
  130 + present_partial article, :with => Entities::Article
  131 + end
  132 +
  133 + def present_article(asset)
  134 + article = find_article(asset.articles, params[:id])
  135 + present_partial article, :with => Entities::Article, :params => params
  136 + end
  137 +
  138 + def present_articles_for_asset(asset, method = 'articles')
  139 + articles = find_articles(asset, method)
  140 + present_articles(articles)
  141 + end
  142 +
  143 + def present_articles(articles)
  144 + present_partial paginate(articles), :with => Entities::Article, :params => params
  145 + end
  146 +
  147 + def find_articles(asset, method = 'articles')
  148 + articles = select_filtered_collection_of(asset, method, params)
  149 + if current_person.present?
  150 + articles = articles.display_filter(current_person, nil)
  151 + else
  152 + articles = articles.published
  153 + end
  154 + articles
  155 + end
  156 +
  157 + def find_task(asset, id)
  158 + task = asset.tasks.find(id)
  159 + current_person.has_permission?(task.permission, asset) ? task : forbidden!
  160 + end
  161 +
  162 + def post_task(asset, params)
  163 + klass_type= params[:content_type].nil? ? 'Task' : params[:content_type]
  164 + return forbidden! unless klass_type.constantize <= Task
  165 +
  166 + task = klass_type.constantize.new(params[:task])
  167 + task.requestor_id = current_person.id
  168 + task.target_id = asset.id
  169 + task.target_type = 'Profile'
  170 +
  171 + if !task.save
  172 + render_api_errors!(task.errors.full_messages)
  173 + end
  174 + present_partial task, :with => Entities::Task
  175 + end
  176 +
  177 + def present_task(asset)
  178 + task = find_task(asset, params[:id])
  179 + present_partial task, :with => Entities::Task
  180 + end
  181 +
  182 + def present_tasks(asset)
  183 + tasks = select_filtered_collection_of(asset, 'tasks', params)
  184 + tasks = tasks.select {|t| current_person.has_permission?(t.permission, asset)}
  185 + return forbidden! if tasks.empty? && !current_person.has_permission?(:perform_task, asset)
  186 + present_partial tasks, :with => Entities::Task
  187 + end
  188 +
  189 + def make_conditions_with_parameter(params = {})
  190 + parsed_params = parser_params(params)
  191 + conditions = {}
  192 + from_date = DateTime.parse(parsed_params.delete(:from)) if parsed_params[:from]
  193 + until_date = DateTime.parse(parsed_params.delete(:until)) if parsed_params[:until]
  194 +
  195 + conditions[:type] = parse_content_type(parsed_params.delete(:content_type)) unless parsed_params[:content_type].nil?
  196 +
  197 + conditions[:created_at] = period(from_date, until_date) if from_date || until_date
  198 + conditions.merge!(parsed_params)
  199 +
  200 + conditions
  201 + end
  202 +
  203 + # changing make_order_with_parameters to avoid sql injection
  204 + def make_order_with_parameters(object, method, params)
  205 + order = "created_at DESC"
  206 + unless params[:order].blank?
  207 + if params[:order].include? '\'' or params[:order].include? '"'
  208 + order = "created_at DESC"
  209 + elsif ['RANDOM()', 'RANDOM'].include? params[:order].upcase
  210 + order = 'RANDOM()'
  211 + else
  212 + field_name, direction = params[:order].split(' ')
  213 + assoc = object.class.reflect_on_association(method.to_sym)
  214 + if !field_name.blank? and assoc
  215 + if assoc.klass.attribute_names.include? field_name
  216 + if direction.present? and ['ASC','DESC'].include? direction.upcase
  217 + order = "#{field_name} #{direction.upcase}"
  218 + end
  219 + end
  220 + end
  221 + end
  222 + end
  223 + return order
  224 + end
  225 +
  226 + def make_timestamp_with_parameters_and_method(params, method)
  227 + timestamp = nil
  228 + if params[:timestamp]
  229 + datetime = DateTime.parse(params[:timestamp])
  230 + table_name = method.to_s.singularize.camelize.constantize.table_name
  231 + timestamp = "#{table_name}.updated_at >= '#{datetime}'"
  232 + end
  233 +
  234 + timestamp
  235 + end
  236 +
  237 + def by_reference(scope, params)
  238 + reference_id = params[:reference_id].to_i == 0 ? nil : params[:reference_id].to_i
  239 + if reference_id.nil?
  240 + scope
  241 + else
  242 + created_at = scope.find(reference_id).created_at
  243 + scope.send("#{params.key?(:oldest) ? 'older_than' : 'younger_than'}", created_at)
  244 + end
  245 + end
  246 +
  247 + def by_categories(scope, params)
  248 + category_ids = params[:category_ids]
  249 + if category_ids.nil?
  250 + scope
  251 + else
  252 + scope.joins(:categories).where(:categories => {:id => category_ids})
  253 + end
  254 + end
  255 +
  256 + def by_period(scope, params, attribute)
  257 + from_param = "from_#{attribute}".to_sym
  258 + until_param = "until_#{attribute}".to_sym
  259 + from_date = DateTime.parse(params.delete(from_param)) if params[from_param]
  260 + until_date = DateTime.parse(params.delete(until_param)) if params[until_param]
  261 + scope = scope.where("#{attribute} >= ?", from_date) unless from_date.nil?
  262 + scope = scope.where("#{attribute} <= ?", until_date) unless until_date.nil?
  263 + scope
  264 + end
  265 +
  266 + def select_filtered_collection_of(object, method, params)
  267 + conditions = make_conditions_with_parameter(params)
  268 + order = make_order_with_parameters(object,method,params)
  269 + timestamp = make_timestamp_with_parameters_and_method(params, method)
  270 +
  271 + objects = object.send(method)
  272 + objects = by_reference(objects, params)
  273 + objects = by_categories(objects, params)
  274 + [:start_date, :end_date].each { |attribute| objects = by_period(objects, params, attribute) }
  275 +
  276 + objects = objects.where(conditions).where(timestamp).reorder(order)
  277 +
  278 + params[:page] ||= 1
  279 + params[:per_page] ||= limit
  280 + paginate(objects)
  281 + end
  282 +
  283 + def authenticate!
  284 + unauthorized! unless current_user
  285 + end
  286 +
  287 + # Allows the anonymous captcha user authentication
  288 + # to pass the check. Used by the articles/vote to allow
  289 + # the vote without login
  290 + def authenticate_allow_captcha!
  291 + unauthorized! unless current_tmp_user || current_user
  292 + end
  293 +
  294 + def profiles_for_person(profiles, person)
  295 + if person
  296 + profiles.listed_for_person(person)
  297 + else
  298 + profiles.visible
  299 + end
  300 + end
  301 +
  302 + # Checks the occurrences of uniqueness of attributes, each attribute must be present in the params hash
  303 + # or a Bad Request error is invoked.
  304 + #
  305 + # Parameters:
  306 + # keys (unique) - A hash consisting of keys that must be unique
  307 + def unique_attributes!(obj, keys)
  308 + keys.each do |key|
  309 + cant_be_saved_request!(key) if obj.find_by(key.to_s => params[key])
  310 + end
  311 + end
  312 +
  313 + def attributes_for_keys(keys)
  314 + attrs = {}
  315 + keys.each do |key|
  316 + attrs[key] = params[key] if params[key].present? or (params.has_key?(key) and params[key] == false)
  317 + end
  318 + attrs
  319 + end
  320 +
  321 + ##########################################
  322 + # error helpers #
  323 + ##########################################
  324 +
  325 + def not_found!
  326 + render_api_error!('404 Not found', 404)
  327 + end
  328 +
  329 + def forbidden!
  330 + render_api_error!('403 Forbidden', 403)
  331 + end
  332 +
  333 + def cant_be_saved_request!(attribute)
  334 + message = _("(Invalid request) %s can't be saved") % attribute
  335 + render_api_error!(message, 400)
  336 + end
  337 +
  338 + def bad_request!(attribute)
  339 + message = _("(Invalid request) %s not given") % attribute
  340 + render_api_error!(message, 400)
  341 + end
  342 +
  343 + def something_wrong!
  344 + message = _("Something wrong happened")
  345 + render_api_error!(message, 400)
  346 + end
  347 +
  348 + def unauthorized!
  349 + render_api_error!(_('Unauthorized'), 401)
  350 + end
  351 +
  352 + def not_allowed!
  353 + render_api_error!(_('Method Not Allowed'), 405)
  354 + end
  355 +
  356 + # javascript_console_message is supposed to be executed as console.log()
  357 + def render_api_error!(user_message, status, log_message = nil, javascript_console_message = nil)
  358 + message_hash = {'message' => user_message, :code => status}
  359 + message_hash[:javascript_console_message] = javascript_console_message if javascript_console_message.present?
  360 + log_msg = "#{status}, User message: #{user_message}"
  361 + log_msg = "#{log_message}, #{log_msg}" if log_message.present?
  362 + log_msg = "#{log_msg}, Javascript Console Message: #{javascript_console_message}" if javascript_console_message.present?
  363 + logger.error log_msg unless Rails.env.test?
  364 + error!(message_hash, status)
  365 + end
  366 +
  367 + def render_api_errors!(messages)
  368 + messages = messages.to_a if messages.class == ActiveModel::Errors
  369 + render_api_error!(messages.join(','), 400)
  370 + end
  371 +
  372 + ####################################################################
  373 + #### VOTE
  374 + ####################################################################
  375 + def do_vote(article, current_person, value)
  376 + begin
  377 + vote = Vote.new(:voteable => article, :voter => current_person, :vote => value)
  378 + return vote.save!
  379 + rescue ActiveRecord::RecordInvalid => e
  380 + render_api_error!(e.message, 400)
  381 + return false
  382 + end
  383 + end
  384 +
  385 + protected
  386 +
  387 + def set_session_cookie
  388 + cookies['_noosfero_api_session'] = { value: @current_user.private_token, httponly: true } if @current_user.present?
  389 + # Set also the private_token for the current_tmp_user
  390 + cookies['_noosfero_api_session'] = { value: @current_tmp_user.private_token, httponly: true } if @current_tmp_user.present?
  391 + end
  392 +
  393 + def setup_multitenancy
  394 + Noosfero::MultiTenancy.setup!(request.host)
  395 + end
  396 +
  397 + def detect_stuff_by_domain
  398 + @domain = Domain.by_name(request.host)
  399 + if @domain.nil?
  400 + @environment = Environment.default
  401 + if @environment.nil? && Rails.env.development?
  402 + # This should only happen in development ...
  403 + @environment = Environment.create!(:name => "Noosfero", :is_default => true)
  404 + end
  405 + else
  406 + @environment = @domain.environment
  407 + end
  408 + end
  409 +
  410 + def filter_disabled_plugins_endpoints
  411 + not_found! if Api::App.endpoint_unavailable?(self, @environment)
  412 + end
  413 +
  414 + def asset_with_image params
  415 + if params.has_key? :image_builder
  416 + asset_api_params = params
  417 + asset_api_params[:image_builder] = base64_to_uploadedfile(asset_api_params[:image_builder])
  418 + return asset_api_params
  419 + end
  420 + params
  421 + end
  422 +
  423 + def base64_to_uploadedfile(base64_image)
  424 + tempfile = base64_to_tempfile base64_image
  425 + converted_image = base64_image
  426 + converted_image[:tempfile] = tempfile
  427 + return {uploaded_data: ActionDispatch::Http::UploadedFile.new(converted_image)}
  428 + end
  429 +
  430 + def base64_to_tempfile base64_image
  431 + base64_img_str = base64_image[:tempfile]
  432 + decoded_base64_str = Base64.decode64(base64_img_str)
  433 + tempfile = Tempfile.new(base64_image[:filename])
  434 + tempfile.write(decoded_base64_str.encode("ascii-8bit").force_encoding("utf-8"))
  435 + tempfile.rewind
  436 + tempfile
  437 + end
  438 + private
  439 +
  440 + def parser_params(params)
  441 + parsed_params = {}
  442 + params.map do |k,v|
  443 + parsed_params[k.to_sym] = v if DEFAULT_ALLOWED_PARAMETERS.include?(k.to_sym)
  444 + end
  445 + parsed_params
  446 + end
  447 +
  448 + def default_limit
  449 + 20
  450 + end
  451 +
  452 + def parse_content_type(content_type)
  453 + return nil if content_type.blank?
  454 + content_types = content_type.split(',').map do |content_type|
  455 + content_type = content_type.camelcase
  456 + content_type == 'TextArticle' ? Article.text_article_types : content_type
  457 + end
  458 + content_types.flatten.uniq
  459 + end
  460 +
  461 + def period(from_date, until_date)
  462 + begin_period = from_date.nil? ? Time.at(0).to_datetime : from_date
  463 + end_period = until_date.nil? ? DateTime.now : until_date
  464 + begin_period..end_period
  465 + end
  466 +
  467 +
  468 + ##########################################
  469 + # captcha_helpers #
  470 + ##########################################
  471 +
  472 + def verify_captcha(remote_ip, params, environment)
  473 + captcha_plugin_enabled = plugins.dispatch(:verify_captcha, remote_ip, params, environment).map {|p| p if ! ( p===nil ) }
  474 + return true if captcha_plugin_enabled.size == 0
  475 + if captcha_plugin_enabled.size > 1
  476 + return render_api_error!(_("Error processing Captcha"), 500, nil, "More than one captcha plugin enabled")
  477 + end
  478 + test_result = captcha_plugin_enabled[0]
  479 + return true if test_result === true
  480 + render_api_error!(test_result[:user_message], test_result[:status], test_result[:log_message], test_result[:javascript_console_message])
  481 + end
  482 +
  483 + end
  484 +end
... ...
app/api/v1/activities.rb 0 → 100644
... ... @@ -0,0 +1,20 @@
  1 +module Api
  2 + module V1
  3 + class Activities < Grape::API
  4 + before { authenticate! }
  5 +
  6 + resource :profiles do
  7 +
  8 + get ':id/activities' do
  9 + profile = Profile.find_by id: params[:id]
  10 +
  11 + not_found! if profile.blank? || profile.secret || !profile.visible
  12 + forbidden! if !profile.secret && profile.visible && !profile.display_private_info_to?(current_person)
  13 +
  14 + activities = profile.activities.map(&:activity)
  15 + present activities, :with => Entities::Activity, :current_person => current_person
  16 + end
  17 + end
  18 + end
  19 + end
  20 +end
... ...
app/api/v1/articles.rb 0 → 100644
... ... @@ -0,0 +1,329 @@
  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 the articles followed by me"
  40 + get 'followed_by_me' do
  41 + present_articles_for_asset(current_person, 'following_articles')
  42 + end
  43 +
  44 + desc "Return one article by id" do
  45 + detail 'Get only one article by id. If not found the "forbidden" http error is showed'
  46 + params Entities::Article.documentation
  47 + success Entities::Article
  48 + failure [[403, 'Forbidden']]
  49 + named 'ArticleById'
  50 + end
  51 + get ':id', requirements: {id: /[0-9]+/} do
  52 + present_article(environment)
  53 + end
  54 +
  55 + post ':id' do
  56 + article = environment.articles.find(params[:id])
  57 + return forbidden! unless article.allow_edit?(current_person)
  58 + article.update_attributes!(asset_with_image(params[:article]))
  59 + present_partial article, :with => Entities::Article
  60 + end
  61 +
  62 + delete ':id' do
  63 + article = environment.articles.find(params[:id])
  64 + return forbidden! unless article.allow_delete?(current_person)
  65 + begin
  66 + article.destroy
  67 + { :success => true }
  68 + rescue Exception => exception
  69 + render_api_error!(_('The article couldn\'t be removed due to some problem. Please contact the administrator.'), 400)
  70 + end
  71 + end
  72 +
  73 + desc 'Report a abuse and/or violent content in a article by id' do
  74 + detail 'Submit a abuse (in general, a content violation) report about a specific article'
  75 + params Entities::Article.documentation
  76 + failure [[400, 'Bad Request']]
  77 + named 'ArticleReportAbuse'
  78 + end
  79 + post ':id/report_abuse' do
  80 + article = find_article(environment.articles, params[:id])
  81 + profile = article.profile
  82 + begin
  83 + abuse_report = AbuseReport.new(:reason => params[:report_abuse])
  84 + if !params[:content_type].blank?
  85 + article = params[:content_type].constantize.find(params[:content_id])
  86 + abuse_report.content = article_reported_version(article)
  87 + end
  88 +
  89 + current_person.register_report(abuse_report, profile)
  90 +
  91 + if !params[:content_type].blank?
  92 + abuse_report = AbuseReport.find_by reporter_id: current_person.id, abuse_complaint_id: profile.opened_abuse_complaint.id
  93 + Delayed::Job.enqueue DownloadReportedImagesJob.new(abuse_report, article)
  94 + end
  95 +
  96 + {
  97 + :success => true,
  98 + :message => _('Your abuse report was registered. The administrators are reviewing your report.'),
  99 + }
  100 + rescue Exception => exception
  101 + #logger.error(exception.to_s)
  102 + render_api_error!(_('Your report couldn\'t be saved due to some problem. Please contact the administrator.'), 400)
  103 + end
  104 +
  105 + end
  106 +
  107 + desc "Returns the articles I voted" do
  108 + detail 'Get the Articles I make a vote'
  109 + failure [[403, 'Forbidden']]
  110 + named 'ArticleFollowers'
  111 + end
  112 + #FIXME refactor this method
  113 + get 'voted_by_me' do
  114 + present_articles(current_person.votes.where(:voteable_type => 'Article').collect(&:voteable))
  115 + end
  116 +
  117 + desc 'Perform a vote on a article by id' do
  118 + detail 'Vote on a specific article with values: 1 (if you like) or -1 (if not)'
  119 + params Entities::UserLogin.documentation
  120 + failure [[401,'Unauthorized']]
  121 + named 'ArticleVote'
  122 + end
  123 + post ':id/vote' do
  124 + ## The vote api should allow regular login or with captcha
  125 + authenticate_allow_captcha!
  126 + value = (params[:value] || 1).to_i
  127 + # FIXME verify allowed values
  128 + render_api_error!('Vote value not allowed', 400) unless [-1, 1].include?(value)
  129 + article = find_article(environment.articles, params[:id])
  130 + ## If login with captcha
  131 + if @current_tmp_user
  132 + # Vote allowed only if data does not include this article
  133 + vote = (@current_tmp_user.data.include? article.id) ? false : true
  134 + if vote
  135 + @current_tmp_user.data << article.id
  136 + @current_tmp_user.store
  137 + {:vote => do_vote(article, current_person, value)}
  138 + else
  139 + {:vote => false}
  140 + end
  141 + else
  142 + {:vote => do_vote(article, current_person, value)}
  143 + end
  144 + end
  145 +
  146 +
  147 + desc "Returns the total followers for the article" do
  148 + detail 'Get the followers of a specific article by id'
  149 + failure [[403, 'Forbidden']]
  150 + named 'ArticleFollowers'
  151 + end
  152 + get ':id/followers' do
  153 + article = find_article(environment.articles, params[:id])
  154 + total = article.person_followers.count
  155 + {:total_followers => total}
  156 + end
  157 +
  158 + desc "Return the articles followed by me"
  159 + get 'followed_by_me' do
  160 + present_articles_for_asset(current_person, 'following_articles')
  161 + end
  162 +
  163 + desc "Add a follower for the article" do
  164 + detail 'Add the current user identified by private token, like a follower of a article'
  165 + params Entities::UserLogin.documentation
  166 + failure [[401, 'Unauthorized']]
  167 + named 'ArticleFollow'
  168 + end
  169 + post ':id/follow' do
  170 + authenticate!
  171 + article = find_article(environment.articles, params[:id])
  172 + if article.article_followers.exists?(:person_id => current_person.id)
  173 + {:success => false, :already_follow => true}
  174 + else
  175 + article_follower = ArticleFollower.new
  176 + article_follower.article = article
  177 + article_follower.person = current_person
  178 + article_follower.save!
  179 + {:success => true}
  180 + end
  181 + end
  182 +
  183 + desc 'Return the children of a article identified by id' do
  184 + detail 'Get all children articles of a specific article'
  185 + params Entities::Article.documentation
  186 + failure [[403, 'Forbidden']]
  187 + named 'ArticleChildren'
  188 + end
  189 +
  190 + paginate per_page: MAX_PER_PAGE, max_per_page: MAX_PER_PAGE
  191 + get ':id/children' do
  192 + article = find_article(environment.articles, params[:id])
  193 +
  194 + #TODO make tests for this situation
  195 + votes_order = params.delete(:order) if params[:order]=='votes_score'
  196 + articles = select_filtered_collection_of(article, 'children', params)
  197 + articles = articles.display_filter(current_person, article.profile)
  198 +
  199 + #TODO make tests for this situation
  200 + if votes_order
  201 + articles = articles.joins('left join votes on articles.id=votes.voteable_id').group('articles.id').reorder('sum(coalesce(votes.vote, 0)) DESC')
  202 + end
  203 + Article.hit(articles)
  204 + present_articles(articles)
  205 + end
  206 +
  207 + desc 'Return one child of a article identified by id' do
  208 + detail 'Get a child of a specific article'
  209 + params Entities::Article.documentation
  210 + success Entities::Article
  211 + failure [[403, 'Forbidden']]
  212 + named 'ArticleChild'
  213 + end
  214 + get ':id/children/:child_id' do
  215 + article = find_article(environment.articles, params[:id])
  216 + child = find_article(article.children, params[:child_id])
  217 + child.hit
  218 + present_partial child, :with => Entities::Article
  219 + end
  220 +
  221 + desc 'Suggest a article to another profile' do
  222 + detail 'Suggest a article to another profile (person, community...)'
  223 + params Entities::Article.documentation
  224 + success Entities::Task
  225 + failure [[401,'Unauthorized']]
  226 + named 'ArticleSuggest'
  227 + end
  228 + post ':id/children/suggest' do
  229 + authenticate!
  230 + parent_article = environment.articles.find(params[:id])
  231 +
  232 + suggest_article = SuggestArticle.new
  233 + suggest_article.article = params[:article]
  234 + suggest_article.article[:parent_id] = parent_article.id
  235 + suggest_article.target = parent_article.profile
  236 + suggest_article.requestor = current_person
  237 +
  238 + unless suggest_article.save
  239 + render_api_errors!(suggest_article.article_object.errors.full_messages)
  240 + end
  241 + present_partial suggest_article, :with => Entities::Task
  242 + end
  243 +
  244 + # Example Request:
  245 + # POST api/v1/articles/:id/children?private_token=234298743290432&article[name]=title&article[body]=body
  246 + desc 'Add a child article to a parent identified by id' do
  247 + detail 'Create a new article and associate to a parent'
  248 + params Entities::Article.documentation
  249 + success Entities::Article
  250 + failure [[401,'Unauthorized']]
  251 + named 'ArticleAddChild'
  252 + end
  253 + post ':id/children' do
  254 + parent_article = environment.articles.find(params[:id])
  255 + params[:article][:parent_id] = parent_article.id
  256 + post_article(parent_article.profile, params)
  257 + end
  258 + end
  259 +
  260 + resource :profiles do
  261 + get ':id/home_page' do
  262 + profiles = environment.profiles
  263 + profiles = profiles.visible_for_person(current_person)
  264 + profile = profiles.find_by id: params[:id]
  265 + present_partial profile.home_page, :with => Entities::Article
  266 + end
  267 + end
  268 +
  269 + kinds = %w[profile community person enterprise]
  270 + kinds.each do |kind|
  271 + resource kind.pluralize.to_sym do
  272 + segment "/:#{kind}_id" do
  273 + resource :articles do
  274 +
  275 + desc "Return all articles associate with a profile of type #{kind}" do
  276 + detail 'Get a list of articles of a profile'
  277 + params Entities::Article.documentation
  278 + success Entities::Article
  279 + failure [[403, 'Forbidden']]
  280 + named 'ArticlesOfProfile'
  281 + end
  282 + get do
  283 + profile = environment.send(kind.pluralize).find(params["#{kind}_id"])
  284 +
  285 + if params[:path].present?
  286 + article = profile.articles.find_by path: params[:path]
  287 + if !article || !article.display_to?(current_person)
  288 + article = forbidden!
  289 + end
  290 +
  291 + present_partial article, :with => Entities::Article
  292 + else
  293 +
  294 + present_articles_for_asset(profile)
  295 + end
  296 + end
  297 +
  298 + desc "Return a article associate with a profile of type #{kind}" do
  299 + detail 'Get only one article of a profile'
  300 + params Entities::Article.documentation
  301 + success Entities::Article
  302 + failure [[403, 'Forbidden']]
  303 + named 'ArticleOfProfile'
  304 + end
  305 + get ':id' do
  306 + profile = environment.send(kind.pluralize).find(params["#{kind}_id"])
  307 + present_article(profile)
  308 + end
  309 +
  310 + # Example Request:
  311 + # POST api/v1/{people,communities,enterprises}/:asset_id/articles?private_token=234298743290432&article[name]=title&article[body]=body
  312 + desc "Add a new article associated with a profile of type #{kind}" do
  313 + detail 'Create a new article and associate with a profile'
  314 + params Entities::Article.documentation
  315 + success Entities::Article
  316 + failure [[403, 'Forbidden']]
  317 + named 'ArticleCreateToProfile'
  318 + end
  319 + post do
  320 + profile = environment.send(kind.pluralize).find(params["#{kind}_id"])
  321 + post_article(profile, params)
  322 + end
  323 + end
  324 + end
  325 + end
  326 + end
  327 + end
  328 + end
  329 +end
... ...
app/api/v1/blocks.rb 0 → 100644
... ... @@ -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 @@
  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 @@
  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 @@
  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 @@
  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 @@
  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 @@
  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 @@
  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 @@
  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 @@
  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 @@
  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,190 @@
  1 +require "uri"
  2 +
  3 +module Api
  4 + module V1
  5 + class Session < Grape::API
  6 +
  7 + ################################
  8 + # => Login with captcha only
  9 + # This method will attempt to login the user using only the captcha.
  10 + # To do this, we generate a temporary in-memory user and generate a private
  11 + # token to it.
  12 + ################################
  13 + post "/login-captcha" do
  14 + remote_ip = (request.respond_to?(:remote_ip) && request.remote_ip) || (env && env['REMOTE_ADDR'])
  15 + # verify_captcha will render_api_error! and exit in case of any problem
  16 + # this return is just to improve the clarity of the execution path
  17 + return unless verify_captcha(remote_ip, params, environment)
  18 + ## Creates and caches a captcha session store
  19 + store = Noosfero::API::SessionStore.create("captcha")
  20 + ## Initialize the data for the session store
  21 + store.data = []
  22 + ## Put it back in cache
  23 + store.store
  24 + { "private_token" => "#{store.private_token}" }
  25 + end
  26 +
  27 + # Login to get token
  28 + #
  29 + # Parameters:
  30 + # login (*required) - user login or email
  31 + # password (required) - user password
  32 + #
  33 + # Example Request:
  34 + # POST http://localhost:3000/api/v1/login?login=adminuser&password=admin
  35 + post "/login" do
  36 + begin
  37 + user ||= User.authenticate(params[:login], params[:password], environment)
  38 + rescue User::UserNotActivated => e
  39 + render_api_error!(e.message, 401)
  40 + end
  41 +
  42 + return unauthorized! unless user
  43 + @current_user = user
  44 + present user, :with => Entities::UserLogin, :current_person => current_person
  45 + end
  46 +
  47 + post "/login_from_cookie" do
  48 + return unauthorized! if cookies[:auth_token].blank?
  49 + user = User.where(remember_token: cookies[:auth_token]).first
  50 + return unauthorized! unless user && user.activated?
  51 + @current_user = user
  52 + present user, :with => Entities::UserLogin, :current_person => current_person
  53 + end
  54 +
  55 + # Create user.
  56 + #
  57 + # Parameters:
  58 + # email (required) - Email
  59 + # password (required) - Password
  60 + # login - login
  61 + # Example Request:
  62 + # POST /register?email=some@mail.com&password=pas&password_confirmation=pas&login=some
  63 + params do
  64 + requires :email, type: String, desc: _("Email")
  65 + requires :login, type: String, desc: _("Login")
  66 + #requires :password, type: String, desc: _("Password")
  67 + #requires :password_confirmation, type: String, desc: _("Password confirmation")
  68 + end
  69 +
  70 + post "/register" do
  71 + attrs = attributes_for_keys [:email, :login, :password, :password_confirmation] + environment.signup_person_fields
  72 + remote_ip = (request.respond_to?(:remote_ip) && request.remote_ip) || (env && env['REMOTE_ADDR'])
  73 + # verify_captcha will render_api_error! and exit in case of any problem
  74 + # this return is just to improve the clarity of the execution path
  75 + return unless verify_captcha(remote_ip, params, environment)
  76 +
  77 + name = params[:name].present? ? params[:name] : attrs[:email]
  78 + attrs[:password_confirmation] = attrs[:password] if !attrs.has_key?(:password_confirmation)
  79 + user = User.new(attrs.merge(:name => name))
  80 +
  81 + begin
  82 + user.signup!
  83 + user.generate_private_token! if user.activated?
  84 + present user, :with => Entities::UserLogin, :current_person => user.person
  85 + rescue ActiveRecord::RecordInvalid
  86 + message = user.errors.as_json.merge((user.person.present? ? user.person.errors : {}).as_json).to_json
  87 + render_api_error!(message, 400)
  88 + end
  89 + end
  90 +
  91 + params do
  92 + requires :activation_code, type: String, desc: _("Activation token")
  93 + end
  94 +
  95 + # Activate a user.
  96 + #
  97 + # Parameter:
  98 + # activation_code (required) - Activation token
  99 + # Example Request:
  100 + # PATCH /activate?activation_code=28259abd12cc6a64ef9399cf3286cb998b96aeaf
  101 + patch "/activate" do
  102 + user = User.find_by activation_code: params[:activation_code]
  103 + if user
  104 + unless user.environment.enabled?('admin_must_approve_new_users')
  105 + if user.activate
  106 + user.generate_private_token!
  107 + present user, :with => Entities::UserLogin, :current_person => current_person
  108 + end
  109 + else
  110 + if user.create_moderate_task
  111 + user.activation_code = nil
  112 + user.save!
  113 +
  114 + # Waiting for admin moderate user registration
  115 + status 202
  116 + body({
  117 + :message => 'Waiting for admin moderate user registration'
  118 + })
  119 + end
  120 + end
  121 + else
  122 + # Token not found in database
  123 + render_api_error!(_('Token is invalid'), 412)
  124 + end
  125 + end
  126 +
  127 + # Request a new password.
  128 + #
  129 + # Parameters:
  130 + # value (required) - Email or login
  131 + # Example Request:
  132 + # POST /forgot_password?value=some@mail.com
  133 + post "/forgot_password" do
  134 + requestors = fetch_requestors(params[:value])
  135 + not_found! if requestors.blank?
  136 + remote_ip = (request.respond_to?(:remote_ip) && request.remote_ip) || (env && env['REMOTE_ADDR'])
  137 + # verify_captcha will render_api_error! and exit in case of any problem
  138 + # this return is just to improve the clarity of the execution path
  139 + return unless verify_captcha(remote_ip, params, environment)
  140 + requestors.each do |requestor|
  141 + ChangePassword.create!(:requestor => requestor)
  142 + end
  143 + end
  144 +
  145 + # Resend activation code.
  146 + #
  147 + # Parameters:
  148 + # value (required) - Email or login
  149 + # Example Request:
  150 + # POST /resend_activation_code?value=some@mail.com
  151 + post "/resend_activation_code" do
  152 + requestors = fetch_requestors(params[:value])
  153 + not_found! if requestors.blank?
  154 + remote_ip = (request.respond_to?(:remote_ip) && request.remote_ip) || (env && env['REMOTE_ADDR'])
  155 + # verify_captcha will render_api_error! and exit in case of any problem
  156 + # this return is just to improve the clarity of the execution path
  157 + return unless verify_captcha(remote_ip, params, environment)
  158 +
  159 + requestors.each do |requestor|
  160 + requestor.user.resend_activation_code
  161 + end
  162 + present requestors.map(&:user), :with => Entities::UserLogin
  163 + end
  164 +
  165 + params do
  166 + requires :code, type: String, desc: _("Forgot password code")
  167 + end
  168 + # Change password
  169 + #
  170 + # Parameters:
  171 + # code (required) - Change password code
  172 + # password (required)
  173 + # password_confirmation (required)
  174 + # Example Request:
  175 + # PATCH /new_password?code=xxxx&password=secret&password_confirmation=secret
  176 + patch "/new_password" do
  177 + change_password = ChangePassword.find_by code: params[:code]
  178 + not_found! if change_password.nil?
  179 +
  180 + if change_password.update_attributes(:password => params[:password], :password_confirmation => params[:password_confirmation])
  181 + change_password.finish
  182 + present change_password.requestor.user, :with => Entities::UserLogin, :current_person => current_person
  183 + else
  184 + something_wrong!
  185 + end
  186 + end
  187 +
  188 + end
  189 + end
  190 +end
... ...
app/api/v1/tags.rb 0 → 100644
... ... @@ -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 @@
  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 @@
  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,160 @@
  1 +module AuthenticatedSystem
  2 +
  3 + protected
  4 +
  5 + def self.included base
  6 + if base < ActionController::Base
  7 + base.around_filter :user_set_current
  8 + base.before_filter :login_from_cookie
  9 + end
  10 +
  11 + # Inclusion hook to make #current_user and #logged_in?
  12 + # available as ActionView helper methods.
  13 + base.helper_method :current_user, :logged_in?
  14 + end
  15 +
  16 + # Returns true or false if the user is logged in.
  17 + # Preloads @current_user with the user model if they're logged in.
  18 + def logged_in?
  19 + current_user != nil
  20 + end
  21 +
  22 + # Accesses the current user from the session.
  23 + def current_user
  24 + @current_user ||= begin
  25 + id = session[:user]
  26 + user = User.where(id: id).first if id
  27 + user.session = session if user
  28 + User.current = user
  29 + user
  30 + end
  31 + end
  32 +
  33 + # Store the given user in the session.
  34 + def current_user=(new_user)
  35 + if new_user.nil?
  36 + session.delete(:user)
  37 + else
  38 + session[:user] = new_user.id
  39 + new_user.session = session
  40 + new_user.register_login
  41 + end
  42 + @current_user = User.current = new_user
  43 + end
  44 +
  45 + # See impl. from http://stackoverflow.com/a/2513456/670229
  46 + def user_set_current
  47 + User.current = current_user
  48 + yield
  49 + ensure
  50 + # to address the thread variable leak issues in Puma/Thin webserver
  51 + User.current = nil
  52 + end
  53 +
  54 + # Check if the user is authorized.
  55 + #
  56 + # Override this method in your controllers if you want to restrict access
  57 + # to only a few actions or if you want to check if the user
  58 + # has the correct rights.
  59 + #
  60 + # Example:
  61 + #
  62 + # # only allow nonbobs
  63 + # def authorize?
  64 + # current_user.login != "bob"
  65 + # end
  66 + def authorized?
  67 + true
  68 + end
  69 +
  70 + # Filter method to enforce a login requirement.
  71 + #
  72 + # To require logins for all actions, use this in your controllers:
  73 + #
  74 + # before_filter :login_required
  75 + #
  76 + # To require logins for specific actions, use this in your controllers:
  77 + #
  78 + # before_filter :login_required, :only => [ :edit, :update ]
  79 + #
  80 + # To skip this in a subclassed controller:
  81 + #
  82 + # skip_before_filter :login_required
  83 + #
  84 + def login_required
  85 + username, passwd = get_auth_data
  86 + if username && passwd
  87 + self.current_user ||= User.authenticate(username, passwd) || nil
  88 + end
  89 + if logged_in? && authorized?
  90 + true
  91 + else
  92 + if params[:require_login_popup]
  93 + render :json => { :require_login_popup => true }
  94 + else
  95 + access_denied
  96 + end
  97 + end
  98 + end
  99 +
  100 + # Redirect as appropriate when an access request fails.
  101 + #
  102 + # The default action is to redirect to the login screen.
  103 + #
  104 + # Override this method in your controllers if you want to have special
  105 + # behavior in case the user is not authorized
  106 + # to access the requested action. For example, a popup window might
  107 + # simply close itself.
  108 + def access_denied
  109 + respond_to do |accepts|
  110 + accepts.html do
  111 + if request.xhr?
  112 + render :text => _('Access denied'), :status => 401
  113 + else
  114 + store_location
  115 + redirect_to :controller => '/account', :action => 'login'
  116 + end
  117 + end
  118 + accepts.xml do
  119 + headers["Status"] = "Unauthorized"
  120 + headers["WWW-Authenticate"] = %(Basic realm="Web Password")
  121 + render :text => "Could't authenticate you", :status => '401 Unauthorized'
  122 + end
  123 + end
  124 + false
  125 + end
  126 +
  127 + # Store the URI of the current request in the session.
  128 + #
  129 + # We can return to this location by calling #redirect_back_or_default.
  130 + def store_location(location = request.url)
  131 + session[:return_to] = location
  132 + end
  133 +
  134 + # Redirect to the URI stored by the most recent store_location call or
  135 + # to the passed default.
  136 + def redirect_back_or_default(default)
  137 + if session[:return_to]
  138 + redirect_to(session.delete(:return_to))
  139 + else
  140 + redirect_to(default)
  141 + end
  142 + end
  143 +
  144 + # When called with before_filter :login_from_cookie will check for an :auth_token
  145 + # cookie and log the user back in if apropriate
  146 + def login_from_cookie
  147 + return if cookies[:auth_token].blank? or logged_in?
  148 + user = User.where(remember_token: cookies[:auth_token]).first
  149 + self.current_user = user if user and user.remember_token?
  150 + end
  151 +
  152 + private
  153 + @@http_auth_headers = %w(X-HTTP_AUTHORIZATION HTTP_AUTHORIZATION Authorization)
  154 + # gets BASIC auth info
  155 + def get_auth_data
  156 + auth_key = @@http_auth_headers.detect { |h| request.env.has_key?(h) }
  157 + auth_data = request.env[auth_key].to_s.split unless auth_key.blank?
  158 + return auth_data && auth_data[0] == 'Basic' ? Base64.decode64(auth_data[1]).split(':')[0..1] : [nil, nil]
  159 + end
  160 +end
... ...
app/controllers/application_controller.rb
... ... @@ -18,6 +18,13 @@ class ApplicationController &lt; ActionController::Base
18 18 end
19 19 before_filter :redirect_to_current_user
20 20  
  21 + before_filter :set_session_theme
  22 + def set_session_theme
  23 + if params[:theme]
  24 + session[:theme] = environment.theme_ids.include?(params[:theme]) ? params[:theme] : nil
  25 + end
  26 + end
  27 +
21 28 def require_login_for_environment
22 29 login_required
23 30 end
... ...
app/controllers/my_profile/profile_themes_controller.rb
... ... @@ -63,12 +63,12 @@ class ProfileThemesController &lt; ThemesController
63 63 end
64 64  
65 65 def start_test
66   - session[:theme] = params[:id]
  66 + session[:user_theme] = params[:id]
67 67 redirect_to :controller => 'content_viewer', :profile => profile.identifier, :action => 'view_page'
68 68 end
69 69  
70 70 def stop_test
71   - session[:theme] = nil
  71 + session[:user_theme] = nil
72 72 redirect_to :action => 'index'
73 73 end
74 74  
... ...
app/controllers/public/api_controller.rb
... ... @@ -13,7 +13,7 @@ class ApiController &lt; PublicController
13 13 private
14 14  
15 15 def endpoints
16   - Noosfero::API::API.endpoints(environment)
  16 + Api::App.endpoints(environment)
17 17 end
18 18  
19 19 end
... ...
app/controllers/public/content_viewer_controller.rb
... ... @@ -213,7 +213,7 @@ class ContentViewerController &lt; ApplicationController
213 213 end
214 214  
215 215 if @page.published && @page.uploaded_file?
216   - redirect_to @page.public_filename
  216 + redirect_to "#{Noosfero.root}#{@page.public_filename}"
217 217 else
218 218 send_data data, @page.download_headers
219 219 end
... ...
app/helpers/application_helper.rb
... ... @@ -235,13 +235,6 @@ module ApplicationHelper
235 235 link_to(content_tag('span', text), url, html_options.merge(:class => the_class, :title => text))
236 236 end
237 237  
238   - def button_bar(options = {}, &block)
239   - options[:class].nil? ?
240   - options[:class]='button-bar' :
241   - options[:class]+=' button-bar'
242   - concat(content_tag('div', capture(&block).to_s + tag('br', :style => 'clear: left;'), options))
243   - end
244   -
245 238 def render_profile_actions klass
246 239 name = klass.to_s.underscore
247 240 begin
... ... @@ -353,7 +346,7 @@ module ApplicationHelper
353 346 end
354 347  
355 348 def is_testing_theme
356   - !controller.session[:theme].nil?
  349 + !controller.session[:user_theme].nil?
357 350 end
358 351  
359 352 def theme_owner
... ... @@ -602,8 +595,8 @@ module ApplicationHelper
602 595 end
603 596  
604 597 if block
605   - field_html ||= ''
606   - field_html += capture(&block)
  598 + field_html ||= ''.html_safe
  599 + field_html = [field_html, capture(&block)].safe_join
607 600 end
608 601  
609 602 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?)
... ... @@ -612,7 +605,9 @@ module ApplicationHelper
612 605 end
613 606 else
614 607 if profile.active_fields.include?(name)
615   - result = content_tag('div', field_html + profile_field_privacy_selector(profile, name), :class => 'field-with-privacy-selector')
  608 + result = content_tag :div, class: 'field-with-privacy-selector' do
  609 + [field_html, profile_field_privacy_selector(profile, name)].safe_join
  610 + end
616 611 end
617 612 end
618 613  
... ... @@ -620,10 +615,6 @@ module ApplicationHelper
620 615 result = required(result)
621 616 end
622 617  
623   - if block
624   - concat(result)
625   - end
626   -
627 618 result
628 619 end
629 620  
... ... @@ -868,7 +859,7 @@ module ApplicationHelper
868 859 alias :browse_communities_menu :search_communities_menu
869 860  
870 861 def pagination_links(collection, options={})
871   - 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)
  862 + 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)
872 863 will_paginate(collection, options)
873 864 end
874 865  
... ... @@ -990,6 +981,7 @@ module ApplicationHelper
990 981 values = {}
991 982 values.merge!(task.information[:variables]) if task.information[:variables]
992 983 values.merge!({:requestor => link_to(task.requestor.name, task.requestor.url)}) if task.requestor
  984 + values.merge!({:target => link_to(task.target.name, task.target.url)}) if (task.target && task.target.respond_to?(:url))
993 985 values.merge!({:subject => content_tag('span', task.subject, :class=>'task_target')}) if task.subject
994 986 values.merge!({:linked_subject => link_to(content_tag('span', task.linked_subject[:text], :class => 'task_target'), task.linked_subject[:url])}) if task.linked_subject
995 987 (task.information[:message] % values).html_safe
... ...
app/helpers/article_helper.rb
... ... @@ -188,9 +188,9 @@ module ArticleHelper
188 188 def following_button(page, user)
189 189 if !user.blank? and user != page.author
190 190 if page.is_followed_by? user
191   - button :cancel, unfollow_button_text(page), {:controller => 'profile', :action => 'unfollow_article', :article_id => page.id, :profile => page.profile.identifier}
  191 + button :cancel, unfollow_button_text(page), {controller: :profile, profile: page.profile.identifier, action: :unfollow_article, article_id: page.id}
192 192 else
193   - button :add, follow_button_text(page), {:controller => 'profile', :action => 'follow_article', :article_id => page.id, :profile => page.profile.identifier}
  193 + button :add, follow_button_text(page), {controller: :profile, profile: page.profile.identifier, action: :follow_article, article_id: page.id}
194 194 end
195 195 end
196 196 end
... ...
app/helpers/boxes_helper.rb
... ... @@ -99,15 +99,10 @@ module BoxesHelper
99 99 end
100 100  
101 101 def render_block_content block
102   - # FIXME: this conditional should be removed after all
103   - # block footer from plugins methods get refactored into helpers and views.
104   - # They are a failsafe until all of them are done.
105   - return block.content if block.method(:content).owner != Block
106 102 render_block block
107 103 end
108 104  
109 105 def render_block_footer block
110   - return block.footer if block.method(:footer).owner != Block
111 106 render_block block, 'footers/'
112 107 end
113 108  
... ...
app/helpers/buttons_helper.rb
1 1 module ButtonsHelper
  2 +
  3 + def button_bar(options = {}, &block)
  4 + options[:class] ||= ''
  5 + options[:class] << ' button-bar'
  6 +
  7 + content_tag :div, options do
  8 + [
  9 + capture(&block).to_s,
  10 + tag(:br, style: 'clear: left;'),
  11 + ].safe_join
  12 + end
  13 + end
  14 +
2 15 def button(type, label, url, html_options = {})
3 16 html_options ||= {}
4 17 the_class = 'with-text'
... ...
app/helpers/theme_loader_helper.rb
... ... @@ -2,8 +2,8 @@ module ThemeLoaderHelper
2 2 def current_theme
3 3 @current_theme ||=
4 4 begin
5   - if session[:theme]
6   - session[:theme]
  5 + if session[:user_theme]
  6 + session[:user_theme]
7 7 else
8 8 # utility for developers: set the theme to 'random' in development mode and
9 9 # you will get a different theme every request. This is interesting for
... ... @@ -11,8 +11,8 @@ module ThemeLoaderHelper
11 11 if Rails.env.development? && environment.theme == 'random'
12 12 @random_theme ||= Dir.glob('public/designs/themes/*').map { |f| File.basename(f) }.rand
13 13 @random_theme
14   - elsif Rails.env.development? && respond_to?(:params) && params[:theme] && File.exists?(Rails.root.join('public/designs/themes', params[:theme]))
15   - params[:theme]
  14 + elsif Rails.env.development? && respond_to?(:params) && params[:user_theme] && File.exists?(Rails.root.join('public/designs/themes', params[:user_theme]))
  15 + params[:user_theme]
16 16 else
17 17 if profile && !profile.theme.nil?
18 18 profile.theme
... ... @@ -34,8 +34,10 @@ module ThemeLoaderHelper
34 34 end
35 35  
36 36 def theme_path
37   - if session[:theme]
  37 + if session[:user_theme]
38 38 '/user_themes/' + current_theme
  39 + elsif session[:theme]
  40 + '/designs/themes/' + session[:theme]
39 41 else
40 42 '/designs/themes/' + current_theme
41 43 end
... ...
app/helpers/users_helper.rb
1 1 module UsersHelper
2 2  
3   - FILTER_TRANSLATION = {
  3 + def filter_translation
  4 + {
4 5 'all_users' => _('All users'),
5 6 'admin_users' => _('Admin users'),
6 7 'activated_users' => _('Activated users'),
7 8 'deactivated_users' => _('Deativated users'),
8   - }
  9 + }
  10 + end
9 11  
10 12 def filter_selector(filter, float = 'right')
11   - options = options_for_select(FILTER_TRANSLATION.map {|key, name| [name, key]}, :selected => filter)
  13 + options = options_for_select(filter_translation.map {|key, name| [name, key]}, :selected => filter)
12 14 url_params = url_for(params.merge(:filter => 'FILTER'))
13 15 onchange = "document.location.href = '#{url_params}'.replace('FILTER', this.value)"
14 16 select_field = select_tag(:filter, options, :onchange => onchange)
... ... @@ -19,7 +21,7 @@ module UsersHelper
19 21 end
20 22  
21 23 def users_filter_title(filter)
22   - FILTER_TRANSLATION[filter]
  24 + filter_translation[filter]
23 25 end
24 26  
25 27 end
... ...
app/jobs/activities_counter_cache_job.rb 0 → 100644
... ... @@ -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 @@
  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 @@
  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 @@
  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 @@
  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 @@
  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 @@
  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 @@
  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 @@
  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 @@
  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/models/add_member.rb
... ... @@ -37,6 +37,10 @@ class AddMember &lt; Task
37 37 true
38 38 end
39 39  
  40 + def reject_details
  41 + true
  42 + end
  43 +
40 44 def footer
41 45 true
42 46 end
... ... @@ -72,8 +76,9 @@ class AddMember &lt; Task
72 76 end
73 77  
74 78 def task_cancelled_message
75   - _("Your request to enter community \"%{target} with the profile \"%{requestor}\" was not accepted. Please contact any profile admin from %{url} for more information.") %
76   - {:target => self.target.name, :url => self.target.url,
77   - :requestor => self.requestor.name}
  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}
78 83 end
79 84 end
... ...
app/models/application_record.rb
1 1 class ApplicationRecord < ActiveRecord::Base
2 2  
3   - self.abstract_class = true
4   -
5   - def self.postgresql?
6   - self.connection.adapter_name == 'PostgreSQL'
7   - end
  3 + self.abstract_class = true
  4 + self.store_full_sti_class = true
8 5  
9 6 # an ActionView instance for rendering views on models
10 7 def self.action_view
... ... @@ -25,7 +22,7 @@ class ApplicationRecord &lt; ActiveRecord::Base
25 22 alias :meta_cache_key :cache_key
26 23 def cache_key
27 24 key = [Noosfero::VERSION, meta_cache_key]
28   - key.unshift(ApplicationRecord.connection.schema_search_path) if ApplicationRecord.postgresql?
  25 + key.unshift ApplicationRecord.connection.schema_search_path
29 26 key.join('/')
30 27 end
31 28  
... ...
app/models/article.rb
... ... @@ -29,6 +29,8 @@ class Article &lt; ApplicationRecord
29 29 :display => %w[full]
30 30 }
31 31  
  32 + N_('article')
  33 +
32 34 def initialize(*params)
33 35 super
34 36 if params.present? && params.first.present?
... ...
app/models/block.rb
... ... @@ -181,30 +181,6 @@ class Block &lt; ApplicationRecord
181 181 "/images/block_preview.png"
182 182 end
183 183  
184   - # Returns the content to be used for this block.
185   - #
186   - # This method can return several types of objects:
187   - #
188   - # * <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.
189   - # * <tt>Hash</tt>: the hash is used to build an URL that is used as the address for a IFRAME.
190   - # * <tt>Proc</tt>: the Proc is evaluated in the scope of BoxesHelper. The
191   - # block can then use <tt>render</tt>, <tt>link_to</tt>, etc.
192   - #
193   - # The method can also return <tt>nil</tt>, which means "no content".
194   - #
195   - # See BoxesHelper#extract_block_content for implementation details.
196   - def content(args={})
197   - "This is block number %d" % self.id
198   - end
199   -
200   - # A footer to be appended to the end of the block. Returns <tt>nil</tt>.
201   - #
202   - # Override in your subclasses. You can return the same types supported by
203   - # #content.
204   - def footer
205   - nil
206   - end
207   -
208 184 # Is this block editable? (Default to <tt>true</tt>)
209 185 def editable?(user=nil)
210 186 self.edit_modes == "all"
... ...
app/models/community.rb
... ... @@ -9,7 +9,7 @@ class Community &lt; Organization
9 9 _('Community')
10 10 end
11 11  
12   - N_('Community')
  12 + N_('community')
13 13 N_('Language')
14 14  
15 15 settings_items :language
... ...
app/models/enterprise.rb
... ... @@ -12,7 +12,7 @@ class Enterprise &lt; Organization
12 12 _('Enterprise')
13 13 end
14 14  
15   - N_('Enterprise')
  15 + N_('enterprise')
16 16  
17 17 acts_as_trackable after_add: proc{ |p, t| notify_activity t }
18 18  
... ...
app/models/environment.rb
... ... @@ -750,6 +750,10 @@ class Environment &lt; ApplicationRecord
750 750 end
751 751 end
752 752  
  753 + def theme_ids
  754 + settings[:themes] || []
  755 + end
  756 +
753 757 def themes=(values)
754 758 settings[:themes] = values
755 759 end
... ...
app/models/link_list_block.rb
... ... @@ -81,10 +81,8 @@ class LinkListBlock &lt; Block
81 81 end
82 82 end
83 83  
84   - def icons_options
85   - ICONS.map do |i|
86   - "<span title=\"#{i[1]}\" class=\"icon-#{i[0]}\" onclick=\"changeIcon(this, '#{i[0]}')\"></span>".html_safe
87   - end
  84 + def icons
  85 + ICONS
88 86 end
89 87  
90 88 end
... ...
app/models/person.rb
... ... @@ -13,6 +13,8 @@ class Person &lt; Profile
13 13 _('Person')
14 14 end
15 15  
  16 + N_('person')
  17 +
16 18 acts_as_trackable :after_add => Proc.new {|p,t| notify_activity(t)}
17 19 acts_as_accessor
18 20  
... ...
app/models/person_notifier.rb
... ... @@ -82,7 +82,7 @@ class PersonNotifier
82 82 helper ActionTrackerHelper
83 83  
84 84 def session
85   - {:theme => nil}
  85 + {:user_theme => nil}
86 86 end
87 87  
88 88 def content_summary(person, notifications, tasks)
... ...
app/models/profile.rb
... ... @@ -1145,6 +1145,11 @@ private :generate_url, :url_options
1145 1145 self.data[:fields_privacy]
1146 1146 end
1147 1147  
  1148 + # abstract
  1149 + def active_fields
  1150 + []
  1151 + end
  1152 +
1148 1153 def public_fields
1149 1154 self.active_fields
1150 1155 end
... ...
app/models/recent_documents_block.rb
... ... @@ -31,7 +31,7 @@ class RecentDocumentsBlock &lt; Block
31 31 end
32 32  
33 33 def api_content
34   - Noosfero::API::Entities::ArticleBase.represent(docs).as_json
  34 + Api::Entities::ArticleBase.represent(docs).as_json
35 35 end
36 36  
37 37 def display_api_content_by_default?
... ...
app/views/account/_login_form.html.erb
... ... @@ -11,7 +11,7 @@
11 11 <%= hidden_field_tag :terms_accepted, params[:terms_accepted] %>
12 12 <% end %>
13 13  
14   -<% button_bar do %>
  14 +<%= button_bar do %>
15 15 <%= submit_button( 'login', _('Log in') )%>
16 16 <%= modal_close_button _('Cancel') if request.xhr? %>
17 17 <% end %>
... ...
app/views/account/accept_terms.html.erb
... ... @@ -20,7 +20,7 @@
20 20 <%= hidden_field_tag :answer, params[:answer] %>
21 21  
22 22 <%= labelled_check_box(environment.terms_of_use_acceptance_text.blank? ? _('I read the terms of use and accepted them') : environment.terms_of_use_acceptance_text, :terms_accepted, '1', false, :id => 'accept-terms') %>
23   - <% button_bar do %>
  23 + <%= button_bar do %>
24 24 <%= button 'cancel', _('Cancel'), :controller => 'home', :action => 'index' %>
25 25 <%= submit_button 'forward', _('Continue'), {:disabled => true, :class => 'disabled', :id => 'submit-accept-terms'} %>
26 26 <% end %>
... ...
app/views/account/activation_question.html.erb
... ... @@ -24,7 +24,7 @@
24 24  
25 25 <div class='activation-box'>
26 26 <h2><%= _('Enterprise activation') + ' - ' + (logged_in? ? _('part 1 of 2') : _('part 1 of 3')) %></h2>
27   - <%= form_tag( {:action => 'accept_terms'}, {:method => 'get', :onsubmit => (@question == :foundation_year ? 'return check_valid_year(this)' : 'return check_valid_cnpj(this)')}) do %>
  27 + <%= form_tag( {:action => 'accept_terms'}, {:method => 'get', :onsubmit => (@question == :foundation_year ? 'return check_valid_year(this)' : 'return check_valid_cnpj(this)')}) do %>
28 28  
29 29 <p> <strong><%= _('Pay atention! You have only one chance!') %></strong> </p>
30 30  
... ... @@ -34,7 +34,7 @@
34 34  
35 35 <%= hidden_field_tag :enterprise_code, params[:enterprise_code] %>
36 36  
37   - <% button_bar do %>
  37 + <%= button_bar do %>
38 38 <%= button 'cancel', _('Cancel'), :action => 'index' %>
39 39 <%= submit_button 'forward', _('Continue') %>
40 40 <% end %>
... ...
app/views/account/forgot_password.html.erb
... ... @@ -9,7 +9,7 @@
9 9 <%= recaptcha_tags(:display => { :theme => 'clean' }, :ajax => true) %>
10 10  
11 11 <div>
12   - <% button_bar do %>
  12 + <%= button_bar do %>
13 13 <%= submit_button('send', _('Send instructions')) %>
14 14 <% end %>
15 15 </div>
... ...
app/views/account/login.html.erb
... ... @@ -22,7 +22,7 @@
22 22  
23 23 <%= safe_join(@plugins.dispatch(:login_extra_contents).collect { |content| instance_exec(&content) }, "") %>
24 24  
25   - <% button_bar do %>
  25 + <%= button_bar do %>
26 26 <%= submit_button( 'login', _('Log in') )%>
27 27 <% if is_popin %>
28 28 <%= modal_close_button(_('Cancel')) %>
... ... @@ -31,7 +31,7 @@
31 31  
32 32 <% end %>
33 33  
34   -<% button_bar do %>
  34 +<%= button_bar do %>
35 35 <% unless @plugins.dispatch(:allow_user_registration).include?(false) %>
36 36 <%= button :add, _("New user"), :controller => 'account', :action => 'signup' %>
37 37 <% end %>
... ...
app/views/account/login_block.html.erb
... ... @@ -17,7 +17,7 @@
17 17  
18 18 <%= safe_join(@plugins.dispatch(:login_extra_contents).collect { |content| instance_eval(&content) }, "") %>
19 19  
20   - <% button_bar do %>
  20 + <%= button_bar do %>
21 21 <%= submit_button( 'login', _('Log in') )%>
22 22 <% unless @plugins.dispatch(:allow_user_registration).include?(false) %>
23 23 <%= button(:add, _('New user'), { :controller => 'account', :action => 'signup' }) %>
... ...
app/views/account/logout_popup.html.erb
1 1 <h2><%= _('Are you sure you want to get out?') %></h2>
2 2 <p>
3   -<% button_bar do %>
  3 +<%= button_bar do %>
4 4 <%= button :ok, _('Yes'), { :controller => 'account', :action => 'logout' } %>
5 5 <%= modal_close_button _('No, I want to stay.') %>
6 6 <% end %>
... ...
app/views/account/new_password.html.erb
... ... @@ -10,7 +10,7 @@
10 10  
11 11 <%= labelled_form_field(_('Enter new password'), (f.password_field :password)) %>
12 12 <%= labelled_form_field(_('Confirm the new password'), (f.password_field :password_confirmation)) %>
13   - <% button_bar do %>
  13 + <%= button_bar do %>
14 14 <%= submit_button(:ok, _('Change password')) %>
15 15 <% end %>
16 16  
... ...
app/views/account/welcome.html.erb
... ... @@ -3,8 +3,8 @@
3 3  
4 4 <%= _('%s was successfuly activated. Now you may go to your control panel or to the control panel of your enterprise') % @enterprise.name %>
5 5  
6   - <% button_bar do %>
7   - <%= button 'forward', _('Go to my control panel'), :action => 'index', :controller => 'profile_editor', :profile => current_user.person.identifier %>
  6 + <%= button_bar do %>
  7 + <%= button 'forward', _('Go to my control panel'), :action => 'index', :controller => 'profile_editor', :profile => current_user.person.identifier %>
8 8 <%= button 'forward', _('Go to my enterprise control panel') % @enterprise.name, :action => 'index', :controller => 'profile_editor', :profile => @enterprise.identifier %>
9 9 <% end %>
10 10 <% end %>
... ...
app/views/admin_panel/message_for_disabled_enterprise.html.erb
... ... @@ -6,7 +6,7 @@
6 6  
7 7 <%= f.text_area :message_for_disabled_enterprise, :cols => 40, :style => 'width: 90%' %>
8 8  
9   - <% button_bar do %>
  9 + <%= button_bar do %>
10 10 <%= submit_button(:save, _('Save')) %>
11 11 <%= button(:cancel, _('Cancel'), :action => 'index') %>
12 12 <% end %>
... ...
app/views/admin_panel/set_portal_community.html.erb
... ... @@ -4,14 +4,14 @@
4 4 <%= form_tag do %>
5 5 <%= labelled_form_field(_('Portal identifier'), text_field_tag('portal_community_identifier', @portal_community.identifier, :size => 40) ) %>
6 6  
7   - <% button_bar do %>
  7 + <%= button_bar do %>
8 8 <%= submit_button 'save', _('Save'), :cancel => { :action => 'index' } %>
9 9 <% end %>
10 10 <% end %>
11 11 <% else %>
12   - <%= _('Portal identifier: %s') % link_to(@portal_community.identifier, @portal_community.url) %>
  12 + <%= _('Portal identifier: %s').html_safe % link_to(@portal_community.identifier, @portal_community.url) %>
13 13  
14   - <% button_bar do %>
  14 + <%= button_bar do %>
15 15 <%if @portal_community.environment.enabled?('use_portal_community') %>
16 16 <%= button 'cancel', _('Disable'), {:action => 'manage_portal_community', :activate => 0} %>
17 17 <% else %>
... ...
app/views/admin_panel/set_portal_folders.html.erb
... ... @@ -37,7 +37,7 @@
37 37 <%= _('The same order in which you arrange the folders here will be used for arranging the boxes in the environment initial page.') %>
38 38 </p>
39 39  
40   - <% button_bar do %>
  40 + <%= button_bar do %>
41 41 <%= submit_button 'save', _('Save'), :cancel => {:action => 'index'} %>
42 42 <% end %>
43 43 <% end %>
... ...
app/views/admin_panel/set_portal_news_amount.html.erb
... ... @@ -6,7 +6,7 @@
6 6 <%= labelled_form_field _('Number of portal news'), select(:environment, :portal_news_amount, (0..10).to_a) %>
7 7 <%= labelled_form_field _('Number of news by folder'), select(:environment, :news_amount_by_folder, (1..10).to_a) %>
8 8  
9   - <% button_bar do %>
  9 + <%= button_bar do %>
10 10 <%= submit_button(:save, _('Save')) %>
11 11 <%= button(:cancel, _('Cancel'), :action => 'index') %>
12 12 <% end %>
... ...
app/views/admin_panel/site_info.html.erb
... ... @@ -20,7 +20,7 @@
20 20 <% tabs << {:title => _('Signup introduction text'), :id => 'signup-intro',
21 21 :content => (render :partial => 'signup_intro', :locals => {:f => f})} %>
22 22 <%= render_tabs(tabs) %>
23   - <% button_bar do %>
  23 + <%= button_bar do %>
24 24 <%= submit_button(:save, _('Save'), :cancel => {:action => 'index'}) %>
25 25 <% end %>
26 26 <% end %>
... ...
app/views/api/playground.html.erb
1 1 <h1>API Playground</h1>
2 2  
3 3 <script>
4   -<% prefix = Noosfero::API::API.prefix %>
  4 +<% prefix = Api::App.prefix %>
5 5 var prefix = <%= prefix.to_json %>;
6 6 var endpoints = <%=
7 7 endpoints.map do |endpoint|
... ...
app/views/box_organizer/_icon_selector.html.erb
... ... @@ -2,6 +2,8 @@
2 2 <%= hidden_field_tag 'block[links][][icon]', icon %>
3 3 <span class='icon-<%= icon %>' style='display:block; width:16px; height:16px;'></span>
4 4 <div class="icon-selector" style='display:none;'>
5   - <%= @block.icons_options.join %>
  5 + <% @block.icons.map do |i| %>
  6 + <%= content_tag('span', '', :title => i[1], :class => "icon-#{i[0]}", :onclick => "changeIcon(this, '#{i[0]}')") %>
  7 + <% end %>
6 8 </div>
7 9 </div>
... ...
app/views/box_organizer/edit.html.erb
... ... @@ -35,7 +35,7 @@
35 35 </div>
36 36 <% end %>
37 37  
38   - <% button_bar do %>
  38 + <%= button_bar do %>
39 39 <%= submit_button(:save, _('Save')) %>
40 40 <%= modal_close_button(_('Cancel')) %>
41 41 <% end %>
... ...
app/views/box_organizer/index.html.erb
... ... @@ -3,7 +3,7 @@
3 3  
4 4 <h1><%= _('Editing sideboxes')%></h1>
5 5  
6   -<% button_bar :class=>'design-menu' do %>
  6 +<%= button_bar :class=>'design-menu' do %>
7 7 <%= button(:back, _('Back to control panel'), :controller => (profile.nil? ? 'admin_panel': 'profile_editor')) %>
8 8 <% end %>
9 9  
... ...
app/views/categories/_form.html.erb
... ... @@ -28,7 +28,7 @@
28 28 <%= file_field_or_thumbnail(_('Image:'), @category.image, i) %>
29 29 <% end %>
30 30  
31   - <% button_bar do %>
  31 + <%= button_bar do %>
32 32 <%= submit_button('save', _('Save'), :cancel => {:action => 'index'}) %>
33 33 <% end%>
34 34 <% end %>
... ...
app/views/cms/_media_new_folder.html.erb
... ... @@ -9,7 +9,7 @@
9 9 <%= labelled_radio_button _('Folder'), :folder_type, 'Folder', false %>
10 10  
11 11 <%= labelled_form_field _('Name:'), text_field_tag(:new_folder, nil, 'data-url' => url_for({:action => 'new', :profile => profile.identifier})) %>
12   - <% button_bar do %>
  12 + <%= button_bar do %>
13 13 <%= submit_button(:newfolder, _('Create')) %>
14 14 <% end %>
15 15 <% end %>
... ...
app/views/cms/_text_fields.html.erb
1   -<%= labelled_form_field(_('Publish date'), date_field('article[published_at]', @article.published_at || DateTime.current, {:max_date => '+0d', :date_format => 'yy-mm-dd'}, {:id => "article_published_at"})) %>
  1 +<%= labelled_form_field(_('Publish date'), date_field('article[published_at]', @article.published_at || DateTime.current, {:max_date => '+0d', :time => true}, {:id => "article_published_at"})) %>
... ...
app/views/cms/_upload_file_form.html.erb
... ... @@ -12,7 +12,7 @@
12 12  
13 13 <%= hidden_field_tag('back_to', @back_to) %>
14 14  
15   -<% button_bar do %>
  15 +<%= button_bar do %>
16 16 <%= button_to_function :add, _('More files'), "add_new_file_fields()" %>
17 17 <% if @back_to %>
18 18 <%= submit_button :save, _('Upload'), :cancel => @back_to %>
... ...
app/views/cms/destroy.html.erb
... ... @@ -12,7 +12,7 @@
12 12 <% end %>
13 13 </strong>
14 14  
15   - <% button_bar do %>
  15 + <%= button_bar do %>
16 16 <%= submit_button :save, _('Yes, I want.') %>
17 17 <%= button :cancel, _("No, I don't want."), @article.url %>
18 18 <% end %>
... ...
app/views/cms/edit.html.erb
... ... @@ -21,7 +21,7 @@
21 21 </div>
22 22 <% end %>
23 23  
24   - <% button_bar do %>
  24 + <%= button_bar do %>
25 25 <%= submit_button :save, _('Save') %>
26 26 <%= submit_button :save, _('Save and continue'), :name => "continue" %>
27 27 <% end %>
... ... @@ -48,7 +48,7 @@
48 48 <%= options_for_article(@article, @tokenized_children) %>
49 49 </div>
50 50  
51   - <% button_bar do %>
  51 + <%= button_bar do %>
52 52 <%= submit_button :save, _('Save') %>
53 53  
54 54 <% if @back_to %>
... ...
app/views/cms/publish.html.erb
... ... @@ -25,7 +25,7 @@
25 25 <%= hidden_field_tag :back_to, @back_to %>
26 26 <%= labelled_form_field _('Title'), text_field_tag('name', @article.name) %>
27 27  
28   - <% button_bar do %>
  28 + <%= button_bar do %>
29 29 <%= submit_button 'spread', _('Spread this') %>
30 30 <% end %>
31 31 <% end %>
... ... @@ -41,7 +41,7 @@
41 41 <% search_action = url_for(:action => 'search_communities_to_publish') %>
42 42 <%= token_input_field_tag(:q, 'search-communities-to-publish', search_action, { :hint_text => _('Type in a search for your community'), :zindex => 10000, :focus => false }) %>
43 43 <%= labelled_form_field _('Title'), text_field_tag('name', @article.name) %>
44   - <% button_bar do %>
  44 + <%= button_bar do %>
45 45 <%= submit_button 'spread', _('Spread this') %>
46 46 <% end %>
47 47 <% end %>
... ... @@ -58,7 +58,7 @@
58 58 <%= hidden_field_tag :back_to, @back_to %>
59 59 <%= labelled_form_field _('Title'), text_field_tag('name', @article.name) %>
60 60  
61   - <% button_bar do %>
  61 + <%= button_bar do %>
62 62 <%= submit_button 'spread', _('Spread this') %>
63 63 <% end %>
64 64 <% end %>
... ...
app/views/cms/publish_on_portal_community.html.erb
... ... @@ -5,7 +5,7 @@
5 5 <%= hidden_field_tag :back_to, @back_to %>
6 6 <%= labelled_text_field _('Title') + ': ', :name, @article.name, :style => 'width: 100%' %>
7 7  
8   - <% button_bar do %>
  8 + <%= button_bar do %>
9 9 <%= submit_button 'spread', _('Spread this'), :cancel => @back_to %>
10 10 <% end %>
11 11 <% end %>
... ... @@ -14,7 +14,7 @@
14 14 <%= _("There is no portal community in this environment.") %>
15 15 </div>
16 16  
17   - <% button_bar do %>
  17 + <%= button_bar do %>
18 18 <%= button :back, _('Back'), :controller => 'cms' %>
19 19 <% end %>
20 20 <% end %>
... ...
app/views/cms/suggest_an_article.html.erb
... ... @@ -23,7 +23,7 @@
23 23  
24 24 <%= recaptcha_tags(:display => { :theme => 'clean' }, :ajax => true) unless logged_in? %>
25 25  
26   - <% button_bar do %>
  26 + <%= button_bar do %>
27 27 <%= submit_button :save, _('Save') %>
28 28 <%= button :cancel, _('Cancel'), @back_to %>
29 29 <% end %>
... ...
app/views/cms/view.html.erb
... ... @@ -14,7 +14,7 @@
14 14 </div>
15 15 <% end %>
16 16  
17   -<% button_bar(:style => 'margin-bottom: 1em;') do %>
  17 +<%= button_bar(:style => 'margin-bottom: 1em;') do %>
18 18 <% parent_id = ((@article && @article.allow_children?) ? @article : nil) %>
19 19  
20 20 <%= modal_button('new', _('New content'), url_for({:action => 'new', :parent_id => parent_id, :cms => true}).html_safe) %>
... ...
app/views/cms/why_categorize.html.erb
... ... @@ -4,6 +4,6 @@
4 4 <%= _('By categorizing your content, you increase the possibility that other people access it. When they are looking for, say, articles about Bahia and you categorize your article in "Regions/Bahia", then there is a good change that your article is going to be found.') %>
5 5 </p>
6 6  
7   -<% button_bar do %>
  7 +<%= button_bar do %>
8 8 <%= modal_close_button _('Close') %>
9 9 <% end %>
... ...
app/views/comment/_comment_form.html.erb
... ... @@ -8,7 +8,7 @@
8 8 <div id="recaptcha-container" style="display: none">
9 9 <h3><%= _('Please type the two words below') %></h3>
10 10 <%= recaptcha_tags(:display => { :theme => 'clean' }, :ajax => true) %>
11   - <% button_bar do %>
  11 + <%= button_bar do %>
12 12 <%= button_to_function :add, _('Confirm'), "return false", :id => "confirm-captcha" %>
13 13 <%= button_to_function :cancel, _('Cancel'), "noosfero.modal.close()" %>
14 14 <% end %>
... ... @@ -87,7 +87,7 @@ function check_captcha(button, confirm_action) {
87 87  
88 88 <%= safe_join(@plugins.dispatch(:comment_form_extra_contents, local_assigns.merge(:comment => @comment)).collect { |content| instance_exec(&content) }, "") %>
89 89  
90   - <% button_bar do %>
  90 + <%= button_bar do %>
91 91 <%= submit_button('add', _('Post comment'), :onclick => "if(check_captcha(this)) { save_comment(this) } else { check_captcha(this, save_comment)};return false;") %>
92 92 <% if !edition_mode %>
93 93 <%= button :cancel, _('Cancel'), '', :id => 'cancel-comment' %>
... ...
app/views/content_viewer/_comment_form.html.erb
... ... @@ -36,7 +36,7 @@ function submit_comment_form(button) {
36 36 <div id="recaptcha-container" style="display: none">
37 37 <h3><%= _('Please type the two words below') %></h3>
38 38 <%= recaptcha_tags(:display => { :theme => 'clean' }, :ajax => true) %>
39   - <% button_bar do %>
  39 + <%= button_bar do %>
40 40 <%= button_to_function :add, _('Confirm'), "return false", :id => "confirm-captcha" %>
41 41 <%= button_to_function :cancel, _('Cancel'), "noosfero.modal.close()" %>
42 42 <% end %>
... ... @@ -73,7 +73,7 @@ function submit_comment_form(button) {
73 73 <%= hidden_field_tag(:confirm, 'false') %>
74 74 <%= hidden_field_tag(:view, params[:view])%>
75 75  
76   - <% button_bar do %>
  76 + <%= button_bar do %>
77 77 <%= submit_button('add', _('Post comment'), :onclick => "submit_comment_form(this); return false") %>
78 78 <% if cancel_triggers_hide %>
79 79 <%= button :cancel, _('Cancel'), '', :id => 'cancel-comment' %>
... ...
app/views/content_viewer/_confirm_unfollow.html.erb
... ... @@ -5,7 +5,7 @@
5 5 <%= form_tag(@page.view_url.merge({:only_path => true}), {:method => 'post', :class => 'comment_form'}) do %>
6 6 <%= hidden_field_tag(:unfollow, 'commit') %>
7 7 <%= labelled_form_field(_('Enter your e-Mail'), text_field_tag(:email, nil, {:size => 40})) %>
8   - <% button_bar do %>
  8 + <%= button_bar do %>
9 9 <%= submit_button(:ok, _('Cancel notifications for e-mail above') ) %>
10 10 <% end %>
11 11 <% end %>
... ...
app/views/content_viewer/blog_page.html.erb
... ... @@ -4,7 +4,7 @@
4 4  
5 5 <div>
6 6 <div class='blog-description'>
7   - <%= blog.body %>
  7 + <%= (blog.body || '').html_safe %>
8 8 </div>
9 9 </div>
10 10 <hr class="pre-posts"/>
... ...
app/views/content_viewer/forum_page.html.erb
... ... @@ -16,7 +16,7 @@
16 16 <p><%= @page.terms_of_use %></p>
17 17  
18 18 <%= form_tag @page.url.merge(:terms_accepted => true) do %>
19   - <% button_bar do %>
  19 + <%= button_bar do %>
20 20 <% if user %>
21 21 <%= submit_button :save, _("Accept") %>
22 22 <% else %>
... ...
app/views/enterprise_registration/basic_information.html.erb
... ... @@ -8,7 +8,7 @@
8 8 <%= _('There are no validators to validate the registration of this new enterprise. Contact your administrator for instructions.') %>
9 9 </div>
10 10  
11   - <% button_bar do %>
  11 + <%= button_bar do %>
12 12 <%= button :back, _('Go back'), { :profile => current_user.person.identifier, :action=>"enterprises", :controller=>"profile" }%>
13 13 <% end %>
14 14 <% else %>
... ... @@ -36,7 +36,7 @@
36 36  
37 37 <%= template_options(:enterprises, 'create_enterprise')%>
38 38  
39   - <% button_bar do %>
  39 + <%= button_bar do %>
40 40 <%= submit_button('next', _('Next'), :cancel => {:profile => current_user.person.identifier, :action=>"enterprises", :controller=>"profile"}) %>
41 41 <% end %>
42 42 <% end %>
... ...
app/views/enterprise_registration/creation.html.erb
... ... @@ -4,7 +4,7 @@
4 4  
5 5 <%= render :partial => 'shared/template_welcome_page', :locals => {:template => @enterprise.template, :header => _("What can I do with a %s?")} %>
6 6  
7   -<% button_bar do %>
  7 +<%= button_bar do %>
8 8 <%= button :back, _('Back'), {:controller => 'memberships', :action => 'index', :profile => user.identifier} %>
9 9 <% end %>
10 10  
... ...
app/views/enterprise_registration/select_validator.html.erb
... ... @@ -22,7 +22,7 @@
22 22 <% end %>
23 23 </table>
24 24  
25   - <% button_bar do %>
  25 + <%= button_bar do %>
26 26 <%= submit_button 'save', _('Confirm') %>
27 27 <% end %>
28 28 <% end %>
... ...
app/views/enterprise_validation/details.html.erb
... ... @@ -15,7 +15,7 @@
15 15 <p><%= _('If this enterprise passes the criteria to be considered an solidarity enconomy enterprise, you can approve it by click the button below.') %></p>
16 16  
17 17 <%= form_tag :action => 'approve', :id => @pending.code do %>
18   - <% button_bar do %>
  18 + <%= button_bar do %>
19 19 <%= submit_button('approve', _('Approve')) %>
20 20 <% end %>
21 21 <% end %>
... ... @@ -27,7 +27,7 @@
27 27 <%= form_tag :action => 'reject', :id => @pending.code do %>
28 28 <%= labelled_form_field(_('Please provide an explanation for the rejection. This explanation will be sent to the requestor (required).'), text_area_tag('reject_explanation'))%>
29 29 <div>
30   - <% button_bar do %>
  30 + <%= button_bar do %>
31 31 <%= submit_button('reject', _('Reject')) %>
32 32 <% end %>
33 33 </div>
... ...
app/views/enterprise_validation/edit_validation_info.html.erb
... ... @@ -5,7 +5,7 @@
5 5 <%= labelled_form_for :info do |f| %>
6 6 <%= f.text_area(:validation_methodology, :cols => 50, :rows => 10) %>
7 7 <%= f.text_area(:restrictions, :cols => 50, :rows => 7) %>
8   - <% button_bar do %>
  8 + <%= button_bar do %>
9 9 <%= submit_button('save', _('Save'), :cancel => {:action => 'index'}) %>
10 10 <% end %>
11 11 <% end %>
... ...
app/views/enterprise_validation/index.html.erb
1 1 <h1><%= _('Enterprise validations') %></h1>
2 2  
3   -<% button_bar do %>
  3 +<%= button_bar do %>
4 4 <%= button(:edit, _('Edit validation info'), { :action => 'edit_validation_info' }) %>
5 5 <%= button(:back, _('Go Back'), { :controller => 'profile_editor' }) %>
6 6 <% end %>
... ...
app/views/environment_role_manager/affiliate.html.erb
... ... @@ -3,7 +3,7 @@
3 3 <%= form_tag( {:action => 'give_role'}, {:method => :post}) do %>
4 4 <%= select_tag 'role', options_for_select(@roles.map{|r|[r.name,r.id]}) %>
5 5 <%= hidden_field_tag 'person', current_user.person.id %>
6   - <% button_bar do %>
  6 + <%= button_bar do %>
7 7 <%= submit_button('affiliate', _('Affiliate', :cancel => {:action => 'index'}) %>
8 8 <% end %>
9 9 <% end %>
... ...
app/views/environment_role_manager/change_role.html.erb
... ... @@ -7,7 +7,7 @@
7 7 <% end %>
8 8 <%= hidden_field_tag 'person', @admin.id %>
9 9  
10   - <% button_bar do %>
  10 + <%= button_bar do %>
11 11 <%= submit_button('save', _('Save changes'), :cancel => {:action => 'index'}) %>
12 12 <% end %>
13 13 <% end %>
... ...
app/views/environment_role_manager/make_admin.html.erb
... ... @@ -9,7 +9,7 @@
9 9 <% @roles.each do |r| %>
10 10 <%= labelled_form_field(r.name, (check_box_tag "roles[]", r.id)) %>
11 11 <% end %>
12   - <% button_bar do %>
  12 + <%= button_bar do %>
13 13 <%= submit_button( 'save', _('Make'), :cancel => {:action => 'index'} ) %>
14 14 <% end %>
15 15 <% end %>
... ...
app/views/environment_themes/index.html.erb
... ... @@ -3,6 +3,6 @@
3 3  
4 4 <br style="clear:both" />
5 5  
6   -<% button_bar do %>
  6 +<%= button_bar do %>
7 7 <%= button(:back, _('Back'), :controller => 'admin_panel', :action => 'index') %>
8 8 <% end %>
... ...
app/views/favorite_enterprises/index.html.erb
... ... @@ -23,7 +23,7 @@
23 23 </p>
24 24 <% end %>
25 25  
26   -<% button_bar do %>
  26 +<%= button_bar do %>
27 27 <%= button(:back, _('Go back'), :controller => 'profile_editor') %>
28 28 <% end %>
29 29  
... ...
app/views/features/_manage_community_fields.html.erb
... ... @@ -55,7 +55,7 @@
55 55 </script>
56 56  
57 57 <div>
58   - <% button_bar do %>
  58 + <%= button_bar do %>
59 59 <%= submit_button('save', _('Save changes'), :id=>"save_community_fields") %>
60 60 <%= button :back, _('Back to admin panel'), :controller => 'admin_panel', :action => 'index' %>
61 61 <% end %>
... ...