Commit d71ef742d7548e843701028bfc0b79682afe68e6

Authored by Braulio Bhavamitra
1 parent da1968e4

api: move to app/api

Showing 59 changed files with 1992 additions and 2041 deletions   Show diff stats
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
  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,267 @@
  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
  97 + end
  98 +
  99 + class Profile < Entity
  100 + expose :identifier, :name, :id
  101 + expose :created_at, :format_with => :timestamp
  102 + expose :updated_at, :format_with => :timestamp
  103 + expose :additional_data do |profile, options|
  104 + hash ={}
  105 + profile.public_values.each do |value|
  106 + hash[value.custom_field.name]=value.value
  107 + end
  108 +
  109 + private_values = profile.custom_field_values - profile.public_values
  110 + private_values.each do |value|
  111 + if Entities.can_display_profile_field?(profile,options)
  112 + hash[value.custom_field.name]=value.value
  113 + end
  114 + end
  115 + hash
  116 + end
  117 + expose :image, :using => Image
  118 + expose :region, :using => Region
  119 + expose :type
  120 + expose :custom_header
  121 + expose :custom_footer
  122 + end
  123 +
  124 + class UserBasic < Entity
  125 + expose :id
  126 + expose :login
  127 + end
  128 +
  129 + class Person < Profile
  130 + root 'people', 'person'
  131 + expose :user, :using => UserBasic, documentation: {type: 'User', desc: 'The user data of a person' }
  132 + expose :vote_count
  133 + expose :comments_count do |person, options|
  134 + person.comments.count
  135 + end
  136 + expose :following_articles_count do |person, options|
  137 + person.following_articles.count
  138 + end
  139 + expose :articles_count do |person, options|
  140 + person.articles.count
  141 + end
  142 + end
  143 +
  144 + class Enterprise < Profile
  145 + root 'enterprises', 'enterprise'
  146 + end
  147 +
  148 + class Community < Profile
  149 + root 'communities', 'community'
  150 + expose :description
  151 + expose :admins, :if => lambda { |community, options| community.display_info_to? options[:current_person]} do |community, options|
  152 + community.admins.map{|admin| {"name"=>admin.name, "id"=>admin.id, "username" => admin.identifier}}
  153 + end
  154 + expose :categories, :using => Category
  155 + expose :members, :using => Person , :if => lambda{ |community, options| community.display_info_to? options[:current_person] }
  156 + end
  157 +
  158 + class CommentBase < Entity
  159 + expose :body, :title, :id
  160 + expose :created_at, :format_with => :timestamp
  161 + expose :author, :using => Profile
  162 + expose :reply_of, :using => CommentBase
  163 + end
  164 +
  165 + class Comment < CommentBase
  166 + root 'comments', 'comment'
  167 + expose :children, as: :replies, :using => Comment
  168 + end
  169 +
  170 + class ArticleBase < Entity
  171 + root 'articles', 'article'
  172 + expose :id
  173 + expose :body
  174 + expose :abstract, documentation: {type: 'String', desc: 'Teaser of the body'}
  175 + expose :created_at, :format_with => :timestamp
  176 + expose :updated_at, :format_with => :timestamp
  177 + expose :title, :documentation => {:type => "String", :desc => "Title of the article"}
  178 + expose :created_by, :as => :author, :using => Profile, :documentation => {type: 'Profile', desc: 'The profile author that create the article'}
  179 + expose :profile, :using => Profile, :documentation => {type: 'Profile', desc: 'The profile associated with the article'}
  180 + expose :categories, :using => Category
  181 + expose :image, :using => Image
  182 + expose :votes_for
  183 + expose :votes_against
  184 + expose :setting
  185 + expose :position
  186 + expose :hits
  187 + expose :start_date
  188 + expose :end_date, :documentation => {type: 'DateTime', desc: 'The date of finish of the article'}
  189 + expose :tag_list
  190 + expose :children_count
  191 + expose :slug, :documentation => {:type => "String", :desc => "Trimmed and parsed name of a article"}
  192 + expose :path
  193 + expose :followers_count
  194 + expose :votes_count
  195 + expose :comments_count
  196 + expose :archived, :documentation => {:type => "Boolean", :desc => "Defines if a article is readonly"}
  197 + expose :type
  198 + expose :comments, using: CommentBase, :if => lambda{|obj,opt| opt[:params] && ['1','true',true].include?(opt[:params][:show_comments])}
  199 + expose :published
  200 + expose :accept_comments?, as: :accept_comments
  201 + end
  202 +
  203 + class Article < ArticleBase
  204 + root 'articles', 'article'
  205 + expose :parent, :using => ArticleBase
  206 + expose :children, :using => ArticleBase do |article, options|
  207 + article.children.published.limit(V1::Articles::MAX_PER_PAGE)
  208 + end
  209 + end
  210 +
  211 + class User < Entity
  212 + root 'users', 'user'
  213 +
  214 + attrs = [:id,:login,:email,:activated?]
  215 + aliases = {:activated? => :activated}
  216 +
  217 + attrs.each do |attribute|
  218 + name = aliases.has_key?(attribute) ? aliases[attribute] : attribute
  219 + expose attribute, :as => name, :if => lambda{|user,options| Entities.can_display_profile_field?(user.person, options, {:field => attribute})}
  220 + end
  221 +
  222 + expose :person, :using => Person, :if => lambda{|user,options| user.person.display_info_to? options[:current_person]}
  223 + expose :permissions, :if => lambda{|user,options| Entities.can_display_profile_field?(user.person, options, {:field => :permissions, :permission => :self})} do |user, options|
  224 + output = {}
  225 + user.person.role_assignments.map do |role_assigment|
  226 + if role_assigment.resource.respond_to?(:identifier) && !role_assigment.role.nil?
  227 + output[role_assigment.resource.identifier] = role_assigment.role.permissions
  228 + end
  229 + end
  230 + output
  231 + end
  232 + end
  233 +
  234 + class UserLogin < User
  235 + root 'users', 'user'
  236 + expose :private_token, documentation: {type: 'String', desc: 'A valid authentication code for post/delete api actions'}, if: lambda {|object, options| object.activated? }
  237 + end
  238 +
  239 + class Task < Entity
  240 + root 'tasks', 'task'
  241 + expose :id
  242 + expose :type
  243 + end
  244 +
  245 + class Environment < Entity
  246 + expose :name
  247 + expose :id
  248 + expose :description
  249 + expose :settings, if: lambda { |instance, options| options[:is_admin] }
  250 + end
  251 +
  252 + class Tag < Entity
  253 + root 'tags', 'tag'
  254 + expose :name
  255 + end
  256 +
  257 + class Activity < Entity
  258 + root 'activities', 'activity'
  259 + expose :id, :params, :verb, :created_at, :updated_at, :comments_count, :visible
  260 + expose :user, :using => Profile
  261 + expose :target do |activity, opts|
  262 + type_map = {Profile => ::Profile, ArticleBase => ::Article}.find {|h| activity.target.kind_of?(h.last)}
  263 + type_map.first.represent(activity.target) unless type_map.nil?
  264 + end
  265 + end
  266 + end
  267 +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,421 @@
  1 +require 'base64'
  2 +require 'tempfile'
  3 +
  4 +module Api
  5 + module Helpers
  6 + PRIVATE_TOKEN_PARAM = :private_token
  7 + DEFAULT_ALLOWED_PARAMETERS = [:parent_id, :from, :until, :content_type, :author_id, :identifier, :archived]
  8 +
  9 + include SanitizeParams
  10 + include Noosfero::Plugin::HotSpot
  11 + include ForgotPasswordHelper
  12 + include SearchTermHelper
  13 +
  14 + def set_locale
  15 + I18n.locale = (params[:lang] || request.env['HTTP_ACCEPT_LANGUAGE'] || 'en')
  16 + end
  17 +
  18 + def init_noosfero_plugins
  19 + plugins
  20 + end
  21 +
  22 + def current_user
  23 + private_token = (params[PRIVATE_TOKEN_PARAM] || headers['Private-Token']).to_s
  24 + @current_user ||= User.find_by private_token: private_token
  25 + @current_user ||= plugins.dispatch("api_custom_login", request).first
  26 + @current_user
  27 + end
  28 +
  29 + def current_person
  30 + current_user.person unless current_user.nil?
  31 + end
  32 +
  33 + def is_admin?(environment)
  34 + return false unless current_user
  35 + return current_person.is_admin?(environment)
  36 + end
  37 +
  38 + def logout
  39 + @current_user = nil
  40 + end
  41 +
  42 + def environment
  43 + @environment
  44 + end
  45 +
  46 + def present_partial(model, options)
  47 + if(params[:fields].present?)
  48 + begin
  49 + fields = JSON.parse(params[:fields])
  50 + if fields.present?
  51 + options.merge!(fields.symbolize_keys.slice(:only, :except))
  52 + end
  53 + rescue
  54 + fields = params[:fields]
  55 + fields = fields.split(',') if fields.kind_of?(String)
  56 + options[:only] = Array.wrap(fields)
  57 + end
  58 + end
  59 + present model, options
  60 + end
  61 +
  62 + include FindByContents
  63 +
  64 + ####################################################################
  65 + #### SEARCH
  66 + ####################################################################
  67 + def multiple_search?(searches=nil)
  68 + ['index', 'category_index'].include?(params[:action]) || (searches && searches.size > 1)
  69 + end
  70 + ####################################################################
  71 +
  72 + def logger
  73 + logger = Logger.new(File.join(Rails.root, 'log', "#{ENV['RAILS_ENV'] || 'production'}_api.log"))
  74 + logger.formatter = GrapeLogging::Formatters::Default.new
  75 + logger
  76 + end
  77 +
  78 + def limit
  79 + limit = params[:limit].to_i
  80 + limit = default_limit if limit <= 0
  81 + limit
  82 + end
  83 +
  84 + def period(from_date, until_date)
  85 + return nil if from_date.nil? && until_date.nil?
  86 +
  87 + begin_period = from_date.nil? ? Time.at(0).to_datetime : from_date
  88 + end_period = until_date.nil? ? DateTime.now : until_date
  89 +
  90 + begin_period..end_period
  91 + end
  92 +
  93 + def parse_content_type(content_type)
  94 + return nil if content_type.blank?
  95 + content_type.split(',').map do |content_type|
  96 + content_type.camelcase
  97 + end
  98 + end
  99 +
  100 + def find_article(articles, id)
  101 + article = articles.find(id)
  102 + article.display_to?(current_person) ? article : forbidden!
  103 + end
  104 +
  105 + def post_article(asset, params)
  106 + return forbidden! unless current_person.can_post_content?(asset)
  107 +
  108 + klass_type = params[:content_type] || params[:article].delete(:type) || TinyMceArticle.name
  109 + return forbidden! unless klass_type.constantize <= Article
  110 +
  111 + article = klass_type.constantize.new(params[:article])
  112 + article.last_changed_by = current_person
  113 + article.created_by= current_person
  114 + article.profile = asset
  115 +
  116 + if !article.save
  117 + render_api_errors!(article.errors.full_messages)
  118 + end
  119 + present_partial article, :with => Entities::Article
  120 + end
  121 +
  122 + def present_article(asset)
  123 + article = find_article(asset.articles, params[:id])
  124 + present_partial article, :with => Entities::Article, :params => params
  125 + end
  126 +
  127 + def present_articles_for_asset(asset, method = 'articles')
  128 + articles = find_articles(asset, method)
  129 + present_articles(articles)
  130 + end
  131 +
  132 + def present_articles(articles)
  133 + present_partial paginate(articles), :with => Entities::Article, :params => params
  134 + end
  135 +
  136 + def find_articles(asset, method = 'articles')
  137 + articles = select_filtered_collection_of(asset, method, params)
  138 + if current_person.present?
  139 + articles = articles.display_filter(current_person, nil)
  140 + else
  141 + articles = articles.published
  142 + end
  143 + articles
  144 + end
  145 +
  146 + def find_task(asset, id)
  147 + task = asset.tasks.find(id)
  148 + current_person.has_permission?(task.permission, asset) ? task : forbidden!
  149 + end
  150 +
  151 + def post_task(asset, params)
  152 + klass_type= params[:content_type].nil? ? 'Task' : params[:content_type]
  153 + return forbidden! unless klass_type.constantize <= Task
  154 +
  155 + task = klass_type.constantize.new(params[:task])
  156 + task.requestor_id = current_person.id
  157 + task.target_id = asset.id
  158 + task.target_type = 'Profile'
  159 +
  160 + if !task.save
  161 + render_api_errors!(task.errors.full_messages)
  162 + end
  163 + present_partial task, :with => Entities::Task
  164 + end
  165 +
  166 + def present_task(asset)
  167 + task = find_task(asset, params[:id])
  168 + present_partial task, :with => Entities::Task
  169 + end
  170 +
  171 + def present_tasks(asset)
  172 + tasks = select_filtered_collection_of(asset, 'tasks', params)
  173 + tasks = tasks.select {|t| current_person.has_permission?(t.permission, asset)}
  174 + return forbidden! if tasks.empty? && !current_person.has_permission?(:perform_task, asset)
  175 + present_partial tasks, :with => Entities::Task
  176 + end
  177 +
  178 + def make_conditions_with_parameter(params = {})
  179 + parsed_params = parser_params(params)
  180 + conditions = {}
  181 + from_date = DateTime.parse(parsed_params.delete(:from)) if parsed_params[:from]
  182 + until_date = DateTime.parse(parsed_params.delete(:until)) if parsed_params[:until]
  183 +
  184 + conditions[:type] = parse_content_type(parsed_params.delete(:content_type)) unless parsed_params[:content_type].nil?
  185 +
  186 + conditions[:created_at] = period(from_date, until_date) if from_date || until_date
  187 + conditions.merge!(parsed_params)
  188 +
  189 + conditions
  190 + end
  191 +
  192 + # changing make_order_with_parameters to avoid sql injection
  193 + def make_order_with_parameters(object, method, params)
  194 + order = "created_at DESC"
  195 + unless params[:order].blank?
  196 + if params[:order].include? '\'' or params[:order].include? '"'
  197 + order = "created_at DESC"
  198 + elsif ['RANDOM()', 'RANDOM'].include? params[:order].upcase
  199 + order = 'RANDOM()'
  200 + else
  201 + field_name, direction = params[:order].split(' ')
  202 + assoc = object.class.reflect_on_association(method.to_sym)
  203 + if !field_name.blank? and assoc
  204 + if assoc.klass.attribute_names.include? field_name
  205 + if direction.present? and ['ASC','DESC'].include? direction.upcase
  206 + order = "#{field_name} #{direction.upcase}"
  207 + end
  208 + end
  209 + end
  210 + end
  211 + end
  212 + return order
  213 + end
  214 +
  215 + def make_timestamp_with_parameters_and_method(params, method)
  216 + timestamp = nil
  217 + if params[:timestamp]
  218 + datetime = DateTime.parse(params[:timestamp])
  219 + table_name = method.to_s.singularize.camelize.constantize.table_name
  220 + timestamp = "#{table_name}.updated_at >= '#{datetime}'"
  221 + end
  222 +
  223 + timestamp
  224 + end
  225 +
  226 + def by_reference(scope, params)
  227 + reference_id = params[:reference_id].to_i == 0 ? nil : params[:reference_id].to_i
  228 + if reference_id.nil?
  229 + scope
  230 + else
  231 + created_at = scope.find(reference_id).created_at
  232 + scope.send("#{params.key?(:oldest) ? 'older_than' : 'younger_than'}", created_at)
  233 + end
  234 + end
  235 +
  236 + def by_categories(scope, params)
  237 + category_ids = params[:category_ids]
  238 + if category_ids.nil?
  239 + scope
  240 + else
  241 + scope.joins(:categories).where(:categories => {:id => category_ids})
  242 + end
  243 + end
  244 +
  245 + def select_filtered_collection_of(object, method, params)
  246 + conditions = make_conditions_with_parameter(params)
  247 + order = make_order_with_parameters(object,method,params)
  248 + timestamp = make_timestamp_with_parameters_and_method(params, method)
  249 +
  250 + objects = object.send(method)
  251 + objects = by_reference(objects, params)
  252 + objects = by_categories(objects, params)
  253 +
  254 + objects = objects.where(conditions).where(timestamp).reorder(order)
  255 +
  256 + params[:page] ||= 1
  257 + params[:per_page] ||= limit
  258 + paginate(objects)
  259 + end
  260 +
  261 + def authenticate!
  262 + unauthorized! unless current_user
  263 + end
  264 +
  265 + def profiles_for_person(profiles, person)
  266 + if person
  267 + profiles.listed_for_person(person)
  268 + else
  269 + profiles.visible
  270 + end
  271 + end
  272 +
  273 + # Checks the occurrences of uniqueness of attributes, each attribute must be present in the params hash
  274 + # or a Bad Request error is invoked.
  275 + #
  276 + # Parameters:
  277 + # keys (unique) - A hash consisting of keys that must be unique
  278 + def unique_attributes!(obj, keys)
  279 + keys.each do |key|
  280 + cant_be_saved_request!(key) if obj.find_by(key.to_s => params[key])
  281 + end
  282 + end
  283 +
  284 + def attributes_for_keys(keys)
  285 + attrs = {}
  286 + keys.each do |key|
  287 + attrs[key] = params[key] if params[key].present? or (params.has_key?(key) and params[key] == false)
  288 + end
  289 + attrs
  290 + end
  291 +
  292 + ##########################################
  293 + # error helpers #
  294 + ##########################################
  295 +
  296 + def not_found!
  297 + render_api_error!('404 Not found', 404)
  298 + end
  299 +
  300 + def forbidden!
  301 + render_api_error!('403 Forbidden', 403)
  302 + end
  303 +
  304 + def cant_be_saved_request!(attribute)
  305 + message = _("(Invalid request) %s can't be saved") % attribute
  306 + render_api_error!(message, 400)
  307 + end
  308 +
  309 + def bad_request!(attribute)
  310 + message = _("(Invalid request) %s not given") % attribute
  311 + render_api_error!(message, 400)
  312 + end
  313 +
  314 + def something_wrong!
  315 + message = _("Something wrong happened")
  316 + render_api_error!(message, 400)
  317 + end
  318 +
  319 + def unauthorized!
  320 + render_api_error!(_('Unauthorized'), 401)
  321 + end
  322 +
  323 + def not_allowed!
  324 + render_api_error!(_('Method Not Allowed'), 405)
  325 + end
  326 +
  327 + # javascript_console_message is supposed to be executed as console.log()
  328 + def render_api_error!(user_message, status, log_message = nil, javascript_console_message = nil)
  329 + message_hash = {'message' => user_message, :code => status}
  330 + message_hash[:javascript_console_message] = javascript_console_message if javascript_console_message.present?
  331 + log_msg = "#{status}, User message: #{user_message}"
  332 + log_msg = "#{log_message}, #{log_msg}" if log_message.present?
  333 + log_msg = "#{log_msg}, Javascript Console Message: #{javascript_console_message}" if javascript_console_message.present?
  334 + logger.error log_msg unless Rails.env.test?
  335 + error!(message_hash, status)
  336 + end
  337 +
  338 + def render_api_errors!(messages)
  339 + messages = messages.to_a if messages.class == ActiveModel::Errors
  340 + render_api_error!(messages.join(','), 400)
  341 + end
  342 +
  343 + protected
  344 +
  345 + def set_session_cookie
  346 + cookies['_noosfero_api_session'] = { value: @current_user.private_token, httponly: true } if @current_user.present?
  347 + end
  348 +
  349 + def setup_multitenancy
  350 + Noosfero::MultiTenancy.setup!(request.host)
  351 + end
  352 +
  353 + def detect_stuff_by_domain
  354 + @domain = Domain.by_name(request.host)
  355 + if @domain.nil?
  356 + @environment = Environment.default
  357 + if @environment.nil? && Rails.env.development?
  358 + # This should only happen in development ...
  359 + @environment = Environment.create!(:name => "Noosfero", :is_default => true)
  360 + end
  361 + else
  362 + @environment = @domain.environment
  363 + end
  364 + end
  365 +
  366 + def filter_disabled_plugins_endpoints
  367 + not_found! if Api::App.endpoint_unavailable?(self, @environment)
  368 + end
  369 +
  370 + def asset_with_image params
  371 + if params.has_key? :image_builder
  372 + asset_api_params = params
  373 + asset_api_params[:image_builder] = base64_to_uploadedfile(asset_api_params[:image_builder])
  374 + return asset_api_params
  375 + end
  376 + params
  377 + end
  378 +
  379 + def base64_to_uploadedfile(base64_image)
  380 + tempfile = base64_to_tempfile base64_image
  381 + converted_image = base64_image
  382 + converted_image[:tempfile] = tempfile
  383 + return {uploaded_data: ActionDispatch::Http::UploadedFile.new(converted_image)}
  384 + end
  385 +
  386 + def base64_to_tempfile base64_image
  387 + base64_img_str = base64_image[:tempfile]
  388 + decoded_base64_str = Base64.decode64(base64_img_str)
  389 + tempfile = Tempfile.new(base64_image[:filename])
  390 + tempfile.write(decoded_base64_str.encode("ascii-8bit").force_encoding("utf-8"))
  391 + tempfile.rewind
  392 + tempfile
  393 + end
  394 + private
  395 +
  396 + def parser_params(params)
  397 + parsed_params = {}
  398 + params.map do |k,v|
  399 + parsed_params[k.to_sym] = v if DEFAULT_ALLOWED_PARAMETERS.include?(k.to_sym)
  400 + end
  401 + parsed_params
  402 + end
  403 +
  404 + def default_limit
  405 + 20
  406 + end
  407 +
  408 + def parse_content_type(content_type)
  409 + return nil if content_type.blank?
  410 + content_type.split(',').map do |content_type|
  411 + content_type.camelcase
  412 + end
  413 + end
  414 +
  415 + def period(from_date, until_date)
  416 + begin_period = from_date.nil? ? Time.at(0).to_datetime : from_date
  417 + end_period = until_date.nil? ? DateTime.now : until_date
  418 + begin_period..end_period
  419 + end
  420 + end
  421 +end
... ...
app/api/v1/activities.rb 0 → 100644
... ... @@ -0,0 +1,20 @@
  1 +module Api
  2 + module V1
  3 + class Activities < Grape::API
  4 + before { authenticate! }
  5 +
  6 + resource :profiles do
  7 +
  8 + get ':id/activities' do
  9 + profile = Profile.find_by id: params[:id]
  10 +
  11 + not_found! if profile.blank? || profile.secret || !profile.visible
  12 + forbidden! if !profile.secret && profile.visible && !profile.display_private_info_to?(current_person)
  13 +
  14 + activities = profile.activities.map(&:activity)
  15 + present activities, :with => Entities::Activity, :current_person => current_person
  16 + end
  17 + end
  18 + end
  19 + end
  20 +end
... ...
app/api/v1/articles.rb 0 → 100644
... ... @@ -0,0 +1,303 @@
  1 +module Api
  2 + module V1
  3 + class Articles < Grape::API
  4 +
  5 + ARTICLE_TYPES = Article.descendants.map{|a| a.to_s}
  6 +
  7 + MAX_PER_PAGE = 50
  8 +
  9 + resource :articles do
  10 +
  11 + paginate max_per_page: MAX_PER_PAGE
  12 + # Collect articles
  13 + #
  14 + # Parameters:
  15 + # from - date where the search will begin. If nothing is passed the default date will be the date of the first article created
  16 + # oldest - Collect the oldest articles. If nothing is passed the newest articles are collected
  17 + # limit - amount of articles returned. The default value is 20
  18 + #
  19 + # Example Request:
  20 + # GET host/api/v1/articles?from=2013-04-04-14:41:43&until=2015-04-04-14:41:43&limit=10&private_token=e96fff37c2238fdab074d1dcea8e6317
  21 +
  22 + desc 'Return all articles of all kinds' do
  23 + detail 'Get all articles filtered by fields in query params'
  24 + params Entities::Article.documentation
  25 + success Entities::Article
  26 + failure [[403, 'Forbidden']]
  27 + named 'ArticlesList'
  28 + headers [
  29 + 'Per-Page' => {
  30 + description: 'Total number of records',
  31 + required: false
  32 + }
  33 + ]
  34 + end
  35 + get do
  36 + present_articles_for_asset(environment)
  37 + end
  38 +
  39 + desc "Return one article by id" do
  40 + detail 'Get only one article by id. If not found the "forbidden" http error is showed'
  41 + params Entities::Article.documentation
  42 + success Entities::Article
  43 + failure [[403, 'Forbidden']]
  44 + named 'ArticleById'
  45 + end
  46 + get ':id', requirements: {id: /[0-9]+/} do
  47 + present_article(environment)
  48 + end
  49 +
  50 + post ':id' do
  51 + article = environment.articles.find(params[:id])
  52 + return forbidden! unless article.allow_edit?(current_person)
  53 + article.update_attributes!(asset_with_image(params[:article]))
  54 + present_partial article, :with => Entities::Article
  55 + end
  56 +
  57 + desc 'Report a abuse and/or violent content in a article by id' do
  58 + detail 'Submit a abuse (in general, a content violation) report about a specific article'
  59 + params Entities::Article.documentation
  60 + failure [[400, 'Bad Request']]
  61 + named 'ArticleReportAbuse'
  62 + end
  63 + post ':id/report_abuse' do
  64 + article = find_article(environment.articles, params[:id])
  65 + profile = article.profile
  66 + begin
  67 + abuse_report = AbuseReport.new(:reason => params[:report_abuse])
  68 + if !params[:content_type].blank?
  69 + article = params[:content_type].constantize.find(params[:content_id])
  70 + abuse_report.content = article_reported_version(article)
  71 + end
  72 +
  73 + current_person.register_report(abuse_report, profile)
  74 +
  75 + if !params[:content_type].blank?
  76 + abuse_report = AbuseReport.find_by reporter_id: current_person.id, abuse_complaint_id: profile.opened_abuse_complaint.id
  77 + Delayed::Job.enqueue DownloadReportedImagesJob.new(abuse_report, article)
  78 + end
  79 +
  80 + {
  81 + :success => true,
  82 + :message => _('Your abuse report was registered. The administrators are reviewing your report.'),
  83 + }
  84 + rescue Exception => exception
  85 + #logger.error(exception.to_s)
  86 + render_api_error!(_('Your report couldn\'t be saved due to some problem. Please contact the administrator.'), 400)
  87 + end
  88 +
  89 + end
  90 +
  91 + desc "Returns the articles I voted" do
  92 + detail 'Get the Articles I make a vote'
  93 + failure [[403, 'Forbidden']]
  94 + named 'ArticleFollowers'
  95 + end
  96 + #FIXME refactor this method
  97 + get 'voted_by_me' do
  98 + present_articles(current_person.votes.where(:voteable_type => 'Article').collect(&:voteable))
  99 + end
  100 +
  101 + desc 'Perform a vote on a article by id' do
  102 + detail 'Vote on a specific article with values: 1 (if you like) or -1 (if not)'
  103 + params Entities::UserLogin.documentation
  104 + failure [[401,'Unauthorized']]
  105 + named 'ArticleVote'
  106 + end
  107 + post ':id/vote' do
  108 + authenticate!
  109 + value = (params[:value] || 1).to_i
  110 + # FIXME verify allowed values
  111 + render_api_error!('Vote value not allowed', 400) unless [-1, 1].include?(value)
  112 + article = find_article(environment.articles, params[:id])
  113 + begin
  114 + vote = Vote.new(:voteable => article, :voter => current_person, :vote => value)
  115 + {:vote => vote.save!}
  116 + rescue ActiveRecord::RecordInvalid => e
  117 + render_api_error!(e.message, 400)
  118 + end
  119 + end
  120 +
  121 + desc "Returns the total followers for the article" do
  122 + detail 'Get the followers of a specific article by id'
  123 + failure [[403, 'Forbidden']]
  124 + named 'ArticleFollowers'
  125 + end
  126 + get ':id/followers' do
  127 + article = find_article(environment.articles, params[:id])
  128 + total = article.person_followers.count
  129 + {:total_followers => total}
  130 + end
  131 +
  132 + desc "Return the articles followed by me"
  133 + get 'followed_by_me' do
  134 + present_articles_for_asset(current_person, 'following_articles')
  135 + end
  136 +
  137 + desc "Add a follower for the article" do
  138 + detail 'Add the current user identified by private token, like a follower of a article'
  139 + params Entities::UserLogin.documentation
  140 + failure [[401, 'Unauthorized']]
  141 + named 'ArticleFollow'
  142 + end
  143 + post ':id/follow' do
  144 + authenticate!
  145 + article = find_article(environment.articles, params[:id])
  146 + if article.article_followers.exists?(:person_id => current_person.id)
  147 + {:success => false, :already_follow => true}
  148 + else
  149 + article_follower = ArticleFollower.new
  150 + article_follower.article = article
  151 + article_follower.person = current_person
  152 + article_follower.save!
  153 + {:success => true}
  154 + end
  155 + end
  156 +
  157 + desc 'Return the children of a article identified by id' do
  158 + detail 'Get all children articles of a specific article'
  159 + params Entities::Article.documentation
  160 + failure [[403, 'Forbidden']]
  161 + named 'ArticleChildren'
  162 + end
  163 +
  164 + paginate per_page: MAX_PER_PAGE, max_per_page: MAX_PER_PAGE
  165 + get ':id/children' do
  166 + article = find_article(environment.articles, params[:id])
  167 +
  168 + #TODO make tests for this situation
  169 + votes_order = params.delete(:order) if params[:order]=='votes_score'
  170 + articles = select_filtered_collection_of(article, 'children', params)
  171 + articles = articles.display_filter(current_person, article.profile)
  172 +
  173 + #TODO make tests for this situation
  174 + if votes_order
  175 + articles = articles.joins('left join votes on articles.id=votes.voteable_id').group('articles.id').reorder('sum(coalesce(votes.vote, 0)) DESC')
  176 + end
  177 + Article.hit(articles)
  178 + present_articles(articles)
  179 + end
  180 +
  181 + desc 'Return one child of a article identified by id' do
  182 + detail 'Get a child of a specific article'
  183 + params Entities::Article.documentation
  184 + success Entities::Article
  185 + failure [[403, 'Forbidden']]
  186 + named 'ArticleChild'
  187 + end
  188 + get ':id/children/:child_id' do
  189 + article = find_article(environment.articles, params[:id])
  190 + child = find_article(article.children, params[:child_id])
  191 + child.hit
  192 + present_partial child, :with => Entities::Article
  193 + end
  194 +
  195 + desc 'Suggest a article to another profile' do
  196 + detail 'Suggest a article to another profile (person, community...)'
  197 + params Entities::Article.documentation
  198 + success Entities::Task
  199 + failure [[401,'Unauthorized']]
  200 + named 'ArticleSuggest'
  201 + end
  202 + post ':id/children/suggest' do
  203 + authenticate!
  204 + parent_article = environment.articles.find(params[:id])
  205 +
  206 + suggest_article = SuggestArticle.new
  207 + suggest_article.article = params[:article]
  208 + suggest_article.article[:parent_id] = parent_article.id
  209 + suggest_article.target = parent_article.profile
  210 + suggest_article.requestor = current_person
  211 +
  212 + unless suggest_article.save
  213 + render_api_errors!(suggest_article.article_object.errors.full_messages)
  214 + end
  215 + present_partial suggest_article, :with => Entities::Task
  216 + end
  217 +
  218 + # Example Request:
  219 + # POST api/v1/articles/:id/children?private_token=234298743290432&article[name]=title&article[body]=body
  220 + desc 'Add a child article to a parent identified by id' do
  221 + detail 'Create a new article and associate to a parent'
  222 + params Entities::Article.documentation
  223 + success Entities::Article
  224 + failure [[401,'Unauthorized']]
  225 + named 'ArticleAddChild'
  226 + end
  227 + post ':id/children' do
  228 + parent_article = environment.articles.find(params[:id])
  229 + params[:article][:parent_id] = parent_article.id
  230 + post_article(parent_article.profile, params)
  231 + end
  232 + end
  233 +
  234 + resource :profiles do
  235 + get ':id/home_page' do
  236 + profiles = environment.profiles
  237 + profiles = profiles.visible_for_person(current_person)
  238 + profile = profiles.find_by id: params[:id]
  239 + present_partial profile.home_page, :with => Entities::Article
  240 + end
  241 + end
  242 +
  243 + kinds = %w[profile community person enterprise]
  244 + kinds.each do |kind|
  245 + resource kind.pluralize.to_sym do
  246 + segment "/:#{kind}_id" do
  247 + resource :articles do
  248 +
  249 + desc "Return all articles associate with a profile of type #{kind}" do
  250 + detail 'Get a list of articles of a profile'
  251 + params Entities::Article.documentation
  252 + success Entities::Article
  253 + failure [[403, 'Forbidden']]
  254 + named 'ArticlesOfProfile'
  255 + end
  256 + get do
  257 + profile = environment.send(kind.pluralize).find(params["#{kind}_id"])
  258 +
  259 + if params[:path].present?
  260 + article = profile.articles.find_by path: params[:path]
  261 + if !article || !article.display_to?(current_person)
  262 + article = forbidden!
  263 + end
  264 +
  265 + present_partial article, :with => Entities::Article
  266 + else
  267 +
  268 + present_articles_for_asset(profile)
  269 + end
  270 + end
  271 +
  272 + desc "Return a article associate with a profile of type #{kind}" do
  273 + detail 'Get only one article of a profile'
  274 + params Entities::Article.documentation
  275 + success Entities::Article
  276 + failure [[403, 'Forbidden']]
  277 + named 'ArticleOfProfile'
  278 + end
  279 + get ':id' do
  280 + profile = environment.send(kind.pluralize).find(params["#{kind}_id"])
  281 + present_article(profile)
  282 + end
  283 +
  284 + # Example Request:
  285 + # POST api/v1/{people,communities,enterprises}/:asset_id/articles?private_token=234298743290432&article[name]=title&article[body]=body
  286 + desc "Add a new article associated with a profile of type #{kind}" do
  287 + detail 'Create a new article and associate with a profile'
  288 + params Entities::Article.documentation
  289 + success Entities::Article
  290 + failure [[403, 'Forbidden']]
  291 + named 'ArticleCreateToProfile'
  292 + end
  293 + post do
  294 + profile = environment.send(kind.pluralize).find(params["#{kind}_id"])
  295 + post_article(profile, params)
  296 + end
  297 + end
  298 + end
  299 + end
  300 + end
  301 + end
  302 + end
  303 +end
