Read about our upcoming Code of Conduct on this issue

settings_panel.vue 15.7 KB
Newer Older
1
<script>
2
3
import { GlSprintf, GlLink } from '@gitlab/ui';

Filipa Lacerda's avatar
Filipa Lacerda committed
4
import settingsMixin from 'ee_else_ce/pages/projects/shared/permissions/mixins/settings_pannel_mixin';
5
import { s__ } from '~/locale';
6
import projectFeatureSetting from './project_feature_setting.vue';
Filipa Lacerda's avatar
Filipa Lacerda committed
7
import projectFeatureToggle from '~/vue_shared/components/toggle_button.vue';
8
import projectSettingRow from './project_setting_row.vue';
9
10
11
12
13
14
import {
  visibilityOptions,
  visibilityLevelDescriptions,
  featureAccessLevelMembers,
  featureAccessLevelEveryone,
} from '../constants';
15
16
import { toggleHiddenClassBySelector } from '../external';

17
const PAGE_FEATURE_ACCESS_LEVEL = s__('ProjectSettings|Everyone');
18

19
20
21
22
23
export default {
  components: {
    projectFeatureSetting,
    projectFeatureToggle,
    projectSettingRow,
24
25
    GlSprintf,
    GlLink,
26
  },
Filipa Lacerda's avatar
Filipa Lacerda committed
27
  mixins: [settingsMixin],
28
29
30
31
32
33

  props: {
    currentSettings: {
      type: Object,
      required: true,
    },
34
35
36
37
38
    canDisableEmails: {
      type: Boolean,
      required: false,
      default: false,
    },
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
    canChangeVisibilityLevel: {
      type: Boolean,
      required: false,
      default: false,
    },
    allowedVisibilityOptions: {
      type: Array,
      required: false,
      default: () => [0, 10, 20],
    },
    lfsAvailable: {
      type: Boolean,
      required: false,
      default: false,
    },
    registryAvailable: {
      type: Boolean,
      required: false,
      default: false,
    },
Filipa Lacerda's avatar
Filipa Lacerda committed
59
60
61
62
63
    packagesAvailable: {
      type: Boolean,
      required: false,
      default: false,
    },
64
65
66
67
68
69
70
71
72
73
    visibilityHelpPath: {
      type: String,
      required: false,
      default: '',
    },
    lfsHelpPath: {
      type: String,
      required: false,
      default: '',
    },
74
75
76
77
78
79
80
81
82
83
    lfsObjectsExist: {
      type: Boolean,
      required: false,
      default: false,
    },
    lfsObjectsRemovalHelpPath: {
      type: String,
      required: false,
      default: '',
    },
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
    registryHelpPath: {
      type: String,
      required: false,
      default: '',
    },
    pagesAvailable: {
      type: Boolean,
      required: false,
      default: false,
    },
    pagesAccessControlEnabled: {
      type: Boolean,
      required: false,
      default: false,
    },
99
100
101
102
103
    pagesAccessControlForced: {
      type: Boolean,
      required: false,
      default: false,
    },
104
105
106
107
108
    pagesHelpPath: {
      type: String,
      required: false,
      default: '',
    },
Filipa Lacerda's avatar
Filipa Lacerda committed
109
110
111
112
113
    packagesHelpPath: {
      type: String,
      required: false,
      default: '',
    },
114
115
116
117
118
119
120
  },
  data() {
    const defaults = {
      visibilityOptions,
      visibilityLevel: visibilityOptions.PUBLIC,
      issuesAccessLevel: 20,
      repositoryAccessLevel: 20,
121
      forkingAccessLevel: 20,
122
123
124
125
126
127
128
129
130
      mergeRequestsAccessLevel: 20,
      buildsAccessLevel: 20,
      wikiAccessLevel: 20,
      snippetsAccessLevel: 20,
      pagesAccessLevel: 20,
      containerRegistryEnabled: true,
      lfsEnabled: true,
      requestAccessEnabled: true,
      highlightChangesClass: false,
131
      emailsDisabled: false,
132
133
134
135
136
137
138
    };

    return { ...defaults, ...this.currentSettings };
  },

  computed: {
    featureAccessLevelOptions() {
139
      const options = [featureAccessLevelMembers];
140
      if (this.visibilityLevel !== visibilityOptions.PRIVATE) {
141
        options.push(featureAccessLevelEveryone);
142
143
144
145
146
147
148
149
150
151
152
      }
      return options;
    },

    repoFeatureAccessLevelOptions() {
      return this.featureAccessLevelOptions.filter(
        ([value]) => value <= this.repositoryAccessLevel,
      );
    },

    pagesFeatureAccessLevelOptions() {
153
154
155
156
157
158
159
160
161
162
163
164
165
166
      const options = [featureAccessLevelMembers];

      if (this.pagesAccessControlForced) {
        if (this.visibilityLevel === visibilityOptions.INTERNAL) {
          options.push(featureAccessLevelEveryone);
        }
      } else {
        if (this.visibilityLevel !== visibilityOptions.PRIVATE) {
          options.push(featureAccessLevelEveryone);
        }

        if (this.visibilityLevel !== visibilityOptions.PUBLIC) {
          options.push([30, PAGE_FEATURE_ACCESS_LEVEL]);
        }
167
      }
168
      return options;
169
170
171
172
173
174
175
176
177
    },

    repositoryEnabled() {
      return this.repositoryAccessLevel > 0;
    },

    visibilityLevelDescription() {
      return visibilityLevelDescriptions[this.visibilityLevel];
    },
178
179
180
181

    showContainerRegistryPublicNote() {
      return this.visibilityLevel === visibilityOptions.PUBLIC;
    },
182
183
184
185
186
187
188
189
190
191

    repositoryHelpText() {
      if (this.visibilityLevel === visibilityOptions.PRIVATE) {
        return s__('ProjectSettings|View and edit files in this project');
      }

      return s__(
        'ProjectSettings|View and edit files in this project. Non-project members will only have read access',
      );
    },
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
  },

  watch: {
    visibilityLevel(value, oldValue) {
      if (value === visibilityOptions.PRIVATE) {
        // when private, features are restricted to "only team members"
        this.issuesAccessLevel = Math.min(10, this.issuesAccessLevel);
        this.repositoryAccessLevel = Math.min(10, this.repositoryAccessLevel);
        this.mergeRequestsAccessLevel = Math.min(10, this.mergeRequestsAccessLevel);
        this.buildsAccessLevel = Math.min(10, this.buildsAccessLevel);
        this.wikiAccessLevel = Math.min(10, this.wikiAccessLevel);
        this.snippetsAccessLevel = Math.min(10, this.snippetsAccessLevel);
        if (this.pagesAccessLevel === 20) {
          // When from Internal->Private narrow access for only members
          this.pagesAccessLevel = 10;
207
        }
208
209
210
211
212
213
214
215
216
217
218
219
220
        this.highlightChanges();
      } else if (oldValue === visibilityOptions.PRIVATE) {
        // if changing away from private, make enabled features more permissive
        if (this.issuesAccessLevel > 0) this.issuesAccessLevel = 20;
        if (this.repositoryAccessLevel > 0) this.repositoryAccessLevel = 20;
        if (this.mergeRequestsAccessLevel > 0) this.mergeRequestsAccessLevel = 20;
        if (this.buildsAccessLevel > 0) this.buildsAccessLevel = 20;
        if (this.wikiAccessLevel > 0) this.wikiAccessLevel = 20;
        if (this.snippetsAccessLevel > 0) this.snippetsAccessLevel = 20;
        if (this.pagesAccessLevel === 10) this.pagesAccessLevel = 20;
        this.highlightChanges();
      }
    },
221

222
223
224
    issuesAccessLevel(value, oldValue) {
      if (value === 0) toggleHiddenClassBySelector('.issues-feature', true);
      else if (oldValue === 0) toggleHiddenClassBySelector('.issues-feature', false);
225
226
    },

227
228
229
230
231
232
233
234
235
236
237
238
239
    mergeRequestsAccessLevel(value, oldValue) {
      if (value === 0) toggleHiddenClassBySelector('.merge-requests-feature', true);
      else if (oldValue === 0) toggleHiddenClassBySelector('.merge-requests-feature', false);
    },
  },

  methods: {
    highlightChanges() {
      this.highlightChangesClass = true;
      this.$nextTick(() => {
        this.highlightChangesClass = false;
      });
    },
Filipa Lacerda's avatar
Filipa Lacerda committed
240

241
242
    visibilityAllowed(option) {
      return this.allowedVisibilityOptions.includes(option);
Filipa Lacerda's avatar
Filipa Lacerda committed
243
    },
244
245
  },
};
246
247
248
249
250
</script>

