Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Boxiang Sun
gitlab-ce
Commits
03b14b48
Commit
03b14b48
authored
Sep 07, 2017
by
Mike Greiling
Committed by
Phil Hughes
Sep 07, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Resolve "Make project and features visibility settings less confusing"
parent
21b16c5a
Changes
21
Show whitespace changes
Inline
Side-by-side
Showing
21 changed files
with
815 additions
and
166 deletions
+815
-166
app/assets/javascripts/dispatcher.js
app/assets/javascripts/dispatcher.js
+3
-0
app/assets/javascripts/projects/permissions/components/project_feature_setting.vue
...ojects/permissions/components/project_feature_setting.vue
+104
-0
app/assets/javascripts/projects/permissions/components/project_feature_toggle.vue
...rojects/permissions/components/project_feature_toggle.vue
+51
-0
app/assets/javascripts/projects/permissions/components/project_setting_row.vue
...s/projects/permissions/components/project_setting_row.vue
+36
-0
app/assets/javascripts/projects/permissions/components/settings_panel.vue
...cripts/projects/permissions/components/settings_panel.vue
+312
-0
app/assets/javascripts/projects/permissions/constants.js
app/assets/javascripts/projects/permissions/constants.js
+11
-0
app/assets/javascripts/projects/permissions/external.js
app/assets/javascripts/projects/permissions/external.js
+18
-0
app/assets/javascripts/projects/permissions/index.js
app/assets/javascripts/projects/permissions/index.js
+13
-0
app/assets/stylesheets/framework/variables.scss
app/assets/stylesheets/framework/variables.scss
+5
-0
app/assets/stylesheets/pages/projects.scss
app/assets/stylesheets/pages/projects.scss
+156
-35
app/helpers/projects_helper.rb
app/helpers/projects_helper.rb
+37
-0
app/views/projects/_merge_request_merge_settings.html.haml
app/views/projects/_merge_request_merge_settings.html.haml
+1
-1
app/views/projects/edit.html.haml
app/views/projects/edit.html.haml
+4
-76
changelogs/unreleased/32665-refactor-project-visibility-settings.yml
...unreleased/32665-refactor-project-visibility-settings.yml
+5
-0
features/steps/project/project.rb
features/steps/project/project.rb
+0
-1
features/steps/shared/project.rb
features/steps/shared/project.rb
+1
-1
spec/features/projects/edit_spec.rb
spec/features/projects/edit_spec.rb
+23
-8
spec/features/projects/features_visibility_spec.rb
spec/features/projects/features_visibility_spec.rb
+23
-20
spec/features/projects/settings/merge_requests_settings_spec.rb
...eatures/projects/settings/merge_requests_settings_spec.rb
+3
-3
spec/features/projects/settings/visibility_settings_spec.rb
spec/features/projects/settings/visibility_settings_spec.rb
+9
-10
spec/views/projects/edit.html.haml_spec.rb
spec/views/projects/edit.html.haml_spec.rb
+0
-11
No files found.
app/assets/javascripts/dispatcher.js
View file @
03b14b48
...
...
@@ -577,6 +577,9 @@ import initChangesDropdown from './init_changes_dropdown';
case
'
edit
'
:
shortcut_handler
=
new
ShortcutsNavigation
();
new
ProjectNew
();
import
(
/* webpackChunkName: 'project_permissions' */
'
./projects/permissions
'
)
.
then
(
permissions
=>
permissions
.
default
())
.
catch
(()
=>
{});
break
;
case
'
new
'
:
new
ProjectNew
();
...
...
app/assets/javascripts/projects/permissions/components/project_feature_setting.vue
0 → 100644
View file @
03b14b48
<
script
>
import
projectFeatureToggle
from
'
./project_feature_toggle.vue
'
;
export
default
{
props
:
{
name
:
{
type
:
String
,
required
:
false
,
default
:
''
,
},
options
:
{
type
:
Array
,
required
:
false
,
default
:
()
=>
[],
},
value
:
{
type
:
Number
,
required
:
false
,
default
:
0
,
},
disabledInput
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
},
components
:
{
projectFeatureToggle
,
},
computed
:
{
featureEnabled
()
{
return
this
.
value
!==
0
;
},
displayOptions
()
{
if
(
this
.
featureEnabled
)
{
return
this
.
options
;
}
return
[
[
0
,
'
Enable feature to choose access level
'
],
];
},
displaySelectInput
()
{
return
this
.
disabledInput
||
!
this
.
featureEnabled
||
this
.
displayOptions
.
length
<
2
;
},
},
model
:
{
prop
:
'
value
'
,
event
:
'
change
'
,
},
methods
:
{
toggleFeature
(
featureEnabled
)
{
if
(
featureEnabled
===
false
||
this
.
options
.
length
<
1
)
{
this
.
$emit
(
'
change
'
,
0
);
}
else
{
const
[
firstOptionValue
]
=
this
.
options
[
this
.
options
.
length
-
1
];
this
.
$emit
(
'
change
'
,
firstOptionValue
);
}
},
selectOption
(
e
)
{
this
.
$emit
(
'
change
'
,
Number
(
e
.
target
.
value
));
},
},
};
</
script
>
<
template
>
<div
class=
"project-feature-controls"
:data-for=
"name"
>
<input
v-if=
"name"
type=
"hidden"
:name=
"name"
:value=
"value"
/>
<project-feature-toggle
:value=
"featureEnabled"
@
change=
"toggleFeature"
:disabledInput=
"disabledInput"
/>
<div
class=
"select-wrapper"
>
<select
class=
"form-control project-repo-select select-control"
@
change=
"selectOption"
:disabled=
"displaySelectInput"
>
<option
v-for=
"[optionValue, optionName] in displayOptions"
:key=
"optionValue"
:value=
"optionValue"
:selected=
"optionValue === value"
>
{{
optionName
}}
</option>
</select>
<i
aria-hidden=
"true"
class=
"fa fa-chevron-down"
></i>
</div>
</div>
</
template
>
app/assets/javascripts/projects/permissions/components/project_feature_toggle.vue
0 → 100644
View file @
03b14b48
<
script
>
export
default
{
props
:
{
name
:
{
type
:
String
,
required
:
false
,
default
:
''
,
},
value
:
{
type
:
Boolean
,
required
:
true
,
},
disabledInput
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
},
model
:
{
prop
:
'
value
'
,
event
:
'
change
'
,
},
methods
:
{
toggleFeature
()
{
if
(
!
this
.
disabledInput
)
this
.
$emit
(
'
change
'
,
!
this
.
value
);
},
},
};
</
script
>
<
template
>
<label
class=
"toggle-wrapper"
>
<input
v-if=
"name"
type=
"hidden"
:name=
"name"
:value=
"value"
/>
<button
type=
"button"
aria-label=
"Toggle"
class=
"project-feature-toggle"
data-enabled-text=
"Enabled"
data-disabled-text=
"Disabled"
:class=
"
{ checked: value, disabled: disabledInput }"
@click="toggleFeature"
/>
</label>
</
template
>
app/assets/javascripts/projects/permissions/components/project_setting_row.vue
0 → 100644
View file @
03b14b48
<
script
>
export
default
{
props
:
{
label
:
{
type
:
String
,
required
:
false
,
default
:
null
,
},
helpPath
:
{
type
:
String
,
required
:
false
,
default
:
null
,
},
helpText
:
{
type
:
String
,
required
:
false
,
default
:
null
,
},
},
};
</
script
>
<
template
>
<div
class=
"project-feature-row"
>
<label
v-if=
"label"
class=
"label-light"
>
{{
label
}}
<a
v-if=
"helpPath"
:href=
"helpPath"
target=
"_blank"
>
<i
aria-hidden=
"true"
data-hidden=
"true"
class=
"fa fa-question-circle"
></i>
</a>
</label>
<span
v-if=
"helpText"
class=
"help-block"
>
{{
helpText
}}
</span>
<slot
/>
</div>
</
template
>
app/assets/javascripts/projects/permissions/components/settings_panel.vue
0 → 100644
View file @
03b14b48
<
script
>
import
projectFeatureSetting
from
'
./project_feature_setting.vue
'
;
import
projectFeatureToggle
from
'
./project_feature_toggle.vue
'
;
import
projectSettingRow
from
'
./project_setting_row.vue
'
;
import
{
visibilityOptions
,
visibilityLevelDescriptions
}
from
'
../constants
'
;
import
{
toggleHiddenClassBySelector
}
from
'
../external
'
;
export
default
{
props
:
{
currentSettings
:
{
type
:
Object
,
required
:
true
,
},
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
,
},
visibilityHelpPath
:
{
type
:
String
,
required
:
false
,
},
lfsHelpPath
:
{
type
:
String
,
required
:
false
,
},
registryHelpPath
:
{
type
:
String
,
required
:
false
,
},
},
data
()
{
const
defaults
=
{
visibilityOptions
,
visibilityLevel
:
visibilityOptions
.
PUBLIC
,
issuesAccessLevel
:
20
,
repositoryAccessLevel
:
20
,
mergeRequestsAccessLevel
:
20
,
buildsAccessLevel
:
20
,
wikiAccessLevel
:
20
,
snippetsAccessLevel
:
20
,
containerRegistryEnabled
:
true
,
lfsEnabled
:
true
,
requestAccessEnabled
:
true
,
highlightChangesClass
:
false
,
};
return
{
...
defaults
,
...
this
.
currentSettings
};
},
components
:
{
projectFeatureSetting
,
projectFeatureToggle
,
projectSettingRow
,
},
computed
:
{
featureAccessLevelOptions
()
{
const
options
=
[
[
10
,
'
Only Project Members
'
],
];
if
(
this
.
visibilityLevel
!==
visibilityOptions
.
PRIVATE
)
{
options
.
push
([
20
,
'
Everyone With Access
'
]);
}
return
options
;
},
repoFeatureAccessLevelOptions
()
{
return
this
.
featureAccessLevelOptions
.
filter
(
([
value
])
=>
value
<=
this
.
repositoryAccessLevel
,
);
},
repositoryEnabled
()
{
return
this
.
repositoryAccessLevel
>
0
;
},
visibilityLevelDescription
()
{
return
visibilityLevelDescriptions
[
this
.
visibilityLevel
];
},
},
methods
:
{
highlightChanges
()
{
this
.
highlightChangesClass
=
true
;
this
.
$nextTick
(()
=>
{
this
.
highlightChangesClass
=
false
;
});
},
visibilityAllowed
(
option
)
{
return
this
.
allowedVisibilityOptions
.
includes
(
option
);
},
},
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
);
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
;
this
.
highlightChanges
();
}
},
repositoryAccessLevel
(
value
,
oldValue
)
{
if
(
value
<
oldValue
)
{
// sub-features cannot have more premissive access level
this
.
mergeRequestsAccessLevel
=
Math
.
min
(
this
.
mergeRequestsAccessLevel
,
value
);
this
.
buildsAccessLevel
=
Math
.
min
(
this
.
buildsAccessLevel
,
value
);
if
(
value
===
0
)
{
this
.
containerRegistryEnabled
=
false
;
this
.
lfsEnabled
=
false
;
}
}
else
if
(
oldValue
===
0
)
{
this
.
mergeRequestsAccessLevel
=
value
;
this
.
buildsAccessLevel
=
value
;
this
.
containerRegistryEnabled
=
true
;
this
.
lfsEnabled
=
true
;
}
},
issuesAccessLevel
(
value
,
oldValue
)
{
if
(
value
===
0
)
toggleHiddenClassBySelector
(
'
.issues-feature
'
,
true
);
else
if
(
oldValue
===
0
)
toggleHiddenClassBySelector
(
'
.issues-feature
'
,
false
);
},
mergeRequestsAccessLevel
(
value
,
oldValue
)
{
if
(
value
===
0
)
toggleHiddenClassBySelector
(
'
.merge-requests-feature
'
,
true
);
else
if
(
oldValue
===
0
)
toggleHiddenClassBySelector
(
'
.merge-requests-feature
'
,
false
);
},
buildsAccessLevel
(
value
,
oldValue
)
{
if
(
value
===
0
)
toggleHiddenClassBySelector
(
'
.builds-feature
'
,
true
);
else
if
(
oldValue
===
0
)
toggleHiddenClassBySelector
(
'
.builds-feature
'
,
false
);
},
},
};
</
script
>
<
template
>
<div>
<div
class=
"project-visibility-setting"
>
<project-setting-row
label=
"Project visibility"
:help-path=
"visibilityHelpPath"
>
<div
class=
"project-feature-controls"
>
<div
class=
"select-wrapper"
>
<select
name=
"project[visibility_level]"
v-model=
"visibilityLevel"
class=
"form-control select-control"
:disabled=
"!canChangeVisibilityLevel"
>
<option
:value=
"visibilityOptions.PRIVATE"
:disabled=
"!visibilityAllowed(visibilityOptions.PRIVATE)"
>
Private
</option>
<option
:value=
"visibilityOptions.INTERNAL"
:disabled=
"!visibilityAllowed(visibilityOptions.INTERNAL)"
>
Internal
</option>
<option
:value=
"visibilityOptions.PUBLIC"
:disabled=
"!visibilityAllowed(visibilityOptions.PUBLIC)"
>
Public
</option>
</select>
<i
aria-hidden=
"true"
data-hidden=
"true"
class=
"fa fa-chevron-down"
></i>
</div>
</div>
<span
class=
"help-block"
>
{{
visibilityLevelDescription
}}
</span>
<label
v-if=
"visibilityLevel !== visibilityOptions.PUBLIC"
class=
"request-access"
>
<input
type=
"hidden"
name=
"project[request_access_enabled]"
:value=
"requestAccessEnabled"
/>
<input
type=
"checkbox"
v-model=
"requestAccessEnabled"
/>
Allow users to request access
</label>
</project-setting-row>
</div>
<div
class=
"project-feature-settings"
:class=
"
{ 'highlight-changes': highlightChangesClass }">
<project-setting-row
label=
"Issues"
help-text=
"Lightweight issue tracking system for this project"
>
<project-feature-setting
name=
"project[project_feature_attributes][issues_access_level]"
:options=
"featureAccessLevelOptions"
v-model=
"issuesAccessLevel"
/>
</project-setting-row>
<project-setting-row
label=
"Repository"
help-text=
"View and edit files in this project"
>
<project-feature-setting
name=
"project[project_feature_attributes][repository_access_level]"
:options=
"featureAccessLevelOptions"
v-model=
"repositoryAccessLevel"
/>
</project-setting-row>
<div
class=
"project-feature-setting-group"
>
<project-setting-row
label=
"Merge requests"
help-text=
"Submit changes to be merged upstream"
>
<project-feature-setting
name=
"project[project_feature_attributes][merge_requests_access_level]"
:options=
"repoFeatureAccessLevelOptions"
v-model=
"mergeRequestsAccessLevel"
:disabledInput=
"!repositoryEnabled"
/>
</project-setting-row>
<project-setting-row
label=
"Pipelines"
help-text=
"Build, test, and deploy your changes"
>
<project-feature-setting
name=
"project[project_feature_attributes][builds_access_level]"
:options=
"repoFeatureAccessLevelOptions"
v-model=
"buildsAccessLevel"
:disabledInput=
"!repositoryEnabled"
/>
</project-setting-row>
<project-setting-row
v-if=
"registryAvailable"
label=
"Container registry"
:help-path=
"registryHelpPath"
help-text=
"Every project can have its own space to store its Docker images"
>
<project-feature-toggle
name=
"project[container_registry_enabled]"
v-model=
"containerRegistryEnabled"
:disabledInput=
"!repositoryEnabled"
/>
</project-setting-row>
<project-setting-row
v-if=
"lfsAvailable"
label=
"Git Large File Storage"
:help-path=
"lfsHelpPath"
help-text=
"Manages large files such as audio, video, and graphics files"
>
<project-feature-toggle
name=
"project[lfs_enabled]"
v-model=
"lfsEnabled"
:disabledInput=
"!repositoryEnabled"
/>
</project-setting-row>
</div>
<project-setting-row
label=
"Wiki"
help-text=
"Pages for project documentation"
>
<project-feature-setting
name=
"project[project_feature_attributes][wiki_access_level]"
:options=
"featureAccessLevelOptions"
v-model=
"wikiAccessLevel"
/>
</project-setting-row>
<project-setting-row
label=
"Snippets"
help-text=
"Share code pastes with others out of Git repository"
>
<project-feature-setting
name=
"project[project_feature_attributes][snippets_access_level]"
:options=
"featureAccessLevelOptions"
v-model=
"snippetsAccessLevel"
/>
</project-setting-row>
</div>
</div>
</
template
>
app/assets/javascripts/projects/permissions/constants.js
0 → 100644
View file @
03b14b48
export
const
visibilityOptions
=
{
PRIVATE
:
0
,
INTERNAL
:
10
,
PUBLIC
:
20
,
};
export
const
visibilityLevelDescriptions
=
{
[
visibilityOptions
.
PRIVATE
]:
'
The project is accessible only by members of the project. Access must be granted explicitly to each user.
'
,
[
visibilityOptions
.
INTERNAL
]:
'
The project can be accessed by any user who is logged in.
'
,
[
visibilityOptions
.
PUBLIC
]:
'
The project can be accessed by anyone, regardless of authentication.
'
,
};
app/assets/javascripts/projects/permissions/external.js
0 → 100644
View file @
03b14b48
const
selectorCache
=
[];
// workaround since we don't have a polyfill for classList.toggle 2nd parameter
export
function
toggleHiddenClass
(
element
,
hidden
)
{
if
(
hidden
)
{
element
.
classList
.
add
(
'
hidden
'
);
}
else
{
element
.
classList
.
remove
(
'
hidden
'
);
}
}
// hide external feature-specific settings when a given feature is disabled
export
function
toggleHiddenClassBySelector
(
selector
,
hidden
)
{
if
(
!
selectorCache
[
selector
])
{
selectorCache
[
selector
]
=
document
.
querySelectorAll
(
selector
);
}
selectorCache
[
selector
].
forEach
(
elm
=>
toggleHiddenClass
(
elm
,
hidden
));
}
app/assets/javascripts/projects/permissions/index.js
0 → 100644
View file @
03b14b48
import
Vue
from
'
vue
'
;
import
settingsPanel
from
'
./components/settings_panel.vue
'
;
export
default
function
initProjectPermissionsSettings
()
{
const
mountPoint
=
document
.
querySelector
(
'
.js-project-permissions-form
'
);
const
componentPropsEl
=
document
.
querySelector
(
'
.js-project-permissions-form-data
'
);
const
componentProps
=
JSON
.
parse
(
componentPropsEl
.
innerHTML
);
return
new
Vue
({
el
:
mountPoint
,
render
:
createElement
=>
createElement
(
settingsPanel
,
{
props
:
{
...
componentProps
}
}),
});
}
app/assets/stylesheets/framework/variables.scss
View file @
03b14b48
...
...
@@ -579,6 +579,11 @@ $project-breadcrumb-color: #999;
$project-private-forks-notice-odd
:
$green-600
;
$project-network-controls-color
:
#888
;
$feature-toggle-color
:
#fff
;
$feature-toggle-text-color
:
#fff
;
$feature-toggle-color-disabled
:
#999
;
$feature-toggle-color-enabled
:
#4a8bee
;
/*
* Runners
*/
...
...
app/assets/stylesheets/pages/projects.scss
View file @
03b14b48
...
...
@@ -10,41 +10,6 @@
.edit-project
,
.import-project
{
.sharing-and-permissions
{
.header
{
padding-top
:
$gl-vert-padding
;
}
.label-light
{
margin-bottom
:
0
;
}
.help-block
{
margin-top
:
0
;
}
.form-group
{
margin-bottom
:
5px
;
}
>
.form-group
{
padding-left
:
0
;
}
select
option
[
disabled
]
{
display
:
none
;
}
}
select
{
transition
:
background
2s
ease-out
;
&
.highlight-changes
{
background
:
$highlight-changes-color
;
transition
:
none
;
}
}
.help-block
{
margin-bottom
:
10px
;
}
...
...
@@ -90,6 +55,162 @@
}
}
.toggle-wrapper
{
margin-top
:
5px
;
}
.project-feature-row
>
.toggle-wrapper
{
margin
:
10px
0
;
}
.project-visibility-setting
,
.project-feature-settings
{
border
:
1px
solid
$border-color
;
padding
:
10px
32px
;
@media
(
max-width
:
$screen-xs-min
)
{
padding
:
10px
20px
;
}
}
.project-visibility-setting
.request-access
{
line-height
:
2
;
}
.project-feature-settings
{
background
:
$gray-lighter
;
border-top
:
none
;
margin-bottom
:
16px
;
}
.project-repo-select
{
transition
:
background
2s
ease-out
;
&
:disabled
{
opacity
:
0
.75
;
}
.highlight-changes
&
{
background
:
$highlight-changes-color
;
transition
:
none
;
}
}
.project-feature-controls
{
display
:
flex
;
align-items
:
center
;
margin
:
8px
0
;
max-width
:
432px
;
.toggle-wrapper
{
flex
:
0
;
margin-right
:
10px
;
}
.select-wrapper
{
flex
:
1
;
}
}
.project-feature-setting-group
{
padding-left
:
32px
;
.project-feature-controls
{
max-width
:
400px
;
}
@media
(
max-width
:
$screen-xs-min
)
{
padding-left
:
20px
;
}
}
.project-feature-toggle
{
position
:
relative
;
border
:
none
;
outline
:
0
;
display
:
block
;
width
:
100px
;
height
:
24px
;
cursor
:
pointer
;
user-select
:
none
;
background
:
$feature-toggle-color-disabled
;
border-radius
:
12px
;
padding
:
3px
;
transition
:
all
.4s
ease
;
&
:
:
selection
,
&::
before
::
selection
,
&::
after
::
selection
{
background
:
none
;
}
&
:
:
before
{
color
:
$feature-toggle-text-color
;
font-size
:
12px
;
line-height
:
24px
;
position
:
absolute
;
top
:
0
;
left
:
25px
;
right
:
5px
;
text-align
:
center
;
overflow
:
hidden
;
text-overflow
:
ellipsis
;
animation
:
animate-disabled
.2s
ease-in
;
content
:
attr
(
data-disabled-text
);
}
&
:
:
after
{
position
:
relative
;
display
:
block
;
content
:
""
;
width
:
22px
;
height
:
18px
;
left
:
0
;
border-radius
:
9px
;
background
:
$feature-toggle-color
;
transition
:
all
.2s
ease
;
}
&
.checked
{
background
:
$feature-toggle-color-enabled
;
&
:
:
before
{
left
:
5px
;
right
:
25px
;
animation
:
animate-enabled
.2s
ease-in
;
content
:
attr
(
data-enabled-text
);
}
&
:
:
after
{
left
:
calc
(
100%
-
22px
);
}
}
&
.disabled
{
opacity
:
0
.4
;
cursor
:
not
-
allowed
;
}
@media
(
max-width
:
$screen-xs-min
)
{
width
:
50px
;
&
:
:
before
,
&.
checked
::
before
{
display
:
none
;
}
}
@keyframes
animate-enabled
{
0
%
,
35
%
{
opacity
:
0
;
}
100
%
{
opacity
:
1
;
}
}
@keyframes
animate-disabled
{
0
%
,
35
%
{
opacity
:
0
;
}
100
%
{
opacity
:
1
;
}
}
}
.project-home-panel
,
.group-home-panel
{
padding-top
:
24px
;
...
...
app/helpers/projects_helper.rb
View file @
03b14b48
...
...
@@ -545,6 +545,43 @@ module ProjectsHelper
current_application_settings
.
restricted_visibility_levels
||
[]
end
def
project_permissions_settings
(
project
)
feature
=
project
.
project_feature
{
visibilityLevel:
project
.
visibility_level
,
requestAccessEnabled:
!!
project
.
request_access_enabled
,
issuesAccessLevel:
feature
.
issues_access_level
,
repositoryAccessLevel:
feature
.
repository_access_level
,
mergeRequestsAccessLevel:
feature
.
merge_requests_access_level
,
buildsAccessLevel:
feature
.
builds_access_level
,
wikiAccessLevel:
feature
.
wiki_access_level
,
snippetsAccessLevel:
feature
.
snippets_access_level
,
containerRegistryEnabled:
!!
project
.
container_registry_enabled
,
lfsEnabled:
!!
project
.
lfs_enabled
}
end
def
project_permissions_panel_data
(
project
)
data
=
{
currentSettings:
project_permissions_settings
(
project
),
canChangeVisibilityLevel:
can_change_visibility_level?
(
project
,
current_user
),
allowedVisibilityOptions:
project_allowed_visibility_levels
(
project
),
visibilityHelpPath:
help_page_path
(
'public_access/public_access'
),
registryAvailable:
Gitlab
.
config
.
registry
.
enabled
,
registryHelpPath:
help_page_path
(
'user/project/container_registry'
),
lfsAvailable:
Gitlab
.
config
.
lfs
.
enabled
&&
current_user
.
admin?
,
lfsHelpPath:
help_page_path
(
'workflow/lfs/manage_large_binaries_with_git_lfs'
)
}
data
.
to_json
.
html_safe
end
def
project_allowed_visibility_levels
(
project
)
Gitlab
::
VisibilityLevel
.
values
.
select
do
|
level
|
project
.
visibility_level_allowed?
(
level
)
&&
!
restricted_levels
.
include?
(
level
)
end
end
def
find_file_path
return
unless
@project
&&
!
@project
.
empty_repo?
...
...
app/views/projects/_merge_request_merge_settings.html.haml
View file @
03b14b48
-
form
=
local_assigns
.
fetch
(
:form
)
.form-group
.checkbox.builds-feature
.checkbox.builds-feature
{
class:
(
"hidden"
if
@project
&&
@project
.
project_feature
.
send
(
:builds_access_level
)
==
0
)
}
=
form
.
label
:only_allow_merge_if_pipeline_succeeds
do
=
form
.
check_box
:only_allow_merge_if_pipeline_succeeds
%strong
Only allow merge requests to be merged if the pipeline succeeds
...
...
app/views/projects/edit.html.haml
View file @
03b14b48
...
...
@@ -66,90 +66,18 @@
%section
.settings.sharing-permissions
.settings-header
%h4
Sharing and p
ermissions
P
ermissions
%button
.btn.js-settings-toggle
=
expanded
?
'Collapse'
:
'Expand'
%p
Enable or disable certain project features and choose access levels.
.settings-content.no-animate
{
class:
(
'expanded'
if
expanded
)
}
=
form_for
[
@project
.
namespace
.
becomes
(
Namespace
),
@project
],
remote:
true
,
html:
{
multipart:
true
,
class:
"sharing-permissions-form"
},
authenticity_token:
true
do
|
f
|
.form_group.sharing-and-permissions
.row.js-visibility-select
.col-md-8
.label-light
=
label_tag
:project_visibility
,
'Project Visibility'
,
class:
'label-light'
,
for: :project_visibility_level
=
link_to
icon
(
'question-circle'
),
help_page_path
(
"public_access/public_access"
)
%span
.help-block
.col-md-4.visibility-select-container
=
render
(
'projects/visibility_select'
,
model_method: :visibility_level
,
form:
f
,
selected_level:
@project
.
visibility_level
)
=
f
.
fields_for
:project_feature
do
|
feature_fields
|
%fieldset
.features
.row
.col-md-8.project-feature
=
feature_fields
.
label
:repository_access_level
,
"Repository"
,
class:
'label-light'
%span
.help-block
View and edit files in this project
.col-md-4.js-repo-access-level
=
project_feature_access_select
(
:repository_access_level
)
.row
.col-md-8.project-feature.nested
=
feature_fields
.
label
:merge_requests_access_level
,
"Merge requests"
,
class:
'label-light'
%span
.help-block
Submit changes to be merged upstream
.col-md-4
=
project_feature_access_select
(
:merge_requests_access_level
)
.row
.col-md-8.project-feature.nested
=
feature_fields
.
label
:builds_access_level
,
"Pipelines"
,
class:
'label-light'
%span
.help-block
Build, test, and deploy your changes
.col-md-4
=
project_feature_access_select
(
:builds_access_level
)
.row
.col-md-8.project-feature
=
feature_fields
.
label
:snippets_access_level
,
"Snippets"
,
class:
'label-light'
%span
.help-block
Share code pastes with others out of Git repository
.col-md-4
=
project_feature_access_select
(
:snippets_access_level
)
.row
.col-md-8.project-feature
=
feature_fields
.
label
:issues_access_level
,
"Issues"
,
class:
'label-light'
%span
.help-block
Lightweight issue tracking system for this project
.col-md-4
=
project_feature_access_select
(
:issues_access_level
)
.row
.col-md-8.project-feature
=
feature_fields
.
label
:wiki_access_level
,
"Wiki"
,
class:
'label-light'
%span
.help-block
Pages for project documentation
.col-md-4
=
project_feature_access_select
(
:wiki_access_level
)
.form-group
=
render
'shared/allow_request_access'
,
form:
f
-
if
Gitlab
.
config
.
lfs
.
enabled
&&
current_user
.
admin?
.row.js-lfs-enabled.form-group.sharing-and-permissions
.col-md-8
=
f
.
label
:lfs_enabled
,
'Git Large File Storage'
,
class:
'label-light'
=
link_to
icon
(
'question-circle'
),
help_page_path
(
'workflow/lfs/manage_large_binaries_with_git_lfs'
)
%span
.help-block
Manages large files such as audio, video and graphics files.
.col-md-4
.select-wrapper
=
f
.
select
:lfs_enabled
,
[
%w(Enabled true)
,
%w(Disabled false)
],
{},
selected:
@project
.
lfs_enabled?
,
class:
'pull-right form-control project-repo-select select-control'
,
data:
{
field:
'lfs_enabled'
}
=
icon
(
'chevron-down'
)
-
if
Gitlab
.
config
.
registry
.
enabled
.form-group.js-container-registry
{
style:
(
"display: none;"
if
@project
.
project_feature
.
send
(
:repository_access_level
)
==
0
)
}
.checkbox
=
f
.
label
:container_registry_enabled
do
=
f
.
check_box
:container_registry_enabled
%strong
Container Registry
%br
%span
.descr
Enable Container Registry for this project
=
link_to
icon
(
'question-circle'
),
help_page_path
(
'user/project/container_registry'
),
target:
'_blank'
%script
.js-project-permissions-form-data
{
type:
"application/json"
}=
project_permissions_panel_data
(
@project
)
.js-project-permissions-form
=
f
.
submit
'Save changes'
,
class:
"btn btn-save"
%section
.settings.merge-requests-feature
{
style:
(
"display: none;"
if
@project
.
project_feature
.
send
(
:merge_requests_access_level
)
==
0
)
}
%section
.settings.merge-requests-feature
{
class:
(
"hidden"
if
@project
.
project_feature
.
send
(
:merge_requests_access_level
)
==
0
)
}
.settings-header
%h4
Merge request settings
...
...
changelogs/unreleased/32665-refactor-project-visibility-settings.yml
0 → 100644
View file @
03b14b48
---
title
:
Redesign project feature permissions settings
merge_request
:
14062
author
:
type
:
changed
features/steps/project/project.rb
View file @
03b14b48
...
...
@@ -6,7 +6,6 @@ class Spinach::Features::Project < Spinach::FeatureSteps
step
'change project settings'
do
fill_in
'project_name_edit'
,
with:
'NewName'
select
'Disabled'
,
from:
'project_project_feature_attributes_issues_access_level'
end
step
'I save project'
do
...
...
features/steps/shared/project.rb
View file @
03b14b48
...
...
@@ -89,7 +89,7 @@ module SharedProject
step
'I should see project settings'
do
expect
(
current_path
).
to
eq
edit_project_path
(
@project
)
expect
(
page
).
to
have_content
(
"Project name"
)
expect
(
page
).
to
have_content
(
"
Sharing and p
ermissions"
)
expect
(
page
).
to
have_content
(
"
P
ermissions"
)
end
def
current_project
...
...
spec/features/projects/edit_spec.rb
View file @
03b14b48
require
'rails_helper'
feature
'Project edit'
,
js:
true
do
let
(
:admin
)
{
create
(
:admin
)
}
let
(
:user
)
{
create
(
:user
)
}
let
(
:project
)
{
create
(
:project
)
}
context
'feature visibility'
do
before
do
project
.
team
<<
[
user
,
:master
]
sign_in
(
user
)
...
...
@@ -11,10 +13,9 @@ feature 'Project edit', js: true do
visit
edit_project_path
(
project
)
end
context
'feature visibility'
do
context
'merge requests select'
do
it
'hides merge requests section'
do
select
(
'Disabled'
,
from:
'project_project_feature_attributes_merge_requests_access_level'
)
find
(
'.project-feature-controls[data-for="project[project_feature_attributes][merge_requests_access_level]"] .project-feature-toggle'
).
click
expect
(
page
).
to
have_selector
(
'.merge-requests-feature'
,
visible:
false
)
end
...
...
@@ -30,7 +31,7 @@ feature 'Project edit', js: true do
context
'builds select'
do
it
'hides builds select section'
do
select
(
'Disabled'
,
from:
'project_project_feature_attributes_builds_access_level'
)
find
(
'.project-feature-controls[data-for="project[project_feature_attributes][builds_access_level]"] .project-feature-toggle'
).
click
expect
(
page
).
to
have_selector
(
'.builds-feature'
,
visible:
false
)
end
...
...
@@ -44,4 +45,18 @@ feature 'Project edit', js: true do
end
end
end
context
'LFS enabled setting'
do
before
do
sign_in
(
admin
)
end
it
'displays the correct elements'
do
allow
(
Gitlab
.
config
.
lfs
).
to
receive
(
:enabled
).
and_return
(
true
)
visit
edit_project_path
(
project
)
expect
(
page
).
to
have_content
(
'Git Large File Storage'
)
expect
(
page
).
to
have_selector
(
'input[name="project[lfs_enabled]"] + button'
,
visible:
true
)
end
end
end
spec/features/projects/features_visibility_spec.rb
View file @
03b14b48
...
...
@@ -19,21 +19,16 @@ describe 'Edit Project Settings' do
it
'toggles visibility'
do
visit
edit_project_path
(
project
)
select
'Disabled'
,
from:
"project_project_feature_attributes_
#{
tool_name
}
_access_level"
# disable by clicking toggle
toggle_feature_off
(
"project[project_feature_attributes][
#{
tool_name
}
_access_level]"
)
page
.
within
(
'.sharing-permissions'
)
do
click_button
'Save changes'
end
wait_for_requests
expect
(
page
).
not_to
have_selector
(
".shortcuts-
#{
shortcut_name
}
"
)
select
'Everyone with access'
,
from:
"project_project_feature_attributes_
#{
tool_name
}
_access_level"
page
.
within
(
'.sharing-permissions'
)
do
click_button
'Save changes'
end
wait_for_requests
expect
(
page
).
to
have_selector
(
".shortcuts-
#{
shortcut_name
}
"
)
select
'Only team members'
,
from:
"project_project_feature_attributes_
#{
tool_name
}
_access_level"
# re-enable by clicking toggle again
toggle_feature_on
(
"project[project_feature_attributes][
#{
tool_name
}
_access_level]"
)
page
.
within
(
'.sharing-permissions'
)
do
click_button
'Save changes'
end
...
...
@@ -176,19 +171,19 @@ describe 'Edit Project Settings' do
end
it
"disables repository related features"
do
select
"Disabled"
,
from:
"project_project_feature_attributes_repository_access_level"
toggle_feature_off
(
'project[project_feature_attributes][repository_access_level]'
)
page
.
within
(
'.sharing-permissions'
)
do
click_button
"Save changes"
end
expect
(
find
(
".sharing-permissions"
)).
to
have_selector
(
"
select
.disabled"
,
count:
2
)
expect
(
find
(
".sharing-permissions"
)).
to
have_selector
(
"
.project-feature-toggle
.disabled"
,
count:
2
)
end
it
"shows empty features project homepage"
do
select
"Disabled"
,
from:
"project_project_feature_attributes_repository_access_level"
select
"Disabled"
,
from:
"project_project_feature_attributes_issues_access_level"
select
"Disabled"
,
from:
"project_project_feature_attributes_wiki_access_level"
toggle_feature_off
(
'project[project_feature_attributes][repository_access_level]'
)
toggle_feature_off
(
'project[project_feature_attributes][issues_access_level]'
)
toggle_feature_off
(
'project[project_feature_attributes][wiki_access_level]'
)
page
.
within
(
'.sharing-permissions'
)
do
click_button
"Save changes"
...
...
@@ -201,9 +196,9 @@ describe 'Edit Project Settings' do
end
it
"hides project activity tabs"
do
select
"Disabled"
,
from:
"project_project_feature_attributes_repository_access_level"
select
"Disabled"
,
from:
"project_project_feature_attributes_issues_access_level"
select
"Disabled"
,
from:
"project_project_feature_attributes_wiki_access_level"
toggle_feature_off
(
'project[project_feature_attributes][repository_access_level]'
)
toggle_feature_off
(
'project[project_feature_attributes][issues_access_level]'
)
toggle_feature_off
(
'project[project_feature_attributes][wiki_access_level]'
)
page
.
within
(
'.sharing-permissions'
)
do
click_button
"Save changes"
...
...
@@ -222,7 +217,7 @@ describe 'Edit Project Settings' do
# Regression spec for https://gitlab.com/gitlab-org/gitlab-ce/issues/25272
it
"hides comments activity tab only on disabled issues, merge requests and repository"
do
select
"Disabled"
,
from:
"project_project_feature_attributes_issues_access_level"
toggle_feature_off
(
'project[project_feature_attributes][issues_access_level]'
)
save_changes_and_check_activity_tab
do
expect
(
page
).
to
have_content
(
"Comments"
)
...
...
@@ -230,7 +225,7 @@ describe 'Edit Project Settings' do
visit
edit_project_path
(
project
)
select
"Disabled"
,
from:
"project_project_feature_attributes_merge_requests_access_level"
toggle_feature_off
(
'project[project_feature_attributes][merge_requests_access_level]'
)
save_changes_and_check_activity_tab
do
expect
(
page
).
to
have_content
(
"Comments"
)
...
...
@@ -238,7 +233,7 @@ describe 'Edit Project Settings' do
visit
edit_project_path
(
project
)
select
"Disabled"
,
from:
"project_project_feature_attributes_repository_access_level"
toggle_feature_off
(
'project[project_feature_attributes][repository_access_level]'
)
save_changes_and_check_activity_tab
do
expect
(
page
).
not_to
have_content
(
"Comments"
)
...
...
@@ -275,4 +270,12 @@ describe 'Edit Project Settings' do
expect
(
page
).
not_to
have_selector
(
'.project-stats'
)
end
end
def
toggle_feature_off
(
feature_name
)
find
(
".project-feature-controls[data-for=
\"
#{
feature_name
}
\"
] .project-feature-toggle.checked"
).
click
end
def
toggle_feature_on
(
feature_name
)
find
(
".project-feature-controls[data-for=
\"
#{
feature_name
}
\"
] .project-feature-toggle:not(.checked)"
).
click
end
end
spec/features/projects/settings/merge_requests_settings_spec.rb
View file @
03b14b48
...
...
@@ -19,8 +19,8 @@ feature 'Project settings > Merge Requests', :js do
expect
(
page
).
to
have_content
(
'Only allow merge requests to be merged if the pipeline succeeds'
)
expect
(
page
).
to
have_content
(
'Only allow merge requests to be merged if all discussions are resolved'
)
select
'Disabled'
,
from:
"project_project_feature_attributes_merge_requests_access_level"
within
(
'.sharing-permissions-form'
)
do
find
(
'.project-feature-controls[data-for="project[project_feature_attributes][merge_requests_access_level]"] .project-feature-toggle'
).
click
click_on
(
'Save changes'
)
end
...
...
@@ -39,8 +39,8 @@ feature 'Project settings > Merge Requests', :js do
expect
(
page
).
not_to
have_content
(
'Only allow merge requests to be merged if the pipeline succeeds'
)
expect
(
page
).
to
have_content
(
'Only allow merge requests to be merged if all discussions are resolved'
)
select
'Everyone with access'
,
from:
"project_project_feature_attributes_builds_access_level"
within
(
'.sharing-permissions-form'
)
do
find
(
'.project-feature-controls[data-for="project[project_feature_attributes][builds_access_level]"] .project-feature-toggle'
).
click
click_on
(
'Save changes'
)
end
...
...
@@ -60,8 +60,8 @@ feature 'Project settings > Merge Requests', :js do
expect
(
page
).
not_to
have_content
(
'Only allow merge requests to be merged if the pipeline succeeds'
)
expect
(
page
).
not_to
have_content
(
'Only allow merge requests to be merged if all discussions are resolved'
)
select
'Everyone with access'
,
from:
"project_project_feature_attributes_merge_requests_access_level"
within
(
'.sharing-permissions-form'
)
do
find
(
'.project-feature-controls[data-for="project[project_feature_attributes][merge_requests_access_level]"] .project-feature-toggle'
).
click
click_on
(
'Save changes'
)
end
...
...
spec/features/projects/settings/visibility_settings_spec.rb
View file @
03b14b48
...
...
@@ -11,19 +11,19 @@ feature 'Visibility settings', js: true do
end
scenario
'project visibility select is available'
do
visibility_select_container
=
find
(
'.
js-visibility-select
'
)
visibility_select_container
=
find
(
'.
project-visibility-setting
'
)
expect
(
visibility_select_container
.
find
(
'
.visibility-
select'
).
value
).
to
eq
project
.
visibility_level
.
to_s
expect
(
visibility_select_container
).
to
have_content
'The project can be accessed
without any
authentication.'
expect
(
visibility_select_container
.
find
(
'select'
).
value
).
to
eq
project
.
visibility_level
.
to_s
expect
(
visibility_select_container
).
to
have_content
'The project can be accessed
by anyone, regardless of
authentication.'
end
scenario
'project visibility description updates on change'
do
visibility_select_container
=
find
(
'.
js-visibility-select
'
)
visibility_select
=
visibility_select_container
.
find
(
'
.visibility-
select'
)
visibility_select_container
=
find
(
'.
project-visibility-setting
'
)
visibility_select
=
visibility_select_container
.
find
(
'select'
)
visibility_select
.
select
(
'Private'
)
expect
(
visibility_select
.
value
).
to
eq
'0'
expect
(
visibility_select_container
).
to
have_content
'
Project a
ccess must be granted explicitly to each user.'
expect
(
visibility_select_container
).
to
have_content
'
A
ccess must be granted explicitly to each user.'
end
end
...
...
@@ -37,11 +37,10 @@ feature 'Visibility settings', js: true do
end
scenario
'project visibility is locked'
do
visibility_select_container
=
find
(
'.
js-visibility-select
'
)
visibility_select_container
=
find
(
'.
project-visibility-setting
'
)
expect
(
visibility_select_container
).
not_to
have_select
'.visibility-select'
expect
(
visibility_select_container
).
to
have_content
'Public'
expect
(
visibility_select_container
).
to
have_content
'The project can be accessed without any authentication.'
expect
(
visibility_select_container
).
to
have_selector
'select[name="project[visibility_level]"]:disabled'
expect
(
visibility_select_container
).
to
have_content
'The project can be accessed by anyone, regardless of authentication.'
end
end
end
spec/views/projects/edit.html.haml_spec.rb
View file @
03b14b48
...
...
@@ -15,17 +15,6 @@ describe 'projects/edit' do
current_application_settings:
Gitlab
::
CurrentSettings
.
current_application_settings
)
end
context
'LFS enabled setting'
do
it
'displays the correct elements'
do
allow
(
Gitlab
.
config
.
lfs
).
to
receive
(
:enabled
).
and_return
(
true
)
render
expect
(
rendered
).
to
have_select
(
'project_lfs_enabled'
)
expect
(
rendered
).
to
have_content
(
'Git Large File Storage'
)
end
end
context
'project export disabled'
do
it
'does not display the project export option'
do
stub_application_setting
(
project_export_enabled?:
false
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment