1# frozen_string_literal: true
2
3# Gitlab::VisibilityLevel module
4#
5# Define allowed public modes that can be used for
6# GitLab projects to determine project public mode
7#
8module Gitlab
9  module VisibilityLevel
10    extend ActiveSupport::Concern
11
12    included do
13      scope :public_only,               -> { where(visibility_level: PUBLIC) }
14      scope :public_and_internal_only,  -> { where(visibility_level: [PUBLIC, INTERNAL] ) }
15      scope :private_only,              -> { where(visibility_level: PRIVATE) }
16      scope :non_public_only,           -> { where.not(visibility_level: PUBLIC) }
17
18      scope :public_to_user, -> (user = nil) do
19        where(visibility_level: VisibilityLevel.levels_for_user(user))
20      end
21    end
22
23    PRIVATE  = 0 unless const_defined?(:PRIVATE)
24    INTERNAL = 10 unless const_defined?(:INTERNAL)
25    PUBLIC   = 20 unless const_defined?(:PUBLIC)
26
27    class << self
28      delegate :values, to: :options
29
30      def levels_for_user(user = nil)
31        return [PUBLIC] unless user
32
33        if user.can_read_all_resources?
34          [PRIVATE, INTERNAL, PUBLIC]
35        elsif user.external?
36          [PUBLIC]
37        else
38          [INTERNAL, PUBLIC]
39        end
40      end
41
42      def string_values
43        string_options.keys
44      end
45
46      def options
47        {
48          s_('VisibilityLevel|Private')  => PRIVATE,
49          s_('VisibilityLevel|Internal') => INTERNAL,
50          s_('VisibilityLevel|Public')   => PUBLIC
51        }
52      end
53
54      def string_options
55        {
56          'private'  => PRIVATE,
57          'internal' => INTERNAL,
58          'public'   => PUBLIC
59        }
60      end
61
62      def allowed_levels
63        restricted_levels = Gitlab::CurrentSettings.restricted_visibility_levels
64
65        self.values - Array(restricted_levels)
66      end
67
68      def closest_allowed_level(target_level)
69        highest_allowed_level = allowed_levels.select { |level| level <= target_level }.max
70
71        # If all levels are restricted, fall back to PRIVATE
72        highest_allowed_level || PRIVATE
73      end
74
75      def allowed_for?(user, level)
76        user.admin? || allowed_level?(level.to_i)
77      end
78
79      # Level should be a numeric value, e.g. `20`
80      # Return true if the specified level is allowed for the current user.
81      def allowed_level?(level)
82        valid_level?(level) && non_restricted_level?(level)
83      end
84
85      def non_restricted_level?(level)
86        !restricted_level?(level)
87      end
88
89      def restricted_level?(level)
90        restricted_levels = Gitlab::CurrentSettings.restricted_visibility_levels
91
92        if restricted_levels.nil?
93          false
94        else
95          restricted_levels.include?(level)
96        end
97      end
98
99      def public_visibility_restricted?
100        restricted_level?(PUBLIC)
101      end
102
103      def valid_level?(level)
104        options.value?(level)
105      end
106
107      def level_name(level)
108        options.key(level.to_i) || s_('VisibilityLevel|Unknown')
109      end
110
111      def level_value(level)
112        return level.to_i if level.to_i.to_s == level.to_s && string_options.key(level.to_i)
113
114        string_options[level] || PRIVATE
115      end
116
117      def string_level(level)
118        string_options.key(level)
119      end
120    end
121
122    def visibility_level_previous_changes
123      previous_changes[:visibility_level]
124    end
125
126    def private?
127      visibility_level_value == PRIVATE
128    end
129
130    def internal?
131      visibility_level_value == INTERNAL
132    end
133
134    def public?
135      visibility_level_value == PUBLIC
136    end
137
138    def visibility_level_value
139      self[visibility_level_field]
140    end
141
142    def visibility
143      Gitlab::VisibilityLevel.string_level(visibility_level_value)
144    end
145
146    def visibility=(level)
147      self[visibility_level_field] = Gitlab::VisibilityLevel.level_value(level)
148    end
149
150    def visibility_attribute_present?(attributes)
151      visibility_level_attributes.each do |attr|
152        return true if attributes[attr].present?
153      end
154
155      false
156    end
157
158    def visibility_attribute_value(attributes)
159      visibility_level_attributes.each do |attr|
160        return attributes[attr] if attributes.has_key?(attr)
161      end
162
163      nil
164    end
165
166    def visibility_level_attributes
167      [visibility_level_field, visibility_level_field.to_s,
168       :visibility, 'visibility']
169    end
170  end
171end
172