Merge remote-tracking branch 'origin/master' into ci-permissions
# Conflicts: # app/views/projects/builds/index.html.haml
@@ -2,9 +2,11 @@ Please view this file on the master branch, on stable branches it's out of date.
|
||||
|
||||
v 8.5.0 (unreleased)
|
||||
- Ensure rake tasks that don't need a DB connection can be run without one
|
||||
- Update New Relic gem to 3.14.1.311 (Stan Hu)
|
||||
- Add "visibility" flag to GET /projects api endpoint
|
||||
- Ignore binary files in code search to prevent Error 500 (Stan Hu)
|
||||
- Render sanitized SVG images (Stan Hu)
|
||||
- Support download access by PRIVATE-TOKEN header (Stan Hu)
|
||||
- Upgrade gitlab_git to 7.2.23 to fix commit message mentions in first branch push
|
||||
- New UI for pagination
|
||||
- Don't prevent sign out when 2FA enforcement is enabled and user hasn't yet
|
||||
@@ -19,9 +21,15 @@ v 8.5.0 (unreleased)
|
||||
- Update the ExternalIssue regex pattern (Blake Hitchcock)
|
||||
- Optimized performance of finding issues to be closed by a merge request
|
||||
- Revert "Add IP check against DNSBLs at account sign-up"
|
||||
- Fix API to keep request parameters in Link header (Michael Potthoff)
|
||||
- Deprecate API "merge_request/:merge_request_id/comments". Use "merge_requests/:merge_request_id/notes" instead
|
||||
- Deprecate API "merge_request/:merge_request_id/...". Use "merge_requests/:merge_request_id/..." instead
|
||||
- Mark inline difference between old and new paths when a file is renamed
|
||||
- Support Akismet spam checking for creation of issues via API (Stan Hu)
|
||||
- Improve UI consistency between projects and groups lists
|
||||
- Add sort dropdown to dashboard projects page
|
||||
- Hide remove source branch button when the MR is merged but new commits are pushed (Zeger-Jan van de Weg)
|
||||
- In seach autocomplete show only groups and projects you are member of
|
||||
|
||||
v 8.4.3
|
||||
- Increase lfs_objects size column to 8-byte integer to allow files larger
|
||||
@@ -30,6 +38,9 @@ v 8.4.3
|
||||
- Fix highlighting in blame view
|
||||
- Update sentry-raven gem to prevent "Not a git repository" console output
|
||||
when running certain commands
|
||||
- Add instrumentation to additional Gitlab::Git and Rugged methods for
|
||||
performance monitoring
|
||||
- Allow autosize textareas to also be manually resized
|
||||
|
||||
v 8.4.2
|
||||
- Bump required gitlab-workhorse version to bring in a fix for missing
|
||||
@@ -171,6 +182,7 @@ v 8.3.0
|
||||
- Handle and report SSL errors in Web hook test (Stan Hu)
|
||||
- Bump Redis requirement to 2.8 for Sidekiq 4 (Stan Hu)
|
||||
- Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera)
|
||||
- WIP identifier on merge requests no longer requires trailing space
|
||||
- Add rake tasks for git repository maintainance (Zeger-Jan van de Weg)
|
||||
- Fix 500 error when update group member permission
|
||||
- Fix: As an admin, cannot add oneself as a member to a group/project
|
||||
|
||||
@@ -177,6 +177,26 @@ is probably 1, adding a new Git Hook maybe 4 or 5, big features 7-9.
|
||||
issues or chunks. You can simply not set the weight of a parent issue and set
|
||||
weights to children issues.
|
||||
|
||||
### Regression issues
|
||||
|
||||
Every monthly release has a corresponding issue on the CE issue tracker to keep
|
||||
track of functionality broken by that release and any fixes that need to be
|
||||
included in a patch release (see [8.3 Regressions] as an example).
|
||||
|
||||
As outlined in the issue description, the intended workflow is to post one note
|
||||
with a reference to an issue describing the regression, and then to update that
|
||||
note with a reference to the merge request that fixes it as it becomes available.
|
||||
|
||||
If you're a contributor who doesn't have the required permissions to update
|
||||
other users' notes, please post a new note with a reference to both the issue
|
||||
and the merge request.
|
||||
|
||||
The release manager will [update the notes] in the regression issue as fixes are
|
||||
addressed.
|
||||
|
||||
[8.3 Regressions]: https://gitlab.com/gitlab-org/gitlab-ce/issues/4127
|
||||
[update the notes]: https://gitlab.com/gitlab-org/release-tools/blob/master/doc/pro-tips.md#update-the-regression-issue
|
||||
|
||||
## Merge requests
|
||||
|
||||
We welcome merge requests with fixes and improvements to GitLab code, tests,
|
||||
@@ -257,6 +277,18 @@ Please ensure that your merge request meets the contribution acceptance criteria
|
||||
|
||||
When having your code reviewed and when reviewing merge requests please take the [thoughtbot code review guidelines](https://github.com/thoughtbot/guides/tree/master/code-review) into account.
|
||||
|
||||
## Changes for Stable Releases
|
||||
|
||||
Sometimes certain changes have to be added to an existing stable release.
|
||||
Two examples are bug fixes and performance improvements. In these cases the
|
||||
corresponding merge request should be updated to have the following:
|
||||
|
||||
1. A milestone indicating what release the merge request should be merged into.
|
||||
1. The label "Pick into Stable"
|
||||
|
||||
This makes it easier for release managers to keep track of what still has to be
|
||||
merged and where changes have to be merged into.
|
||||
|
||||
## Definition of done
|
||||
|
||||
If you contribute to GitLab please know that changes involve more than just
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.6.2
|
||||
0.6.3
|
||||
|
||||
@@ -30,14 +30,15 @@ gem 'omniauth-github', '~> 1.1.1'
|
||||
gem 'omniauth-gitlab', '~> 1.0.0'
|
||||
gem 'omniauth-google-oauth2', '~> 0.2.0'
|
||||
gem 'omniauth-kerberos', '~> 0.3.0', group: :kerberos
|
||||
gem 'omniauth-saml', '~> 1.4.0'
|
||||
gem 'omniauth-saml', '~> 1.4.2'
|
||||
gem 'omniauth-shibboleth', '~> 1.2.0'
|
||||
gem 'omniauth-twitter', '~> 1.2.0'
|
||||
gem 'omniauth_crowd', '~> 2.2.0'
|
||||
gem 'rack-oauth2', '~> 1.2.1'
|
||||
|
||||
# reCAPTCHA protection
|
||||
# Spam and anti-bot protection
|
||||
gem 'recaptcha', require: 'recaptcha/rails'
|
||||
gem 'akismet', '~> 2.0'
|
||||
|
||||
# Two-factor authentication
|
||||
gem 'devise-two-factor', '~> 2.0.0'
|
||||
@@ -49,7 +50,7 @@ gem "browser", '~> 1.0.0'
|
||||
|
||||
# Extracting information from a git repository
|
||||
# Provide access to Gitlab::Git library
|
||||
gem "gitlab_git", '~> 7.2.23'
|
||||
gem "gitlab_git", '~> 8.0.0'
|
||||
|
||||
# LDAP Auth
|
||||
# GitLab fork with several improvements to original library. For full list of changes
|
||||
@@ -302,9 +303,6 @@ group :production do
|
||||
gem "gitlab_meta", '7.0'
|
||||
end
|
||||
|
||||
gem "newrelic_rpm", '~> 3.9.4.245'
|
||||
gem 'newrelic-grape'
|
||||
|
||||
gem 'octokit', '~> 3.8.0'
|
||||
|
||||
gem "mail_room", "~> 0.6.1"
|
||||
|
||||
@@ -49,6 +49,7 @@ GEM
|
||||
addressable (2.3.8)
|
||||
after_commit_queue (1.3.0)
|
||||
activerecord (>= 3.0)
|
||||
akismet (2.0.0)
|
||||
allocations (1.0.3)
|
||||
annotate (2.6.10)
|
||||
activerecord (>= 3.2, <= 4.3)
|
||||
@@ -356,7 +357,7 @@ GEM
|
||||
posix-spawn (~> 0.3)
|
||||
gitlab_emoji (0.2.0)
|
||||
gemojione (~> 2.1)
|
||||
gitlab_git (7.2.24)
|
||||
gitlab_git (8.0.0)
|
||||
activesupport (~> 4.0)
|
||||
charlock_holmes (~> 0.7.3)
|
||||
github-linguist (~> 4.7.0)
|
||||
@@ -478,10 +479,6 @@ GEM
|
||||
net-ldap (0.12.1)
|
||||
net-ssh (3.0.1)
|
||||
netrc (0.11.0)
|
||||
newrelic-grape (2.1.0)
|
||||
grape
|
||||
newrelic_rpm
|
||||
newrelic_rpm (3.9.4.245)
|
||||
nokogiri (1.6.7.2)
|
||||
mini_portile2 (~> 2.0.0.rc2)
|
||||
nprogress-rails (0.1.6.7)
|
||||
@@ -534,9 +531,9 @@ GEM
|
||||
omniauth-oauth2 (1.3.1)
|
||||
oauth2 (~> 1.0)
|
||||
omniauth (~> 1.2)
|
||||
omniauth-saml (1.4.1)
|
||||
omniauth-saml (1.4.2)
|
||||
omniauth (~> 1.1)
|
||||
ruby-saml (~> 1.0.0)
|
||||
ruby-saml (~> 1.1, >= 1.1.1)
|
||||
omniauth-shibboleth (1.2.1)
|
||||
omniauth (>= 1.0.0)
|
||||
omniauth-twitter (1.2.1)
|
||||
@@ -692,7 +689,7 @@ GEM
|
||||
ruby-fogbugz (0.2.1)
|
||||
crack (~> 0.4)
|
||||
ruby-progressbar (1.7.5)
|
||||
ruby-saml (1.0.0)
|
||||
ruby-saml (1.1.1)
|
||||
nokogiri (>= 1.5.10)
|
||||
uuid (~> 2.3)
|
||||
ruby2ruby (2.2.0)
|
||||
@@ -884,6 +881,7 @@ DEPENDENCIES
|
||||
acts-as-taggable-on (~> 3.4)
|
||||
addressable (~> 2.3.8)
|
||||
after_commit_queue
|
||||
akismet (~> 2.0)
|
||||
allocations (~> 1.0)
|
||||
annotate (~> 2.6.0)
|
||||
asana (~> 0.4.0)
|
||||
@@ -934,7 +932,7 @@ DEPENDENCIES
|
||||
github-markup (~> 1.3.1)
|
||||
gitlab-flowdock-git-hook (~> 1.0.1)
|
||||
gitlab_emoji (~> 0.2.0)
|
||||
gitlab_git (~> 7.2.23)
|
||||
gitlab_git (~> 8.0.0)
|
||||
gitlab_meta (= 7.0)
|
||||
gitlab_omniauth-ldap (~> 1.2.1)
|
||||
gollum-lib (~> 4.1.0)
|
||||
@@ -961,8 +959,6 @@ DEPENDENCIES
|
||||
mysql2 (~> 0.3.16)
|
||||
nested_form (~> 0.3.2)
|
||||
net-ssh (~> 3.0.1)
|
||||
newrelic-grape
|
||||
newrelic_rpm (~> 3.9.4.245)
|
||||
nokogiri (= 1.6.7.2)
|
||||
nprogress-rails (~> 0.1.6.7)
|
||||
oauth2 (~> 1.0.0)
|
||||
@@ -976,7 +972,7 @@ DEPENDENCIES
|
||||
omniauth-gitlab (~> 1.0.0)
|
||||
omniauth-google-oauth2 (~> 0.2.0)
|
||||
omniauth-kerberos (~> 0.3.0)
|
||||
omniauth-saml (~> 1.4.0)
|
||||
omniauth-saml (~> 1.4.2)
|
||||
omniauth-shibboleth (~> 1.2.0)
|
||||
omniauth-twitter (~> 1.2.0)
|
||||
omniauth_crowd (~> 2.2.0)
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
callback(namespaces)
|
||||
|
||||
# Return projects list. Filtered by query
|
||||
projects: (query, callback) ->
|
||||
projects: (query, order, callback) ->
|
||||
url = Api.buildUrl(Api.projects_path)
|
||||
|
||||
$.ajax(
|
||||
@@ -55,6 +55,7 @@
|
||||
data:
|
||||
private_token: gon.api_token
|
||||
search: query
|
||||
order_by: order
|
||||
per_page: 20
|
||||
dataType: "json"
|
||||
).done (projects) ->
|
||||
|
||||
@@ -65,8 +65,7 @@ class @DropzoneInput
|
||||
return
|
||||
|
||||
success: (header, response) ->
|
||||
child = $(dropzone[0]).children("textarea")
|
||||
$(child).val $(child).val() + response.link.markdown + "\n"
|
||||
pasteText response.link.markdown
|
||||
return
|
||||
|
||||
error: (temp, errorMessage) ->
|
||||
@@ -128,6 +127,7 @@ class @DropzoneInput
|
||||
beforeSelection = $(child).val().substring 0, caretStart
|
||||
afterSelection = $(child).val().substring caretEnd, textEnd
|
||||
$(child).val beforeSelection + text + afterSelection
|
||||
child.get(0).setSelectionRange caretStart + text.length, caretEnd + text.length
|
||||
form_textarea.trigger "input"
|
||||
|
||||
getFilename = (e) ->
|
||||
|
||||
@@ -50,3 +50,19 @@ class @Project
|
||||
$('#notifications-button').empty().append("<i class='fa fa-bell'></i>" + label + "<i class='fa fa-angle-down'></i>")
|
||||
$(@).parents('ul').find('li.active').removeClass 'active'
|
||||
$(@).parent().addClass 'active'
|
||||
|
||||
@projectSelectDropdown()
|
||||
|
||||
projectSelectDropdown: ->
|
||||
new ProjectSelect()
|
||||
|
||||
$('.project-item-select').on 'click', (e) =>
|
||||
@changeProject $(e.currentTarget).val()
|
||||
|
||||
$('.js-projects-dropdown-toggle').on 'click', (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
$('.js-projects-dropdown').select2('open')
|
||||
|
||||
changeProject: (url) ->
|
||||
window.location = url
|
||||
|
||||
@@ -3,6 +3,7 @@ class @ProjectSelect
|
||||
$('.ajax-project-select').each (i, select) ->
|
||||
@groupId = $(select).data('group-id')
|
||||
@includeGroups = $(select).data('include-groups')
|
||||
@orderBy = $(select).data('order-by') || 'id'
|
||||
|
||||
placeholder = "Search for project"
|
||||
placeholder += " or group" if @includeGroups
|
||||
@@ -28,7 +29,7 @@ class @ProjectSelect
|
||||
if @groupId
|
||||
Api.groupProjects @groupId, query.term, projectsCallback
|
||||
else
|
||||
Api.projects query.term, projectsCallback
|
||||
Api.projects query.term, @orderBy, projectsCallback
|
||||
|
||||
id: (project) ->
|
||||
project.web_url
|
||||
|
||||
@@ -82,8 +82,7 @@
|
||||
&.btn-success,
|
||||
&.btn-new,
|
||||
&.btn-create,
|
||||
&.btn-save,
|
||||
&.btn-green {
|
||||
&.btn-save {
|
||||
@include btn-green;
|
||||
}
|
||||
|
||||
@@ -159,7 +158,6 @@
|
||||
|
||||
.input-group-btn {
|
||||
.btn {
|
||||
@include btn-gray;
|
||||
@include btn-middle;
|
||||
|
||||
&:hover {
|
||||
@@ -186,8 +184,4 @@
|
||||
border: 1px solid #c6cacf !important;
|
||||
background-color: #e4e7ed !important;
|
||||
}
|
||||
|
||||
.btn-green {
|
||||
@include btn-green
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,11 +376,11 @@ table {
|
||||
margin-bottom: $gl-padding;
|
||||
}
|
||||
|
||||
.new-project-item-select-holder {
|
||||
.project-item-select-holder {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
|
||||
.new-project-item-select {
|
||||
.project-item-select {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
|
||||
@@ -73,7 +73,6 @@ header {
|
||||
|
||||
.title {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
font-size: 19px;
|
||||
line-height: $header-height;
|
||||
font-weight: normal;
|
||||
@@ -88,6 +87,13 @@ header {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-toggle-caret {
|
||||
position: relative;
|
||||
top: -2px;
|
||||
margin-left: 5px;
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-collapse {
|
||||
|
||||
@@ -37,3 +37,89 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.top-area {
|
||||
@include clearfix;
|
||||
|
||||
border-bottom: 1px solid #EEE;
|
||||
|
||||
.nav-text {
|
||||
padding-top: 16px;
|
||||
padding-bottom: 11px;
|
||||
display: inline-block;
|
||||
width: 50%;
|
||||
line-height: 28px;
|
||||
|
||||
/* Small devices (phones, tablets, 768px and lower) */
|
||||
@media (max-width: $screen-sm-min) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
display: inline-block;
|
||||
width: 50%;
|
||||
margin-bottom: 0px;
|
||||
border-bottom: none;
|
||||
|
||||
/* Small devices (phones, tablets, 768px and lower) */
|
||||
@media (max-width: $screen-sm-min) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-controls {
|
||||
width: 50%;
|
||||
display: inline-block;
|
||||
float: right;
|
||||
text-align: right;
|
||||
padding: 11px 0;
|
||||
margin-bottom: 0px;
|
||||
|
||||
> .dropdown {
|
||||
margin-right: 10px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
> .btn {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
input {
|
||||
height: 34px;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
margin-right: 10px;
|
||||
|
||||
/* Medium devices (desktops, 992px and up) */
|
||||
@media (min-width: $screen-md-min) { width: 200px; }
|
||||
|
||||
/* Large devices (large desktops, 1200px and up) */
|
||||
@media (min-width: $screen-lg-min) { width: 250px; }
|
||||
|
||||
&.input-short {
|
||||
/* Medium devices (desktops, 992px and up) */
|
||||
@media (min-width: $screen-md-min) { width: 170px; }
|
||||
|
||||
/* Large devices (large desktops, 1200px and up) */
|
||||
@media (min-width: $screen-lg-min) { width: 210px; }
|
||||
}
|
||||
}
|
||||
|
||||
/* Hide on extra small devices (phones) */
|
||||
@media (max-width: $screen-xs-max) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Small devices (tablets, 768px and lower) */
|
||||
@media (max-width: $screen-sm-max) {
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
|
||||
input {
|
||||
width: 300px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,3 @@
|
||||
font-size: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.explore-trending-block {
|
||||
.lead {
|
||||
line-height: 32px;
|
||||
font-size: 18px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
input[type='search'] {
|
||||
width: 225px;
|
||||
vertical-align: bottom;
|
||||
|
||||
|
||||
@media (max-width: $screen-xs-max) {
|
||||
width: 100px;
|
||||
vertical-align: bottom;
|
||||
@@ -21,3 +21,21 @@
|
||||
height: 42px;
|
||||
}
|
||||
}
|
||||
|
||||
.group-row {
|
||||
&.no-description {
|
||||
.group-name {
|
||||
line-height: 44px;
|
||||
}
|
||||
}
|
||||
|
||||
.stats {
|
||||
float: right;
|
||||
line-height: 44px;
|
||||
color: $gl-gray;
|
||||
|
||||
span {
|
||||
margin-right: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,36 +281,6 @@
|
||||
margin-top: -1px;
|
||||
}
|
||||
|
||||
.top-area {
|
||||
border-bottom: 1px solid #EEE;
|
||||
|
||||
ul.nav-links {
|
||||
display: inline-block;
|
||||
width: 50%;
|
||||
margin-bottom: 0px;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.projects-search-form {
|
||||
width: 50%;
|
||||
display: inline-block;
|
||||
float: right;
|
||||
padding-top: 11px;
|
||||
text-align: right;
|
||||
|
||||
.btn-green {
|
||||
margin-left: 10px;
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: $screen-xs-max) {
|
||||
.projects-search-form {
|
||||
padding-top: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.fork-namespaces {
|
||||
.fork-thumbnail {
|
||||
text-align: center;
|
||||
@@ -386,22 +356,6 @@ pre.light-well {
|
||||
border-color: #f1f1f1;
|
||||
}
|
||||
|
||||
.projects-search-form {
|
||||
padding: $gl-padding 0;
|
||||
padding-bottom: 0;
|
||||
margin-bottom: 0px;
|
||||
|
||||
input {
|
||||
display: inline-block;
|
||||
width: calc(100% - 151px);
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-block;
|
||||
width: 135px;
|
||||
}
|
||||
}
|
||||
|
||||
.git-empty {
|
||||
margin: 0 7px 0 7px;
|
||||
|
||||
@@ -461,6 +415,10 @@ pre.light-well {
|
||||
a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
> span {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.project-description {
|
||||
@@ -559,52 +517,12 @@ pre.light-well {
|
||||
}
|
||||
}
|
||||
|
||||
.cannot-be-merged,
|
||||
.cannot-be-merged,
|
||||
.cannot-be-merged:hover {
|
||||
color: #E62958;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
/*
|
||||
* Forks list rendered on Project's forks page
|
||||
*/
|
||||
|
||||
.forks-top-block {
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
.projects-search-form {
|
||||
.dropdown-toggle.btn {
|
||||
margin-top: -3px;
|
||||
}
|
||||
|
||||
&.fork-search-form {
|
||||
margin: 0;
|
||||
margin-top: -$gl-padding;
|
||||
padding-bottom: 0;
|
||||
|
||||
input {
|
||||
/* Small devices (tablets, 768px and up) */
|
||||
@media (min-width: $screen-sm-min) { width: 180px; }
|
||||
|
||||
/* Medium devices (desktops, 992px and up) */
|
||||
@media (min-width: $screen-md-min) { width: 350px; }
|
||||
|
||||
/* Large devices (large desktops, 1200px and up) */
|
||||
@media (min-width: $screen-lg-min) { width: 400px; }
|
||||
}
|
||||
|
||||
.sort-forks {
|
||||
width: 160px;
|
||||
}
|
||||
|
||||
.fork-link {
|
||||
float: right;
|
||||
margin-left: $gl-padding;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.private-forks-notice .private-fork-icon {
|
||||
i:nth-child(1) {
|
||||
color: #2AA056;
|
||||
|
||||
@@ -79,6 +79,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
|
||||
:recaptcha_private_key,
|
||||
:sentry_enabled,
|
||||
:sentry_dsn,
|
||||
:akismet_enabled,
|
||||
:akismet_api_key,
|
||||
restricted_visibility_levels: [],
|
||||
import_sources: []
|
||||
)
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
class Admin::SpamLogsController < Admin::ApplicationController
|
||||
def index
|
||||
@spam_logs = SpamLog.order(id: :desc).page(params[:page])
|
||||
end
|
||||
|
||||
def destroy
|
||||
spam_log = SpamLog.find(params[:id])
|
||||
|
||||
if params[:remove_user]
|
||||
spam_log.remove_user
|
||||
redirect_to admin_spam_logs_path, notice: "User #{spam_log.user.username} was successfully removed."
|
||||
else
|
||||
spam_log.destroy
|
||||
render nothing: true
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -60,6 +60,8 @@ class ApplicationController < ActionController::Base
|
||||
params[:authenticity_token].presence
|
||||
elsif params[:private_token].presence
|
||||
params[:private_token].presence
|
||||
elsif request.headers['PRIVATE-TOKEN'].present?
|
||||
request.headers['PRIVATE-TOKEN']
|
||||
end
|
||||
user = user_token && User.find_by_authentication_token(user_token.to_s)
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
|
||||
|
||||
def index
|
||||
@projects = current_user.authorized_projects.sorted_by_activity.non_archived
|
||||
@projects = @projects.sort(@sort = params[:sort])
|
||||
@projects = @projects.includes(:namespace)
|
||||
@last_push = current_user.recent_push
|
||||
|
||||
|
||||
@@ -2,15 +2,13 @@ class Projects::AvatarsController < Projects::ApplicationController
|
||||
before_action :project
|
||||
|
||||
def show
|
||||
@blob = @project.repository.blob_at_branch('master', @project.avatar_in_git)
|
||||
@blob = @repository.blob_at_branch('master', @project.avatar_in_git)
|
||||
if @blob
|
||||
headers['X-Content-Type-Options'] = 'nosniff'
|
||||
send_data(
|
||||
@blob.data,
|
||||
type: @blob.mime_type,
|
||||
disposition: 'inline',
|
||||
filename: @blob.name
|
||||
)
|
||||
headers.store(*Gitlab::Workhorse.send_git_blob(@repository, @blob))
|
||||
headers['Content-Disposition'] = 'inline'
|
||||
headers['Content-Type'] = @blob.content_type
|
||||
head :ok # 'render nothing: true' messes up the Content-Type
|
||||
else
|
||||
render_404
|
||||
end
|
||||
|
||||
@@ -33,6 +33,7 @@ class Projects::BlobController < Projects::ApplicationController
|
||||
|
||||
def edit
|
||||
@last_commit = Gitlab::Git::Commit.last_for_path(@repository, @ref, @path).sha
|
||||
blob.load_all_data!(@repository)
|
||||
end
|
||||
|
||||
def update
|
||||
@@ -51,6 +52,7 @@ class Projects::BlobController < Projects::ApplicationController
|
||||
|
||||
def preview
|
||||
@content = params[:content]
|
||||
@blob.load_all_data!(@repository)
|
||||
diffy = Diffy::Diff.new(@blob.data, @content, diff: '-U 3', include_diff_info: true)
|
||||
diff_lines = diffy.diff.scan(/.*\n/)[2..-1]
|
||||
diff_lines = Gitlab::Diff::Parser.new.parse(diff_lines)
|
||||
|
||||
@@ -15,7 +15,10 @@ class Projects::RawController < Projects::ApplicationController
|
||||
if @blob.lfs_pointer?
|
||||
send_lfs_object
|
||||
else
|
||||
stream_data
|
||||
headers.store(*Gitlab::Workhorse.send_git_blob(@repository, @blob))
|
||||
headers['Content-Disposition'] = 'inline'
|
||||
headers['Content-Type'] = get_blob_type
|
||||
head :ok # 'render nothing: true' messes up the Content-Type
|
||||
end
|
||||
else
|
||||
render_404
|
||||
@@ -34,16 +37,6 @@ class Projects::RawController < Projects::ApplicationController
|
||||
end
|
||||
end
|
||||
|
||||
def stream_data
|
||||
type = get_blob_type
|
||||
|
||||
send_data(
|
||||
@blob.data,
|
||||
type: type,
|
||||
disposition: 'inline'
|
||||
)
|
||||
end
|
||||
|
||||
def send_lfs_object
|
||||
lfs_object = find_lfs_object
|
||||
|
||||
|
||||
@@ -23,6 +23,10 @@ module ApplicationSettingsHelper
|
||||
current_application_settings.user_oauth_applications
|
||||
end
|
||||
|
||||
def askimet_enabled?
|
||||
current_application_settings.akismet_enabled?
|
||||
end
|
||||
|
||||
# Return a group of checkboxes that use Bootstrap's button plugin for a
|
||||
# toggle button effect.
|
||||
def restricted_level_checkboxes(help_block_id)
|
||||
|
||||
@@ -10,8 +10,19 @@ module ExploreHelper
|
||||
|
||||
options = exist_opts.merge(options)
|
||||
|
||||
path = explore_projects_path
|
||||
path = if explore_controller?
|
||||
explore_projects_path
|
||||
elsif current_action?(:starred)
|
||||
starred_dashboard_projects_path
|
||||
else
|
||||
dashboard_projects_path
|
||||
end
|
||||
|
||||
path << "?#{options.to_param}"
|
||||
path
|
||||
end
|
||||
|
||||
def explore_controller?
|
||||
controller.class.name.split("::").first == "Explore"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -53,14 +53,23 @@ module ProjectsHelper
|
||||
link_to(simple_sanitize(owner.name), user_path(owner))
|
||||
end
|
||||
|
||||
project_link = link_to(simple_sanitize(project.name), project_path(project))
|
||||
project_link = link_to project_path(project), { class: "project-item-select-holder #{"js-projects-dropdown-toggle" if current_user}" } do
|
||||
link_output = simple_sanitize(project.name)
|
||||
link_output += content_tag :span, nil, { class: "fa fa-chevron-down dropdown-toggle-caret" } if current_user
|
||||
|
||||
if current_user
|
||||
link_output += project_select_tag :project_path,
|
||||
class: "project-item-select js-projects-dropdown",
|
||||
data: { include_groups: false, order_by: 'last_activity_at' }
|
||||
end
|
||||
|
||||
link_output
|
||||
end
|
||||
|
||||
full_title = namespace_link + ' / ' + project_link
|
||||
full_title += ' · '.html_safe + link_to(simple_sanitize(name), url) if name
|
||||
|
||||
content_tag :span do
|
||||
full_title
|
||||
end
|
||||
full_title
|
||||
end
|
||||
|
||||
def remove_project_message(project)
|
||||
|
||||
@@ -70,7 +70,7 @@ module SearchHelper
|
||||
|
||||
# Autocomplete results for the current user's groups
|
||||
def groups_autocomplete(term, limit = 5)
|
||||
Group.search(term).limit(limit).map do |group|
|
||||
current_user.authorized_groups.search(term).limit(limit).map do |group|
|
||||
{
|
||||
label: "group: #{search_result_sanitize(group.name)}",
|
||||
url: group_path(group)
|
||||
@@ -80,7 +80,7 @@ module SearchHelper
|
||||
|
||||
# Autocomplete results for the current user's projects
|
||||
def projects_autocomplete(term, limit = 5)
|
||||
ProjectsFinder.new.execute(current_user).search_by_title(term).
|
||||
current_user.authorized_projects.search_by_title(term).
|
||||
sorted_by_stars.non_archived.limit(limit).map do |p|
|
||||
{
|
||||
label: "project: #{search_result_sanitize(p.name_with_namespace)}",
|
||||
|
||||
@@ -10,7 +10,7 @@ class EmailRejectionMailer < BaseMailer
|
||||
subject: "[Rejected] #{@original_message.subject}"
|
||||
}
|
||||
|
||||
headers['Message-ID'] = SecureRandom.hex
|
||||
headers['Message-ID'] = "<#{SecureRandom.hex}@#{Gitlab.config.gitlab.host}>"
|
||||
headers['In-Reply-To'] = @original_message.message_id
|
||||
headers['References'] = @original_message.message_id
|
||||
|
||||
|
||||
@@ -88,6 +88,10 @@ class ApplicationSetting < ActiveRecord::Base
|
||||
presence: true,
|
||||
if: :sentry_enabled
|
||||
|
||||
validates :akismet_api_key,
|
||||
presence: true,
|
||||
if: :akismet_enabled
|
||||
|
||||
validates_each :restricted_visibility_levels do |record, attr, value|
|
||||
unless value.nil?
|
||||
value.each do |level|
|
||||
@@ -143,7 +147,9 @@ class ApplicationSetting < ActiveRecord::Base
|
||||
shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'],
|
||||
max_artifacts_size: Settings.artifacts['max_size'],
|
||||
require_two_factor_authentication: false,
|
||||
two_factor_grace_period: 48
|
||||
two_factor_grace_period: 48,
|
||||
recaptcha_enabled: false,
|
||||
akismet_enabled: false
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
@@ -205,7 +205,11 @@ module Ci
|
||||
end
|
||||
|
||||
def ci_yaml_file
|
||||
@ci_yaml_file ||= project.repository.blob_at(sha, '.gitlab-ci.yml').data
|
||||
@ci_yaml_file ||= begin
|
||||
blob = project.repository.blob_at(sha, '.gitlab-ci.yml')
|
||||
blob.load_all_data!(project.repository)
|
||||
blob.data
|
||||
end
|
||||
rescue
|
||||
nil
|
||||
end
|
||||
|
||||
@@ -258,7 +258,7 @@ class MergeRequest < ActiveRecord::Base
|
||||
end
|
||||
|
||||
def work_in_progress?
|
||||
!!(title =~ /\A\[?WIP\]?:? /i)
|
||||
!!(title =~ /\A\[?WIP(\]|:| )/i)
|
||||
end
|
||||
|
||||
def mergeable?
|
||||
@@ -284,7 +284,8 @@ class MergeRequest < ActiveRecord::Base
|
||||
def can_remove_source_branch?(current_user)
|
||||
!source_project.protected_branch?(source_branch) &&
|
||||
!source_project.root_ref?(source_branch) &&
|
||||
Ability.abilities.allowed?(current_user, :push_code, source_project)
|
||||
Ability.abilities.allowed?(current_user, :push_code, source_project) &&
|
||||
last_commit == source_project.commit(source_branch)
|
||||
end
|
||||
|
||||
def mr_and_commit_notes
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
class SpamLog < ActiveRecord::Base
|
||||
belongs_to :user
|
||||
|
||||
validates :user, presence: true
|
||||
|
||||
def remove_user
|
||||
user.block
|
||||
user.destroy
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,5 @@
|
||||
class SpamReport < ActiveRecord::Base
|
||||
belongs_to :user
|
||||
|
||||
validates :user, presence: true
|
||||
end
|
||||
@@ -39,6 +39,8 @@ class Tree
|
||||
|
||||
git_repo = repository.raw_repository
|
||||
@readme = Gitlab::Git::Blob.find(git_repo, sha, readme_path)
|
||||
@readme.load_all_data!(git_repo)
|
||||
@readme
|
||||
end
|
||||
|
||||
def trees
|
||||
|
||||
@@ -138,6 +138,7 @@ class User < ActiveRecord::Base
|
||||
has_many :assigned_merge_requests, dependent: :destroy, foreign_key: :assignee_id, class_name: "MergeRequest"
|
||||
has_many :oauth_applications, class_name: 'Doorkeeper::Application', as: :owner, dependent: :destroy
|
||||
has_one :abuse_report, dependent: :destroy
|
||||
has_many :spam_logs, dependent: :destroy
|
||||
has_many :builds, dependent: :nullify, class_name: 'Ci::Build'
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
class CreateSpamLogService < BaseService
|
||||
def initialize(project, user, params)
|
||||
super(project, user, params)
|
||||
end
|
||||
|
||||
def execute
|
||||
spam_params = params.merge({ user_id: @current_user.id,
|
||||
project_id: @project.id } )
|
||||
spam_log = SpamLog.new(spam_params)
|
||||
spam_log.save
|
||||
spam_log
|
||||
end
|
||||
end
|
||||
@@ -218,20 +218,37 @@
|
||||
= f.label :recaptcha_enabled do
|
||||
= f.check_box :recaptcha_enabled
|
||||
Enable reCAPTCHA
|
||||
%span.help-block#recaptcha_help_block Helps preventing bots from creating accounts
|
||||
%span.help-block#recaptcha_help_block Helps prevent bots from creating accounts
|
||||
|
||||
.form-group
|
||||
= f.label :recaptcha_site_key, 'reCAPTCHA Site Key', class: 'control-label col-sm-2'
|
||||
.col-sm-10
|
||||
= f.text_field :recaptcha_site_key, class: 'form-control'
|
||||
.help-block
|
||||
Generate site and private keys here:
|
||||
%a{ href: 'http://www.google.com/recaptcha', target: '_blank'} http://www.google.com/recaptcha
|
||||
Generate site and private keys at
|
||||
%a{ href: 'http://www.google.com/recaptcha', target: 'blank'} http://www.google.com/recaptcha
|
||||
|
||||
.form-group
|
||||
= f.label :recaptcha_private_key, 'reCAPTCHA Private Key', class: 'control-label col-sm-2'
|
||||
.col-sm-10
|
||||
= f.text_field :recaptcha_private_key, class: 'form-control'
|
||||
|
||||
.form-group
|
||||
.col-sm-offset-2.col-sm-10
|
||||
.checkbox
|
||||
= f.label :akismet_enabled do
|
||||
= f.check_box :akismet_enabled
|
||||
Enable Akismet
|
||||
%span.help-block#akismet_help_block Helps prevent bots from creating issues
|
||||
|
||||
.form-group
|
||||
= f.label :akismet_api_key, 'Akismet API Key', class: 'control-label col-sm-2'
|
||||
.col-sm-10
|
||||
= f.text_field :akismet_api_key, class: 'form-control'
|
||||
.help-block
|
||||
Generate API key at
|
||||
%a{ href: 'http://www.akismet.com', target: 'blank'} http://www.akismet.com
|
||||
|
||||
%fieldset
|
||||
%legend Error Reporting and Logging
|
||||
%p
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
- user = spam_log.user
|
||||
%tr
|
||||
%td
|
||||
= time_ago_with_tooltip(spam_log.created_at)
|
||||
%td
|
||||
- if user
|
||||
= link_to user.name, [:admin, user]
|
||||
.light.small
|
||||
Joined #{time_ago_with_tooltip(user.created_at)}
|
||||
- else
|
||||
(removed)
|
||||
%td
|
||||
= spam_log.source_ip
|
||||
%td
|
||||
= spam_log.via_api? ? 'Y' : 'N'
|
||||
%td
|
||||
= spam_log.noteable_type
|
||||
%td
|
||||
= spam_log.title
|
||||
%td
|
||||
= truncate(spam_log.description, length: 100)
|
||||
%td
|
||||
- if user
|
||||
= link_to 'Remove user', admin_spam_log_path(spam_log, remove_user: true),
|
||||
data: { confirm: "USER #{user.name} WILL BE REMOVED! Are you sure?" }, method: :delete, class: "btn btn-xs btn-remove"
|
||||
%td
|
||||
- if user && !user.blocked?
|
||||
= link_to 'Block user', block_admin_user_path(user), data: {confirm: 'USER WILL BE BLOCKED! Are you sure?'}, method: :put, class: "btn btn-xs"
|
||||
- else
|
||||
.btn.btn-xs.disabled
|
||||
Already Blocked
|
||||
= link_to 'Remove log', [:admin, spam_log], remote: true, method: :delete, class: "btn btn-xs btn-close js-remove-tr"
|
||||
@@ -0,0 +1,21 @@
|
||||
- page_title "Spam Logs"
|
||||
%h3.page-title Spam Logs
|
||||
%hr
|
||||
- if @spam_logs.present?
|
||||
.table-holder
|
||||
%table.table
|
||||
%thead
|
||||
%tr
|
||||
%th Date
|
||||
%th User
|
||||
%th Source IP
|
||||
%th API?
|
||||
%th Type
|
||||
%th Title
|
||||
%th Description
|
||||
%th Primary Action
|
||||
%th
|
||||
= render @spam_logs
|
||||
= paginate @spam_logs
|
||||
- else
|
||||
%h4 There are no Spam Logs
|
||||
@@ -1,7 +1,13 @@
|
||||
%ul.nav-links
|
||||
= nav_link(page: dashboard_groups_path) do
|
||||
= link_to dashboard_groups_path, title: 'Your groups', data: {placement: 'right'} do
|
||||
Your Groups
|
||||
= nav_link(page: explore_groups_path) do
|
||||
= link_to explore_groups_path, title: 'Explore groups', data: {placement: 'bottom'} do
|
||||
Explore Groups
|
||||
.top-area
|
||||
%ul.nav-links
|
||||
= nav_link(page: dashboard_groups_path) do
|
||||
= link_to dashboard_groups_path, title: 'Your groups' do
|
||||
Your Groups
|
||||
= nav_link(page: explore_groups_path) do
|
||||
= link_to explore_groups_path, title: 'Explore groups' do
|
||||
Explore Groups
|
||||
- if current_user.can_create_group?
|
||||
.nav-controls
|
||||
= link_to new_group_path, class: "btn btn-new" do
|
||||
= icon('plus')
|
||||
New Group
|
||||
|
||||
@@ -8,13 +8,14 @@
|
||||
= nav_link(page: starred_dashboard_projects_path) do
|
||||
= link_to starred_dashboard_projects_path, title: 'Starred Projects', data: {placement: 'right'} do
|
||||
Starred Projects
|
||||
= nav_link(page: [explore_root_path, trending_explore_projects_path, starred_explore_projects_path, explore_projects_path], html_options: { class: 'hidden-xs' }) do
|
||||
= nav_link(page: [explore_root_path, trending_explore_projects_path, starred_explore_projects_path, explore_projects_path]) do
|
||||
= link_to explore_root_path, title: 'Explore', data: {placement: 'right'} do
|
||||
Explore Projects
|
||||
|
||||
.projects-search-form
|
||||
= search_field_tag :filter_projects, nil, placeholder: 'Filter by name...', class: 'projects-list-filter form-control hidden-xs', spellcheck: false
|
||||
.nav-controls
|
||||
= search_field_tag :filter_projects, nil, placeholder: 'Filter by name...', class: 'projects-list-filter form-control hidden-xs input-short', spellcheck: false
|
||||
= render 'explore/projects/dropdown'
|
||||
- if current_user.can_create_project?
|
||||
= link_to new_project_path, class: 'btn btn-green' do
|
||||
%i.fa.fa-plus
|
||||
= link_to new_project_path, class: 'btn btn-new' do
|
||||
= icon('plus')
|
||||
New Project
|
||||
|
||||
@@ -2,15 +2,6 @@
|
||||
- header_title "Groups", dashboard_groups_path
|
||||
= render 'dashboard/groups_head'
|
||||
|
||||
.gray-content-block
|
||||
- if current_user.can_create_group?
|
||||
%span.pull-right.hidden-xs
|
||||
= link_to new_group_path, class: "btn btn-new" do
|
||||
%i.fa.fa-plus
|
||||
New Group
|
||||
.oneline
|
||||
Group members have access to all group projects.
|
||||
|
||||
%ul.content-list
|
||||
- @group_members.each do |group_member|
|
||||
- group = group_member.group
|
||||
|
||||
@@ -16,8 +16,5 @@
|
||||
|
||||
= render 'shared/issuable/filter', type: :issues
|
||||
|
||||
.gray-content-block.second-block
|
||||
List all issues from all projects you have access to.
|
||||
|
||||
.prepend-top-default
|
||||
= render 'shared/issues'
|
||||
|
||||
@@ -7,8 +7,5 @@
|
||||
|
||||
= render 'shared/issuable/filter', type: :merge_requests
|
||||
|
||||
.gray-content-block.second-block
|
||||
List all merge requests from all projects you have access to.
|
||||
|
||||
.prepend-top-default
|
||||
= render 'shared/merge_requests'
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
- page_title "Milestones"
|
||||
- header_title "Milestones", dashboard_milestones_path
|
||||
|
||||
.project-issuable-filter
|
||||
.controls
|
||||
= render 'shared/new_project_item_select', path: 'milestones/new', label: "New Milestone", include_groups: true
|
||||
|
||||
.top-area
|
||||
= render 'shared/milestones_filter'
|
||||
|
||||
.gray-content-block
|
||||
List all milestones from all projects you have access to.
|
||||
.nav-controls
|
||||
= render 'shared/new_project_item_select', path: 'milestones/new', label: "New Milestone", include_groups: true
|
||||
|
||||
.milestones
|
||||
%ul.content-list
|
||||
|
||||
@@ -3,19 +3,13 @@
|
||||
%span.light
|
||||
- if @sort.present?
|
||||
= sort_options_hash[@sort]
|
||||
- elsif current_page?(trending_explore_projects_path) || current_page?(explore_root_path)
|
||||
Trending projects
|
||||
- elsif current_page?(starred_explore_projects_path)
|
||||
Most stars
|
||||
- else
|
||||
= sort_title_recently_created
|
||||
= sort_title_recently_updated
|
||||
%b.caret
|
||||
%ul.dropdown-menu
|
||||
%li
|
||||
= link_to trending_explore_projects_path do
|
||||
Trending projects
|
||||
= link_to starred_explore_projects_path do
|
||||
Most stars
|
||||
= link_to explore_projects_filter_path(sort: sort_value_name) do
|
||||
= sort_title_name
|
||||
= link_to explore_projects_filter_path(sort: sort_value_recently_created) do
|
||||
= sort_title_recently_created
|
||||
= link_to explore_projects_filter_path(sort: sort_value_oldest_created) do
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
= form_tag explore_projects_filter_path, method: :get, class: 'form-inline form-tiny' do |f|
|
||||
.form-group
|
||||
= search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input", id: "projects_search", spellcheck: false
|
||||
= hidden_field_tag :sort, @sort
|
||||
.form-group
|
||||
= button_tag 'Search', class: "btn"
|
||||
|
||||
@@ -46,4 +47,3 @@
|
||||
= link_to explore_projects_filter_path(tag: tag.name) do
|
||||
%i.fa.fa-tag
|
||||
= tag.name
|
||||
= render 'explore/projects/dropdown'
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
%ul.nav-links
|
||||
= nav_link(page: [trending_explore_projects_path, explore_root_path]) do
|
||||
= link_to trending_explore_projects_path do
|
||||
Trending
|
||||
= nav_link(page: starred_explore_projects_path) do
|
||||
= link_to starred_explore_projects_path do
|
||||
Most stars
|
||||
= nav_link(page: explore_projects_path) do
|
||||
= link_to explore_projects_path do
|
||||
All
|
||||
@@ -6,7 +6,11 @@
|
||||
- else
|
||||
= render 'explore/head'
|
||||
|
||||
.gray-content-block.clearfix.second-block
|
||||
.top-area
|
||||
= render 'explore/projects/nav'
|
||||
|
||||
.gray-content-block.second-block.clearfix
|
||||
= render 'filter'
|
||||
|
||||
= render 'projects', projects: @projects
|
||||
= paginate @projects, theme: "gitlab"
|
||||
|
||||
@@ -6,12 +6,6 @@
|
||||
- else
|
||||
= render 'explore/head'
|
||||
|
||||
.explore-trending-block
|
||||
.gray-content-block.second-block
|
||||
.pull-right
|
||||
= render 'explore/projects/dropdown'
|
||||
.oneline
|
||||
%i.fa.fa-star
|
||||
See most starred projects
|
||||
= render 'projects', projects: @starred_projects
|
||||
= paginate @starred_projects, theme: 'gitlab'
|
||||
= render 'explore/projects/nav'
|
||||
= render 'projects', projects: @starred_projects
|
||||
= paginate @starred_projects, theme: 'gitlab'
|
||||
|
||||
@@ -6,11 +6,5 @@
|
||||
- else
|
||||
= render 'explore/head'
|
||||
|
||||
.explore-trending-block
|
||||
.gray-content-block.second-block
|
||||
.pull-right
|
||||
= render 'explore/projects/dropdown'
|
||||
.oneline
|
||||
%i.fa.fa-comments-o
|
||||
See most discussed projects for last month
|
||||
= render 'projects', projects: @trending_projects
|
||||
= render 'explore/projects/nav'
|
||||
= render 'projects', projects: @trending_projects
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
.projects-list-holder
|
||||
.projects-search-form
|
||||
.input-group
|
||||
= search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control', spellcheck: false
|
||||
- if can? current_user, :create_projects, @group
|
||||
%span.input-group-btn
|
||||
= link_to new_project_path(namespace_id: @group.id), class: 'btn btn-green' do
|
||||
%i.fa.fa-plus
|
||||
New Project
|
||||
.projects-list-holder.prepend-top-default
|
||||
.input-group
|
||||
= search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control', spellcheck: false
|
||||
- if can? current_user, :create_projects, @group
|
||||
%span.input-group-btn
|
||||
= link_to new_project_path(namespace_id: @group.id), class: 'btn btn-new' do
|
||||
= icon('plus')
|
||||
New Project
|
||||
|
||||
= render 'shared/projects/list', projects: @projects, projects_limit: 20, stars: false, skip_namespace: true
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
- page_title "Milestones"
|
||||
- header_title group_title(@group, "Milestones", group_milestones_path(@group))
|
||||
|
||||
.project-issuable-filter
|
||||
.controls
|
||||
- if can?(current_user, :admin_milestones, @group)
|
||||
.pull-right
|
||||
%span.pull-right.hidden-xs
|
||||
= link_to new_group_milestone_path(@group), class: "btn btn-new" do
|
||||
= icon('plus')
|
||||
New Milestone
|
||||
|
||||
.top-area
|
||||
= render 'shared/milestones_filter'
|
||||
|
||||
.nav-controls
|
||||
- if can?(current_user, :admin_milestones, @group)
|
||||
= link_to new_group_milestone_path(@group), class: "btn btn-new" do
|
||||
= icon('plus')
|
||||
New Milestone
|
||||
|
||||
.gray-content-block
|
||||
Only milestones from
|
||||
%strong #{@group.name}
|
||||
|
||||
@@ -138,8 +138,32 @@
|
||||
|
||||
%h2#navs Navigation
|
||||
|
||||
%h4
|
||||
%code .top-area
|
||||
%p Holder for top page navigation. Includes navigation, search field, sorting and button
|
||||
|
||||
.example
|
||||
.top-area
|
||||
%ul.nav-links
|
||||
%li.active
|
||||
%a Open
|
||||
%li
|
||||
%a Closed
|
||||
.nav-controls
|
||||
= text_field_tag 'sample', nil, class: 'form-control'
|
||||
.dropdown
|
||||
%button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
|
||||
%span Sort by name
|
||||
%b.caret
|
||||
%ul.dropdown-menu
|
||||
%li
|
||||
%a Sort by date
|
||||
|
||||
= link_to 'New issue', '#', class: 'btn btn-new'
|
||||
|
||||
%h4
|
||||
%code .nav-links
|
||||
%p Only nav links without button and search
|
||||
.example
|
||||
%ul.nav-links
|
||||
%li.active
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
%ul.nav.nav-sidebar
|
||||
= nav_link(controller: :dashboard, html_options: {class: 'home'}) do
|
||||
= link_to admin_root_path, title: "Stats" do
|
||||
= link_to admin_root_path, title: 'Overview' do
|
||||
= icon('dashboard fw')
|
||||
%span
|
||||
Overview
|
||||
@@ -25,13 +25,13 @@
|
||||
%span
|
||||
Deploy Keys
|
||||
= nav_link path: ['runners#index', 'runners#show'] do
|
||||
= link_to admin_runners_path do
|
||||
= link_to admin_runners_path, title: 'Runners' do
|
||||
= icon('cog fw')
|
||||
%span
|
||||
Runners
|
||||
%span.count= number_with_delimiter(Ci::Runner.count(:all))
|
||||
= nav_link path: 'builds#index' do
|
||||
= link_to admin_builds_path do
|
||||
= link_to admin_builds_path, title: 'Builds' do
|
||||
= icon('link fw')
|
||||
%span
|
||||
Builds
|
||||
@@ -82,6 +82,14 @@
|
||||
Abuse Reports
|
||||
%span.count= number_with_delimiter(AbuseReport.count(:all))
|
||||
|
||||
- if askimet_enabled?
|
||||
= nav_link(controller: :spam_logs) do
|
||||
= link_to admin_spam_logs_path, title: "Spam Logs" do
|
||||
= icon('exclamation-triangle fw')
|
||||
%span
|
||||
Spam Logs
|
||||
%span.count= number_with_delimiter(SpamLog.count(:all))
|
||||
|
||||
= nav_link(controller: :application_settings, html_options: { class: 'separate-item'}) do
|
||||
= link_to admin_application_settings_path, title: 'Settings' do
|
||||
= icon('cogs fw')
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
.file-content.image_file
|
||||
%img{ src: "data:#{blob.mime_type};base64,#{Base64.encode64(blob.data)}"}
|
||||
%img{ src: namespace_project_raw_path(@project.namespace, @project, @id)}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
- blob.load_all_data!(@repository)
|
||||
- if markup?(blob.name)
|
||||
.file-content.wiki
|
||||
= render_markup(blob.name, blob.data)
|
||||
|
||||
@@ -1,18 +1,7 @@
|
||||
- page_title "Builds"
|
||||
= render "header_title"
|
||||
|
||||
.project-issuable-filter
|
||||
.controls
|
||||
- if can?(current_user, :update_build, @project)
|
||||
.pull-left.hidden-xs
|
||||
- if @all_builds.running_or_pending.any?
|
||||
= link_to 'Cancel running', cancel_all_namespace_project_builds_path(@project.namespace, @project),
|
||||
data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post
|
||||
|
||||
= link_to ci_lint_path, class: 'btn btn-default' do
|
||||
= icon('wrench')
|
||||
%span CI Lint
|
||||
|
||||
.top-area
|
||||
%ul.nav-links
|
||||
%li{class: ('active' if @scope.nil?)}
|
||||
= link_to project_builds_path(@project) do
|
||||
@@ -32,6 +21,16 @@
|
||||
%span.badge.js-running-count
|
||||
= number_with_delimiter(@all_builds.finished.count(:id))
|
||||
|
||||
.nav-controls
|
||||
- if can?(current_user, :update_build, @project)
|
||||
- if @all_builds.running_or_pending.any?
|
||||
= link_to 'Cancel running', cancel_all_namespace_project_builds_path(@project.namespace, @project),
|
||||
data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post
|
||||
|
||||
= link_to ci_lint_path, class: 'btn btn-default' do
|
||||
= icon('wrench')
|
||||
%span CI Lint
|
||||
|
||||
.gray-content-block
|
||||
#{(@scope || 'running').capitalize} builds from this project
|
||||
|
||||
|
||||
@@ -1,44 +1,42 @@
|
||||
.gray-content-block.top-block.clearfix.white.forks-top-block
|
||||
.pull-left
|
||||
.top-area
|
||||
.nav-text
|
||||
- public_count = @public_forks.size
|
||||
- protected_count = @protected_forks.size
|
||||
- full_count_title = "#{public_count} public and #{protected_count} private"
|
||||
== #{pluralize(@all_forks.size, 'fork')}: #{full_count_title}
|
||||
|
||||
.pull-right
|
||||
.projects-search-form.fork-search-form
|
||||
= search_field_tag :filter_projects, nil, placeholder: 'Search forks', class: 'projects-list-filter form-control',
|
||||
spellcheck: false, data: { 'filter-selector' => 'span.namespace-name' }
|
||||
.nav-controls
|
||||
= search_field_tag :filter_projects, nil, placeholder: 'Search forks', class: 'projects-list-filter form-control input-short',
|
||||
spellcheck: false, data: { 'filter-selector' => 'span.namespace-name' }
|
||||
|
||||
.dropdown.inline.prepend-left-10
|
||||
%button.dropdown-toggle.btn.sort-forks{type: 'button', 'data-toggle' => 'dropdown'}
|
||||
%span.light sort:
|
||||
- if @sort.present?
|
||||
= sort_options_hash[@sort]
|
||||
- else
|
||||
= sort_title_recently_created
|
||||
%b.caret
|
||||
%ul.dropdown-menu.dropdown-menu-align-right
|
||||
%li
|
||||
- excluded_filters = [:state, :scope, :label_name, :milestone_id, :assignee_id, :author_id]
|
||||
= link_to page_filter_path(sort: sort_value_recently_created, without: excluded_filters) do
|
||||
= sort_title_recently_created
|
||||
= link_to page_filter_path(sort: sort_value_oldest_created, without: excluded_filters) do
|
||||
= sort_title_oldest_created
|
||||
= link_to page_filter_path(sort: sort_value_recently_updated, without: excluded_filters) do
|
||||
= sort_title_recently_updated
|
||||
= link_to page_filter_path(sort: sort_value_oldest_updated, without: excluded_filters) do
|
||||
= sort_title_oldest_updated
|
||||
|
||||
.fork-link.inline
|
||||
- if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2
|
||||
= link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'pull-right btn btn-new' do
|
||||
= icon('code-fork fw')
|
||||
Fork
|
||||
.dropdown
|
||||
%button.dropdown-toggle.btn.sort-forks{type: 'button', 'data-toggle' => 'dropdown'}
|
||||
%span.light sort:
|
||||
- if @sort.present?
|
||||
= sort_options_hash[@sort]
|
||||
- else
|
||||
= link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'pull-right btn btn-new' do
|
||||
= icon('code-fork fw')
|
||||
Fork
|
||||
= sort_title_recently_created
|
||||
%b.caret
|
||||
%ul.dropdown-menu.dropdown-menu-align-right
|
||||
%li
|
||||
- excluded_filters = [:state, :scope, :label_name, :milestone_id, :assignee_id, :author_id]
|
||||
= link_to page_filter_path(sort: sort_value_recently_created, without: excluded_filters) do
|
||||
= sort_title_recently_created
|
||||
= link_to page_filter_path(sort: sort_value_oldest_created, without: excluded_filters) do
|
||||
= sort_title_oldest_created
|
||||
= link_to page_filter_path(sort: sort_value_recently_updated, without: excluded_filters) do
|
||||
= sort_title_recently_updated
|
||||
= link_to page_filter_path(sort: sort_value_oldest_updated, without: excluded_filters) do
|
||||
= sort_title_oldest_updated
|
||||
|
||||
- if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2
|
||||
= link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn btn-new' do
|
||||
= icon('code-fork fw')
|
||||
Fork
|
||||
- else
|
||||
= link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn btn-new' do
|
||||
= icon('code-fork fw')
|
||||
Fork
|
||||
|
||||
|
||||
.projects-list-holder
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
- page_title "Labels"
|
||||
= render "header_title"
|
||||
|
||||
.gray-content-block.top-block
|
||||
- if can? current_user, :admin_label, @project
|
||||
= link_to new_namespace_project_label_path(@project.namespace, @project), class: "pull-right btn btn-new" do
|
||||
= icon('plus')
|
||||
New label
|
||||
.oneline
|
||||
.top-area
|
||||
.nav-text
|
||||
Labels can be applied to issues and merge requests.
|
||||
.nav-controls
|
||||
- if can? current_user, :admin_label, @project
|
||||
= link_to new_namespace_project_label_path(@project.namespace, @project), class: "btn btn-new" do
|
||||
= icon('plus')
|
||||
New label
|
||||
|
||||
.labels
|
||||
- if @labels.present?
|
||||
|
||||
@@ -2,17 +2,14 @@
|
||||
= render "header_title"
|
||||
|
||||
|
||||
.project-issuable-filter
|
||||
.controls
|
||||
- if can?(current_user, :admin_milestone, @project)
|
||||
= link_to new_namespace_project_milestone_path(@project.namespace, @project), class: "pull-right btn btn-new", title: "New Milestone" do
|
||||
%i.fa.fa-plus
|
||||
New Milestone
|
||||
|
||||
.top-area
|
||||
= render 'shared/milestones_filter'
|
||||
|
||||
.gray-content-block
|
||||
Milestone allows you to group issues and set due date for it
|
||||
.nav-controls
|
||||
- if can?(current_user, :admin_milestone, @project)
|
||||
= link_to new_namespace_project_milestone_path(@project.namespace, @project), class: "btn btn-new", title: "New Milestone" do
|
||||
= icon('plus')
|
||||
New Milestone
|
||||
|
||||
.milestones
|
||||
%ul.content-list
|
||||
|
||||
@@ -1,12 +1,4 @@
|
||||
.project-issuable-filter
|
||||
.controls
|
||||
- if can?(current_user, :create_wiki, @project)
|
||||
= link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do
|
||||
%i.fa.fa-plus
|
||||
New Page
|
||||
|
||||
= render 'projects/wikis/new'
|
||||
|
||||
.top-area
|
||||
%ul.nav-links
|
||||
= nav_link(html_options: {class: params[:id] == 'home' ? 'active' : '' }) do
|
||||
= link_to 'Home', namespace_project_wiki_path(@project.namespace, @project, :home)
|
||||
@@ -17,3 +9,11 @@
|
||||
= nav_link(path: 'wikis#git_access') do
|
||||
= link_to namespace_project_wikis_git_access_path(@project.namespace, @project) do
|
||||
Git Access
|
||||
|
||||
.nav-controls
|
||||
- if can?(current_user, :create_wiki, @project)
|
||||
= link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do
|
||||
= icon('plus')
|
||||
New Page
|
||||
|
||||
= render 'projects/wikis/new'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
.file-content.code.js-syntax-highlight
|
||||
.line-numbers
|
||||
- if blob.data.present?
|
||||
- blob.data.lines.each_index do |index|
|
||||
- blob.data.each_line.each_with_index do |_, index|
|
||||
- offset = defined?(first_line_number) ? first_line_number : 1
|
||||
- i = index + offset
|
||||
-# We're not using `link_to` because it is too slow once we get to thousands of lines.
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
.milestones-filters
|
||||
%ul.nav-links
|
||||
%li{class: ("active" if params[:state].blank? || params[:state] == 'opened')}
|
||||
= link_to milestones_filter_path(state: 'opened') do
|
||||
Open
|
||||
%li{class: ("active" if params[:state] == 'closed')}
|
||||
= link_to milestones_filter_path(state: 'closed') do
|
||||
Closed
|
||||
%li{class: ("active" if params[:state] == 'all')}
|
||||
= link_to milestones_filter_path(state: 'all') do
|
||||
All
|
||||
%ul.nav-links
|
||||
%li{class: ("active" if params[:state].blank? || params[:state] == 'opened')}
|
||||
= link_to milestones_filter_path(state: 'opened') do
|
||||
Open
|
||||
%li{class: ("active" if params[:state] == 'closed')}
|
||||
= link_to milestones_filter_path(state: 'closed') do
|
||||
Closed
|
||||
%li{class: ("active" if params[:state] == 'all')}
|
||||
= link_to milestones_filter_path(state: 'all') do
|
||||
All
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
- if @projects.any?
|
||||
.prepend-left-10.new-project-item-select-holder
|
||||
= project_select_tag :project_path, class: "new-project-item-select", data: { include_groups: local_assigns[:include_groups] }
|
||||
.prepend-left-10.project-item-select-holder
|
||||
= project_select_tag :project_path, class: "project-item-select", data: { include_groups: local_assigns[:include_groups], order_by: 'last_activity_at' }
|
||||
%a.btn.btn-new.new-project-item-select-button
|
||||
= icon('plus')
|
||||
= local_assigns[:label]
|
||||
@@ -8,12 +8,12 @@
|
||||
|
||||
:javascript
|
||||
$('.new-project-item-select-button').on('click', function() {
|
||||
$('.new-project-item-select').select2('open');
|
||||
$('.project-item-select').select2('open');
|
||||
});
|
||||
|
||||
var relativePath = '#{local_assigns[:path]}';
|
||||
|
||||
$('.new-project-item-select').on('click', function() {
|
||||
$('.project-item-select').on('click', function() {
|
||||
window.location = $(this).val() + '/' + relativePath;
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
- group_member = local_assigns[:group_member]
|
||||
%li
|
||||
- css_class = '' unless local_assigns[:css_class]
|
||||
- css_class += " no-description" if group.description.blank?
|
||||
|
||||
%li.group-row{ class: css_class }
|
||||
- if group_member
|
||||
.controls.hidden-xs
|
||||
- if can?(current_user, :admin_group, group)
|
||||
@@ -9,6 +12,15 @@
|
||||
= link_to leave_group_group_members_path(group), data: { confirm: leave_group_message(group.name) }, method: :delete, class: "btn-sm btn btn-grouped", title: 'Leave this group' do
|
||||
%i.fa.fa-sign-out
|
||||
|
||||
.stats
|
||||
%span
|
||||
= icon('home')
|
||||
= number_with_delimiter(group.projects.count)
|
||||
|
||||
%span
|
||||
= icon('users')
|
||||
= number_with_delimiter(group.users.count)
|
||||
|
||||
= image_tag group_icon(group), class: "avatar s46 hidden-xs"
|
||||
= link_to group, class: 'group-name' do
|
||||
%span.item-title= group.name
|
||||
@@ -17,5 +29,6 @@
|
||||
as
|
||||
%span #{group_member.human_access}
|
||||
|
||||
%div.light
|
||||
#{pluralize(group.projects.count, "project")}, #{pluralize(group.users.count, "user")}
|
||||
- if group.description.present?
|
||||
.light
|
||||
= markdown(group.description, pipeline: :description)
|
||||
|
||||
@@ -29,8 +29,8 @@
|
||||
|
||||
.project-controls
|
||||
- if ci_commit
|
||||
= render_ci_status(ci_commit)
|
||||
|
||||
%span
|
||||
= render_ci_status(ci_commit)
|
||||
- if forks
|
||||
%span
|
||||
= icon('code-fork')
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
<%= ENV['RAILS_ENV'] %>:
|
||||
## Connection information
|
||||
# Please be aware that the DATABASE_URL environment variable will take
|
||||
# precedence over the following 6 parameters. For more information, see
|
||||
# doc/administration/environment_variables.md
|
||||
adapter: <%= ENV['GITLAB_DATABASE_ADAPTER'] || 'postgresql' %>
|
||||
encoding: <%= ENV['GITLAB_DATABASE_ENCODING'] || 'unicode' %>
|
||||
database: <%= ENV['GITLAB_DATABASE_DATABASE'] || "gitlab_#{ENV['RAILS_ENV']}" %>
|
||||
pool: <%= ENV['GITLAB_DATABASE_POOL'] || '10' %>
|
||||
username: <%= ENV['GITLAB_DATABASE_USERNAME'] || 'root' %>
|
||||
password: <%= ENV['GITLAB_DATABASE_PASSWORD'] || '' %>
|
||||
host: <%= ENV['GITLAB_DATABASE_HOST'] || 'localhost' %>
|
||||
port: <%= ENV['GITLAB_DATABASE_PORT'] || '5432' %>
|
||||
|
||||
## Behavior information
|
||||
# The following parameters will be used even if you're using the DATABASE_URL
|
||||
# environment variable.
|
||||
encoding: <%= ENV['GITLAB_DATABASE_ENCODING'] || 'unicode' %>
|
||||
pool: <%= ENV['GITLAB_DATABASE_POOL'] || '10' %>
|
||||
|
||||
@@ -49,12 +49,14 @@ if Gitlab::Metrics.enabled?
|
||||
config.instrument_instance_methods(Gitlab::Shell)
|
||||
|
||||
config.instrument_methods(Gitlab::Git)
|
||||
config.instrument_instance_methods(Gitlab::Git::Repository)
|
||||
|
||||
Gitlab::Git.constants.each do |name|
|
||||
const = Gitlab::Git.const_get(name)
|
||||
|
||||
config.instrument_methods(const) if const.is_a?(Module)
|
||||
next unless const.is_a?(Module)
|
||||
|
||||
config.instrument_methods(const)
|
||||
config.instrument_instance_methods(const)
|
||||
end
|
||||
|
||||
Dir[Rails.root.join('app', 'finders', '*.rb')].each do |path|
|
||||
@@ -62,6 +64,16 @@ if Gitlab::Metrics.enabled?
|
||||
|
||||
config.instrument_instance_methods(const)
|
||||
end
|
||||
|
||||
[
|
||||
:Blame, :Branch, :BranchCollection, :Blob, :Commit, :Diff, :Repository,
|
||||
:Tag, :TagCollection, :Tree
|
||||
].each do |name|
|
||||
const = Rugged.const_get(name)
|
||||
|
||||
config.instrument_methods(const)
|
||||
config.instrument_instance_methods(const)
|
||||
end
|
||||
end
|
||||
|
||||
GC::Profiler.enable
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
# New Relic configuration file
|
||||
#
|
||||
# This file is here to make sure the New Relic gem stays
|
||||
# quiet by default.
|
||||
#
|
||||
# To enable and configure New Relic, please use
|
||||
# environment variables, e.g. NEW_RELIC_ENABLED=true
|
||||
|
||||
production:
|
||||
enabled: false
|
||||
|
||||
development:
|
||||
enabled: false
|
||||
|
||||
test:
|
||||
enabled: false
|
||||
@@ -211,6 +211,8 @@ Rails.application.routes.draw do
|
||||
end
|
||||
|
||||
resources :abuse_reports, only: [:index, :destroy]
|
||||
resources :spam_logs, only: [:index, :destroy]
|
||||
|
||||
resources :applications
|
||||
|
||||
resources :groups, constraints: { id: /[^\/]+/ } do
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
class AddAkismetToApplicationSettings < ActiveRecord::Migration
|
||||
def change
|
||||
change_table :application_settings do |t|
|
||||
t.boolean :akismet_enabled, default: false
|
||||
t.string :akismet_api_key
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,16 @@
|
||||
class CreateSpamLogs < ActiveRecord::Migration
|
||||
def change
|
||||
create_table :spam_logs do |t|
|
||||
t.integer :user_id
|
||||
t.string :source_ip
|
||||
t.string :user_agent
|
||||
t.boolean :via_api
|
||||
t.integer :project_id
|
||||
t.string :noteable_type
|
||||
t.string :title
|
||||
t.text :description
|
||||
|
||||
t.timestamps null: false
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -64,6 +64,8 @@ ActiveRecord::Schema.define(version: 20160202164642) do
|
||||
t.integer "metrics_sample_interval", default: 15
|
||||
t.boolean "sentry_enabled", default: false
|
||||
t.string "sentry_dsn"
|
||||
t.boolean "akismet_enabled", default: false
|
||||
t.string "akismet_api_key"
|
||||
end
|
||||
|
||||
create_table "audit_events", force: :cascade do |t|
|
||||
@@ -771,6 +773,19 @@ ActiveRecord::Schema.define(version: 20160202164642) do
|
||||
add_index "snippets", ["project_id"], name: "index_snippets_on_project_id", using: :btree
|
||||
add_index "snippets", ["visibility_level"], name: "index_snippets_on_visibility_level", using: :btree
|
||||
|
||||
create_table "spam_logs", force: :cascade do |t|
|
||||
t.integer "user_id"
|
||||
t.string "source_ip"
|
||||
t.string "user_agent"
|
||||
t.boolean "via_api"
|
||||
t.integer "project_id"
|
||||
t.string "noteable_type"
|
||||
t.string "title"
|
||||
t.text "description"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
end
|
||||
|
||||
create_table "subscriptions", force: :cascade do |t|
|
||||
t.integer "user_id"
|
||||
t.integer "subscribable_id"
|
||||
|
||||
@@ -1,56 +1,61 @@
|
||||
# Environment Variables
|
||||
|
||||
## Introduction
|
||||
GitLab exposes certain environment variables which can be used to override
|
||||
their defaults values.
|
||||
|
||||
Commonly people configure GitLab via the gitlab.rb configuration file in the Omnibus package.
|
||||
People usually configure GitLab via `/etc/gitlab/gitlab.rb` for Omnibus
|
||||
installations, or `gitlab.yml` for installations from source.
|
||||
|
||||
But if you prefer to use environment variables we allow that too.
|
||||
Below you will find the supported environment variables which you can use to
|
||||
override certain values.
|
||||
|
||||
## Supported environment variables
|
||||
|
||||
Variable | Type | Explanation
|
||||
Variable | Type | Description
|
||||
-------- | ---- | -----------
|
||||
GITLAB_ROOT_PASSWORD | string | sets the password for the `root` user on installation
|
||||
GITLAB_HOST | url | hostname of the GitLab server includes http or https
|
||||
RAILS_ENV | production / development / staging / test | Rails environment
|
||||
DATABASE_URL | url | For example: postgresql://localhost/blog_development?pool=5
|
||||
GITLAB_EMAIL_FROM | email | Email address used in the "From" field in mails sent by GitLab
|
||||
GITLAB_EMAIL_DISPLAY_NAME | string | Name used in the "From" field in mails sent by GitLab
|
||||
GITLAB_EMAIL_REPLY_TO | email | Email address used in the "Reply-To" field in mails sent by GitLab
|
||||
GITLAB_UNICORN_MEMORY_MIN | integer | The minimum memory threshold (in bytes) for the Unicorn worker killer
|
||||
GITLAB_UNICORN_MEMORY_MAX | integer | The maximum memory threshold (in bytes) for the Unicorn worker killer
|
||||
`GITLAB_ROOT_PASSWORD` | string | Sets the password for the `root` user on installation
|
||||
`GITLAB_HOST` | string | The full URL of the GitLab server (including `http://` or `https://`)
|
||||
`RAILS_ENV` | string | The Rails environment; can be one of `production`, `development`, `staging` or `test`
|
||||
`DATABASE_URL` | string | The database URL; is of the form: `postgresql://localhost/blog_development`
|
||||
`GITLAB_EMAIL_FROM` | string | The e-mail address used in the "From" field in e-mails sent by GitLab
|
||||
`GITLAB_EMAIL_DISPLAY_NAME` | string | The name used in the "From" field in e-mails sent by GitLab
|
||||
`GITLAB_EMAIL_REPLY_TO` | string | The e-mail address used in the "Reply-To" field in e-mails sent by GitLab
|
||||
`GITLAB_UNICORN_MEMORY_MIN` | integer | The minimum memory threshold (in bytes) for the Unicorn worker killer
|
||||
`GITLAB_UNICORN_MEMORY_MAX` | integer | The maximum memory threshold (in bytes) for the Unicorn worker killer
|
||||
|
||||
## Complete database variables
|
||||
|
||||
As explained in the [Heroku documentation](https://devcenter.heroku.com/articles/rails-database-connection-behavior) the DATABASE_URL doesn't let you set:
|
||||
The recommended way of specifying your database connection information is to set
|
||||
the `DATABASE_URL` environment variable. This variable only holds connection
|
||||
information (`adapter`, `database`, `username`, `password`, `host` and `port`),
|
||||
but not behavior information (`encoding`, `pool`). If you don't want to use
|
||||
`DATABASE_URL` and/or want to set database behavior information, you will have
|
||||
to either:
|
||||
|
||||
- adapter
|
||||
- database
|
||||
- username
|
||||
- password
|
||||
- host
|
||||
- port
|
||||
- copy our template file: `cp config/database.yml.env config/database.yml`, or
|
||||
- set a value for some `GITLAB_DATABASE_XXX` variables
|
||||
|
||||
To do so please `cp config/database.yml.env config/database.yml` and use the following variables:
|
||||
The list of `GITLAB_DATABASE_XXX` variables that you can set is:
|
||||
|
||||
Variable | Default
|
||||
--- | ---
|
||||
GITLAB_DATABASE_ADAPTER | postgresql
|
||||
GITLAB_DATABASE_ENCODING | unicode
|
||||
GITLAB_DATABASE_DATABASE | gitlab_#{ENV['RAILS_ENV']
|
||||
GITLAB_DATABASE_POOL | 10
|
||||
GITLAB_DATABASE_USERNAME | root
|
||||
GITLAB_DATABASE_PASSWORD |
|
||||
GITLAB_DATABASE_HOST | localhost
|
||||
GITLAB_DATABASE_PORT | 5432
|
||||
Variable | Default value | Overridden by `DATABASE_URL`?
|
||||
-------- | ------------- | -----------------------------
|
||||
`GITLAB_DATABASE_ADAPTER` | `postgresql` (for MySQL use `mysql2`) | Yes
|
||||
`GITLAB_DATABASE_DATABASE` | `gitlab_#{ENV['RAILS_ENV']` | Yes
|
||||
`GITLAB_DATABASE_USERNAME` | `root` | Yes
|
||||
`GITLAB_DATABASE_PASSWORD` | None | Yes
|
||||
`GITLAB_DATABASE_HOST` | `localhost` | Yes
|
||||
`GITLAB_DATABASE_PORT` | `5432` | Yes
|
||||
`GITLAB_DATABASE_ENCODING` | `unicode` | No
|
||||
`GITLAB_DATABASE_POOL` | `10` | No
|
||||
|
||||
## Adding more variables
|
||||
|
||||
We welcome merge requests to make more settings configurable via variables.
|
||||
Please make changes in the file config/initializers/1_settings.rb
|
||||
Please stick to the naming scheme "GITLAB_#{name 1_settings.rb in upper case}".
|
||||
Please make changes in the `config/initializers/1_settings.rb` file and stick
|
||||
to the naming scheme `GITLAB_#{name in 1_settings.rb in upper case}`.
|
||||
|
||||
## Omnibus configuration
|
||||
|
||||
It's possible to preconfigure the GitLab image by adding the environment variable: `GITLAB_OMNIBUS_CONFIG` to docker run command.
|
||||
It's possible to preconfigure the GitLab docker image by adding the environment
|
||||
variable `GITLAB_OMNIBUS_CONFIG` to the `docker run` command.
|
||||
For more information see the ['preconfigure-docker-container' section in the Omnibus documentation](http://doc.gitlab.com/omnibus/docker/#preconfigure-docker-container).
|
||||
|
||||
@@ -18,7 +18,7 @@ GET /ci/projects
|
||||
Returns:
|
||||
|
||||
```json
|
||||
[
|
||||
[
|
||||
{
|
||||
"id" : 271,
|
||||
"name" : "gitlabhq",
|
||||
|
||||
@@ -97,7 +97,7 @@ image: php:5.6
|
||||
|
||||
before_script:
|
||||
# Install dependencies
|
||||
- ci/docker_install.sh > /dev/null
|
||||
- bash ci/docker_install.sh > /dev/null
|
||||
|
||||
test:app:
|
||||
script:
|
||||
@@ -112,7 +112,7 @@ with a different docker image version and the runner will do the rest:
|
||||
```yaml
|
||||
before_script:
|
||||
# Install dependencies
|
||||
- ci/docker_install.sh > /dev/null
|
||||
- bash ci/docker_install.sh > /dev/null
|
||||
|
||||
# We test PHP5.6
|
||||
test:5.6:
|
||||
|
||||
@@ -393,8 +393,12 @@ The above script will:
|
||||
|
||||
### artifacts
|
||||
|
||||
_**Note:** Introduced in GitLab Runner v0.7.0. Also, the Windows shell executor
|
||||
does not currently support artifact uploads._
|
||||
_**Note:** Introduced in GitLab Runner v0.7.0 for non-Windows platforms._
|
||||
|
||||
_**Note:** Limited Windows support was added in GitLab Runner v.1.0.0.
|
||||
Currently not all executors are supported._
|
||||
|
||||
_**Note:** Build artifacts are only collected for successful builds._
|
||||
|
||||
`artifacts` is used to specify list of files and directories which should be
|
||||
attached to build after success. Below are some examples.
|
||||
|
||||
@@ -355,7 +355,7 @@ GitLab Shell is an SSH access and repository management software developed speci
|
||||
cd /home/git
|
||||
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-workhorse.git
|
||||
cd gitlab-workhorse
|
||||
sudo -u git -H git checkout 0.6.2
|
||||
sudo -u git -H git checkout 0.6.3
|
||||
sudo -u git -H make
|
||||
|
||||
### Initialize Database and Activate Advanced Features
|
||||
|
||||
@@ -15,6 +15,7 @@ See the documentation below for details on how to configure these services.
|
||||
- [OAuth2 provider](oauth_provider.md) OAuth2 application creation
|
||||
- [Gmail actions buttons](gmail_action_buttons_for_gitlab.md) Adds GitLab actions to messages
|
||||
- [reCAPTCHA](recaptcha.md) Configure GitLab to use Google reCAPTCHA for new users
|
||||
- [Akismet](akismet.md) Configure Akismet to stop spam
|
||||
|
||||
GitLab Enterprise Edition contains [advanced Jenkins support][jenkins].
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
# Akismet
|
||||
|
||||
GitLab leverages [Akismet](http://akismet.com) to protect against spam. Currently
|
||||
GitLab uses Akismet to prevent users who are not members of a project from
|
||||
creating spam via the GitLab API. Detected spam will be rejected, and
|
||||
an entry in the "Spam Log" section in the Admin page will be created.
|
||||
|
||||
Privacy note: GitLab submits the user's IP and user agent to Akismet. Note that
|
||||
adding a user to a project will disable the Akismet check and prevent this
|
||||
from happening.
|
||||
|
||||
## Configuration
|
||||
|
||||
To use Akismet:
|
||||
|
||||
1. Go to the URL: https://akismet.com/account/
|
||||
|
||||
2. Sign-in or create a new account.
|
||||
|
||||
3. Click on "Show" to reveal the API key.
|
||||
|
||||
4. Go to Applications Settings on Admin Area (`admin/application_settings`)
|
||||
|
||||
5. Check the `Enable Akismet` checkbox
|
||||
|
||||
6. Fill in the API key from step 3.
|
||||
|
||||
7. Save the configuration.
|
||||
|
||||

|
||||
|
After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 40 KiB |
@@ -1,35 +1,80 @@
|
||||
## GitLab as OAuth2 authentication service provider
|
||||
# GitLab as OAuth2 authentication service provider
|
||||
|
||||
This document is about using GitLab as an OAuth authentication service provider to sign into other services.
|
||||
If you want to use other OAuth authentication service providers to sign into GitLab please see the [OAuth2 client documentation](../api/oauth2.md)
|
||||
This document is about using GitLab as an OAuth authentication service provider
|
||||
to sign in to other services.
|
||||
|
||||
OAuth2 provides client applications a 'secure delegated access' to server resources on behalf of a resource owner. Or you can allow users to sign in to your application with their GitLab.com account.
|
||||
In fact OAuth allows to issue access token to third-party clients by an authorization server,
|
||||
with the approval of the resource owner, or end-user.
|
||||
Mostly, OAuth2 is using for SSO (Single sign-on). But you can find a lot of different usages for this functionality.
|
||||
For example, our feature 'GitLab Importer' is using OAuth protocol to give an access to repositories without sharing user credentials to GitLab.com account.
|
||||
Also GitLab.com application can be used for authentication to your GitLab instance if needed [GitLab OmniAuth](gitlab.md).
|
||||
If you want to use other OAuth authentication service providers to sign in to
|
||||
GitLab, please see the [OAuth2 client documentation](../api/oauth2.md).
|
||||
|
||||
GitLab has two ways to add new OAuth2 application to an instance, you can add application as regular user and through admin area. So GitLab actually can have an instance-wide and a user-wide applications. There is no defferences between them except the different permission levels.
|
||||
## Introduction to OAuth
|
||||
|
||||
### Adding application through profile
|
||||
Go to your profile section 'Application' and press button 'New Application'
|
||||
[OAuth] provides to client applications a 'secure delegated access' to server
|
||||
resources on behalf of a resource owner. In fact, OAuth allows an authorization
|
||||
server to issue access tokens to third-party clients with the approval of the
|
||||
resource owner, or the end-user.
|
||||
|
||||

|
||||
OAuth is mostly used as a Single Sign-On service (SSO), but you can find a
|
||||
lot of different uses for this functionality. For example, you can allow users
|
||||
to sign in to your application with their GitLab.com account, or GitLab.com
|
||||
can be used for authentication to your GitLab instance
|
||||
(see [GitLab OmniAuth](gitlab.md)).
|
||||
|
||||
After this you will see application form, where "Name" is arbitrary name, "Redirect URI" is URL in your app where users will be sent after authorization on GitLab.com.
|
||||
The 'GitLab Importer' feature is also using the OAuth protocol to give access
|
||||
to repositories without sharing user credentials to your GitLab.com account.
|
||||
|
||||

|
||||
---
|
||||
|
||||
### Authorized application
|
||||
Every application you authorized will be shown in your "Authorized application" sections.
|
||||
GitLab supports two ways of adding a new OAuth2 application to an instance. You
|
||||
can either add an application as a regular user or add it in the admin area.
|
||||
What this means is that GitLab can actually have instance-wide and a user-wide
|
||||
applications. There is no difference between them except for the different
|
||||
permission levels they are set (user/admin).
|
||||
|
||||

|
||||
## Adding an application through the profile
|
||||
|
||||
At any time you can revoke access just clicking button "Revoke"
|
||||
In order to add a new application via your profile, navigate to
|
||||
**Profile Settings > Applications** and select **New Application**.
|
||||
|
||||
### OAuth applications in admin area
|
||||

|
||||
|
||||
If you want to create application that does not belong to certain user you can create it from admin area
|
||||
---
|
||||
|
||||

|
||||
In the application form, enter a **Name** (arbitrary), and make sure to set up
|
||||
correctly the **Redirect URI** which is the URL where users will be sent after
|
||||
they authorize with GitLab.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
When you hit **Submit** you will be provided with the application ID and
|
||||
the application secret which you can then use with your application that
|
||||
connects to GitLab.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## OAuth applications in the admin area
|
||||
|
||||
To create an application that does not belong to a certain user, you can create
|
||||
it from the admin area.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## Authorized applications
|
||||
|
||||
Every application you authorized to use your GitLab credentials will be shown
|
||||
in the **Authorized applications** section under **Profile Settings > Applications**.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
As you can see, the default scope `api` is used, which is the only scope that
|
||||
GitLab supports so far. At any time you can revoke any access by just clicking
|
||||
**Revoke**.
|
||||
|
||||
[oauth]: http://oauth.net/2/ "OAuth website"
|
||||
|
||||
@@ -14,6 +14,12 @@ possible to edit the label text and color. The characters `?`, `&` and `,` are
|
||||
no longer allowed however so those will be removed from your tags during the
|
||||
database migrations for GitLab 7.2.
|
||||
|
||||
## Stash changes
|
||||
|
||||
If you [deleted the vendors folder during your original installation](https://github.com/gitlabhq/gitlabhq/issues/4883#issuecomment-31108431), [you will get an error](https://gitlab.com/gitlab-org/gitlab-ce/issues/1494) when you attempt to rebuild the assets in step 7. To avoid this, stash the changes in your GitLab working copy before starting:
|
||||
|
||||
git stash
|
||||
|
||||
## 0. Stop server
|
||||
|
||||
sudo service gitlab stop
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
Feature: Admin spam logs
|
||||
Background:
|
||||
Given I sign in as an admin
|
||||
And spam logs exist
|
||||
|
||||
Scenario: Browse spam logs
|
||||
When I visit spam logs page
|
||||
Then I should see list of spam logs
|
||||
@@ -0,0 +1,28 @@
|
||||
class Spinach::Features::AdminSpamLogs < Spinach::FeatureSteps
|
||||
include SharedAuthentication
|
||||
include SharedPaths
|
||||
include SharedAdmin
|
||||
|
||||
step 'I should see list of spam logs' do
|
||||
expect(page).to have_content('Spam Logs')
|
||||
expect(page).to have_content spam_log.source_ip
|
||||
expect(page).to have_content spam_log.noteable_type
|
||||
expect(page).to have_content 'N'
|
||||
expect(page).to have_content spam_log.title
|
||||
expect(page).to have_content truncate(spam_log.description)
|
||||
expect(page).to have_link('Remove user')
|
||||
expect(page).to have_link('Block user')
|
||||
end
|
||||
|
||||
step 'spam logs exist' do
|
||||
create(:spam_log)
|
||||
end
|
||||
|
||||
def spam_log
|
||||
@spam_log ||= SpamLog.first
|
||||
end
|
||||
|
||||
def truncate(description)
|
||||
"#{spam_log.description[0...97]}..."
|
||||
end
|
||||
end
|
||||
@@ -5,7 +5,7 @@ class Spinach::Features::ProjectBuildsSummary < Spinach::FeatureSteps
|
||||
include RepoHelpers
|
||||
|
||||
step 'I see button to CI Lint' do
|
||||
page.within('.controls') do
|
||||
page.within('.nav-controls') do
|
||||
ci_lint_tool_link = page.find_link('CI Lint')
|
||||
expect(ci_lint_tool_link[:href]).to eq ci_lint_path
|
||||
end
|
||||
|
||||
@@ -52,7 +52,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
|
||||
end
|
||||
|
||||
step 'I should see raw file content' do
|
||||
expect(source).to eq sample_blob.data
|
||||
expect(source).to eq '' # Body is filled in by gitlab-workhorse
|
||||
end
|
||||
|
||||
step 'I click button "Edit"' do
|
||||
|
||||
@@ -191,6 +191,10 @@ module SharedPaths
|
||||
visit admin_application_settings_path
|
||||
end
|
||||
|
||||
step 'I visit spam logs page' do
|
||||
visit admin_spam_logs_path
|
||||
end
|
||||
|
||||
step 'I visit applications page' do
|
||||
visit admin_applications_path
|
||||
end
|
||||
|
||||
@@ -58,9 +58,11 @@ module API
|
||||
commit = user_project.commit(ref)
|
||||
not_found! 'Commit' unless commit
|
||||
|
||||
blob = user_project.repository.blob_at(commit.sha, file_path)
|
||||
repo = user_project.repository
|
||||
blob = repo.blob_at(commit.sha, file_path)
|
||||
|
||||
if blob
|
||||
blob.load_all_data!(repo)
|
||||
status(200)
|
||||
|
||||
{
|
||||
@@ -72,7 +74,7 @@ module API
|
||||
ref: ref,
|
||||
blob_id: blob.id,
|
||||
commit_id: commit.id,
|
||||
last_commit_id: user_project.repository.last_commit_for_path(commit.sha, file_path).id
|
||||
last_commit_id: repo.last_commit_for_path(commit.sha, file_path).id
|
||||
}
|
||||
else
|
||||
not_found! 'File'
|
||||
|
||||
@@ -30,7 +30,7 @@ module API
|
||||
end
|
||||
|
||||
def sudo_identifier()
|
||||
identifier ||= params[SUDO_PARAM] ||= env[SUDO_HEADER]
|
||||
identifier ||= params[SUDO_PARAM] || env[SUDO_HEADER]
|
||||
|
||||
# Regex for integers
|
||||
if !!(identifier =~ /^[0-9]+$/)
|
||||
@@ -344,12 +344,22 @@ module API
|
||||
|
||||
def pagination_links(paginated_data)
|
||||
request_url = request.url.split('?').first
|
||||
request_params = params.clone
|
||||
request_params[:per_page] = paginated_data.limit_value
|
||||
|
||||
links = []
|
||||
links << %(<#{request_url}?page=#{paginated_data.current_page - 1}&per_page=#{paginated_data.limit_value}>; rel="prev") unless paginated_data.first_page?
|
||||
links << %(<#{request_url}?page=#{paginated_data.current_page + 1}&per_page=#{paginated_data.limit_value}>; rel="next") unless paginated_data.last_page?
|
||||
links << %(<#{request_url}?page=1&per_page=#{paginated_data.limit_value}>; rel="first")
|
||||
links << %(<#{request_url}?page=#{paginated_data.total_pages}&per_page=#{paginated_data.limit_value}>; rel="last")
|
||||
|
||||
request_params[:page] = paginated_data.current_page - 1
|
||||
links << %(<#{request_url}?#{request_params.to_query}>; rel="prev") unless paginated_data.first_page?
|
||||
|
||||
request_params[:page] = paginated_data.current_page + 1
|
||||
links << %(<#{request_url}?#{request_params.to_query}>; rel="next") unless paginated_data.last_page?
|
||||
|
||||
request_params[:page] = 1
|
||||
links << %(<#{request_url}?#{request_params.to_query}>; rel="first")
|
||||
|
||||
request_params[:page] = paginated_data.total_pages
|
||||
links << %(<#{request_url}?#{request_params.to_query}>; rel="last")
|
||||
|
||||
links.join(', ')
|
||||
end
|
||||
|
||||
@@ -3,6 +3,8 @@ module API
|
||||
class Issues < Grape::API
|
||||
before { authenticate! }
|
||||
|
||||
helpers ::Gitlab::AkismetHelper
|
||||
|
||||
helpers do
|
||||
def filter_issues_state(issues, state)
|
||||
case state
|
||||
@@ -19,6 +21,17 @@ module API
|
||||
def filter_issues_milestone(issues, milestone)
|
||||
issues.includes(:milestone).where('milestones.title' => milestone)
|
||||
end
|
||||
|
||||
def create_spam_log(project, current_user, attrs)
|
||||
params = attrs.merge({
|
||||
source_ip: env['REMOTE_ADDR'],
|
||||
user_agent: env['HTTP_USER_AGENT'],
|
||||
noteable_type: 'Issue',
|
||||
via_api: true
|
||||
})
|
||||
|
||||
::CreateSpamLogService.new(project, current_user, params).execute
|
||||
end
|
||||
end
|
||||
|
||||
resource :issues do
|
||||
@@ -114,7 +127,15 @@ module API
|
||||
render_api_error!({ labels: errors }, 400)
|
||||
end
|
||||
|
||||
issue = ::Issues::CreateService.new(user_project, current_user, attrs).execute
|
||||
project = user_project
|
||||
text = [attrs[:title], attrs[:description]].reject(&:blank?).join("\n")
|
||||
|
||||
if check_for_spam?(project, current_user) && is_spam?(env, current_user, text)
|
||||
create_spam_log(project, current_user, attrs)
|
||||
render_api_error!({ error: 'Spam detected' }, 400)
|
||||
end
|
||||
|
||||
issue = ::Issues::CreateService.new(project, current_user, attrs).execute
|
||||
|
||||
if issue.valid?
|
||||
# Find or create labels and attach to issue. Labels are valid because
|
||||
|
||||
@@ -57,7 +57,7 @@ module API
|
||||
not_found! "File" unless blob
|
||||
|
||||
content_type 'text/plain'
|
||||
present blob.data
|
||||
header *Gitlab::Workhorse.send_git_blob(repo, blob)
|
||||
end
|
||||
|
||||
# Get a raw blob contents by blob sha
|
||||
@@ -83,7 +83,7 @@ module API
|
||||
env['api.format'] = :txt
|
||||
|
||||
content_type blob.mime_type
|
||||
present blob.data
|
||||
header *Gitlab::Workhorse.send_git_blob(repo, blob)
|
||||
end
|
||||
|
||||
# Get a an archive of the repository
|
||||
|
||||
@@ -8,14 +8,7 @@ module Banzai
|
||||
# Extends HTML::Pipeline::SanitizationFilter with a custom whitelist.
|
||||
class SanitizationFilter < HTML::Pipeline::SanitizationFilter
|
||||
def whitelist
|
||||
# Descriptions are more heavily sanitized, allowing only a few elements.
|
||||
# See http://git.io/vkuAN
|
||||
if context[:inline_sanitization]
|
||||
whitelist = LIMITED
|
||||
whitelist[:elements] -= %w(pre code img ol ul li)
|
||||
else
|
||||
whitelist = super
|
||||
end
|
||||
whitelist = super
|
||||
|
||||
customize_whitelist(whitelist)
|
||||
|
||||
|
||||
@@ -4,9 +4,20 @@ module Banzai
|
||||
def self.transform_context(context)
|
||||
super(context).merge(
|
||||
# SanitizationFilter
|
||||
inline_sanitization: true
|
||||
whitelist: whitelist
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def self.whitelist
|
||||
# Descriptions are more heavily sanitized, allowing only a few elements.
|
||||
# See http://git.io/vkuAN
|
||||
whitelist = Banzai::Filter::SanitizationFilter::LIMITED
|
||||
whitelist[:elements] -= %w(pre code img ol ul li)
|
||||
|
||||
whitelist
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||