... ...
app/api/v1/blocks.rb 0 → 100644
... ... @@ -0,0 +1,15 @@
  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,44 @@
  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 + present profile.boxes, :with => Entities::Box
  16 + end
  17 + end
  18 + end
  19 + end
  20 +
  21 + end
  22 +
  23 + resource :environments do
  24 + [ '/default', '/context', ':environment_id' ].each do |route|
  25 + segment route do
  26 + resource :boxes do
  27 + get do
  28 + if (route.match(/default/))
  29 + env = Environment.default
  30 + elsif (route.match(/context/))
  31 + env = environment
  32 + else
  33 + env = Environment.find(params[:environment_id])
  34 + end
  35 + present env.boxes, :with => Entities::Box
  36 + end
  37 + end
  38 + end
  39 + end
  40 + end
  41 + end
  42 +
  43 + end
  44 +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,49 @@
  1 +module Api
  2 + module V1
  3 + class Comments < Grape::API
  4 + MAX_PER_PAGE = 20
  5 +
  6 +
  7 + resource :articles do
  8 + paginate max_per_page: MAX_PER_PAGE
  9 + # Collect comments from articles
  10 + #
  11 + # Parameters:
  12 + # reference_id - comment id used as reference to collect comment
  13 + # oldest - Collect the oldest comments from reference_id comment. If nothing is passed the newest comments are collected
  14 + # limit - amount of comments returned. The default value is 20
  15 + #
  16 + # Example Request:
  17 + # GET /articles/12/comments?oldest&limit=10&reference_id=23
  18 + get ":id/comments" do
  19 + article = find_article(environment.articles, params[:id])
  20 + comments = select_filtered_collection_of(article, :comments, params)
  21 + comments = comments.without_spam
  22 + comments = comments.without_reply if(params[:without_reply].present?)
  23 + comments = plugins.filter(:unavailable_comments, comments)
  24 + present paginate(comments), :with => Entities::Comment, :current_person => current_person
  25 + end
  26 +
  27 + get ":id/comments/:comment_id" do
  28 + article = find_article(environment.articles, params[:id])
  29 + present article.comments.find(params[:comment_id]), :with => Entities::Comment, :current_person => current_person
  30 + end
  31 +
  32 + # Example Request:
  33 + # POST api/v1/articles/12/comments?private_token=2298743290432&body=new comment&title=New
  34 + post ":id/comments" do
  35 + authenticate!
  36 + article = find_article(environment.articles, params[:id])
  37 + options = params.select { |key,v| !['id','private_token'].include?(key) }.merge(:author => current_person, :source => article)
  38 + begin
  39 + comment = Comment.create!(options)
  40 + rescue ActiveRecord::RecordInvalid => e
  41 + render_api_error!(e.message, 400)
  42 + end
  43 + present comment, :with => Entities::Comment, :current_person => current_person
  44 + end
  45 + end
  46 +
  47 + end
  48 + end
  49 +end
... ...
app/api/v1/communities.rb 0 → 100644
... ... @@ -0,0 +1,82 @@
  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,42 @@
  1 +module Api
  2 + module V1
  3 + class Profiles < Grape::API
  4 +
  5 + resource :profiles do
  6 +
  7 + get do
  8 + profiles = select_filtered_collection_of(environment, 'profiles', params)
  9 + profiles = profiles.visible
  10 + profiles = profiles.by_location(params) # Must be the last. May return Exception obj.
  11 + present profiles, :with => Entities::Profile, :current_person => current_person
  12 + end
  13 +
  14 + get ':id' do
  15 + profiles = environment.profiles
  16 + profiles = profiles.visible
  17 + profile = profiles.find_by id: params[:id]
  18 +
  19 + if profile
  20 + present profile, :with => Entities::Profile, :current_person => current_person
  21 + else
  22 + not_found!
  23 + end
  24 + end
  25 +
  26 + delete ':id' do
  27 + authenticate!
  28 + profiles = environment.profiles
  29 + profile = profiles.find_by id: params[:id]
  30 +
  31 + not_found! if profile.blank?
  32 +
  33 + if current_person.has_permission?(:destroy_profile, profile)
  34 + profile.destroy
  35 + else
  36 + forbidden!
  37 + end
  38 + end
  39 + end
  40 + end
  41 + end
  42 +end
... ...
app/api/v1/search.rb 0 → 100644
... ... @@ -0,0 +1,37 @@
  1 +module Api
  2 + module V1
  3 + class Search < Grape::API
  4 +
  5 + resource :search do
  6 + resource :article do
  7 + paginate max_per_page: 200
  8 + get do
  9 + # Security checks
  10 + sanitize_params_hash(params)
  11 + # Api::Helpers
  12 + asset = :articles
  13 + context = environment
  14 +
  15 + profile = environment.profiles.find(params[:profile_id]) if params[:profile_id]
  16 + scope = profile.nil? ? environment.articles.is_public : profile.articles.is_public
  17 + scope = scope.where(:type => params[:type]) if params[:type] && !(params[:type] == 'Article')
  18 + scope = scope.where(make_conditions_with_parameter(params))
  19 + scope = scope.joins(:categories).where(:categories => {:id => params[:category_ids]}) if params[:category_ids].present?
  20 + scope = scope.where('articles.children_count > 0') if params[:has_children].present?
  21 + query = params[:query] || ""
  22 + order = "more_recent"
  23 +
  24 + options = {:filter => order, :template_id => params[:template_id]}
  25 +
  26 + search_result = find_by_contents(asset, context, scope, query, {:page => 1}, options)
  27 +
  28 + articles = search_result[:results]
  29 +
  30 + present_articles(articles)
  31 + end
  32 + end
  33 + end
  34 +
  35 + end
  36 + end
  37 +end
... ...
app/api/v1/session.rb 0 → 100644
... ... @@ -0,0 +1,157 @@
  1 +require "uri"
  2 +
  3 +module Api
  4 + module V1
  5 + class Session < Grape::API
  6 +
  7 + # Login to get token
  8 + #
  9 + # Parameters:
  10 + # login (*required) - user login or email
  11 + # password (required) - user password
  12 + #
  13 + # Example Request:
  14 + # POST http://localhost:3000/api/v1/login?login=adminuser&password=admin
  15 + post "/login" do
  16 + begin
  17 + user ||= User.authenticate(params[:login], params[:password], environment)
  18 + rescue User::UserNotActivated => e
  19 + render_api_error!(e.message, 401)
  20 + end
  21 +
  22 + return unauthorized! unless user
  23 + @current_user = user
  24 + present user, :with => Entities::UserLogin, :current_person => current_person
  25 + end
  26 +
  27 + post "/login_from_cookie" do
  28 + return unauthorized! if cookies[:auth_token].blank?
  29 + user = User.where(remember_token: cookies[:auth_token]).first
  30 + return unauthorized! unless user && user.activated?
  31 + @current_user = user
  32 + present user, :with => Entities::UserLogin, :current_person => current_person
  33 + end
  34 +
  35 + # Create user.
  36 + #
  37 + # Parameters:
  38 + # email (required) - Email
  39 + # password (required) - Password
  40 + # login - login
  41 + # Example Request:
  42 + # POST /register?email=some@mail.com&password=pas&password_confirmation=pas&login=some
  43 + params do
  44 + requires :email, type: String, desc: _("Email")
  45 + requires :login, type: String, desc: _("Login")
  46 + requires :password, type: String, desc: _("Password")
  47 + end
  48 +
  49 + post "/register" do
  50 + attrs = attributes_for_keys [:email, :login, :password, :password_confirmation] + environment.signup_person_fields
  51 + name = params[:name].present? ? params[:name] : attrs[:email]
  52 + attrs[:password_confirmation] = attrs[:password] if !attrs.has_key?(:password_confirmation)
  53 + user = User.new(attrs.merge(:name => name))
  54 +
  55 + begin
  56 + user.signup!
  57 + user.generate_private_token! if user.activated?
  58 + present user, :with => Entities::UserLogin, :current_person => user.person
  59 + rescue ActiveRecord::RecordInvalid
  60 + message = user.errors.as_json.merge((user.person.present? ? user.person.errors : {}).as_json).to_json
  61 + render_api_error!(message, 400)
  62 + end
  63 + end
  64 +
  65 + params do
  66 + requires :activation_code, type: String, desc: _("Activation token")
  67 + end
  68 +
  69 + # Activate a user.
  70 + #
  71 + # Parameter:
  72 + # activation_code (required) - Activation token
  73 + # Example Request:
  74 + # PATCH /activate?activation_code=28259abd12cc6a64ef9399cf3286cb998b96aeaf
  75 + patch "/activate" do
  76 + user = User.find_by activation_code: params[:activation_code]
  77 + if user
  78 + unless user.environment.enabled?('admin_must_approve_new_users')
  79 + if user.activate
  80 + user.generate_private_token!
  81 + present user, :with => Entities::UserLogin, :current_person => current_person
  82 + end
  83 + else
  84 + if user.create_moderate_task
  85 + user.activation_code = nil
  86 + user.save!
  87 +
  88 + # Waiting for admin moderate user registration
  89 + status 202
  90 + body({
  91 + :message => 'Waiting for admin moderate user registration'
  92 + })
  93 + end
  94 + end
  95 + else
  96 + # Token not found in database
  97 + render_api_error!(_('Token is invalid'), 412)
  98 + end
  99 + end
  100 +
  101 + # Request a new password.
  102 + #
  103 + # Parameters:
  104 + # value (required) - Email or login
  105 + # Example Request:
  106 + # POST /forgot_password?value=some@mail.com
  107 + post "/forgot_password" do
  108 + requestors = fetch_requestors(params[:value])
  109 + not_found! if requestors.blank?
  110 + remote_ip = (request.respond_to?(:remote_ip) && request.remote_ip) || (env && env['REMOTE_ADDR'])
  111 + requestors.each do |requestor|
  112 + ChangePassword.create!(:requestor => requestor)
  113 + end
  114 + end
  115 +
  116 + # Resend activation code.
  117 + #
  118 + # Parameters:
  119 + # value (required) - Email or login
  120 + # Example Request:
  121 + # POST /resend_activation_code?value=some@mail.com
  122 + post "/resend_activation_code" do
  123 + requestors = fetch_requestors(params[:value])
  124 + not_found! if requestors.blank?
  125 + remote_ip = (request.respond_to?(:remote_ip) && request.remote_ip) || (env && env['REMOTE_ADDR'])
  126 + requestors.each do |requestor|
  127 + requestor.user.resend_activation_code
  128 + end
  129 + present requestors.map(&:user), :with => Entities::UserLogin
  130 + end
  131 +
  132 + params do
  133 + requires :code, type: String, desc: _("Forgot password code")
  134 + end
  135 + # Change password
  136 + #
  137 + # Parameters:
  138 + # code (required) - Change password code
  139 + # password (required)
  140 + # password_confirmation (required)
  141 + # Example Request:
  142 + # PATCH /new_password?code=xxxx&password=secret&password_confirmation=secret
  143 + patch "/new_password" do
  144 + change_password = ChangePassword.find_by code: params[:code]
  145 + not_found! if change_password.nil?
  146 +
  147 + if change_password.update_attributes(:password => params[:password], :password_confirmation => params[:password_confirmation])
  148 + change_password.finish
  149 + present change_password.requestor.user, :with => Entities::UserLogin, :current_person => current_person
  150 + else
  151 + something_wrong!
  152 + end
  153 + end
  154 +
  155 + end
  156 + end
  157 +end
... ...
app/api/v1/tags.rb 0 → 100644
... ... @@ -0,0 +1,26 @@
  1 +module Api
  2 + module V1
  3 + class Tags < Grape::API
  4 + before { authenticate! }
  5 +
  6 + resource :articles do
  7 +
  8 + resource ':id/tags' do
  9 +
  10 + get do
  11 + article = find_article(environment.articles, params[:id])
  12 + present article.tag_list
  13 + end
  14 +
  15 + desc "Add a tag to an article"
  16 + post do
  17 + article = find_article(environment.articles, params[:id])
  18 + article.tag_list=params[:tags]
  19 + article.save
  20 + present article.tag_list
  21 + end
  22 + end
  23 + end
  24 + end
  25 + end
  26 +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/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/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/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|
