app/helpers/repositories_helper.rb

Summary

Maintainability
A
2 hrs
Test Coverage
# frozen_string_literal: true

# Rendering push activities can be tricky, since tags can come on go, and with
# them, dangling repositories that used to contain them. Because of this, this
# helper renders the proper HTML for push activities, while being safe at it.
# rubocop:disable Metrics/ModuleLength
module RepositoriesHelper
  include ActivitiesHelper

  def render_repository_information(repository)
    user = current_user

    content_tag(:ul) do
      concat content_tag(:li, "You can push images") if can_push?(repository.namespace, user)
      concat content_tag(:li, "You can pull images") if can_pull?(repository.namespace, user)

      if owner?(repository.namespace, user)
        concat content_tag(:li, "You are an owner of this repository")
      elsif contributor?(repository.namespace, user)
        concat content_tag(:li, "You are a contributor in this repository")
      elsif viewer?(repository.namespace, user)
        concat content_tag(:li, "You are a viewer in this repository")
      end
    end
  end

  # Renders a push activity, that is, a repository/tag has been pushed.
  def render_push_activity(activity)
    owner = activity_owner(activity)
    render_repo_activity(activity, activity_action(owner, "pushed"))
  end

  # Renders a delete activity, that is, a repository/tag has been deleted.
  def render_delete_activity(activity)
    owner = activity_owner(activity)
    render_repo_activity(activity, activity_action(owner, "deleted"))
  end

  # Returns if any security module is enabled
  def security_vulns_enabled?
    ::Portus::Security.enabled?
  end

  # Returns true if any vulnerability is found
  # Or false otherwise
  def vulnerable?(vulnerabilities)
    return if vulnerabilities.blank?

    !vulnerabilities.reject { |_, vulns| vulns.empty? }.empty?
  end

  protected

  def owner?(namespace, user)
    role(namespace, user) == :owner
  end

  def contributor?(namespace, user)
    role(namespace, user) == :contributor
  end

  def viewer?(namespace, user)
    role(namespace, user) == :viewer
  end

  # General method for rendering an activity regarding repositories.
  def render_repo_activity(activity, action)
    owner = content_tag(:strong, "#{activity_owner(activity)} #{action} ")

    namespace = render_namespace(activity)
    namespace += "/" unless namespace.empty?

    owner + namespace + render_repository(activity)
  end

  # Renders the namespace part of the activity in a safe manner. If the
  # namespace still exists, it will be taken as the target for the created
  # link. Otherwise, it will try to fetch the name of the namespace and put it
  # inside of a <span> element. If this is not possible, then an empty string
  # will be returned.
  def render_namespace(activity)
    tr = activity.trackable

    if tr.nil? || tr.is_a?(Namespace) || tr.is_a?(Registry)
      if activity.parameters[:namespace_name].nil?
        ""
      else
        namespace = Namespace.find_by(id: activity.parameters[:namespace_id])
        tag_or_link(namespace, activity.parameters[:namespace_name])
      end
    else
      link_to tr.namespace.clean_name, tr.namespace
    end
  end

  # Returns a link if the namespace is not nil, otherwise just a tag with the
  # given name.
  def tag_or_link(namespace, name)
    namespace.nil? ? content_tag(:span, name) : link_to(name, namespace)
  end

  # Renders the repository part of the activity in a safe manner.
  def render_repository(activity)
    repo, link, tag = get_repo_link_tag(activity)

    if link.nil?
      content_tag(:span, "#{repo}#{tag}")
    else
      link_to "#{repo}#{tag}", link
    end
  end

  # Helper for the render_repository method.
  def get_repo_link_tag(activity)
    tr = activity.trackable

    if tr.nil? || tr.is_a?(Registry)
      if repo_name(activity).nil?
        ["a repository", nil, ""]
      else
        repo = repo_name(activity)
        ns   = Repository.find_by(id: activity.parameters[:repository_id])
        link = ns.nil? ? nil : repository_path(ns.id)
        [repo, link, tag_part(activity)]
      end
    else
      name, l = name_and_link(tr, activity)
      [name, l, tag_part(activity)]
    end
  end

  # Renders the tag for the given activity. It will return an empty string if
  # the tag could not be found, otherwise it will return the tag with a ":"
  # prefixed to it.
  def tag_part(activity)
    if activity.recipient.nil?
      if activity.parameters[:tag_name].nil?
        ""
      else
        ":#{activity.parameters[:tag_name]}"
      end
    else
      ":#{activity.recipient.name}"
    end
  end

  # Fetch the name of the repo from the given activity.
  def repo_name(activity)
    activity.parameters[:repo_name] || activity.parameters[:repository_name]
  end

  # Returns the name and the link to the given tr depending on whether it's a
  # Namespace or not.
  def name_and_link(tr, activity)
    tr.is_a?(Namespace) ? [repo_name(activity), nil] : [tr.name, tr]
  end

  # Returns the prefix tags path. This url doesn't exist in routes because it's not needed.
  # This is only useful for the UI.
  def tags_path
    "#{Rails.application.config.relative_url_root}/tags"
  end
end
# rubocop:enable Metrics/ModuleLength