<template>
  <div>
    <div class="project-visibility-setting">
251
      <project-setting-row
252
        ref="project-visibility-settings"
253
254
255
        :help-path="visibilityHelpPath"
        :label="s__('ProjectSettings|Project visibility')"
      >
256
257
258
259
260
        <div class="project-feature-controls">
          <div class="select-wrapper">
            <select
              v-model="visibilityLevel"
              :disabled="!canChangeVisibilityLevel"
261
262
              name="project[visibility_level]"
              class="form-control select-control"
263
              data-qa-selector="project_visibility_dropdown"
264
265
266
267
            >
              <option
                :value="visibilityOptions.PRIVATE"
                :disabled="!visibilityAllowed(visibilityOptions.PRIVATE)"
268
                >{{ s__('ProjectSettings|Private') }}</option
269
270
271
272
              >
              <option
                :value="visibilityOptions.INTERNAL"
                :disabled="!visibilityAllowed(visibilityOptions.INTERNAL)"
273
                >{{ s__('ProjectSettings|Internal') }}</option
274
275
276
277
              >
              <option
                :value="visibilityOptions.PUBLIC"
                :disabled="!visibilityAllowed(visibilityOptions.PUBLIC)"
278
                >{{ s__('ProjectSettings|Public') }}</option
279
280
              >
            </select>