... ...
config.ru
... ... @@ -20,6 +20,6 @@ rails_app = Rack::Builder.new do
20 20 end
21 21  
22 22 run Rack::Cascade.new([
23   - Noosfero::API::API,
  23 + Api::App,
24 24 rails_app
25 25 ])
... ...
config/application.rb
... ... @@ -39,6 +39,7 @@ module Noosfero
39 39  
40 40 # Custom directories with classes and modules you want to be autoloadable.
41 41 config.autoload_paths << config.root.join('lib')
  42 + config.autoload_paths << config.root.join('app')
42 43 config.autoload_paths << config.root.join('app/jobs')
43 44 config.autoload_paths << config.root.join('app/sweepers')
44 45 config.autoload_paths.concat Dir["#{config.root}/app/controllers/**/"]
... ...
lib/noosfero/api/api.rb
... ... @@ -1,94 +0,0 @@
1   -require 'grape'
2   -#require 'rack/contrib'
3   -Dir["#{Rails.root}/lib/noosfero/api/*.rb"].each {|file| require file unless file =~ /api\.rb/}
4   -
5   -module Noosfero
6   - module API
7   - class API < Grape::API
8   - use Rack::JSONP
9   -
10   - logger = Logger.new(File.join(Rails.root, 'log', "#{ENV['RAILS_ENV'] || 'production'}_api.log"))
11   - logger.formatter = GrapeLogging::Formatters::Default.new
12   - #use GrapeLogging::Middleware::RequestLogger, { logger: logger }
13   -
14   - rescue_from :all do |e|
15   - logger.error e
16   - error! e.message, 500
17   - end
18   -
19   - @@NOOSFERO_CONF = nil
20   - def self.NOOSFERO_CONF
21   - if @@NOOSFERO_CONF
22   - @@NOOSFERO_CONF
23   - else
24   - file = Rails.root.join('config', 'noosfero.yml')
25   - @@NOOSFERO_CONF = File.exists?(file) ? YAML.load_file(file)[Rails.env] || {} : {}
26   - end
27   - end
28   -
29   - before { set_locale }
30   - before { setup_multitenancy }
31   - before { detect_stuff_by_domain }
32   - before { filter_disabled_plugins_endpoints }
33   - before { init_noosfero_plugins }
34   - after { set_session_cookie }
35   -
36   - version 'v1'
37   - prefix [ENV['RAILS_RELATIVE_URL_ROOT'], "api"].compact.join('/')
38   - format :json
39   - content_type :txt, "text/plain"
40   -
41   - helpers APIHelpers
42   -
43   - mount V1::Articles
44   - mount V1::Comments
45   - mount V1::Users
46   - mount V1::Communities
47   - mount V1::People
48   - mount V1::Enterprises
49   - mount V1::Categories
50   - mount V1::Tasks
51   - mount V1::Tags
52   - mount V1::Environments
53   - mount V1::Search
54   - mount V1::Contacts
55   - mount V1::Boxes
56   - mount V1::Blocks
57   - mount V1::Profiles
58   - mount V1::Activities
59   -
60   - mount Session
61   -
62   - # hook point which allow plugins to add Grape::API extensions to API::API
63   - #finds for plugins which has api mount points classes defined (the class should extends Grape::API)
64   - @plugins = Noosfero::Plugin.all.map { |p| p.constantize }
65   - @plugins.each do |klass|
66   - if klass.public_methods.include? :api_mount_points
67   - klass.api_mount_points.each do |mount_class|
68   - mount mount_class if mount_class && ( mount_class < Grape::API )
69   - end
70   - end
71   - end
72   -
73   - def self.endpoint_unavailable?(endpoint, environment)
74   - api_class = endpoint.options[:app] || endpoint.options[:for]
75   - if api_class.present?
76   - klass = api_class.name.deconstantize.constantize
77   - return klass < Noosfero::Plugin && !environment.plugin_enabled?(klass)
78   - end
79   - end
80   -
81   - class << self
82   - def endpoints_with_plugins(environment = nil)
83   - if environment.present?
84   - cloned_endpoints = endpoints_without_plugins.dup
85   - cloned_endpoints.delete_if { |endpoint| endpoint_unavailable?(endpoint, environment) }
86   - else
87   - endpoints_without_plugins
88   - end
89   - end
90   - alias_method_chain :endpoints, :plugins
91   - end
92   - end
93   - end
94   -end
lib/noosfero/api/entities.rb
... ... @@ -1,269 +0,0 @@
1   -module Noosfero
2   - module API
3   - module Entities
4   -
5   - Entity.format_with :timestamp do |date|
6   - date.strftime('%Y/%m/%d %H:%M:%S') if date
7   - end
8   -
9   - PERMISSIONS = {
10   - :admin => 0,
11   - :self => 10,
12   - :private_content => 20,
13   - :logged_user => 30,
14   - :anonymous => 40
15   - }
16   -
17   - def self.can_display_profile_field? profile, options, permission_options={}
18   - permissions={:field => "", :permission => :private_content}
19   - permissions.merge!(permission_options)
20   - field = permissions[:field]
21   - permission = permissions[:permission]
22   - return true if profile.public? && profile.public_fields.map{|f| f.to_sym}.include?(field.to_sym)
23   -
24   - current_person = options[:current_person]
25   -
26   - current_permission = if current_person.present?
27   - if current_person.is_admin?
28   - :admin
29   - elsif current_person == profile
30   - :self
31   - elsif profile.display_private_info_to?(current_person)
32   - :private_content
33   - else
34   - :logged_user
35   - end
36   - else
37   - :anonymous
38   - end
39   - PERMISSIONS[current_permission] <= PERMISSIONS[permission]
40   - end
41   -
42   - class Image < Entity
43   - root 'images', 'image'
44   -
45   - expose :url do |image, options|
46   - image.public_filename
47   - end
48   -
49   - expose :icon_url do |image, options|
50   - image.public_filename(:icon)
51   - end
52   -
53   - expose :minor_url do |image, options|
54   - image.public_filename(:minor)
55   - end
56   -
57   - expose :portrait_url do |image, options|
58   - image.public_filename(:portrait)
59   - end
60   -
61   - expose :thumb_url do |image, options|
62   - image.public_filename(:thumb)
63   - end
64   - end
65   -
66   - class CategoryBase < Entity
67   - root 'categories', 'category'
68   - expose :name, :id, :slug
69   - end
70   -
71   - class Category < CategoryBase
72   - root 'categories', 'category'
73   - expose :full_name do |category, options|
74   - category.full_name
75   - end
76   - expose :parent, :using => CategoryBase, if: { parent: true }
77   - expose :children, :using => CategoryBase, if: { children: true }
78   - expose :image, :using => Image
79   - expose :display_color
80   - end
81   -
82   - class Region < Category
83   - root 'regions', 'region'
84   - expose :parent_id
85   - end
86   -
87   - class Block < Entity
88   - root 'blocks', 'block'
89   - expose :id, :type, :settings, :position, :enabled
90   - expose :mirror, :mirror_block_id, :title
91   - expose :api_content, if: lambda { |object, options| options[:display_api_content] || object.display_api_content_by_default? }
92   - end
93   -
94   - class Box < Entity
95   - root 'boxes', 'box'
96   - expose :id, :position
97   - expose :blocks, :using => Block
98   - end
99   -
100   - class Profile < Entity
101   - expose :identifier, :name, :id
102   - expose :created_at, :format_with => :timestamp
103   - expose :updated_at, :format_with => :timestamp
104   - expose :additional_data do |profile, options|
105   - hash ={}
106   - profile.public_values.each do |value|
107   - hash[value.custom_field.name]=value.value
108   - end
109   -
110   - private_values = profile.custom_field_values - profile.public_values
111   - private_values.each do |value|
112   - if Entities.can_display_profile_field?(profile,options)
113   - hash[value.custom_field.name]=value.value
114   - end
115   - end
116   - hash
117   - end
118   - expose :image, :using => Image
119   - expose :region, :using => Region
120   - expose :type
121   - expose :custom_header
122   - expose :custom_footer
123   - end
124   -
125   - class UserBasic < Entity
126   - expose :id
127   - expose :login
128   - end
129   -
130   - class Person < Profile
131   - root 'people', 'person'
132   - expose :user, :using => UserBasic, documentation: {type: 'User', desc: 'The user data of a person' }
133   - expose :vote_count
134   - expose :comments_count do |person, options|
135   - person.comments.count
136   - end
137   - expose :following_articles_count do |person, options|
138   - person.following_articles.count
139   - end
140   - expose :articles_count do |person, options|
141   - person.articles.count
142   - end
143   - end
144   -
145   - class Enterprise < Profile
146   - root 'enterprises', 'enterprise'
147   - end
148   -
149   - class Community < Profile
150   - root 'communities', 'community'
151   - expose :description
152   - expose :admins, :if => lambda { |community, options| community.display_info_to? options[:current_person]} do |community, options|
153   - community.admins.map{|admin| {"name"=>admin.name, "id"=>admin.id, "username" => admin.identifier}}
154   - end
155   - expose :categories, :using => Category
156   - expose :members, :using => Person , :if => lambda{ |community, options| community.display_info_to? options[:current_person] }
157   - end
158   -
159   - class CommentBase < Entity
160   - expose :body, :title, :id
161   - expose :created_at, :format_with => :timestamp
162   - expose :author, :using => Profile
163   - expose :reply_of, :using => CommentBase
164   - end
165   -
166   - class Comment < CommentBase
167   - root 'comments', 'comment'
168   - expose :children, as: :replies, :using => Comment
169   - end
170   -
171   - class ArticleBase < Entity
172   - root 'articles', 'article'
173   - expose :id
174   - expose :body
175   - expose :abstract, documentation: {type: 'String', desc: 'Teaser of the body'}
176   - expose :created_at, :format_with => :timestamp
177   - expose :updated_at, :format_with => :timestamp
178   - expose :title, :documentation => {:type => "String", :desc => "Title of the article"}
179   - expose :created_by, :as => :author, :using => Profile, :documentation => {type: 'Profile', desc: 'The profile author that create the article'}
180   - expose :profile, :using => Profile, :documentation => {type: 'Profile', desc: 'The profile associated with the article'}
181   - expose :categories, :using => Category
182   - expose :image, :using => Image
183   - expose :votes_for
184   - expose :votes_against
185   - expose :setting
186   - expose :position
187   - expose :hits
188   - expose :start_date
189   - expose :end_date, :documentation => {type: 'DateTime', desc: 'The date of finish of the article'}
190   - expose :tag_list
191   - expose :children_count
192   - expose :slug, :documentation => {:type => "String", :desc => "Trimmed and parsed name of a article"}
193   - expose :path
194   - expose :followers_count
195   - expose :votes_count
196   - expose :comments_count
197   - expose :archived, :documentation => {:type => "Boolean", :desc => "Defines if a article is readonly"}
198   - expose :type
199   - expose :comments, using: CommentBase, :if => lambda{|obj,opt| opt[:params] && ['1','true',true].include?(opt[:params][:show_comments])}
200   - expose :published
201   - expose :accept_comments?, as: :accept_comments
202   - end
203   -
204   - class Article < ArticleBase
205   - root 'articles', 'article'
206   - expose :parent, :using => ArticleBase
207   - expose :children, :using => ArticleBase do |article, options|
208   - article.children.published.limit(Noosfero::API::V1::Articles::MAX_PER_PAGE)
209   - end
210   - end
211   -
212   - class User < Entity
213   - root 'users', 'user'
214   -
215   - attrs = [:id,:login,:email,:activated?]
216   - aliases = {:activated? => :activated}
217   -
218   - attrs.each do |attribute|
219   - name = aliases.has_key?(attribute) ? aliases[attribute] : attribute
220   - expose attribute, :as => name, :if => lambda{|user,options| Entities.can_display_profile_field?(user.person, options, {:field => attribute})}
221   - end
222   -
223   - expose :person, :using => Person, :if => lambda{|user,options| user.person.display_info_to? options[:current_person]}
224   - expose :permissions, :if => lambda{|user,options| Entities.can_display_profile_field?(user.person, options, {:field => :permissions, :permission => :self})} do |user, options|
225   - output = {}
226   - user.person.role_assignments.map do |role_assigment|
227   - if role_assigment.resource.respond_to?(:identifier) && !role_assigment.role.nil?
228   - output[role_assigment.resource.identifier] = role_assigment.role.permissions
229   - end
230   - end
231   - output
232   - end
233   - end
234   -
235   - class UserLogin < User
236   - root 'users', 'user'
237   - expose :private_token, documentation: {type: 'String', desc: 'A valid authentication code for post/delete api actions'}, if: lambda {|object, options| object.activated? }
238   - end
239   -
240   - class Task < Entity
241   - root 'tasks', 'task'
242   - expose :id
243   - expose :type
244   - end
245   -
246   - class Environment < Entity
247   - expose :name
248   - expose :id
249   - expose :description
250   - expose :settings, if: lambda { |instance, options| options[:is_admin] }
251   - end
252   -
253   - class Tag < Entity
254   - root 'tags', 'tag'
255   - expose :name
256   - end
257   -
258   - class Activity < Entity
259   - root 'activities', 'activity'
260   - expose :id, :params, :verb, :created_at, :updated_at, :comments_count, :visible
261   - expose :user, :using => Profile
262   - expose :target do |activity, opts|
263   - type_map = {Profile => ::Profile, ArticleBase => ::Article}.find {|h| activity.target.kind_of?(h.last)}
264   - type_map.first.represent(activity.target) unless type_map.nil?
265   - end
266   - end
267   - end
268   - end
269   -end
lib/noosfero/api/entity.rb
... ... @@ -1,25 +0,0 @@
1   -class Noosfero::API::Entity < Grape::Entity
2   -
3   - def initialize(object, options = {})
4   - object = nil if object.is_a? Exception
5   - super object, options
6   - end
7   -
8   - def self.represent(objects, options = {})
9   - if options[:has_exception]
10   - data = super objects, options.merge(is_inner_data: true)
11   - if objects.is_a? Exception
12   - data.merge ok: false, error: {
13   - type: objects.class.name,
14   - message: objects.message
15   - }
16   - else
17   - data = data.serializable_hash if data.is_a? Noosfero::API::Entity
18   - data.merge ok: true, error: { type: 'Success', message: '' }
19   - end
20   - else
21   - super objects, options
22   - end
23   - end
24   -
25   -end
lib/noosfero/api/helpers.rb
... ... @@ -1,425 +0,0 @@
1   -require 'grape'
2   -require 'base64'
3   -require 'tempfile'
4   -require_relative '../../find_by_contents'
5   -
6   -module Noosfero;
7   - module API
8   - module APIHelpers
9   - PRIVATE_TOKEN_PARAM = :private_token
10   - DEFAULT_ALLOWED_PARAMETERS = [:parent_id, :from, :until, :content_type, :author_id, :identifier, :archived]
11   -
12   - include SanitizeParams
13   - include Noosfero::Plugin::HotSpot
14   - include ForgotPasswordHelper
15   - include SearchTermHelper
16   -
17   - def set_locale
18   - I18n.locale = (params[:lang] || request.env['HTTP_ACCEPT_LANGUAGE'] || 'en')
19   - end
20   -
21   - def init_noosfero_plugins
22   - plugins
23   - end
24   -
25   - def current_user
26   - private_token = (params[PRIVATE_TOKEN_PARAM] || headers['Private-Token']).to_s
27   - @current_user ||= User.find_by private_token: private_token
28   - @current_user ||= plugins.dispatch("api_custom_login", request).first
29   - @current_user
30   - end
31   -
32   - def current_person
33   - current_user.person unless current_user.nil?
34   - end
35   -
36   - def is_admin?(environment)
37   - return false unless current_user
38   - return current_person.is_admin?(environment)
39   - end
40   -
41   - def logout
42   - @current_user = nil
43   - end
44   -
45   - def environment
46   - @environment
47   - end
48   -
49   - def present_partial(model, options)
50   - if(params[:fields].present?)
51   - begin
52   - fields = JSON.parse(params[:fields])
53   - if fields.present?
54   - options.merge!(fields.symbolize_keys.slice(:only, :except))
55   - end
56   - rescue
57   - fields = params[:fields]
58   - fields = fields.split(',') if fields.kind_of?(String)
59   - options[:only] = Array.wrap(fields)
60   - end
61   - end
62   - present model, options
63   - end
64   -
65   - include FindByContents
66   -
67   - ####################################################################
68   - #### SEARCH
69   - ####################################################################
70   - def multiple_search?(searches=nil)
71   - ['index', 'category_index'].include?(params[:action]) || (searches && searches.size > 1)
72   - end
73   - ####################################################################
74   -
75   - def logger
76   - logger = Logger.new(File.join(Rails.root, 'log', "#{ENV['RAILS_ENV'] || 'production'}_api.log"))
77   - logger.formatter = GrapeLogging::Formatters::Default.new
78   - logger
79   - end
80   -
81   - def limit
82   - limit = params[:limit].to_i
83   - limit = default_limit if limit <= 0
84   - limit
85   - end
86   -
87   - def period(from_date, until_date)
88   - return nil if from_date.nil? && until_date.nil?
89   -
90   - begin_period = from_date.nil? ? Time.at(0).to_datetime : from_date
91   - end_period = until_date.nil? ? DateTime.now : until_date
92   -
93   - begin_period..end_period
94   - end
95   -
96   - def parse_content_type(content_type)
97   - return nil if content_type.blank?
98   - content_type.split(',').map do |content_type|
99   - content_type.camelcase
100   - end
101   - end
102   -
103   - def find_article(articles, id)
104   - article = articles.find(id)
105   - article.display_to?(current_person) ? article : forbidden!
106   - end
107   -
108   - def post_article(asset, params)
109   - return forbidden! unless current_person.can_post_content?(asset)
110   -
111   - klass_type = params[:content_type] || params[:article].delete(:type) || TinyMceArticle.name
112   - return forbidden! unless klass_type.constantize <= Article
113   -
114   - article = klass_type.constantize.new(params[:article])
115   - article.last_changed_by = current_person
116   - article.created_by= current_person
117   - article.profile = asset
118   -
119   - if !article.save
120   - render_api_errors!(article.errors.full_messages)
121   - end
122   - present_partial article, :with => Entities::Article
123   - end
124   -
125   - def present_article(asset)
126   - article = find_article(asset.articles, params[:id])
127   - present_partial article, :with => Entities::Article, :params => params
128   - end
129   -
130   - def present_articles_for_asset(asset, method = 'articles')
131   - articles = find_articles(asset, method)
132   - present_articles(articles)
133   - end
134   -
135   - def present_articles(articles)
136   - present_partial paginate(articles), :with => Entities::Article, :params => params
137   - end
138   -
139   - def find_articles(asset, method = 'articles')
140   - articles = select_filtered_collection_of(asset, method, params)
141   - if current_person.present?
142   - articles = articles.display_filter(current_person, nil)
143   - else
144   - articles = articles.published
145   - end
146   - articles
147   - end
148   -
149   - def find_task(asset, id)
150   - task = asset.tasks.find(id)
151   - current_person.has_permission?(task.permission, asset) ? task : forbidden!
152   - end
153   -
154   - def post_task(asset, params)
155   - klass_type= params[:content_type].nil? ? 'Task' : params[:content_type]
156   - return forbidden! unless klass_type.constantize <= Task
157   -
158   - task = klass_type.constantize.new(params[:task])
159   - task.requestor_id = current_person.id
160   - task.target_id = asset.id
161   - task.target_type = 'Profile'
162   -
163   - if !task.save
164   - render_api_errors!(task.errors.full_messages)
165   - end
166   - present_partial task, :with => Entities::Task
167   - end
168   -
169   - def present_task(asset)
170   - task = find_task(asset, params[:id])
171   - present_partial task, :with => Entities::Task
172   - end
173   -
174   - def present_tasks(asset)
175   - tasks = select_filtered_collection_of(asset, 'tasks', params)
176   - tasks = tasks.select {|t| current_person.has_permission?(t.permission, asset)}
177   - return forbidden! if tasks.empty? && !current_person.has_permission?(:perform_task, asset)
178   - present_partial tasks, :with => Entities::Task
179   - end
180   -
181   - def make_conditions_with_parameter(params = {})
182   - parsed_params = parser_params(params)
183   - conditions = {}
184   - from_date = DateTime.parse(parsed_params.delete(:from)) if parsed_params[:from]
185   - until_date = DateTime.parse(parsed_params.delete(:until)) if parsed_params[:until]
186   -
187   - conditions[:type] = parse_content_type(parsed_params.delete(:content_type)) unless parsed_params[:content_type].nil?
188   -
189   - conditions[:created_at] = period(from_date, until_date) if from_date || until_date
190   - conditions.merge!(parsed_params)
191   -
192   - conditions
193   - end
194   -
195   - # changing make_order_with_parameters to avoid sql injection
196   - def make_order_with_parameters(object, method, params)
197   - order = "created_at DESC"
198   - unless params[:order].blank?
199   - if params[:order].include? '\'' or params[:order].include? '"'
200   - order = "created_at DESC"
201   - elsif ['RANDOM()', 'RANDOM'].include? params[:order].upcase
202   - order = 'RANDOM()'
203   - else
204   - field_name, direction = params[:order].split(' ')
205   - assoc = object.class.reflect_on_association(method.to_sym)
206   - if !field_name.blank? and assoc
207   - if assoc.klass.attribute_names.include? field_name
208   - if direction.present? and ['ASC','DESC'].include? direction.upcase
209   - order = "#{field_name} #{direction.upcase}"
210   - end
211   - end
212   - end
213   - end
214   - end
215   - return order
216   - end
217   -
218   - def make_timestamp_with_parameters_and_method(params, method)
219   - timestamp = nil
220   - if params[:timestamp]
221   - datetime = DateTime.parse(params[:timestamp])
222   - table_name = method.to_s.singularize.camelize.constantize.table_name
223   - timestamp = "#{table_name}.updated_at >= '#{datetime}'"
224   - end
225   -
226   - timestamp
227   - end
228   -
229   - def by_reference(scope, params)
230   - reference_id = params[:reference_id].to_i == 0 ? nil : params[:reference_id].to_i
231   - if reference_id.nil?
232   - scope
233   - else
234   - created_at = scope.find(reference_id).created_at
235   - scope.send("#{params.key?(:oldest) ? 'older_than' : 'younger_than'}", created_at)
236   - end
237   - end
238   -
239   - def by_categories(scope, params)
240   - category_ids = params[:category_ids]
241   - if category_ids.nil?
242   - scope
243   - else
244   - scope.joins(:categories).where(:categories => {:id => category_ids})
245   - end
246   - end
247   -
248   - def select_filtered_collection_of(object, method, params)
249   - conditions = make_conditions_with_parameter(params)
250   - order = make_order_with_parameters(object,method,params)
251   - timestamp = make_timestamp_with_parameters_and_method(params, method)
252   -
253   - objects = object.send(method)
254   - objects = by_reference(objects, params)
255   - objects = by_categories(objects, params)
256   -
257   - objects = objects.where(conditions).where(timestamp).reorder(order)
258   -
259   - params[:page] ||= 1
260   - params[:per_page] ||= limit
261   - paginate(objects)
262   - end
263   -
264   - def authenticate!
265   - unauthorized! unless current_user
266   - end
267   -
268   - def profiles_for_person(profiles, person)
269   - if person
270   - profiles.listed_for_person(person)
271   - else
272   - profiles.visible
273   - end
274   - end
275   -
276   - # Checks the occurrences of uniqueness of attributes, each attribute must be present in the params hash
277   - # or a Bad Request error is invoked.
278   - #
279   - # Parameters:
280   - # keys (unique) - A hash consisting of keys that must be unique
281   - def unique_attributes!(obj, keys)
282   - keys.each do |key|
283   - cant_be_saved_request!(key) if obj.find_by(key.to_s => params[key])
284   - end
285   - end
286   -
287   - def attributes_for_keys(keys)
288   - attrs = {}
289   - keys.each do |key|
290   - attrs[key] = params[key] if params[key].present? or (params.has_key?(key) and params[key] == false)
291   - end
292   - attrs
293   - end
294   -
295   - ##########################################
296   - # error helpers #
297   - ##########################################
298   -
299   - def not_found!
300   - render_api_error!('404 Not found', 404)
301   - end
302   -
303   - def forbidden!
304   - render_api_error!('403 Forbidden', 403)
305   - end
306   -
307   - def cant_be_saved_request!(attribute)
308   - message = _("(Invalid request) %s can't be saved") % attribute
309   - render_api_error!(message, 400)
310   - end
311   -
312   - def bad_request!(attribute)
313   - message = _("(Invalid request) %s not given") % attribute
314   - render_api_error!(message, 400)
315   - end
316   -
317   - def something_wrong!
318   - message = _("Something wrong happened")
319   - render_api_error!(message, 400)
320   - end
321   -
322   - def unauthorized!
323   - render_api_error!(_('Unauthorized'), 401)
324   - end
325   -
326   - def not_allowed!
327   - render_api_error!(_('Method Not Allowed'), 405)
328   - end
329   -
330   - # javascript_console_message is supposed to be executed as console.log()
331   - def render_api_error!(user_message, status, log_message = nil, javascript_console_message = nil)
332   - message_hash = {'message' => user_message, :code => status}
333   - message_hash[:javascript_console_message] = javascript_console_message if javascript_console_message.present?
334   - log_msg = "#{status}, User message: #{user_message}"
335   - log_msg = "#{log_message}, #{log_msg}" if log_message.present?
336   - log_msg = "#{log_msg}, Javascript Console Message: #{javascript_console_message}" if javascript_console_message.present?
337   - logger.error log_msg unless Rails.env.test?
338   - error!(message_hash, status)
339   - end
340   -
341   - def render_api_errors!(messages)
342   - messages = messages.to_a if messages.class == ActiveModel::Errors
343   - render_api_error!(messages.join(','), 400)
344   - end
345   -
346   - protected
347   -
348   - def set_session_cookie
349   - cookies['_noosfero_api_session'] = { value: @current_user.private_token, httponly: true } if @current_user.present?
350   - end
351   -
352   - def setup_multitenancy
353   - Noosfero::MultiTenancy.setup!(request.host)
354   - end
355   -
356   - def detect_stuff_by_domain
357   - @domain = Domain.by_name(request.host)
358   - if @domain.nil?
359   - @environment = Environment.default
360   - if @environment.nil? && Rails.env.development?
361   - # This should only happen in development ...
362   - @environment = Environment.create!(:name => "Noosfero", :is_default => true)
363   - end
364   - else
365   - @environment = @domain.environment
366   - end
367   - end
368   -
369   - def filter_disabled_plugins_endpoints
370   - not_found! if Noosfero::API::API.endpoint_unavailable?(self, @environment)
371   - end
372   -
373   - def asset_with_image params
374   - if params.has_key? :image_builder
375   - asset_api_params = params
376   - asset_api_params[:image_builder] = base64_to_uploadedfile(asset_api_params[:image_builder])
377   - return asset_api_params
378   - end
379   - params
380   - end
381   -
382   - def base64_to_uploadedfile(base64_image)
383   - tempfile = base64_to_tempfile base64_image
384   - converted_image = base64_image
385   - converted_image[:tempfile] = tempfile
386   - return {uploaded_data: ActionDispatch::Http::UploadedFile.new(converted_image)}
387   - end
388   -
389   - def base64_to_tempfile base64_image
390   - base64_img_str = base64_image[:tempfile]
391   - decoded_base64_str = Base64.decode64(base64_img_str)
392   - tempfile = Tempfile.new(base64_image[:filename])
393   - tempfile.write(decoded_base64_str.encode("ascii-8bit").force_encoding("utf-8"))
394   - tempfile.rewind
395   - tempfile
396   - end
397   - private
398   -
399   - def parser_params(params)
400   - parsed_params = {}
401   - params.map do |k,v|
402   - parsed_params[k.to_sym] = v if DEFAULT_ALLOWED_PARAMETERS.include?(k.to_sym)
403   - end
404   - parsed_params
405   - end
406   -
407   - def default_limit
408   - 20
409   - end
410   -
411   - def parse_content_type(content_type)
412   - return nil if content_type.blank?
413   - content_type.split(',').map do |content_type|
414   - content_type.camelcase
415   - end
416   - end
417   -
418   - def period(from_date, until_date)
419   - begin_period = from_date.nil? ? Time.at(0).to_datetime : from_date
420   - end_period = until_date.nil? ? DateTime.now : until_date
421   - begin_period..end_period
422   - end
423   - end
424   - end
425   -end
lib/noosfero/api/session.rb
... ... @@ -1,157 +0,0 @@
1   -require "uri"
2   -
3   -module Noosfero
4   - module API
5   - class Session < Grape::API
6   -
7   - # Login to get token
8   - #
9   - # Parameters:
10   - # login (*required) - user login or email
11   - # password (required) - user password
12   - #
13   - # Example Request:
14   - # POST http://localhost:3000/api/v1/login?login=adminuser&password=admin
15   - post "/login" do
16   - begin
17   - user ||= User.authenticate(params[:login], params[:password], environment)
18   - rescue User::UserNotActivated => e
19   - render_api_error!(e.message, 401)
20   - end
21   -
22   - return unauthorized! unless user
23   - @current_user = user
24   - present user, :with => Entities::UserLogin, :current_person => current_person
25   - end
26   -
27   - post "/login_from_cookie" do
28   - return unauthorized! if cookies[:auth_token].blank?
29   - user = User.where(remember_token: cookies[:auth_token]).first
30   - return unauthorized! unless user && user.activated?
31   - @current_user = user
32   - present user, :with => Entities::UserLogin, :current_person => current_person
33   - end
34   -
35   - # Create user.
36   - #
37   - # Parameters:
38   - # email (required) - Email
39   - # password (required) - Password
40   - # login - login
41   - # Example Request:
42   - # POST /register?email=some@mail.com&password=pas&password_confirmation=pas&login=some
43   - params do
44   - requires :email, type: String, desc: _("Email")
45   - requires :login, type: String, desc: _("Login")
46   - requires :password, type: String, desc: _("Password")
47   - end
48   -
49   - post "/register" do
50   - attrs = attributes_for_keys [:email, :login, :password, :password_confirmation] + environment.signup_person_fields
51   - name = params[:name].present? ? params[:name] : attrs[:email]
52   - attrs[:password_confirmation] = attrs[:password] if !attrs.has_key?(:password_confirmation)
53   - user = User.new(attrs.merge(:name => name))
54   -
55   - begin
56   - user.signup!
57   - user.generate_private_token! if user.activated?
58   - present user, :with => Entities::UserLogin, :current_person => user.person
59   - rescue ActiveRecord::RecordInvalid
60   - message = user.errors.as_json.merge((user.person.present? ? user.person.errors : {}).as_json).to_json
61   - render_api_error!(message, 400)
62   - end
63   - end
64   -
65   - params do
66   - requires :activation_code, type: String, desc: _("Activation token")
67   - end
68   -
69   - # Activate a user.
70   - #
71   - # Parameter:
72   - # activation_code (required) - Activation token
73   - # Example Request:
74   - # PATCH /activate?activation_code=28259abd12cc6a64ef9399cf3286cb998b96aeaf
75   - patch "/activate" do
76   - user = User.find_by activation_code: params[:activation_code]
77   - if user
78   - unless user.environment.enabled?('admin_must_approve_new_users')
79   - if user.activate
80   - user.generate_private_token!
81   - present user, :with => Entities::UserLogin, :current_person => current_person
82   - end
83   - else
84   - if user.create_moderate_task
85   - user.activation_code = nil
86   - user.save!
87   -
88   - # Waiting for admin moderate user registration
89   - status 202
90   - body({
91   - :message => 'Waiting for admin moderate user registration'
92   - })
93   - end
94   - end
95   - else
96   - # Token not found in database
97   - render_api_error!(_('Token is invalid'), 412)
98   - end
99   - end
100   -
101   - # Request a new password.
102   - #
103   - # Parameters:
104   - # value (required) - Email or login
105   - # Example Request:
106   - # POST /forgot_password?value=some@mail.com
107   - post "/forgot_password" do
108   - requestors = fetch_requestors(params[:value])
109   - not_found! if requestors.blank?
110   - remote_ip = (request.respond_to?(:remote_ip) && request.remote_ip) || (env && env['REMOTE_ADDR'])
111   - requestors.each do |requestor|
112   - ChangePassword.create!(:requestor => requestor)
113   - end
114   - end
115   -
116   - # Resend activation code.
117   - #
118   - # Parameters:
119   - # value (required) - Email or login
120   - # Example Request:
121   - # POST /resend_activation_code?value=some@mail.com
122   - post "/resend_activation_code" do
123   - requestors = fetch_requestors(params[:value])
124   - not_found! if requestors.blank?
125   - remote_ip = (request.respond_to?(:remote_ip) && request.remote_ip) || (env && env['REMOTE_ADDR'])
126   - requestors.each do |requestor|
127   - requestor.user.resend_activation_code
128   - end
129   - present requestors.map(&:user), :with => Entities::UserLogin
130   - end
131   -
132   - params do
133   - requires :code, type: String, desc: _("Forgot password code")
134   - end
135   - # Change password
136   - #
137   - # Parameters:
138   - # code (required) - Change password code
139   - # password (required)
140   - # password_confirmation (required)
141   - # Example Request:
142   - # PATCH /new_password?code=xxxx&password=secret&password_confirmation=secret
143   - patch "/new_password" do
144   - change_password = ChangePassword.find_by code: params[:code]
145   - not_found! if change_password.nil?
146   -
147   - if change_password.update_attributes(:password => params[:password], :password_confirmation => params[:password_confirmation])
148   - change_password.finish
149   - present change_password.requestor.user, :with => Entities::UserLogin, :current_person => current_person
150   - else
151   - something_wrong!
152   - end
153   - end
154   -
155   - end
156   - end
157   -end
lib/noosfero/api/v1/activities.rb
... ... @@ -1,22 +0,0 @@
1   -module Noosfero
2   - module API
3   - module V1
4   - class Activities < Grape::API
5   - before { authenticate! }
6   -
7   - resource :profiles do
8   -
9   - get ':id/activities' do
10   - profile = Profile.find_by id: params[:id]
11   -
12   - not_found! if profile.blank? || profile.secret || !profile.visible
13   - forbidden! if !profile.secret && profile.visible && !profile.display_private_info_to?(current_person)
14   -
15   - activities = profile.activities.map(&:activity)
16   - present activities, :with => Entities::Activity, :current_person => current_person
17   - end
18   - end
19   - end
20   - end
21   - end
22   -end
lib/noosfero/api/v1/articles.rb
... ... @@ -1,305 +0,0 @@
1   -module Noosfero
2   - module API
3   - module V1
4   - class Articles < Grape::API
5   -
6   - ARTICLE_TYPES = Article.descendants.map{|a| a.to_s}
7   -
8   - MAX_PER_PAGE = 50
9   -
10   - resource :articles do
11   -
12   - paginate max_per_page: MAX_PER_PAGE
13   - # Collect articles
14   - #
15   - # Parameters:
16   - # from - date where the search will begin. If nothing is passed the default date will be the date of the first article created
17   - # oldest - Collect the oldest articles. If nothing is passed the newest articles are collected
18   - # limit - amount of articles returned. The default value is 20
19   - #
20   - # Example Request:
21   - # GET host/api/v1/articles?from=2013-04-04-14:41:43&until=2015-04-04-14:41:43&limit=10&private_token=e96fff37c2238fdab074d1dcea8e6317
22   -
23   - desc 'Return all articles of all kinds' do
24   - detail 'Get all articles filtered by fields in query params'
25   - params Noosfero::API::Entities::Article.documentation
26   - success Noosfero::API::Entities::Article
27   - failure [[403, 'Forbidden']]
28   - named 'ArticlesList'
29   - headers [
30   - 'Per-Page' => {
31   - description: 'Total number of records',
32   - required: false
33   - }
34   - ]
35   - end
36   - get do
37   - present_articles_for_asset(environment)
38   - end
39   -
40   - desc "Return one article by id" do
41   - detail 'Get only one article by id. If not found the "forbidden" http error is showed'
42   - params Noosfero::API::Entities::Article.documentation
43   - success Noosfero::API::Entities::Article
44   - failure [[403, 'Forbidden']]
45   - named 'ArticleById'
46   - end
47   - get ':id', requirements: {id: /[0-9]+/} do
48   - present_article(environment)
49   - end
50   -
51   - post ':id' do
52   - article = environment.articles.find(params[:id])
53   - return forbidden! unless article.allow_edit?(current_person)
54   - article.update_attributes!(asset_with_image(params[:article]))
55   - present_partial article, :with => Entities::Article
56   - end
57   -
58   - desc 'Report a abuse and/or violent content in a article by id' do
59   - detail 'Submit a abuse (in general, a content violation) report about a specific article'
60   - params Noosfero::API::Entities::Article.documentation
61   - failure [[400, 'Bad Request']]
62   - named 'ArticleReportAbuse'
63   - end
64   - post ':id/report_abuse' do
65   - article = find_article(environment.articles, params[:id])
66   - profile = article.profile
67   - begin
68   - abuse_report = AbuseReport.new(:reason => params[:report_abuse])
69   - if !params[:content_type].blank?
70   - article = params[:content_type].constantize.find(params[:content_id])
71   - abuse_report.content = article_reported_version(article)
72   - end
73   -
74   - current_person.register_report(abuse_report, profile)
75   -
76   - if !params[:content_type].blank?
77   - abuse_report = AbuseReport.find_by reporter_id: current_person.id, abuse_complaint_id: profile.opened_abuse_complaint.id
78   - Delayed::Job.enqueue DownloadReportedImagesJob.new(abuse_report, article)
79   - end
80   -
81   - {
82   - :success => true,
83   - :message => _('Your abuse report was registered. The administrators are reviewing your report.'),
84   - }
85   - rescue Exception => exception
86   - #logger.error(exception.to_s)
87   - render_api_error!(_('Your report couldn\'t be saved due to some problem. Please contact the administrator.'), 400)
88   - end
89   -
90   - end
91   -
92   - desc "Returns the articles I voted" do
93   - detail 'Get the Articles I make a vote'
94   - failure [[403, 'Forbidden']]
95   - named 'ArticleFollowers'
96   - end
97   - #FIXME refactor this method
98   - get 'voted_by_me' do
99   - present_articles(current_person.votes.where(:voteable_type => 'Article').collect(&:voteable))
100   - end
101   -
102   - desc 'Perform a vote on a article by id' do
103   - detail 'Vote on a specific article with values: 1 (if you like) or -1 (if not)'
104   - params Noosfero::API::Entities::UserLogin.documentation
105   - failure [[401,'Unauthorized']]
106   - named 'ArticleVote'
107   - end
108   - post ':id/vote' do
109   - authenticate!
110   - value = (params[:value] || 1).to_i
111   - # FIXME verify allowed values
112   - render_api_error!('Vote value not allowed', 400) unless [-1, 1].include?(value)
113   - article = find_article(environment.articles, params[:id])
114   - begin
115   - vote = Vote.new(:voteable => article, :voter => current_person, :vote => value)
116   - {:vote => vote.save!}
117   - rescue ActiveRecord::RecordInvalid => e
118   - render_api_error!(e.message, 400)
119   - end
120   - end
121   -
122   - desc "Returns the total followers for the article" do
123   - detail 'Get the followers of a specific article by id'
124   - failure [[403, 'Forbidden']]
125   - named 'ArticleFollowers'
126   - end
127   - get ':id/followers' do
128   - article = find_article(environment.articles, params[:id])
129   - total = article.person_followers.count
130   - {:total_followers => total}
131   - end
132   -
133   - desc "Return the articles followed by me"
134   - get 'followed_by_me' do
135   - present_articles_for_asset(current_person, 'following_articles')
136   - end
137   -
138   - desc "Add a follower for the article" do
139   - detail 'Add the current user identified by private token, like a follower of a article'
140   - params Noosfero::API::Entities::UserLogin.documentation
141   - failure [[401, 'Unauthorized']]
142   - named 'ArticleFollow'
143   - end
144   - post ':id/follow' do
145   - authenticate!
146   - article = find_article(environment.articles, params[:id])
147   - if article.article_followers.exists?(:person_id => current_person.id)
148   - {:success => false, :already_follow => true}
149   - else
150   - article_follower = ArticleFollower.new
151   - article_follower.article = article
152   - article_follower.person = current_person
153   - article_follower.save!
154   - {:success => true}
155   - end
156   - end
157   -
158   - desc 'Return the children of a article identified by id' do
159   - detail 'Get all children articles of a specific article'
160   - params Noosfero::API::Entities::Article.documentation
161   - failure [[403, 'Forbidden']]
162   - named 'ArticleChildren'
163   - end
164   -
165   - paginate per_page: MAX_PER_PAGE, max_per_page: MAX_PER_PAGE
166   - get ':id/children' do
167   - article = find_article(environment.articles, params[:id])
168   -
169   - #TODO make tests for this situation
170   - votes_order = params.delete(:order) if params[:order]=='votes_score'
171   - articles = select_filtered_collection_of(article, 'children', params)
172   - articles = articles.display_filter(current_person, article.profile)
173   -
174   - #TODO make tests for this situation
175   - if votes_order
176   - articles = articles.joins('left join votes on articles.id=votes.voteable_id').group('articles.id').reorder('sum(coalesce(votes.vote, 0)) DESC')
177   - end
178   - Article.hit(articles)
179   - present_articles(articles)
180   - end
181   -
182   - desc 'Return one child of a article identified by id' do
183   - detail 'Get a child of a specific article'
184   - params Noosfero::API::Entities::Article.documentation
185   - success Noosfero::API::Entities::Article
186   - failure [[403, 'Forbidden']]
187   - named 'ArticleChild'
188   - end
189   - get ':id/children/:child_id' do
190   - article = find_article(environment.articles, params[:id])
191   - child = find_article(article.children, params[:child_id])
192   - child.hit
193   - present_partial child, :with => Entities::Article
194   - end
195   -
196   - desc 'Suggest a article to another profile' do
197   - detail 'Suggest a article to another profile (person, community...)'
198   - params Noosfero::API::Entities::Article.documentation
199   - success Noosfero::API::Entities::Task
200   - failure [[401,'Unauthorized']]
201   - named 'ArticleSuggest'
202   - end
203   - post ':id/children/suggest' do
204   - authenticate!
205   - parent_article = environment.articles.find(params[:id])
206   -
207   - suggest_article = SuggestArticle.new
208   - suggest_article.article = params[:article]
209   - suggest_article.article[:parent_id] = parent_article.id
210   - suggest_article.target = parent_article.profile
211   - suggest_article.requestor = current_person
212   -
213   - unless suggest_article.save
214   - render_api_errors!(suggest_article.article_object.errors.full_messages)
215   - end
216   - present_partial suggest_article, :with => Entities::Task
217   - end
218   -
219   - # Example Request:
220   - # POST api/v1/articles/:id/children?private_token=234298743290432&article[name]=title&article[body]=body
221   - desc 'Add a child article to a parent identified by id' do
222   - detail 'Create a new article and associate to a parent'
223   - params Noosfero::API::Entities::Article.documentation
224   - success Noosfero::API::Entities::Article
225   - failure [[401,'Unauthorized']]
226   - named 'ArticleAddChild'
227   - end
228   - post ':id/children' do
229   - parent_article = environment.articles.find(params[:id])
230   - params[:article][:parent_id] = parent_article.id
231   - post_article(parent_article.profile, params)
232   - end
233   - end
234   -
235   - resource :profiles do
236   - get ':id/home_page' do
237   - profiles = environment.profiles
238   - profiles = profiles.visible_for_person(current_person)
239   - profile = profiles.find_by id: params[:id]
240   - present_partial profile.home_page, :with => Entities::Article
241   - end
242   - end
243   -
244   - kinds = %w[profile community person enterprise]
245   - kinds.each do |kind|
246   - resource kind.pluralize.to_sym do
247   - segment "/:#{kind}_id" do
248   - resource :articles do
249   -
250   - desc "Return all articles associate with a profile of type #{kind}" do
251   - detail 'Get a list of articles of a profile'
252   - params Noosfero::API::Entities::Article.documentation
253   - success Noosfero::API::Entities::Article
254   - failure [[403, 'Forbidden']]
255   - named 'ArticlesOfProfile'
256   - end
257   - get do
258   - profile = environment.send(kind.pluralize).find(params["#{kind}_id"])
259   -
260   - if params[:path].present?
261   - article = profile.articles.find_by path: params[:path]
262   - if !article || !article.display_to?(current_person)
263   - article = forbidden!
264   - end
265   -
266   - present_partial article, :with => Entities::Article
267   - else
268   -
269   - present_articles_for_asset(profile)
270   - end
271   - end
272   -
273   - desc "Return a article associate with a profile of type #{kind}" do
274   - detail 'Get only one article of a profile'
275   - params Noosfero::API::Entities::Article.documentation
276   - success Noosfero::API::Entities::Article
277   - failure [[403, 'Forbidden']]
278   - named 'ArticleOfProfile'
279   - end
280   - get ':id' do
281   - profile = environment.send(kind.pluralize).find(params["#{kind}_id"])
282   - present_article(profile)
283   - end
284   -
285   - # Example Request:
286   - # POST api/v1/{people,communities,enterprises}/:asset_id/articles?private_token=234298743290432&article[name]=title&article[body]=body
287   - desc "Add a new article associated with a profile of type #{kind}" do
288   - detail 'Create a new article and associate with a profile'
289   - params Noosfero::API::Entities::Article.documentation
290   - success Noosfero::API::Entities::Article
291   - failure [[403, 'Forbidden']]
292   - named 'ArticleCreateToProfile'
293   - end
294   - post do
295   - profile = environment.send(kind.pluralize).find(params["#{kind}_id"])
296   - post_article(profile, params)
297   - end
298   - end
299   - end
300   - end
301   - end
302   - end
303   - end
304   - end
305   -end
lib/noosfero/api/v1/blocks.rb
... ... @@ -1,17 +0,0 @@
1   -module Noosfero
2   - module API
3   - module V1
4   -
5   - class Blocks < Grape::API
6   - resource :blocks do
7   - get ':id' do
8   - block = Block.find(params["id"])
9   - return forbidden! unless block.visible_to_user?(current_person)
10   - present block, :with => Entities::Block, display_api_content: true
11   - end
12   - end
13   - end
14   -
15   - end
16   - end
17   -end
lib/noosfero/api/v1/boxes.rb
... ... @@ -1,46 +0,0 @@
1   -module Noosfero
2   - module API
3   - module V1
4   -
5   - class Boxes < Grape::API
6   -
7   - kinds = %w[profile community person enterprise]
8   - kinds.each do |kind|
9   -
10   - resource kind.pluralize.to_sym do
11   -
12   - segment "/:#{kind}_id" do
13   - resource :boxes do
14   - get do
15   - profile = environment.send(kind.pluralize).find(params["#{kind}_id"])
16   - present profile.boxes, :with => Entities::Box
17   - end
18   - end
19   - end
20   - end
21   -
22   - end
23   -
24   - resource :environments do
25   - [ '/default', '/context', ':environment_id' ].each do |route|
26   - segment route do
27   - resource :boxes do
28   - get do
29   - if (route.match(/default/))
30   - env = Environment.default
31   - elsif (route.match(/context/))
32   - env = environment
33   - else
34   - env = Environment.find(params[:environment_id])
35   - end
36   - present env.boxes, :with => Entities::Box
37   - end
38   - end
39   - end
40   - end
41   - end
42   - end
43   -
44   - end
45   - end
46   -end
lib/noosfero/api/v1/categories.rb
... ... @@ -1,27 +0,0 @@
1   -module Noosfero
2   - module API
3   - module V1
4   - class Categories < Grape::API
5   -
6   - resource :categories do
7   -
8   - get do
9   - type = params[:category_type]
10   - include_parent = params[:include_parent] == 'true'
11   - include_children = params[:include_children] == 'true'
12   -
13   - categories = type.nil? ? environment.categories : environment.categories.where(:type => type)
14   - present categories, :with => Entities::Category, parent: include_parent, children: include_children
15   - end
16   -
17   - desc "Return the category by id"
18   - get ':id' do
19   - present environment.categories.find(params[:id]), :with => Entities::Category, parent: true, children: true
20   - end
21   -
22   - end
23   -
24   - end
25   - end
26   - end
27   -end
lib/noosfero/api/v1/comments.rb
... ... @@ -1,51 +0,0 @@
1   -module Noosfero
2   - module API
3   - module V1
4   - class Comments < Grape::API
5   - MAX_PER_PAGE = 20
6   -
7   -
8   - resource :articles do
9   - paginate max_per_page: MAX_PER_PAGE
10   - # Collect comments from articles
11   - #
12   - # Parameters:
13   - # reference_id - comment id used as reference to collect comment
14   - # oldest - Collect the oldest comments from reference_id comment. If nothing is passed the newest comments are collected
15   - # limit - amount of comments returned. The default value is 20
16   - #
17   - # Example Request:
18   - # GET /articles/12/comments?oldest&limit=10&reference_id=23
19   - get ":id/comments" do
20   - article = find_article(environment.articles, params[:id])
21   - comments = select_filtered_collection_of(article, :comments, params)
22   - comments = comments.without_spam
23   - comments = comments.without_reply if(params[:without_reply].present?)
24   - comments = plugins.filter(:unavailable_comments, comments)
25   - present paginate(comments), :with => Entities::Comment, :current_person => current_person
26   - end
27   -
28   - get ":id/comments/:comment_id" do
29   - article = find_article(environment.articles, params[:id])
30   - present article.comments.find(params[:comment_id]), :with => Entities::Comment, :current_person => current_person
31   - end
32   -
33   - # Example Request:
34   - # POST api/v1/articles/12/comments?private_token=2298743290432&body=new comment&title=New
35   - post ":id/comments" do
36   - authenticate!
37   - article = find_article(environment.articles, params[:id])
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   - end
47   -
48   - end
49   - end
50   - end
51   -end
lib/noosfero/api/v1/communities.rb
... ... @@ -1,84 +0,0 @@
1   -module Noosfero
2   - module API
3   - module V1
4   - class Communities < Grape::API
5   -
6   - resource :communities do
7   -
8   - # Collect comments from articles
9   - #
10   - # Parameters:
11   - # from - date where the search will begin. If nothing is passed the default date will be the date of the first article created
12   - # oldest - Collect the oldest comments from reference_id comment. If nothing is passed the newest comments are collected
13   - # limit - amount of comments returned. The default value is 20
14   - #
15   - # Example Request:
16   - # GET /communities?from=2013-04-04-14:41:43&until=2014-04-04-14:41:43&limit=10
17   - # GET /communities?reference_id=10&limit=10&oldest
18   - get do
19   - communities = select_filtered_collection_of(environment, 'communities', params)
20   - communities = profiles_for_person(communities, current_person)
21   - communities = communities.by_location(params) # Must be the last. May return Exception obj
22   - present communities, :with => Entities::Community, :current_person => current_person
23   - end
24   -
25   -
26   - # Example Request:
27   - # POST api/v1/communties?private_token=234298743290432&community[name]=some_name
28   - # for each custom field for community, add &community[field_name]=field_value to the request
29   - post do
30   - authenticate!
31   - params[:community] ||= {}
32   -
33   - params[:community][:custom_values]={}
34   - params[:community].keys.each do |key|
35   - params[:community][:custom_values][key]=params[:community].delete(key) if Community.custom_fields(environment).any?{|cf| cf.name==key}
36   - end
37   -
38   - begin
39   - community = Community.create_after_moderation(current_person, params[:community].merge({:environment => environment}))
40   - rescue
41   - community = Community.new(params[:community])
42   - end
43   -
44   - if !community.save
45   - render_api_errors!(community.errors.full_messages)
46   - end
47   -
48   - present community, :with => Entities::Community, :current_person => current_person
49   - end
50   -
51   - get ':id' do
52   - community = profiles_for_person(environment.communities, current_person).find_by_id(params[:id])
53   - present community, :with => Entities::Community, :current_person => current_person
54   - end
55   -
56   - end
57   -
58   - resource :people do
59   -
60   - segment '/:person_id' do
61   -
62   - resource :communities do
63   -
64   - get do
65   - person = environment.people.find(params[:person_id])
66   -
67   - not_found! if person.blank?
68   - forbidden! if !person.display_info_to?(current_person)
69   -
70   - communities = select_filtered_collection_of(person, 'communities', params)
71   - communities = communities.visible
72   - present communities, :with => Entities::Community, :current_person => current_person
73   - end
74   -
75   - end
76   -
77   - end
78   -
79   - end
80   -
81   - end
82   - end
83   - end
84   -end
lib/noosfero/api/v1/contacts.rb
... ... @@ -1,28 +0,0 @@
1   -module Noosfero
2   - module API
3   - module V1
4   - class Contacts < Grape::API
5   -
6   - resource :communities do
7   -
8   - resource ':id/contact' do
9   - #contact => {:name => 'some name', :email => 'test@mail.com', :subject => 'some title', :message => 'some message'}
10   - desc "Send a contact message"
11   - post do
12   - profile = environment.communities.find(params[:id])
13   - forbidden! unless profile.present?
14   - contact = Contact.new params[:contact].merge(dest: profile)
15   - if contact.deliver
16   - {:success => true}
17   - else
18   - {:success => false}
19   - end
20   - end
21   -
22   - end
23   - end
24   -
25   - end
26   - end
27   - end
28   -end
lib/noosfero/api/v1/enterprises.rb
... ... @@ -1,57 +0,0 @@
1   -module Noosfero
2   - module API
3   - module V1
4   - class Enterprises < Grape::API
5   -
6   - resource :enterprises do
7   -
8   - # Collect enterprises from environment
9   - #
10   - # Parameters:
11   - # from - date where the search will begin. If nothing is passed the default date will be the date of the first article created
12   - # oldest - Collect the oldest comments from reference_id comment. If nothing is passed the newest comments are collected
13   - # limit - amount of comments returned. The default value is 20
14   - # georef params - read `Profile.by_location` for more information.
15   - #
16   - # Example Request:
17   - # GET /enterprises?from=2013-04-04-14:41:43&until=2014-04-04-14:41:43&limit=10
18   - # GET /enterprises?reference_id=10&limit=10&oldest
19   - get do
20   - enterprises = select_filtered_collection_of(environment, 'enterprises', params)
21   - enterprises = enterprises.visible
22   - enterprises = enterprises.by_location(params) # Must be the last. May return Exception obj.
23   - present enterprises, :with => Entities::Enterprise, :current_person => current_person
24   - end
25   -
26   - desc "Return one enterprise by id"
27   - get ':id' do
28   - enterprise = environment.enterprises.visible.find_by(id: params[:id])
29   - present enterprise, :with => Entities::Enterprise, :current_person => current_person
30   - end
31   -
32   - end
33   -
34   - resource :people do
35   -
36   - segment '/:person_id' do
37   -
38   - resource :enterprises do
39   -
40   - get do
41   - person = environment.people.find(params[:person_id])
42   - enterprises = select_filtered_collection_of(person, 'enterprises', params)
43   - enterprises = enterprises.visible.by_location(params)
44   - present enterprises, :with => Entities::Enterprise, :current_person => current_person
45   - end
46   -
47   - end
48   -
49   - end
50   -
51   - end
52   -
53   -
54   - end
55   - end
56   - end
57   -end
lib/noosfero/api/v1/environments.rb
... ... @@ -1,30 +0,0 @@
1   -module Noosfero
2   - module API
3   - module V1
4   - class Environments < Grape::API
5   -
6   - resource :environment do
7   -
8   - desc "Return the person information"
9   - get '/signup_person_fields' do
10   - present environment.signup_person_fields
11   - end
12   -
13   - get ':id' do
14   - local_environment = nil
15   - if (params[:id] == "default")
16   - local_environment = Environment.default
17   - elsif (params[:id] == "context")
18   - local_environment = environment
19   - else
20   - local_environment = Environment.find(params[:id])
21   - end
22   - present_partial local_environment, :with => Entities::Environment, :is_admin => is_admin?(local_environment)
23   - end
24   -
25   - end
26   -
27   - end
28   - end
29   - end
30   -end
lib/noosfero/api/v1/people.rb
... ... @@ -1,129 +0,0 @@
1   -module Noosfero
2   - module API
3   - module V1
4   - class People < Grape::API
5   -
6   - MAX_PER_PAGE = 50
7   -
8   - desc 'API Root'
9   -
10   - resource :people do
11   - paginate max_per_page: MAX_PER_PAGE
12   -
13   - # -- A note about privacy --
14   - # We wold find people by location, but we must test if the related
15   - # fields are public. We can't do it now, with SQL, while the location
16   - # data and the fields_privacy are a serialized settings.
17   - # We must build a new table for profile data, where we can set meta-data
18   - # like:
19   - # | id | profile_id | key | value | privacy_level | source |
20   - # | 1 | 99 | city | Salvador | friends | user |
21   - # | 2 | 99 | lng | -38.521 | me only | automatic |
22   -
23   - # Collect people from environment
24   - #
25   - # Parameters:
26   - # from - date where the search will begin. If nothing is passed the default date will be the date of the first article created
27   - # oldest - Collect the oldest comments from reference_id comment. If nothing is passed the newest comments are collected
28   - # limit - amount of comments returned. The default value is 20
29   - #
30   - # Example Request:
31   - # GET /people?from=2013-04-04-14:41:43&until=2014-04-04-14:41:43&limit=10
32   - # GET /people?reference_id=10&limit=10&oldest
33   -
34   - desc "Find environment's people"
35   - get do
36   - people = select_filtered_collection_of(environment, 'people', params)
37   - people = people.visible
38   - present_partial people, :with => Entities::Person, :current_person => current_person
39   - end
40   -
41   - desc "Return the logged user information"
42   - get "/me" do
43   - authenticate!
44   - present_partial current_person, :with => Entities::Person, :current_person => current_person
45   - end
46   -
47   - desc "Return the person information"
48   - get ':id' do
49   - person = environment.people.visible.find_by(id: params[:id])
50   - return not_found! if person.blank?
51   - present person, :with => Entities::Person, :current_person => current_person
52   - end
53   -
54   - desc "Update person information"
55   - post ':id' do
56   - authenticate!
57   - return forbidden! if current_person.id.to_s != params[:id]
58   - current_person.update_attributes!(asset_with_image(params[:person]))
59   - present current_person, :with => Entities::Person, :current_person => current_person
60   - end
61   -
62   - # POST api/v1/people?person[login]=some_login&person[password]=some_password&person[name]=Jack
63   - # for each custom field for person, add &person[field_name]=field_value to the request
64   - desc "Create person"
65   - post do
66   - authenticate!
67   - user_data = {}
68   - user_data[:login] = params[:person].delete(:login) || params[:person][:identifier]
69   - user_data[:email] = params[:person].delete(:email)
70   - user_data[:password] = params[:person].delete(:password)
71   - user_data[:password_confirmation] = params[:person].delete(:password_confirmation)
72   -
73   - params[:person][:custom_values]={}
74   - params[:person].keys.each do |key|
75   - params[:person][:custom_values][key]=params[:person].delete(key) if Person.custom_fields(environment).any?{|cf| cf.name==key}
76   - end
77   -
78   - user = User.build(user_data, asset_with_image(params[:person]), environment)
79   -
80   - begin
81   - user.signup!
82   - rescue ActiveRecord::RecordInvalid
83   - render_api_errors!(user.errors.full_messages)
84   - end
85   -
86   - present user.person, :with => Entities::Person, :current_person => user.person
87   - end
88   -
89   - desc "Return the person friends"
90   - get ':id/friends' do
91   - person = environment.people.visible.find_by(id: params[:id])
92   - return not_found! if person.blank?
93   - friends = person.friends.visible
94   - present friends, :with => Entities::Person, :current_person => current_person
95   - end
96   -
97   - desc "Return the person permissions on other profiles"
98   - get ":id/permissions" do
99   - authenticate!
100   - person = environment.people.find(params[:id])
101   - return not_found! if person.blank?
102   - return forbidden! unless current_person == person || environment.admins.include?(current_person)
103   -
104   - output = {}
105   - person.role_assignments.map do |role_assigment|
106   - if role_assigment.resource.respond_to?(:identifier)
107   - output[role_assigment.resource.identifier] = role_assigment.role.permissions
108   - end
109   - end
110   - present output
111   - end
112   - end
113   -
114   - resource :profiles do
115   - segment '/:profile_id' do
116   - resource :members do
117   - paginate max_per_page: MAX_PER_PAGE
118   - get do
119   - profile = environment.profiles.find_by id: params[:profile_id]
120   - members = select_filtered_collection_of(profile, 'members', params)
121   - present members, :with => Entities::Person, :current_person => current_person
122   - end
123   - end
124   - end
125   - end
126   - end
127   - end
128   - end
129   -end
lib/noosfero/api/v1/profiles.rb
... ... @@ -1,44 +0,0 @@
1   -module Noosfero
2   - module API
3   - module V1
4   - class Profiles < Grape::API
5   -
6   - resource :profiles do
7   -
8   - get do
9   - profiles = select_filtered_collection_of(environment, 'profiles', params)
10   - profiles = profiles.visible
11   - profiles = profiles.by_location(params) # Must be the last. May return Exception obj.
12   - present profiles, :with => Entities::Profile, :current_person => current_person
13   - end
14   -
15   - get ':id' do
16   - profiles = environment.profiles
17   - profiles = profiles.visible
18   - profile = profiles.find_by id: params[:id]
19   -
20   - if profile
21   - present profile, :with => Entities::Profile, :current_person => current_person
22   - else
23   - not_found!
24   - end
25   - end
26   -
27   - delete ':id' do
28   - authenticate!
29   - profiles = environment.profiles
30   - profile = profiles.find_by id: params[:id]
31   -
32   - not_found! if profile.blank?
33   -
34   - if current_person.has_permission?(:destroy_profile, profile)
35   - profile.destroy
36   - else
37   - forbidden!
38   - end
39   - end
40   - end
41   - end
42   - end
43   - end
44   -end
lib/noosfero/api/v1/search.rb
... ... @@ -1,39 +0,0 @@
1   -module Noosfero
2   - module API
3   - module V1
4   - class Search < Grape::API
5   -
6   - resource :search do
7   - resource :article do
8   - paginate max_per_page: 200
9   - get do
10   - # Security checks
11   - sanitize_params_hash(params)
12   - # APIHelpers
13   - asset = :articles
14   - context = environment
15   -
16   - profile = environment.profiles.find(params[:profile_id]) if params[:profile_id]
17   - scope = profile.nil? ? environment.articles.is_public : profile.articles.is_public
18   - scope = scope.where(:type => params[:type]) if params[:type] && !(params[:type] == 'Article')
19   - scope = scope.where(make_conditions_with_parameter(params))
20   - scope = scope.joins(:categories).where(:categories => {:id => params[:category_ids]}) if params[:category_ids].present?
21   - scope = scope.where('articles.children_count > 0') if params[:has_children].present?
22   - query = params[:query] || ""
23   - order = "more_recent"
24   -
25   - options = {:filter => order, :template_id => params[:template_id]}
26   -
27   - search_result = find_by_contents(asset, context, scope, query, {:page => 1}, options)
28   -
29   - articles = search_result[:results]
30   -
31   - present_articles(articles)
32   - end
33   - end
34   - end
35   -
36   - end
37   - end
38   - end
39   -end
lib/noosfero/api/v1/tags.rb
... ... @@ -1,28 +0,0 @@
1   -module Noosfero
2   - module API
3   - module V1
4   - class Tags < Grape::API
5   - before { authenticate! }
6   -
7   - resource :articles do
8   -
9   - resource ':id/tags' do
10   -
11   - get do
12   - article = find_article(environment.articles, params[:id])
13   - present article.tag_list
14   - end
15   -
16   - desc "Add a tag to an article"
17   - post do
18   - article = find_article(environment.articles, params[:id])
19   - article.tag_list=params[:tags]
20   - article.save
21   - present article.tag_list
22   - end
23   - end
24   - end
25   - end
26   - end
27   - end
28   -end
lib/noosfero/api/v1/tasks.rb
... ... @@ -1,59 +0,0 @@
1   -module Noosfero
2   - module API
3   - module V1
4   - class Tasks < Grape::API
5   -# before { authenticate! }
6   -
7   -# ARTICLE_TYPES = Article.descendants.map{|a| a.to_s}
8   -
9   - resource :tasks do
10   -
11   - # Collect tasks
12   - #
13   - # Parameters:
14   - # from - date where the search will begin. If nothing is passed the default date will be the date of the first article created
15   - # oldest - Collect the oldest articles. If nothing is passed the newest articles are collected
16   - # limit - amount of articles returned. The default value is 20
17   - #
18   - # Example Request:
19   - # GET host/api/v1/tasks?from=2013-04-04-14:41:43&until=2015-04-04-14:41:43&limit=10&private_token=e96fff37c2238fdab074d1dcea8e6317
20   - get do
21   - tasks = select_filtered_collection_of(environment, 'tasks', params)
22   - tasks = tasks.select {|t| current_person.has_permission?(t.permission, environment)}
23   - present_partial tasks, :with => Entities::Task
24   - end
25   -
26   - desc "Return the task id"
27   - get ':id' do
28   - task = find_task(environment, params[:id])
29   - present_partial task, :with => Entities::Task
30   - end
31   - end
32   -
33   - kinds = %w[community person enterprise]
34   - kinds.each do |kind|
35   - resource kind.pluralize.to_sym do
36   - segment "/:#{kind}_id" do
37   - resource :tasks do
38   - get do
39   - profile = environment.send(kind.pluralize).find(params["#{kind}_id"])
40   - present_tasks(profile)
41   - end
42   -
43   - get ':id' do
44   - profile = environment.send(kind.pluralize).find(params["#{kind}_id"])
45   - present_task(profile)
46   - end
47   -
48   - post do
49   - profile = environment.send(kind.pluralize).find(params["#{kind}_id"])
50   - post_task(profile, params)
51   - end
52   - end
53   - end
54   - end
55   - end
56   - end
57   - end
58   - end
59   -end
lib/noosfero/api/v1/users.rb
... ... @@ -1,45 +0,0 @@
1   -module Noosfero
2   - module API
3   - module V1
4   - class Users < Grape::API
5   -
6   - resource :users do
7   -
8   - get do
9   - users = select_filtered_collection_of(environment, 'users', params)
10   - users = users.select{|u| u.person.display_info_to? current_person}
11   - present users, :with => Entities::User, :current_person => current_person
12   - end
13   -
14   - get "/me" do
15   - authenticate!
16   - present current_user, :with => Entities::User, :current_person => current_person
17   - end
18   -
19   - get ":id" do
20   - user = environment.users.find_by id: params[:id]
21   - if user
22   - present user, :with => Entities::User, :current_person => current_person
23   - else
24   - not_found!
25   - end
26   - end
27   -
28   - get ":id/permissions" do
29   - authenticate!
30   - user = environment.users.find(params[:id])
31   - output = {}
32   - user.person.role_assignments.map do |role_assigment|
33   - if role_assigment.resource.respond_to?(:identifier) && role_assigment.resource.identifier == params[:profile]
34   - output[:permissions] = role_assigment.role.permissions
35   - end
36   - end
37   - present output
38   - end
39   -
40   - end
41   -
42   - end
43   - end
44   - end
45   -end
lib/tasks/grape_routes.rb
1 1 desc "Print out grape routes"
2 2 task :grape_routes => :environment do
3 3 #require 'api/api.rb'
4   - Noosfero::API::API.routes.each do |route|
  4 + Api::App.routes.each do |route|
