Noosfero/noosfero

View on GitHub
app/api/entities.rb

Summary

Maintainability
B
6 hrs
Test Coverage
module Api
  module Entities
    PERMISSIONS = {
      admin: 0,
      self: 10,
      private_content: 20,
      logged_user: 30,
      anonymous: 40
    }

    def self.can_display_profile_field?(profile, options, permission_options = {})
      permissions = { field: "", permission: :private_content }
      permissions.merge!(permission_options)
      field = permissions[:field]
      permission = permissions[:permission]
      return true if profile.display_to? &&
                     profile.public_fields.map { |f| f.to_sym }.include?(field.to_sym)

      current_person = options[:current_person]

      current_permission = if current_person.present?
                             if current_person.is_admin?
                               :admin
                             elsif current_person == profile
                               :self
                             elsif profile.display_private_info_to?(current_person)
                               :private_content
                             else
                               :logged_user
                             end
                           else
                             :anonymous
      end
      PERMISSIONS[current_permission] <= PERMISSIONS[permission]
    end

    def self.expose_optional_field?(field, options = {})
      return false if options[:params].nil?

      optional_fields = options[:params][:optional_fields] || []
      optional_fields.include?(field.to_s)
    end

    class Image < Entity
      expose :id
      expose :filename
      expose :url do |image, options|
        image.public_filename
      end

      expose :icon_url do |image, options|
        image.public_filename(:icon)
      end

      expose :minor_url do |image, options|
        image.public_filename(:minor)
      end

      expose :portrait_url do |image, options|
        image.public_filename(:portrait)
      end

      expose :thumb_url do |image, options|
        image.public_filename(:thumb)
      end
    end

    class CategoryBase < Entity
      expose :name, :id, :slug
    end

    class Category < CategoryBase
      expose :full_name do |category, options|
        category.full_name
      end
      expose :parent, using: CategoryBase, if: { parent: true }
      expose :children, using: CategoryBase, if: { children: true }
      expose :image, using: Image
      expose :display_color
    end

    class Region < Category
      expose :parent_id
    end

    class BlockDefinition < Entity
      expose :description
      expose :short_description
      expose :pretty_name, as: :name
      expose :name, as: :type
    end

    class Block < Entity
      expose :id, :type, :settings, :position, :enabled, :box_id
      expose :mirror, :mirror_block_id, :title
      expose :api_content, if: lambda { |object, options| options[:display_api_content] || object.display_api_content_by_default? } do |block, options|
        block.api_content({ current_person: options[:current_person] }.merge(options[:api_content_params] || {}))
      end
      expose :permissions do |block, options|
        Entities.permissions_for_entity(block, options[:current_person], :allow_edit?)
      end
      expose :images, using: Image, if: lambda { |profile, options| Entities.expose_optional_field?(:images, options) }
      expose :definition do |block, options|
        BlockDefinition.represent(block.class)
      end
    end

    class Box < Entity
      expose :id, :position
      expose :blocks, using: Block do |box, options|
        box.blocks.select { |block| block.visible_to_user?(options[:current_person]) || block.allow_edit?(options[:current_person]) }
      end
    end

    class Profile < Entity
      expose :identifier, :name, :id
      expose :created_at
      expose :updated_at

      expose :additional_data, if: lambda { |profile, options| Entities.expose_optional_field?(:additional_data, options) } do |profile, options|
        hash = {}
        profile.environment.send("all_custom_#{profile.type.downcase}_fields").each do |field, settings|
          if settings["active"].to_s == "true"
            field_privacy = profile.fields_privacy[field] || profile.fields_privacy[field.to_sym]
            value = field_privacy == "public" ? :anonymous : :private_content
            if Entities.can_display_profile_field?(profile, options, field: field, permission: value)
              hash[field] = profile.send("custom_field_value", field)
            end
          end
        end

        hash
      end
      expose :image, using: Image
      expose :top_image, using: Image
      expose :region, using: Region
      expose :tag_list, if: lambda { |profile, options| Entities.expose_optional_field?(:tag_list, options) }
      expose :type
      expose :custom_header
      expose :custom_footer
      expose :layout_template
      expose :permissions do |profile, options|
        Entities.permissions_for_entity(profile, options[:current_person],
                                        :allow_post_content?, :allow_edit?, :allow_destroy?, :allow_edit_design? , :allow_post_scrap?)
      end
      expose :theme do |profile, options|
        profile.theme || profile.environment.theme
      end
      expose :boxes_with_blocks, using: Box, as: :boxes, if: lambda { |profile, options| Entities.expose_optional_field?(:boxes, options) }
    end

    class UserBasic < Entity
      expose :id
      expose :login
    end

    class Person < Profile
      expose :user, using: UserBasic, documentation: { type: "User", desc: "The user data of a person" }
      expose :vote_count

      attrs = [:email, :country, :state, :city, :nationality, :formation, :schooling]
      attrs.each do |attribute|
        name = attribute
        expose attribute, as: name, if: lambda { |person, options| Entities.can_display_profile_field?(person, options, field: attribute) }
      end

      expose :comments_count do |person, options|
        person.comments.count
      end
      expose :following_articles_count do |person, options|
        person.following_articles.count
      end
      expose :articles_count do |person, options|
        person.articles.count
      end
      expose :friends_count do |person, options|
        person.friends.size
      end
    end

    class Enterprise < Profile
    end

    class Community < Profile
      expose :description
      expose :admins, if: lambda { |community, options| Entities.expose_optional_field?(:admins, options) && community.display_to?(options[:current_person]) } do |community, options|
        community.admins.map { |admin| { "name" => admin.name, "id" => admin.id, "username" => admin.identifier } }
      end
      expose :categories, using: Category, if: lambda { |community, options| Entities.expose_optional_field?(:categories, options) }
      expose :members_count
      expose :closed
      expose :members, if: lambda { |community, options| Entities.expose_optional_field?(:members, options) }
    end

    class CommentBase < Entity
      expose :body, :title, :id
      expose :created_at
      expose :author, using: Profile
      expose :reply_of, using: CommentBase
      expose :permissions do |comment, options|
        Entities.permissions_for_entity(comment, options[:current_person],
                                        :allow_destroy?)
      end
    end

    class Comment < CommentBase
      expose :children, as: :replies, using: Comment
    end

    class ArticleBase < Entity
      expose :id
      expose :body
      expose :abstract, documentation: { type: "String", desc: "Teaser of the body" }
      expose :created_at
      expose :updated_at
      expose :url
      expose :title, documentation: { type: "String", desc: "Title of the article" }
      expose :created_by, as: :author, using: Profile, documentation: { type: "Profile", desc: "The profile author that create the article" }
      expose :profile, using: Profile, documentation: { type: "Profile", desc: "The profile associated with the article" }
      expose :categories, using: Category
      expose :image, using: Image
      expose :votes_for
      expose :votes_against
      expose :setting
      expose :position
      expose :hits
      expose :start_date
      expose :end_date, documentation: { type: "DateTime", desc: "The date of finish of the article" }
      expose :tag_list
      expose :children_count
      expose :slug, documentation: { type: "String", desc: "Trimmed and parsed name of a article" }
      expose :path
      expose :followers_count
      expose :votes_count
      expose :comments_count
      expose :archived, documentation: { type: "Boolean", desc: "Defines if a article is readonly" }
      expose :type
      expose :comments, using: CommentBase, if: lambda { |comment, options| Entities.expose_optional_field?(:comments, options) }
      expose :published
      expose :access
      expose :accept_comments?, as: :accept_comments
      expose :mime_type
      expose :size, if: lambda { |article, options| article.kind_of?(UploadedFile) }
      expose :name
      expose :public_filename, if: lambda { |article, options| article.kind_of?(UploadedFile) }
    end

    def self.permissions_for_entity(entity, current_person, *method_names)
      method_names.map { |method| entity.send(method, current_person) ? method.to_s.gsub(/\?/, "") : nil }.compact
    end

    class Article < ArticleBase
      expose :parent, using: ArticleBase, if: lambda { |article, options| Entities.expose_optional_field?(:parent, options) }
      expose :parent_id
      expose :children, using: ArticleBase do |article, options|
        article.children.published.limit(V1::Articles::MAX_PER_PAGE)
      end
      expose :permissions do |article, options|
        Entities.permissions_for_entity(article, options[:current_person],
                                        :allow_edit?, :allow_post_content?, :allow_delete?, :allow_create?)
      end
    end

    class User < Entity
      attrs = [:id, :login, :email, :activated?]
      aliases = { activated?: :activated }

      attrs.each do |attribute|
        name = aliases.has_key?(attribute) ? aliases[attribute] : attribute
        expose attribute, as: name, if: lambda { |user, options| Entities.can_display_profile_field?(user.person, options, field: attribute) }
      end

      expose :person, using: Person, if: lambda { |user, options| user.person.display_to? options[:current_person] }
      expose :permissions, if: lambda { |user, options| Entities.can_display_profile_field?(user.person, options, field: :permissions, permission: :self) } do |user, options|
        output = {}
        user.person.role_assignments.map do |role_assigment|
          if role_assigment.resource.respond_to?(:identifier) && !role_assigment.role.nil?
            output[role_assigment.resource.identifier] = role_assigment.role.permissions
          end
        end
        output
      end
    end

    class UserLogin < User
      expose :private_token, documentation: { type: "String", desc: "A valid authentication code for post/delete api actions" }, if: lambda { |object, options| object.activated? }
      expose :activation_code
    end

    class Task < Entity
      expose :id
      expose :type
      expose :requestor, using: Profile
      expose :status
      expose :created_at
      expose :data
      expose :accept_details
      expose :reject_details
      expose :accept_disabled?, as: :accept_disabled
      expose :reject_disabled?, as: :reject_disabled
      expose :target do |task, options|
        type_map = { Profile => ::Profile, Environment => ::Environment }.find { |h| task.target.kind_of?(h.last) }
        type_map.first.represent(task.target) unless type_map.nil?
      end
      expose :api_content, if: lambda { |object, options| options[:display_api_content] || object.display_api_content_by_default? } do |task, options|
        task.api_content({ current_person: options[:current_person] }.merge(options[:api_content_params] || {}))
      end
    end

    class Environment < Entity
      expose :name
      expose :id
      expose :description
      expose :layout_template
      expose :signup_intro
      expose :terms_of_use
      expose :contact_email
      expose :captcha_site_key do |environment, options|
        Recaptcha.configuration.site_key
      end
      expose :captcha_signup_enable do |environment, options|
        environment.require_captcha?(:signup, nil, environment)
      end
      expose :top_url, as: :host
      expose :type do |environment, options|
        "Environment"
      end
      expose :settings, if: lambda { |instance, options| options[:is_admin] }
      expose :permissions, if: lambda { |environment, options| options[:current_person].present? } do |environment, options|
        environment.permissions_for(options[:current_person])
      end
      expose :theme
      expose :boxes, using: Box, if: lambda { |instance, options| Entities.expose_optional_field?(:boxes, options) }
    end

    class Tag < Entity
      expose :name
      expose :taggings_count, as: :count
    end

    class Activity < Entity
      expose :id, :created_at, :updated_at
      expose :user, using: Profile

      expose :target do |activity, opts|
        type_map = { Profile => ::Profile, ArticleBase => ::Article }.find { |h| activity.target.kind_of?(h.last) }
        type_map.first.represent(activity.target) unless type_map.nil?
      end
      expose :params, if: lambda { |activity, options| activity.kind_of?(ActionTracker::Record) }
      expose :content, if: lambda { |activity, options| activity.kind_of?(::Scrap) }
      expose :verb do |activity, options|
        activity.kind_of?(::Scrap) ? "leave_scrap" : activity.verb
      end

      expose :comments_count do |activity, opts|
        activity.kind_of?(::Scrap) ? activity.replies.count : activity.comments_count
      end
    end

    class Scrap < Entity
      expose :id, :created_at, :updated_at, :scrap_id
      expose :receiver, using: Profile
      expose :sender, using: Profile      
      expose :content
      expose :replies_count do |scrap, opts|
        scrap.replies.count
      end
    end

    class Role < Entity
      expose :id
      expose :name
      expose :key
      expose :assigned do |role, options|
        (options[:person_roles] || []).include?(role)
      end
    end

    class AbuseReport < Entity
      expose :id
      expose :reporter, using: Person
      expose :reason
      expose :created_at
    end

    class AbuseComplaint < Task
      expose :abuse_reports, using: AbuseReport
    end

    class Domain < Entity
      expose :id
      expose :name
      expose :is_default
      expose :owner do |domain, options|
        type_map = { Profile => ::Profile, Environment => ::Environment }.find { |k, v| domain.owner.kind_of?(v) }
        type_map.first.represent(domain.owner, options) unless type_map.nil?
      end
    end

    class Response < Entity
      expose :success
      expose :code
      expose :message
    end

    class Setting < Entity
      expose :available_blocks, using: BlockDefinition
    end
  end
end