Filipa Lacerda's avatar
Filipa Lacerda committed
281
            <i aria-hidden="true" data-hidden="true" class="fa fa-chevron-down"></i>
282
283
          </div>
        </div>
284
        <span class="form-text text-muted">{{ visibilityLevelDescription }}</span>
Mike Greiling's avatar
Mike Greiling committed
285
        <label v-if="visibilityLevel !== visibilityOptions.PRIVATE" class="request-access">
286
          <input
287
            :value="requestAccessEnabled"
288
289
290
            type="hidden"
            name="project[request_access_enabled]"
          />
291
292
          <input v-model="requestAccessEnabled" type="checkbox" />
          {{ s__('ProjectSettings|Allow users to request access') }}
293
294
295
        </label>
      </project-setting-row>
    </div>
Mike Greiling's avatar
Mike Greiling committed
296
    <div :class="{ 'highlight-changes': highlightChangesClass }" class="project-feature-settings">
297
      <project-setting-row
298
        ref="issues-settings"
299
300
        :label="s__('ProjectSettings|Issues')"
        :help-text="s__('ProjectSettings|Lightweight issue tracking system for this project')"
301
302
303
      >
        <project-feature-setting
          v-model="issuesAccessLevel"
304
          :options="featureAccessLevelOptions"
305
          name="project[project_feature_attributes][issues_access_level]"
306
307
        />
      </project-setting-row>
308
      <project-setting-row
309
        ref="repository-settings"
310
        :label="s__('ProjectSettings|Repository')"
311
        :help-text="repositoryHelpText"
312
      >
313
314
        <project-feature-setting
          v-model="repositoryAccessLevel"
315
          :options="featureAccessLevelOptions"
316
          name="project[project_feature_attributes][repository_access_level]"
317
318
319
320
        />
      </project-setting-row>
      <div class="project-feature-setting-group">
        <project-setting-row
321
          ref="merge-request-settings"
322
323
          :label="s__('ProjectSettings|Merge requests')"
          :help-text="s__('ProjectSettings|Submit changes to be merged upstream')"
324
325
326
        >
          <project-feature-setting
            v-model="mergeRequestsAccessLevel"
327
            :options="repoFeatureAccessLevelOptions"
Filipa Lacerda's avatar
Filipa Lacerda committed
328
            :disabled-input="!repositoryEnabled"
329
            name="project[project_feature_attributes][merge_requests_access_level]"
330
331
          />
        </project-setting-row>
332
        <project-setting-row
333
          ref="fork-settings"
334
335
336
337
338
339
340
341
342
343
344
345
          :label="s__('ProjectSettings|Forks')"
          :help-text="
            s__('ProjectSettings|Allow users to make copies of your repository to a new project')
          "
        >
          <project-feature-setting
            v-model="forkingAccessLevel"
            :options="featureAccessLevelOptions"
            :disabled-input="!repositoryEnabled"
            name="project[project_feature_attributes][forking_access_level]"
          />
        </project-setting-row>
346
        <project-setting-row
347
          ref="pipeline-settings"
348
349
350
          :label="s__('ProjectSettings|Pipelines')"
          :help-text="s__('ProjectSettings|Build, test, and deploy your changes')"
        >
351
352
          <project-feature-setting
            v-model="buildsAccessLevel"
353
            :options="repoFeatureAccessLevelOptions"
Filipa Lacerda's avatar
Filipa Lacerda committed
354
            :disabled-input="!repositoryEnabled"
355
            name="project[project_feature_attributes][builds_access_level]"
356
357
358
359
          />
        </project-setting-row>
        <project-setting-row
          v-if="registryAvailable"
360
          ref="container-registry-settings"
361
          :help-path="registryHelpPath"
362
363
364
365
          :label="s__('ProjectSettings|Container registry')"
          :help-text="
            s__('ProjectSettings|Every project can have its own space to store its Docker images')
          "