5 5 puts route
6 6 method = route.route_method
7 7 path = route.route_path
... ...
plugins/comment_paragraph/lib/comment_paragraph_plugin.rb
... ... @@ -56,7 +56,7 @@ class CommentParagraphPlugin &lt; Noosfero::Plugin
56 56 end
57 57  
58 58 def self.api_mount_points
59   - [CommentParagraphPlugin::API]
  59 + [CommentParagraphPlugin::Api]
60 60 end
61 61  
62 62 def content_types
... ...
plugins/comment_paragraph/lib/comment_paragraph_plugin/api.rb
1   -class CommentParagraphPlugin::API < Grape::API
  1 +class CommentParagraphPlugin::Api < Grape::API
2 2 MAX_PER_PAGE = 20
3 3  
4 4 resource :articles do
... ... @@ -9,7 +9,7 @@ class CommentParagraphPlugin::API &lt; Grape::API
9 9 comments = comments.without_spam
10 10 comments = comments.in_paragraph(params[:paragraph_uuid])
11 11 comments = comments.without_reply if(params[:without_reply].present?)
12   - present paginate(comments), :with => Noosfero::API::Entities::Comment, :current_person => current_person
  12 + present paginate(comments), :with => Api::Entities::Comment, :current_person => current_person
13 13 end
14 14  
15 15 {activate: true, deactivate: false}.each do |method, value|
... ... @@ -19,7 +19,7 @@ class CommentParagraphPlugin::API &lt; Grape::API
19 19 return forbidden! unless article.comment_paragraph_plugin_enabled? && article.allow_edit?(current_person)
20 20 article.comment_paragraph_plugin_activate = value
21 21 article.save!
22   - present_partial article, :with => Noosfero::API::Entities::Article
  22 + present_partial article, :with => Api::Entities::Article
