cartodb/app/controllers/carto/api/visualization_presenter.rb
2020-06-15 10:58:47 +08:00

281 lines
11 KiB
Ruby

require_relative 'external_source_presenter'
require_relative 'permission_presenter'
require_relative 'user_table_presenter'
module Carto
module Api
class VisualizationPresenter
def initialize(visualization, current_viewer, context,
related: true, related_canonical_visualizations: false, show_user: false,
show_stats: true, show_table: true, show_liked: true,
show_permission: true, show_synchronization: true, show_uses_builder_features: true,
show_table_size_and_row_count: true, show_auth_tokens: true, show_user_basemaps: false,
password: nil, with_dependent_visualizations: 0)
@visualization = visualization
@current_viewer = current_viewer
@context = context
@related = related
@load_related_canonical_visualizations = related_canonical_visualizations
@show_user = show_user
@show_stats = show_stats
@show_liked = show_liked
@show_table = show_table
@show_permission = show_permission
@show_synchronization = show_synchronization
@show_uses_builder_features = show_uses_builder_features
@show_table_size_and_row_count = show_table_size_and_row_count
@show_auth_tokens = show_auth_tokens
@show_user_basemaps = show_user_basemaps
@password = password
@with_dependent_visualizations = with_dependent_visualizations
@presenter_cache = Carto::Api::PresenterCache.new
end
def with_presenter_cache(presenter_cache)
@presenter_cache = presenter_cache
self
end
def to_poro
return_private_poro = @visualization.can_view_private_info?(@current_viewer)
poro = return_private_poro ? to_private_poro : to_public_poro
poro[:user] = user if show_user
if load_related_canonical_visualizations
poro[:related_canonical_visualizations] = related_canonicals
# The count doesn't take into account privacy concerns
poro[:related_canonical_visualizations_count] = @visualization.related_canonical_visualizations.count
end
if with_dependent_visualizations > 0
dependencies = @visualization.dependent_visualizations
poro[:dependent_visualizations_count] = dependencies.count
limited_dependencies = most_recent_dependencies(dependencies, with_dependent_visualizations)
poro[:dependent_visualizations] = limited_dependencies.map do |dependent_visualization|
VisualizationPresenter.new(dependent_visualization, @current_viewer, @context).to_summarized_poro
end
end
poro[:liked] = @current_viewer ? @visualization.liked_by?(@current_viewer) : false if show_liked
poro[:permission] = permission if show_permission
poro[:stats] = show_stats ? @visualization.stats : {}
if show_auth_tokens
poro[:auth_tokens] = auth_tokens
elsif return_private_poro || @visualization.is_accessible_with_password?(@current_viewer, @password)
poro[:auth_tokens] = auth_tokens
end
poro[:table] = user_table_presentation if show_table
poro
end
def to_private_poro
poro = {
id: @visualization.id,
name: @visualization.name,
display_name: @visualization.display_name,
map_id: @visualization.map_id,
active_layer_id: @visualization.active_layer_id,
type: @visualization.type,
tags: @visualization.tags,
description: @visualization.description,
privacy: @visualization.privacy.upcase,
created_at: @visualization.created_at,
updated_at: @visualization.updated_at,
locked: @visualization.locked,
source: @visualization.source,
title: @visualization.title,
license: @visualization.license,
attributions: @visualization.attributions,
kind: @visualization.kind,
external_source: Carto::Api::ExternalSourcePresenter.new(@visualization.external_source).to_poro,
url: url,
version: @visualization.version || 2,
# TODO: The following are Odyssey fields and could be removed
# They are kept here for now for compatibility with the old presenter and JS code
# `children` is hardcoded to avoid a performance impact (an extra query)
prev_id: @visualization.prev_id,
next_id: @visualization.next_id,
parent_id: @visualization.parent_id,
transition_options: @visualization.transition_options,
active_child: nil,
children: []
}
poro[:related_tables] = related_tables if related
poro[:synchronization] = synchronization if show_synchronization
poro[:uses_builder_features] = @visualization.uses_builder_features? if show_uses_builder_features
poro
end
def to_public_poro
{
id: @visualization.id,
name: @visualization.name,
display_name: @visualization.display_name,
attributions: @visualization.attributions,
source: @visualization.source,
license: @visualization.license,
type: @visualization.type,
tags: @visualization.tags,
description: @visualization.description,
created_at: @visualization.created_at,
updated_at: @visualization.updated_at,
title: @visualization.title,
kind: @visualization.kind,
privacy: @visualization.privacy.upcase,
}
end
# For dependent visualizations
def to_summarized_poro
{
id: @visualization.id,
name: @visualization.name,
updated_at: @visualization.updated_at,
permission: permission.slice(:id, :owner),
auth_tokens: auth_tokens
}
end
def to_search_preview_poro
{
type: @visualization.type,
name: @visualization.name,
url: url
}
end
# Ideally this should go at a lower level, as relates to url generation, but at least centralize logic here
# INFO: For now, no support for non-org users, as intended use is for sharing urls
def privacy_aware_map_url(additional_params = {}, action = 'public_visualizations_show_map')
organization = @visualization.user.organization
return unless organization
# When a visualization is private, checks of permissions need not only the Id but also the vis owner database schema
# Logic on public_map route will handle permissions so here we only "namespace the id" when proceeds
if @visualization.is_privacy_private?
# Final url will be like ORG.carto.com/u/VIEWER/viz/OWNER_SCHEMA.VIS_ID/public_map
base_url_username = @current_viewer.username
vis_id_schema = @visualization.user.database_schema
else
# Final url will be like ORG.carto.com/u/VIEWER/viz/VIS_ID/public_map
base_url_username = @visualization.user.username
vis_id_schema = nil
end
# this builds only the fragment /viz/xxxxx/public_map
path = CartoDB.path(@context, action, additional_params.merge(id: qualified_visualization_id(vis_id_schema)))
"#{CartoDB.base_url(organization.name, base_url_username)}#{path}"
end
def qualified_visualization_id(schema = nil)
schema.nil? ? @visualization.id : "#{schema}.#{@visualization.id}"
end
private
attr_reader :related, :load_related_canonical_visualizations, :show_user,
:show_stats, :show_table, :show_liked,
:show_permission, :show_synchronization, :show_uses_builder_features,
:show_table_size_and_row_count, :show_auth_tokens,
:show_user_basemaps, :with_dependent_visualizations
def user_table_presentation
Carto::Api::UserTablePresenter.new(@visualization.user_table, @current_viewer,
show_size_and_row_count: show_table_size_and_row_count)
.with_presenter_cache(@presenter_cache).to_poro
end
def synchronization
Carto::Api::SynchronizationPresenter.new(@visualization.synchronization).to_poro
end
def permission
unless @visualization.permission.nil?
Carto::Api::PermissionPresenter.new(@visualization.permission, current_viewer: @current_viewer)
.with_presenter_cache(@presenter_cache).to_poro
end
end
def auth_tokens
if @visualization.password_protected? && (@visualization.user.id == @current_viewer.try(:id) || @visualization.password_valid?(@password))
@visualization.get_auth_tokens
elsif @visualization.is_privacy_private?
@current_viewer.get_auth_tokens
else
[]
end
end
def related_tables
related = if @visualization.user_table
@visualization.related_tables.select { |table| table.id != @visualization.user_table.id }
else
@visualization.related_tables
end
related.map do |table|
Carto::Api::UserTablePresenter.new(table, @current_viewer).with_presenter_cache(@presenter_cache).to_poro
end
end
def related_canonicals
@visualization
.related_canonical_visualizations
.select { |v| v.is_viewable_by_user?(@current_viewer) }
.map { |v| self.class.new(v, @current_viewer, @context).to_poro }
end
def children_poro(visualization)
{
id: visualization.id,
prev_id: visualization.prev_id,
type: visualization.type,
next_id: visualization.next_id,
transition_options: visualization.transition_options,
map_id: visualization.map_id
}
end
def url
if @visualization.canonical?
dataset_name = @visualization.qualified_name(@current_viewer).tr('"', '')
CartoDB.url(@context, 'public_tables_show_bis',
params: { id: dataset_name },
user: @current_viewer)
elsif @visualization.kuviz?
CartoDB.url(@context, 'kuviz_show',
params: { id: @visualization.id },
user: @current_viewer)
else
CartoDB.url(@context, 'public_visualizations_show_map',
params: { id: @visualization.id },
user: @current_viewer)
end
end
def user
Carto::Api::UserPresenter.new(@visualization.user,
current_viewer: @current_viewer,
fetch_db_size: false,
fetch_basemaps: show_user_basemaps,
fetch_profile: false).to_poro
end
def most_recent_dependencies(dependencies, limit)
sorted_dependencies = dependencies.sort_by(&:updated_at).reverse
sorted_dependencies.first(limit)
end
end
end
end