366
        >
367
          <div v-if="showContainerRegistryPublicNote" class="text-muted">
368
369
370
371
372
            {{
              s__(
                'ProjectSettings|Note: the container registry is always visible when a project is public',
              )
            }}
373
          </div>
374
375
          <project-feature-toggle
            v-model="containerRegistryEnabled"
Filipa Lacerda's avatar
Filipa Lacerda committed
376
            :disabled-input="!repositoryEnabled"
377
            name="project[container_registry_enabled]"
378
379
380
381
          />
        </project-setting-row>
        <project-setting-row
          v-if="lfsAvailable"
382
          ref="git-lfs-settings"
383
          :help-path="lfsHelpPath"
384
385
386
387
          :label="s__('ProjectSettings|Git Large File Storage')"
          :help-text="
            s__('ProjectSettings|Manages large files such as audio, video, and graphics files')
          "
388
389
390
        >
          <project-feature-toggle
            v-model="lfsEnabled"
Filipa Lacerda's avatar
Filipa Lacerda committed
391
            :disabled-input="!repositoryEnabled"
392
            name="project[lfs_enabled]"
393
          />
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
          <p v-if="!lfsEnabled && lfsObjectsExist">
            <gl-sprintf
              :message="
                s__(
                  'ProjectSettings|LFS objects from this repository are still available to forks. %{linkStart}How do I remove them?%{linkEnd}',
                )
              "
            >
              <template #link="{ content }">
                <span class="d-block">
                  <gl-link :href="lfsObjectsRemovalHelpPath" target="_blank">
                    {{ content }}
                  </gl-link>
                </span>
              </template>
            </gl-sprintf>
          </p>
411
        </project-setting-row>
Filipa Lacerda's avatar
Filipa Lacerda committed
412
413
        <project-setting-row
          v-if="packagesAvailable"
414
          ref="package-settings"
Filipa Lacerda's avatar
Filipa Lacerda committed
415
          :help-path="packagesHelpPath"
416
417
418
419
          :label="s__('ProjectSettings|Packages')"
          :help-text="
            s__('ProjectSettings|Every project can have its own space to store its packages')
          "
Filipa Lacerda's avatar
Filipa Lacerda committed
420
421
422
423
424
425
426
        >
          <project-feature-toggle
            v-model="packagesEnabled"
            :disabled-input="!repositoryEnabled"
            name="project[packages_enabled]"
          />
        </project-setting-row>
427
      </div>
428
      <project-setting-row
429
        ref="wiki-settings"
430
431
432
        :label="s__('ProjectSettings|Wiki')"
        :help-text="s__('ProjectSettings|Pages for project documentation')"
      >
433
434
        <project-feature-setting
          v-model="wikiAccessLevel"
435
          :options="featureAccessLevelOptions"
436
          name="project[project_feature_attributes][wiki_access_level]"
437
438
439
        />
      </project-setting-row>
      <project-setting-row
440
        ref="snippet-settings"
441
442
        :label="s__('ProjectSettings|Snippets')"
        :help-text="s__('ProjectSettings|Share code pastes with others out of Git repository')"
443
444
445
      >
        <project-feature-setting
          v-model="snippetsAccessLevel"
446
          :options="featureAccessLevelOptions"
447
          name="project[project_feature_attributes][snippets_access_level]"
448
449
        />
      </project-setting-row>
450
451
      <project-setting-row
        v-if="pagesAvailable && pagesAccessControlEnabled"
452
        ref="pages-settings"
453
        :help-path="pagesHelpPath"
454
        :label="s__('ProjectSettings|Pages')"
455
456
457
        :help-text="
          s__('ProjectSettings|With GitLab Pages you can host your static websites on GitLab')
        "
458
459
460
461
462
463
464
      >
        <project-feature-setting
          v-model="pagesAccessLevel"
          :options="pagesFeatureAccessLevelOptions"
          name="project[project_feature_attributes][pages_access_level]"
        />
      </project-setting-row>
465
    </div>
466
    <project-setting-row v-if="canDisableEmails" ref="email-settings" class="mb-3">
467
468
      <label class="js-emails-disabled">
        <input :value="emailsDisabled" type="hidden" name="project[emails_disabled]" />
469
470
        <input v-model="emailsDisabled" type="checkbox" />
        {{ s__('ProjectSettings|Disable email notifications') }}
471
472
      </label>
      <span class="form-text text-muted">{{
473
474
475
        s__(
          'ProjectSettings|This setting will override user notification preferences for all project members.',
        )
476
477
      }}</span>
    </project-setting-row>
478
479
  </div>
</template>