23 23 end
24 24 end
25 25  
... ...
plugins/comment_paragraph/lib/ext/entities.rb
1   -require File.join(Rails.root,'lib','noosfero','api','entities')
2   -module Noosfero
3   - module API
4   - module Entities
5   - class Comment < CommentBase
6   - expose :paragraph_uuid
7   - expose :comment_paragraph_selected_area
8   - expose :comment_paragraph_selected_content
9   - end
  1 +require_dependency 'api/entities'
  2 +
  3 +module API
  4 + module Entities
  5 + class Comment < CommentBase
  6 + expose :paragraph_uuid
  7 + expose :comment_paragraph_selected_area
  8 + expose :comment_paragraph_selected_content
10 9 end
11 10 end
12 11 end
... ...
plugins/push_notification/lib/push_notification_plugin.rb
... ... @@ -39,7 +39,7 @@ class PushNotificationPlugin &lt; Noosfero::Plugin
39 39 end
40 40  
41 41 def self.api_mount_points
42   - [PushNotificationPlugin::API]
  42 + [PushNotificationPlugin::Api]
43 43 end
44 44  
45 45 def self.plugin_description
... ...
plugins/push_notification/lib/push_notification_plugin/api.rb
1   -require File.dirname(__FILE__) + '/../../../../../lib/noosfero/api/helpers'
  1 +require_dependency 'api/helpers'
2 2 require_relative 'api_entities'
3 3  
4   -class PushNotificationPlugin::API < Grape::API
  4 +class PushNotificationPlugin::Api < Grape::API
5 5  
6   - include Noosfero::API::APIHelpers
  6 + include Api::Helpers
7 7  
8 8 resource :push_notification_plugin do
9 9  
... ...
plugins/push_notification/lib/push_notification_plugin/api_entities.rb
1 1 module PushNotificationPlugin::Entities
2   - class DeviceUser < Noosfero::API::Entities::User
  2 + class DeviceUser < Api::Entities::User
3 3 expose :device_token_list, :as => :device_tokens
4 4 expose :notification_settings do |user, options|
5 5 user.notification_settings.hash_flags
... ...
plugins/recent_content/lib/recent_content_block.rb
... ... @@ -50,7 +50,7 @@ class RecentContentBlock &lt; Block
50 50  
51 51 def api_content
52 52 children = self.articles_of_folder(self.root, self.total_items)
53   - Noosfero::API::Entities::ArticleBase.represent(children).as_json
  53 + Api::Entities::ArticleBase.represent(children).as_json
54 54 end
55 55  
56 56 def display_api_content_by_default?
... ...
plugins/statistics/lib/ext/entities.rb
1   -require File.join(Rails.root,'lib','noosfero','api','entities')
2   -module Noosfero
3   - module API
4   - module Entities
5   - class Block < Entity
6   - available_counters = (StatisticsBlock::USER_COUNTERS + StatisticsBlock::COMMUNITY_COUNTERS + StatisticsBlock::ENTERPRISE_COUNTERS).uniq
7   - expose :statistics, :if => lambda { |block, options| block.is_a? StatisticsBlock } do |block, options|
8   - statistics = []
9   - available_counters.each do |counter_attr|
10   - counter_method = counter_attr.to_s.gsub('_counter','').pluralize.to_sym
11   - counter = {
12   - name: counter_method,
13   - display: block.is_counter_available?(counter_attr) && block.is_visible?(counter_attr),
14   - quantity: (block.respond_to?(counter_method) && block.is_visible?(counter_attr)) ? block.send(counter_method) : nil
15   - }
16   - statistics << counter
17   - end
18   - statistics
19   - end
  1 +require_dependency 'api/entities'
20 2  
  3 +module Entities
  4 + class Block < Api::Entity
  5 + available_counters = (StatisticsBlock::USER_COUNTERS + StatisticsBlock::COMMUNITY_COUNTERS + StatisticsBlock::ENTERPRISE_COUNTERS).uniq
  6 + expose :statistics, :if => lambda { |block, options| block.is_a? StatisticsBlock } do |block, options|
  7 + statistics = []
  8 + available_counters.each do |counter_attr|
  9 + counter_method = counter_attr.to_s.gsub('_counter','').pluralize.to_sym
  10 + counter = {
  11 + name: counter_method,
  12 + display: block.is_counter_available?(counter_attr) && block.is_visible?(counter_attr),
  13 + quantity: (block.respond_to?(counter_method) && block.is_visible?(counter_attr)) ? block.send(counter_method) : nil
  14 + }
  15 + statistics << counter
21 16 end
  17 + statistics
22 18 end
  19 +
23 20 end
24 21 end
25   -
... ...
test/api/api_test.rb
1 1 require_relative 'test_helper'
2 2  
3   -class MyPlugin < Noosfero::Plugin;end
4   -class MyPlugin::API;end
  3 +class MyPlugin < Noosfero::Plugin; end
  4 +class MyPlugin::Api; end
5 5  
6   -class APITest < ActiveSupport::TestCase
  6 +class ApiTest < ActiveSupport::TestCase
7 7  
8 8 should 'endpoint should not be available if its plugin is unavailable' do
9 9 endpoint = mock()
10 10 environment = Environment.default
11 11 environment.stubs(:plugin_enabled?).returns(false)
12   - endpoint.stubs(:options).returns({:for => MyPlugin::API})
  12 + endpoint.stubs(:options).returns({:for => MyPlugin::Api})
13 13  
14   - assert Noosfero::API::API.endpoint_unavailable?(endpoint, environment)
  14 + assert Api::App.endpoint_unavailable?(endpoint, environment)
15 15 end
16 16  
17 17 should 'endpoint should be available if its plugin is available' do
18   - class MyPlugin < Noosfero::Plugin;end
19   - class MyPlugin::API;end
20   -
21 18 endpoint = mock()
22 19 environment = Environment.default
23 20 environment.stubs(:plugin_enabled?).returns(true)
24   - endpoint.stubs(:options).returns({:for => MyPlugin::API})
  21 + endpoint.stubs(:options).returns({:for => MyPlugin::Api})
25 22  
26   - assert !Noosfero::API::API.endpoint_unavailable?(endpoint, environment)
  23 + assert !Api::App.endpoint_unavailable?(endpoint, environment)
27 24 end
28 25  
29 26 end
... ...
test/api/helpers_test.rb
1 1 require_relative 'test_helper'
2   -require "base64"
3   -require 'noosfero/api/helpers'
  2 +require 'base64'
4 3  
5   -class APIHelpersTest < ActiveSupport::TestCase
  4 +class Api::HelpersTest < ActiveSupport::TestCase
6 5  
7   - include Noosfero::API::APIHelpers
  6 + include Api::Helpers
8 7  
9 8 def setup
10 9 create_and_activate_user
... ... @@ -206,7 +205,7 @@ class APIHelpersTest &lt; ActiveSupport::TestCase
206 205 end
207 206  
208 207 should 'render not_found if endpoint is unavailable' do
209   - Noosfero::API::API.stubs(:endpoint_unavailable?).returns(true)
  208 + Api::App.stubs(:endpoint_unavailable?).returns(true)
210 209 self.expects(:not_found!)
211 210  
212 211 filter_disabled_plugins_endpoints
... ...
test/api/test_helper.rb
... ... @@ -8,7 +8,7 @@ class ActiveSupport::TestCase
8 8 USER_LOGIN = "testapi"
9 9  
10 10 def app
11   - Noosfero::API::API
  11 + Api::App
12 12 end
13 13  
14 14 def create_and_activate_user
... ...