Merge branch 'master' of https://gitlab.com/gitlab-org/gitlab-ce into git-archive-refactor

This commit is contained in:
Jacob Vosmaer 2016-02-17 11:30:42 +01:00
commit a0ccb0731b
345 changed files with 5711 additions and 2083 deletions

View file

@ -5,6 +5,11 @@ services:
- postgres:latest
- redis:latest
cache:
key: "ruby22"
paths:
- vendor
variables:
MYSQL_ALLOW_EMPTY_PASSWORD: "1"
@ -137,23 +142,145 @@ bundler:audit:
# Ruby 2.1 jobs
spec:ruby21:
spec:feature:ruby21:
image: ruby:2.1
only:
- master
script:
- RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:feature
cache:
key: "ruby21"
paths:
- vendor
tags:
- ruby
- mysql
only:
- master
spinach:ruby21:
spec:api:ruby21:
image: ruby:2.1
only:
- master
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:api
cache:
key: "ruby21"
paths:
- vendor
tags:
- ruby
- mysql
spec:models:ruby21:
image: ruby:2.1
only:
- master
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:models
cache:
key: "ruby21"
paths:
- vendor
tags:
- ruby
- mysql
spec:lib:ruby21:
image: ruby:2.1
only:
- master
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:lib
cache:
key: "ruby21"
paths:
- vendor
tags:
- ruby
- mysql
spec:services:ruby21:
image: ruby:2.1
only:
- master
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:services
cache:
key: "ruby21"
paths:
- vendor
tags:
- ruby
- mysql
spec:benchmark:ruby21:
image: ruby:2.1
only:
- master
script:
- RAILS_ENV=test bundle exec rake spec:benchmark
cache:
key: "ruby21"
paths:
- vendor
tags:
- ruby
- mysql
allow_failure: true
spec:other:ruby21:
image: ruby:2.1
only:
- master
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:other
cache:
key: "ruby21"
paths:
- vendor
tags:
- ruby
- mysql
spinach:project:half:ruby21:
image: ruby:2.1
only:
- master
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:half
cache:
key: "ruby21"
paths:
- vendor
tags:
- ruby
- mysql
spinach:project:rest:ruby21:
image: ruby:2.1
only:
- master
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:rest
cache:
key: "ruby21"
paths:
- vendor
tags:
- ruby
- mysql
spinach:other:ruby21:
image: ruby:2.1
only:
- master
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:other
cache:
key: "ruby21"
paths:
- vendor
tags:
- ruby
- mysql

View file

@ -1,24 +1,73 @@
Please view this file on the master branch, on stable branches it's out of date.
v 8.5.0 (unreleased)
- Fix duplicate "me" in tooltip of the "thumbsup" awards Emoji (Stan Hu)
- Cache various Repository methods to improve performance (Yorick Peterse)
- Fix duplicated branch creation/deletion Web hooks/service notifications when using Web UI (Stan Hu)
- 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
- Add an option to supply root email through an environmental variable (Koichiro Mikami)
- 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
- Add option to include the sender name in body of Notify email (Jason Lee)
- New UI for pagination
- Don't prevent sign out when 2FA enforcement is enabled and user hasn't yet
set it up
- API: Added "merge_requests/:merge_request_id/closes_issues" (Gal Schlezinger)
- Fix diff comments loaded by AJAX to load comment with diff in discussion tab
- Fix relative links in other markup formats (Ben Boeckel)
- Whitelist raw "abbr" elements when parsing Markdown (Benedict Etzel)
- Fix label links for a merge request pointing to issues list
- Don't vendor minified JS
- Increase project import timeout to 15 minutes
- Be more permissive with email address validation: it only has to contain a single '@'
- Display 404 error on group not found
- Track project import failure
- Support Two-factor Authentication for LDAP users
- Display database type and version in Administration dashboard
- Allow limited Markdown in Broadcast Messages
- Fix visibility level text in admin area (Zeger-Jan van de Weg)
- Warn admin during OAuth of granting admin rights (Zeger-Jan van de Weg)
- Update the ExternalIssue regex pattern (Blake Hitchcock)
- Remember user's inline/side-by-side diff view preference in a cookie (Kirill Katsnelson)
- Optimized performance of finding issues to be closed by a merge request
- Add `avatar_url`, `description`, `git_ssh_url`, `git_http_url`, `path_with_namespace`
and `default_branch` in `project` in push, issue, merge-request and note webhooks data (Kirill Zaitsev)
- Deprecate the `ssh_url` in favor of `git_ssh_url` and `http_url` in favor of `git_http_url`
in `project` for push, issue, merge-request and note webhooks data (Kirill Zaitsev)
- Deprecate the `repository` key in push, issue, merge-request and note webhooks data, use `project` instead (Kirill Zaitsev)
- API: Expose MergeRequest#merge_status (Andrei Dziahel)
- Revert "Add IP check against DNSBLs at account sign-up"
- Actually use the `skip_merges` option in Repository#commits (Tony Chu)
- 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
- Prevent parse error when name of project ends with .atom and prevent path issues
- 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)
- API: Allow to set or update a merge-request's milestone (Kirill Skachkov)
- Improve UI consistency between projects and groups lists
- Add sort dropdown to dashboard projects page
- Fixed logo animation on Safari (Roman Rott)
- 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
- Don't process cross-reference notes from forks
- Fix: init.d script not working on OS X
- Faster snippet search
- Title for milestones should be unique (Zeger-Jan van de Weg)
- Validate correctness of maximum attachment size application setting
- Replaces "Create merge request" link with one to the "Merge Request" when one exists
- Fix CI builds badge, add a new link to builds badge, deprecate the old one
- Fix broken link to project in build notification emails
v 8.4.4
- Update omniauth-saml gem to 1.4.2
- Prevent long-running backup tasks from timing out the database connection
- Add a Project setting to allow guests to view build logs (defaults to true)
- Sort project milestones by due date including issue editor (Oliver Rogers / Orih)
v 8.4.3
- Increase lfs_objects size column to 8-byte integer to allow files larger
@ -27,6 +76,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
@ -46,7 +98,6 @@ v 8.4.1
and Nokogiri (1.6.7.2)
- Fix redirect loop during import
- Fix diff highlighting for all syntax themes
- Warn admin during OAuth of granting admin rights (Zeger-Jan van de Weg)
- Delete project and associations in a background worker
v 8.4.0
@ -169,6 +220,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
@ -351,6 +403,7 @@ v 8.1.0
- Improved performance of the trending projects page
- Remove CI migration task
- Improved performance of finding projects by their namespace
- Add assignee data to Issuables' hook_data (Bram Daams)
- Fix bug where transferring a project would result in stale commit links (Stan Hu)
- Fix build trace updating
- Include full path of source and target branch names in New Merge Request page (Stan Hu)

View file

@ -1,3 +1,29 @@
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
- [Contribute to GitLab](#contribute-to-gitlab)
- [Contributor license agreement](#contributor-license-agreement)
- [Security vulnerability disclosure](#security-vulnerability-disclosure)
- [Closing policy for issues and merge requests](#closing-policy-for-issues-and-merge-requests)
- [Helping others](#helping-others)
- [I want to contribute!](#i-want-to-contribute)
- [Issue tracker](#issue-tracker)
- [Feature proposals](#feature-proposals)
- [Issue tracker guidelines](#issue-tracker-guidelines)
- [Issue weight](#issue-weight)
- [Regression issues](#regression-issues)
- [Merge requests](#merge-requests)
- [Merge request guidelines](#merge-request-guidelines)
- [Merge request description format](#merge-request-description-format)
- [Contribution acceptance criteria](#contribution-acceptance-criteria)
- [Changes for Stable Releases](#changes-for-stable-releases)
- [Definition of done](#definition-of-done)
- [Style guides](#style-guides)
- [Code of conduct](#code-of-conduct)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
# Contribute to GitLab
Thank you for your interest in contributing to GitLab. This guide details how
@ -177,6 +203,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,
@ -214,15 +260,17 @@ request is as follows:
1. Add your changes to the [CHANGELOG](CHANGELOG)
1. If you are changing the README, some documentation or other things which
have no effect on the tests, add `[ci skip]` somewhere in the commit message
and make sure to read the [documentation styleguide][doc-styleguide]
1. If you have multiple commits please combine them into one commit by
[squashing them][git-squash]
1. Push the commit(s) to your fork
1. Submit a merge request (MR) to the master branch
1. The MR title should describe the change you want to make
1. The MR description should give a motive for your change and the method you
used to achieve it
used to achieve it, see the [merge request description format]
(#merge-request-description-format)
1. If the MR changes the UI it should include before and after screenshots
1. If the MR changes CSS classes please include the list of affected pages
1. If the MR changes CSS classes please include the list of affected pages,
`grep css-class ./app -R`
1. Link any relevant [issues][ce-tracker] in the merge request description and
leave a comment on them with a link back to the MR
@ -255,7 +303,68 @@ For examples of feedback on merge requests please look at already
request feel free to mention one of the Merge Marshalls of the [core team][].
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.
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.
### Merge request description format
Please submit merge requests using the following template in the merge request
description area. Copy-paste it to retain the markdown format.
```
## What does this MR do?
## Are there points in the code the reviewer needs to double check?
## Why was this MR needed?
## What are the relevant issue numbers?
## Screenshots (if relevant)
```
### Contribution acceptance criteria
1. The change is as small as possible
1. Include proper tests and make all tests pass (unless it contains a test
exposing a bug in existing code)
1. If you suspect a failing CI build is unrelated to your contribution, you may
try and restart the failing CI job or ask a developer to fix the
aforementioned failing test
1. Your MR initially contains a single commit (please use `git rebase -i` to
squash commits)
1. Your changes can merge without problems (if not please merge `master`, never
rebase commits pushed to the remote server)
1. Does not break any existing functionality
1. Fixes one specific issue or implements one specific feature (do not combine
things, send separate merge requests if needed)
1. Migrations should do only one thing (e.g., either create a table, move data
to a new table or remove an old table) to aid retrying on failure
1. Keeps the GitLab code base clean and well structured
1. Contains functionality we think other users will benefit from too
1. Doesn't add configuration options since they complicate future changes
1. Changes after submitting the merge request should be in separate commits
(no squashing). If necessary, you will be asked to squash when the review is
over, before merging.
1. It conforms to the [style guides](#style-guides) and the following:
- If your change touches a line that does not follow the style, modify the
entire line to follow it. This prevents linting tools from generating warnings.
- Don't touch neighbouring lines. As an exception, automatic mass
refactoring modifications may leave style non-compliant.
## 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.
Like all merge requests the target should be master so all bugfixes are in master.
## Definition of done
@ -266,7 +375,7 @@ the feature you contribute through all of these steps.
1. Description explaining the relevancy (see following item)
1. Working and clean code that is commented where needed
1. Unit and integration tests that pass on the CI server
1. Documented in the /doc directory
1. [Documented][doc-styleguide] in the /doc directory
1. Changelog entry added
1. Reviewed and any concerns are addressed
1. Merged by the project lead
@ -287,43 +396,6 @@ merge request:
1. Test suite https://gitlab.com/gitlab-org/gitlab-ce/blob/master/scripts/prepare_build.sh
1. Omnibus package creator https://gitlab.com/gitlab-org/omnibus-gitlab
## Merge request description format
1. What does this MR do?
1. Are there points in the code the reviewer needs to double check?
1. Why was this MR needed?
1. What are the relevant issue numbers?
1. Screenshots (if relevant)
## Contribution acceptance criteria
1. The change is as small as possible (see the above paragraph for details)
1. Include proper tests and make all tests pass (unless it contains a test
exposing a bug in existing code)
1. If you suspect a failing CI build is unrelated to your contribution, you may
try and restart the failing CI job or ask a developer to fix the
aforementioned failing test
1. Your MR initially contains a single commit (please use `git rebase -i` to
squash commits)
1. Your changes can merge without problems (if not please merge `master`, never
rebase commits pushed to the remote server)
1. Does not break any existing functionality
1. Fixes one specific issue or implements one specific feature (do not combine
things, send separate merge requests if needed)
1. Migrations should do only one thing (eg: either create a table, move data to
a new table or remove an old table) to aid retrying on failure
1. Keeps the GitLab code base clean and well structured
1. Contains functionality we think other users will benefit from too
1. Doesn't add configuration options since they complicate future changes
1. Changes after submitting the merge request should be in separate commits
(no squashing). If necessary, you will be asked to squash when the review is
over, before merging.
1. It conforms to the following style guides:
* If your change touches a line that does not follow the style, modify the
entire line to follow it. This prevents linting tools from generating warnings.
* Don't touch neighbouring lines. As an exception, automatic mass
refactoring modifications may leave style non-compliant.
## Style guides
1. [Ruby](https://github.com/bbatsov/ruby-style-guide).
@ -338,7 +410,7 @@ merge request:
contributors to enhance security
1. [Database Migrations](doc/development/migration_style_guide.md)
1. [Markdown](http://www.cirosantilli.com/markdown-styleguide)
1. [Documentation styleguide](doc/development/doc_styleguide.md)
1. [Documentation styleguide][doc-styleguide]
1. Interface text should be written subjectively instead of objectively. It
should be the GitLab core team addressing a person. It should be written in
present time and never use past tense (has been/was). For example instead
@ -379,7 +451,7 @@ reported by emailing `contact@gitlab.com`.
This Code of Conduct is adapted from the [Contributor Covenant][], version 1.1.0,
available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/).
[core team]: https://about.gitlab.com/core-team/
[core-team]: https://about.gitlab.com/core-team/
[getting help page]: https://about.gitlab.com/getting-help/
[Codetriage]: http://www.codetriage.com/gitlabhq/gitlabhq
[up-for-grabs]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=up-for-grabs
@ -400,3 +472,4 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor
[Contributor Covenant]: http://contributor-covenant.org
[rss-source]: https://github.com/bbatsov/ruby-style-guide/blob/master/README.md#source-code-layout
[rss-naming]: https://github.com/bbatsov/ruby-style-guide/blob/master/README.md#naming
[doc-styleguide]: doc/development/doc_styleguide.md "Documentation styleguide"

View file

@ -1 +1 @@
0.6.2
0.6.4

17
Gemfile
View file

@ -21,7 +21,7 @@ gem "pg", '~> 0.18.2', group: :postgres
gem 'devise', '~> 3.5.4'
gem 'devise-async', '~> 0.9.0'
gem 'doorkeeper', '~> 2.2.0'
gem 'omniauth', '~> 1.2.2'
gem 'omniauth', '~> 1.3.1'
gem 'omniauth-azure-oauth2', '~> 0.0.6'
gem 'omniauth-bitbucket', '~> 0.0.2'
gem 'omniauth-cas3', '~> 1.1.2'
@ -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.1'
# LDAP Auth
# GitLab fork with several improvements to original library. For full list of changes
@ -104,7 +105,7 @@ gem 'rouge', '~> 1.10.1'
# See https://groups.google.com/forum/#!topic/ruby-security-ann/aSbgDiwb24s
# and https://groups.google.com/forum/#!topic/ruby-security-ann/Dy7YiKb_pMM
gem 'nokogiri', '1.6.7.2'
gem 'nokogiri', '~> 1.6.7', '>= 1.6.7.2'
# Diffs
gem 'diffy', '~> 3.0.3'
@ -179,6 +180,9 @@ gem "underscore-rails", "~> 1.8.0"
gem "sanitize", '~> 2.0'
gem 'babosa', '~> 1.0.2'
# Sanitizes SVG input
gem "loofah", "~> 2.0.3"
# Protect against bruteforcing
gem "rack-attack", '~> 4.3.1'
@ -299,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"

View file

@ -49,7 +49,8 @@ GEM
addressable (2.3.8)
after_commit_queue (1.3.0)
activerecord (>= 3.0)
allocations (1.0.3)
akismet (2.0.0)
allocations (1.0.4)
annotate (2.6.10)
activerecord (>= 3.2, <= 4.3)
rake (~> 10.4)
@ -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.1.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)
@ -494,9 +491,9 @@ GEM
rack (~> 1.2)
octokit (3.8.0)
sawyer (~> 0.6.0, >= 0.5.3)
omniauth (1.2.2)
omniauth (1.3.1)
hashie (>= 1.2, < 4)
rack (~> 1.0)
rack (>= 1.0, < 3)
omniauth-azure-oauth2 (0.0.6)
jwt (~> 1.0)
omniauth (~> 1.0)
@ -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.1)
gitlab_meta (= 7.0)
gitlab_omniauth-ldap (~> 1.2.1)
gollum-lib (~> 4.1.0)
@ -953,6 +951,7 @@ DEPENDENCIES
jquery-ui-rails (~> 5.0.0)
kaminari (~> 0.16.3)
letter_opener (~> 1.1.2)
loofah (~> 2.0.3)
mail_room (~> 0.6.1)
method_source (~> 0.8)
minitest (~> 5.7.0)
@ -960,13 +959,11 @@ 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)
nokogiri (~> 1.6.7, >= 1.6.7.2)
nprogress-rails (~> 0.1.6.7)
oauth2 (~> 1.0.0)
octokit (~> 3.8.0)
omniauth (~> 1.2.2)
omniauth (~> 1.3.1)
omniauth-azure-oauth2 (~> 0.0.6)
omniauth-bitbucket (~> 0.0.2)
omniauth-cas3 (~> 1.1.2)
@ -975,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)

View file

@ -12,19 +12,6 @@ class @Admin
e.preventDefault()
$('.js-toggle-colors-container').toggle()
$('input#broadcast_message_color').on 'input', ->
previewColor = $(@).val()
$('div.broadcast-message-preview').css('background-color', previewColor)
$('input#broadcast_message_font').on 'input', ->
previewColor = $(@).val()
$('div.broadcast-message-preview').css('color', previewColor)
$('textarea#broadcast_message_message').on 'input', ->
previewMessage = $(@).val()
previewMessage = "Your message here" if previewMessage.trim() == ''
$('div.broadcast-message-preview span').text(previewMessage)
$('.log-tabs a').click (e) ->
e.preventDefault()
$(this).tab('show')

View file

@ -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) ->

View file

@ -211,8 +211,89 @@ $ ->
$this.attr 'value', $this.val()
return
$(document).on 'keyup', 'input[type="search"]' , (e) ->
$this = $(this)
$this.attr 'value', $this.val()
$(document)
.off 'keyup', 'input[type="search"]'
.on 'keyup', 'input[type="search"]' , (e) ->
$this = $(this)
$this.attr 'value', $this.val()
$(document)
.off 'breakpoint:change'
.on 'breakpoint:change', (e, breakpoint) ->
if breakpoint is 'sm' or breakpoint is 'xs'
$gutterIcon = $('.gutter-toggle').find('i')
if $gutterIcon.hasClass('fa-angle-double-right')
$gutterIcon.closest('a').trigger('click')
$(document)
.off 'click', 'aside .gutter-toggle'
.on 'click', 'aside .gutter-toggle', (e) ->
e.preventDefault()
$this = $(this)
$thisIcon = $this.find 'i'
if $thisIcon.hasClass('fa-angle-double-right')
$thisIcon
.removeClass('fa-angle-double-right')
.addClass('fa-angle-double-left')
$this
.closest('aside')
.removeClass('right-sidebar-expanded')
.addClass('right-sidebar-collapsed')
$('.page-with-sidebar')
.removeClass('right-sidebar-expanded')
.addClass('right-sidebar-collapsed')
else
$thisIcon
.removeClass('fa-angle-double-left')
.addClass('fa-angle-double-right')
$this
.closest('aside')
.removeClass('right-sidebar-collapsed')
.addClass('right-sidebar-expanded')
$('.page-with-sidebar')
.removeClass('right-sidebar-collapsed')
.addClass('right-sidebar-expanded')
$.cookie("collapsed_gutter",
$('.right-sidebar')
.hasClass('right-sidebar-collapsed'), { path: '/' })
bootstrapBreakpoint = undefined;
checkBootstrapBreakpoints = ->
if $('.device-xs').is(':visible')
bootstrapBreakpoint = "xs"
else if $('.device-sm').is(':visible')
bootstrapBreakpoint = "sm"
else if $('.device-md').is(':visible')
bootstrapBreakpoint = "md"
else if $('.device-lg').is(':visible')
bootstrapBreakpoint = "lg"
setBootstrapBreakpoints = ->
if $('.device-xs').length
return
$("body")
.append('<div class="device-xs visible-xs"></div>'+
'<div class="device-sm visible-sm"></div>'+
'<div class="device-md visible-md"></div>'+
'<div class="device-lg visible-lg"></div>')
checkBootstrapBreakpoints()
fitSidebarForSize = ->
oldBootstrapBreakpoint = bootstrapBreakpoint
checkBootstrapBreakpoints()
if bootstrapBreakpoint != oldBootstrapBreakpoint
$(document).trigger('breakpoint:change', [bootstrapBreakpoint])
checkInitialSidebarSize = ->
if bootstrapBreakpoint is "xs" or "sm"
$(document).trigger('breakpoint:change', [bootstrapBreakpoint])
$(window)
.off "resize"
.on "resize", (e) ->
fitSidebarForSize()
setBootstrapBreakpoints()
checkInitialSidebarSize()
new Aside()

View file

@ -49,10 +49,11 @@ class @AwardsHandler
counter.text(parseInt(counter.text()) - 1)
emojiIcon.removeClass("active")
@removeMeFromAuthorList(emoji)
else if emoji =="thumbsup" || emoji == "thumbsdown"
else if emoji == "thumbsup" || emoji == "thumbsdown"
emojiIcon.tooltip("destroy")
counter.text(0)
emojiIcon.removeClass("active")
@removeMeFromAuthorList(emoji)
else
emojiIcon.tooltip("destroy")
emojiIcon.remove()

View file

@ -1,4 +1,22 @@
#= require jquery.ba-resize
#= require autosize
$ ->
autosize($('.js-autosize'))
$fields = $('.js-autosize')
$fields.on 'autosize:resized', ->
$field = $(@)
$field.data('height', $field.outerHeight())
$fields.on 'resize.autosize', ->
$field = $(@)
if $field.data('height') != $field.outerHeight()
$field.data('height', $field.outerHeight())
autosize.destroy($field)
$field.css('max-height', window.outerHeight)
autosize($fields)
autosize.update($fields)
$fields.css('resize', 'vertical')

View file

@ -0,0 +1,22 @@
$ ->
$('input#broadcast_message_color').on 'input', ->
previewColor = $(@).val()
$('div.broadcast-message-preview').css('background-color', previewColor)
$('input#broadcast_message_font').on 'input', ->
previewColor = $(@).val()
$('div.broadcast-message-preview').css('color', previewColor)
previewPath = $('textarea#broadcast_message_message').data('preview-path')
$('textarea#broadcast_message_message').on 'input', ->
message = $(@).val()
if message == ''
$('.js-broadcast-message-preview').text("Your message here")
else
$.ajax(
url: previewPath
type: "POST"
data: { broadcast_message: { message: message } }
)

View file

@ -1,3 +1,31 @@
class @Dashboard
constructor: ->
new ProjectsList()
@Dashboard =
init: ->
$(".projects-list-filter").off('keyup')
this.initSearch()
initSearch: ->
@timer = null
$(".projects-list-filter").on('keyup', ->
clearTimeout(@timer)
@timer = setTimeout(Dashboard.filterResults, 500)
)
filterResults: =>
$('.projects-list-holder').fadeTo(250, 0.5)
form = null
form = $("form#project-filter-form")
search = $(".projects-list-filter").val()
project_filter_url = form.attr('action') + '?' + form.serialize()
$.ajax
type: "GET"
url: form.attr('action')
data: form.serialize()
complete: ->
$('.projects-list-holder').fadeTo(250, 1)
success: (data) ->
$('.projects-list-holder').replaceWith(data.html)
# Change url so if user reload a page - search results are saved
history.replaceState {page: project_filter_url}, document.title, project_filter_url
dataType: "json"

View file

@ -16,6 +16,8 @@ class Dispatcher
shortcut_handler = null
switch page
when 'explore:projects:index', 'explore:projects:starred', 'explore:projects:trending'
Dashboard.init()
when 'projects:issues:index'
Issues.init()
shortcut_handler = new ShortcutsNavigation()
@ -58,7 +60,7 @@ class Dispatcher
shortcut_handler = new ShortcutsNavigation()
MergeRequests.init()
when 'dashboard:show', 'root:show'
new Dashboard()
Dashboard.init()
when 'dashboard:activity'
new Activities()
when 'dashboard:projects:starred'

View file

@ -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) ->

View file

@ -10,19 +10,7 @@ class @IssuableContext
$(".issuable-sidebar .inline-update").on "change", ".js-assignee", ->
$(this).submit()
$('.issuable-details').waitForImages ->
$('.issuable-affix').on 'affix.bs.affix', ->
$(@).width($(@).outerWidth())
.on 'affixed-top.bs.affix affixed-bottom.bs.affix', ->
$(@).width('')
$('.issuable-affix').affix offset:
top: ->
@top = ($('.issuable-affix').offset().top - 70)
bottom: ->
@bottom = $('.footer').outerHeight(true)
$(".edit-link").click (e) ->
$(document).on "click",".edit-link", (e) ->
block = $(@).parents('.block')
block.find('.selectbox').show()
block.find('.value').hide()

View file

@ -42,3 +42,9 @@ work = ->
$(document).on('page:fetch', start)
$(document).on('page:change', stop)
$ ->
# Make logo clickable as part of a workaround for Safari visited
# link behaviour (See !2690).
$('#logo').on 'click', ->
$('#js-shortcuts-home').get(0).click()

View file

@ -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

View file

@ -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

View file

@ -3,24 +3,24 @@ class @ProjectsList
$(".projects-list .js-expand").on 'click', (e) ->
e.preventDefault()
list = $(this).closest('.projects-list')
list.find("li").show()
list.find("li.bottom").hide()
$(".projects-list-filter").keyup ->
terms = $(this).val()
uiBox = $('div.projects-list-holder')
filterSelector = $(this).data('filter-selector') || 'span.filter-title'
$("#filter_projects").on 'keyup', ->
ProjectsList.filter_results($("#filter_projects"))
if terms == "" || terms == undefined
uiBox.find("ul.projects-list li").show()
else
uiBox.find("ul.projects-list li").each (index) ->
name = $(this).find(filterSelector).text()
if name.toLowerCase().search(terms.toLowerCase()) == -1
$(this).hide()
else
$(this).show()
uiBox.find("ul.projects-list li.bottom").hide()
@filter_results: ($element) ->
terms = $element.val()
filterSelector = $element.data('filter-selector') || 'span.filter-title'
if not terms
$(".projects-list li").show()
$('.gl-pagination').show()
else
$(".projects-list li").each (index) ->
$this = $(this)
name = $this.find(filterSelector).text()
if name.toLowerCase().indexOf(terms.toLowerCase()) == -1
$this.hide()
else
$this.show()
$('.gl-pagination').hide()

View file

@ -16,12 +16,31 @@ class @ShortcutsIssuable extends ShortcutsNavigation
@replyWithSelectedText()
return false
)
Mousetrap.bind('j', =>
@prevIssue()
return false
)
Mousetrap.bind('k', =>
@nextIssue()
return false
)
if isMergeRequest
@enabledHelp.push('.hidden-shortcut.merge_requests')
else
@enabledHelp.push('.hidden-shortcut.issues')
prevIssue: ->
$prevBtn = $('.prev-btn')
if not $prevBtn.hasClass('disabled')
Turbolinks.visit($prevBtn.attr('href'))
nextIssue: ->
$nextBtn = $('.next-btn')
if not $nextBtn.hasClass('disabled')
Turbolinks.visit($nextBtn.attr('href'))
replyWithSelectedText: ->
if window.getSelection
selected = window.getSelection().toString()

View file

@ -24,6 +24,7 @@
&.s26 { width: 26px; height: 26px; margin-right: 8px; }
&.s32 { width: 32px; height: 32px; margin-right: 10px; }
&.s36 { width: 36px; height: 36px; margin-right: 10px; }
&.s40 { width: 40px; height: 40px; margin-right: 10px; }
&.s46 { width: 46px; height: 46px; margin-right: 15px; }
&.s48 { width: 48px; height: 48px; margin-right: 10px; }
&.s60 { width: 60px; height: 60px; margin-right: 12px; }
@ -40,7 +41,8 @@
&.s16 { font-size: 12px; line-height: 1.33; }
&.s24 { font-size: 14px; line-height: 1.8; }
&.s26 { font-size: 20px; line-height: 1.33; }
&.s32 { font-size: 22px; line-height: 32px; }
&.s32 { font-size: 20px; line-height: 32px; }
&.s40 { font-size: 16px; line-height: 40px; }
&.s60 { font-size: 32px; line-height: 60px; }
&.s90 { font-size: 36px; line-height: 90px; }
&.s110 { font-size: 40px; line-height: 112px; font-weight: 300; }

View file

@ -2,7 +2,7 @@
@include border-radius(3px);
font-size: $gl-font-size;
font-weight: 500;
padding: $gl-vert-padding $gl-padding;
padding: $gl-vert-padding $gl-btn-padding;
&:focus,
&:active {
@ -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
}
}

View file

@ -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;

View file

@ -73,7 +73,6 @@ header {
.title {
margin: 0;
overflow: hidden;
font-size: 19px;
line-height: $header-height;
font-weight: normal;
@ -88,6 +87,22 @@ header {
text-decoration: underline;
}
}
.dropdown-toggle-caret {
position: relative;
top: -2px;
width: 12px;
line-height: 12px;
margin-left: 5px;
font-size: 10px;
text-align: center;
cursor: pointer;
}
.project-item-select {
right: auto;
left: 0;
}
}
.navbar-collapse {

View file

@ -9,7 +9,7 @@
display: block;
float: left;
padding: 0 $gl-padding;
padding: 0 $gl-btn-padding;
font-weight: normal;
margin-right: 10px;
font-size: $gl-font-size;

View file

@ -109,7 +109,6 @@ ul.content-list {
padding: 0;
> li {
padding: $gl-padding 0;
border-color: $table-border-color;
color: $gl-gray;

View file

@ -83,7 +83,7 @@
background: #FFF;
border: 1px solid #ddd;
min-height: 140px;
max-height: 430px;
max-height: 500px;
padding: 5px;
box-shadow: none;
width: 100%;

View file

@ -116,7 +116,7 @@
display: none;
}
aside {
aside:not(.right-sidebar){
display: none;
}

View file

@ -37,3 +37,93 @@
}
}
}
.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;
}
> form {
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;
}
}
}
}

View file

@ -45,6 +45,19 @@
overflow: hidden;
transition-duration: .3s;
.home {
z-index: 1;
position: absolute;
left: 0px;
}
#logo {
z-index: 2;
position: absolute;
width: 58px;
cursor: pointer;
}
a {
float: left;
height: $header-height;
@ -70,7 +83,7 @@
width: 158px;
float: left;
margin: 0;
margin-left: 14px;
margin-left: 50px;
font-size: 19px;
line-height: 41px;
font-weight: normal;
@ -200,6 +213,14 @@
}
}
@mixin expanded-gutter {
padding-right: $gutter_width;
}
@mixin collapsed-gutter {
padding-right: $sidebar_collapsed_width;
}
@mixin collapsed-sidebar {
padding-left: $sidebar_collapsed_width;
@ -266,6 +287,7 @@
background: #f2f6f7;
}
// page is small enough
@media (max-width: $screen-md-max) {
.page-sidebar-collapsed {
@include collapsed-sidebar;
@ -275,12 +297,32 @@
@include collapsed-sidebar;
}
.page-gutter {
&.right-sidebar-collapsed {
@include collapsed-gutter;
}
&.right-sidebar-expanded {
@include expanded-gutter;
}
}
.collapse-nav {
display: none;
}
}
// page is large enough
@media(min-width: $screen-md-max) {
.page-gutter {
&.right-sidebar-collapsed {
@include collapsed-gutter;
}
&.right-sidebar-expanded {
@include expanded-gutter;
}
}
.page-sidebar-collapsed {
@include collapsed-sidebar;
}
@ -288,4 +330,4 @@
.page-sidebar-expanded {
@include expanded-sidebar;
}
}
}

View file

@ -12,6 +12,9 @@ $gl-font-size: 15px;
$list-font-size: 15px;
$sidebar_collapsed_width: 62px;
$sidebar_width: 230px;
$gutter_collapsed_width: 62px;
$gutter_width: 290px;
$gutter_inner_width: 258px;
$avatar_radius: 50%;
$code_font_size: 13px;
$code_line_height: 1.5;
@ -22,9 +25,10 @@ $header-height: 58px;
$fixed-layout-width: 1280px;
$gl-gray: #5a5a5a;
$gl-padding: 16px;
$gl-btn-padding: 10px;
$gl-vert-padding: 6px;
$gl-padding-top:10px;
$gl-avatar-size: 46px;
$gl-avatar-size: 40px;
$secondary-text: #7f8fa4;
$error-exclamation-point: #E62958;
@ -36,11 +40,12 @@ $white-light: #FFFFFF;
$white-normal: #ededed;
$white-dark: #ededed;
$gray-light: #f7f7f7;
$gray-normal: #ededed;
$gray-light: #faf9f9;
$gray-normal: #f5f5f5;
$gray-dark: #ededed;
$gray-darkest: #c9c9c9;
$green-light: #31AF64;
$green-light: #38ae67;
$green-normal: #2FAA60;
$green-dark: #2CA05B;
@ -52,7 +57,7 @@ $blue-medium-light: #3498CB;
$blue-medium: #2F8EBF;
$blue-medium-dark: #2D86B4;
$orange-light: #FC6443;
$orange-light: rgba(252, 109, 38, 0.80);
$orange-normal: #E75E40;
$orange-dark: #CE5237;
@ -64,8 +69,8 @@ $border-white-light: #F1F2F4;
$border-white-normal: #D6DAE2;
$border-white-dark: #C6CACF;
$border-gray-light: #d1d1d1;
$border-gray-normal: #D6DAE2;
$border-gray-light: rgba(0, 0, 0, 0.06);
$border-gray-normal: rgba(0, 0, 0, 0.10);;
$border-gray-dark: #C6CACF;
$border-green-light: #2FAA60;
@ -76,7 +81,7 @@ $border-blue-light: #2D9FD8;
$border-blue-normal: #2897CE;
$border-blue-dark: #258DC1;
$border-orange-light: #ED5C3D;
$border-orange-light: #fc6d26;
$border-orange-normal: #CE5237;
$border-orange-dark: #C14E35;

View file

@ -55,6 +55,16 @@
@extend .alert-warning;
padding: 10px;
text-align: center;
> div, p {
display: inline;
margin: 0;
a {
color: inherit;
text-decoration: underline;
}
}
}
.broadcast-message-preview {

View file

@ -40,10 +40,6 @@
.avatar {
@include border-radius(50%);
}
.identicon {
line-height: 46px;
}
}
.dash-project-access-icon {

View file

@ -4,7 +4,7 @@
*/
.event-item {
font-size: $gl-font-size;
padding: $gl-padding 0 $gl-padding ($gl-avatar-size + 15px);
padding: $gl-padding-top 0 $gl-padding-top ($gl-avatar-size + $gl-padding-top);
border-bottom: 1px solid $table-border-color;
color: #7f8fa4;
@ -16,7 +16,7 @@
.event-title,
.event-item-timestamp {
line-height: 44px;
line-height: 40px;
}
}
@ -25,7 +25,7 @@
}
.avatar {
margin-left: -($gl-avatar-size + 15px);
margin-left: -($gl-avatar-size + $gl-padding-top);
}
.event-title {
@ -41,7 +41,6 @@
margin-right: 174px;
.event-note {
margin-top: 5px;
word-wrap: break-word;
.md {
@ -98,8 +97,6 @@
&:last-child { border:none }
.event_commits {
margin-top: 9px;
li {
&.commit {
background: transparent;

View file

@ -6,11 +6,3 @@
font-size: 30px;
}
}
.explore-trending-block {
.lead {
line-height: 32px;
font-size: 18px;
margin-top: 10px;
}
}

View file

@ -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;
}
}
}

View file

@ -29,21 +29,8 @@
}
}
.project-issuable-filter {
.controls {
float: right;
margin-top: 11px;
}
.nav-links {
text-align: left;
}
}
.issuable-details {
section {
border-right: 1px solid $border-white-light;
.issuable-discussion {
margin-right: 1px;
}
@ -73,11 +60,43 @@
.block {
@include clearfix;
padding: $gl-padding 0;
border-bottom: 1px solid #F0F0F0;
border-bottom: 1px solid $border-gray-light;
// This prevents the mess when resizing the sidebar
// of elements repositioning themselves..
width: $gutter_inner_width;
overflow-x: hidden;
// --
&:first-child {
padding-top: 5px;
}
&:last-child {
border: none;
}
span {
margin-top: 7px;
display: inline-block;
}
.select2-container span {
margin-top: 0;
}
.issuable-count {
}
.gutter-toggle {
margin-left: 20px;
border-left: 1px solid $border-gray-light;
padding-left: 10px;
&:hover {
color: $gray-darkest;
}
}
}
.title {
@ -133,3 +152,115 @@
margin-right: 2px;
}
}
.right-sidebar {
position: fixed;
top: 58px;
right: 0;
height: 100%;
transition-duration: .3s;
background: $gray-light;
overflow: scroll;
padding: 10px 20px;
&.right-sidebar-expanded {
width: $gutter_width;
hr {
display: none;
}
}
.subscribe-button {
span {
margin-top: 0;
}
}
&.right-sidebar-collapsed {
width: $sidebar_collapsed_width;
padding-top: 0;
overflow-x: hidden;
hr {
margin: 0;
color: $gray-normal;
border-color: $gray-normal;
width: 62px;
margin-left: -20px
}
.block {
border-bottom: none;
padding: 15px 0 0 0;
}
}
.btn {
background: $gray-normal;
border: 1px solid $border-gray-normal;
&:hover {
background: $gray-dark;
border: 1px solid $border-gray-dark;
}
}
&.right-sidebar-collapsed {
.issuable-count,
.issuable-nav,
.assignee > *,
.milestone > *,
.labels > *,
.participants > *,
.light > *,
.project-reference > * {
display: none;
}
.gutter-toggle {
margin-left: -$gutter_inner_width + 4;
}
.sidebar-collapsed-icon {
display: block;
float: left;
width: 62px;
text-align: center;
margin-left: -19px;
padding-bottom: 10px;
color: #999999;
span {
display: block;
margin-top: 0;
}
.btn-clipboard {
border: none;
&:hover {
background: transparent;
}
i {
color: #999999;
}
}
}
}
&.right-sidebar-expanded {
.sidebar-collapsed-icon {
display: none;
}
}
}
.detail-page-description {
small {
color: $gray-darkest;
}
}

View file

@ -65,10 +65,6 @@ form.edit-issue {
width: 3em;
}
.merge-request-info {
padding-left: 5px;
}
.merge-request-status {
color: $gl-gray;
font-size: 15px;
@ -143,4 +139,4 @@ form.edit-issue {
.issue-closed-by-widget {
color: $secondary-text;
margin-left: 52px;
}
}

View file

@ -147,7 +147,7 @@
.edit_note {
.markdown-area {
min-height: 140px;
max-height: 430px;
max-height: 500px;
}
.note-form-actions {
background: transparent;

View file

@ -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;
@ -437,12 +391,11 @@ pre.light-well {
@include basic-list;
.project-row {
padding: $gl-padding 0;
border-color: $table-border-color;
&.no-description {
.project {
line-height: 44px;
line-height: 40px;
}
}
@ -455,12 +408,16 @@ pre.light-well {
.project-controls {
float: right;
color: $gl-gray;
line-height: 45px;
line-height: 40px;
color: #7f8fa4;
a:hover {
text-decoration: none;
}
> span {
margin-left: 10px;
}
}
.project-description {
@ -559,52 +516,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;

View file

@ -79,6 +79,9 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:recaptcha_private_key,
:sentry_enabled,
:sentry_dsn,
:akismet_enabled,
:akismet_api_key,
:email_author_in_body,
restricted_visibility_levels: [],
import_sources: []
)

View file

@ -2,7 +2,7 @@ class Admin::BroadcastMessagesController < Admin::ApplicationController
before_action :finder, only: [:edit, :update, :destroy]
def index
@broadcast_messages = BroadcastMessage.reorder("starts_at ASC").page(params[:page])
@broadcast_messages = BroadcastMessage.reorder("ends_at DESC").page(params[:page])
@broadcast_message = BroadcastMessage.new
end
@ -36,6 +36,10 @@ class Admin::BroadcastMessagesController < Admin::ApplicationController
end
end
def preview
@message = broadcast_message_params[:message]
end
protected
def finder

View file

@ -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

View file

@ -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)
@ -162,7 +164,7 @@ class ApplicationController < ActionController::Base
end
def git_not_found!
render html: "errors/git_not_found", layout: "errors", status: 404
render "errors/git_not_found.html", layout: "errors", status: 404
end
def method_missing(method_sym, *arguments, &block)
@ -275,9 +277,10 @@ class ApplicationController < ActionController::Base
}
end
def view_to_html_string(partial)
def view_to_html_string(partial, locals = {})
render_to_string(
partial,
locals: locals,
layout: false,
formats: [:html]
)

View file

@ -3,52 +3,5 @@ module Ci
def self.railtie_helpers_paths
"app/helpers/ci"
end
private
def authorize_access_project!
unless can?(current_user, :read_project, project)
return page_404
end
end
def authorize_manage_builds!
unless can?(current_user, :manage_builds, project)
return page_404
end
end
def authenticate_admin!
return render_404 unless current_user.is_admin?
end
def authorize_manage_project!
unless can?(current_user, :admin_project, project)
return page_404
end
end
def page_404
render file: "#{Rails.root}/public/404.html", status: 404, layout: false
end
def default_headers
headers['X-Frame-Options'] = 'DENY'
headers['X-XSS-Protection'] = '1; mode=block'
end
# JSON for infinite scroll via Pager object
def pager_json(partial, count)
html = render_to_string(
partial,
layout: false,
formats: [:html]
)
render json: {
html: html,
count: count
}
end
end
end

View file

@ -1,8 +1,7 @@
module Ci
class ProjectsController < Ci::ApplicationController
before_action :project, except: [:index]
before_action :authenticate_user!, except: [:index, :build, :badge]
before_action :authorize_access_project!, except: [:index, :badge]
before_action :project
before_action :authorize_read_project!, except: [:badge]
before_action :no_cache, only: [:badge]
protect_from_forgery
@ -13,9 +12,13 @@ module Ci
# Project status badge
# Image with build status for sha or ref
#
# This action in DEPRECATED, this is here only for backwards compatibility
# with projects migrated from GitLab CI.
#
def badge
return render_404 unless @project
image = Ci::ImageForBuildService.new.execute(@project, params)
send_file image.path, filename: image.name, disposition: 'inline', type:"image/svg+xml"
end

View file

@ -3,7 +3,16 @@ 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)
terms = params['filter_projects']
if terms.present?
@projects = @projects.search(terms)
end
@projects = @projects.page(params[:page]).per(PER_PAGE) if terms.blank?
@last_push = current_user.recent_push
respond_to do |format|
@ -13,6 +22,11 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
load_events
render layout: false
end
format.json do
render json: {
html: view_to_html_string("dashboard/projects/_projects", locals: { projects: @projects })
}
end
end
end
@ -20,6 +34,14 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
@projects = current_user.starred_projects
@projects = @projects.includes(:namespace, :forked_from_project, :tags)
@projects = @projects.sort(@sort = params[:sort])
terms = params['filter_projects']
if terms.present?
@projects = @projects.search(terms)
end
@projects = @projects.page(params[:page]).per(PER_PAGE) if terms.blank?
@last_push = current_user.recent_push
@groups = []
@ -27,8 +49,9 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
format.html
format.json do
load_events
pager_json("events/_events", @events.count)
render json: {
html: view_to_html_string("dashboard/projects/_projects", locals: { projects: @projects })
}
end
end
end

View file

@ -6,19 +6,49 @@ class Explore::ProjectsController < Explore::ApplicationController
@projects = @projects.where(visibility_level: params[:visibility_level]) if params[:visibility_level].present?
@projects = @projects.non_archived
@projects = @projects.search(params[:search]) if params[:search].present?
@projects = @projects.search(params[:filter_projects]) if params[:filter_projects].present?
@projects = @projects.sort(@sort = params[:sort])
@projects = @projects.includes(:namespace).page(params[:page]).per(PER_PAGE)
@projects = @projects.includes(:namespace).page(params[:page]).per(PER_PAGE) if params[:filter_projects].blank?
respond_to do |format|
format.html
format.json do
render json: {
html: view_to_html_string("dashboard/projects/_projects", locals: { projects: @projects })
}
end
end
end
def trending
@trending_projects = TrendingProjectsFinder.new.execute(current_user)
@trending_projects = @trending_projects.non_archived
@trending_projects = @trending_projects.page(params[:page]).per(PER_PAGE)
@projects = TrendingProjectsFinder.new.execute(current_user)
@projects = @projects.non_archived
@projects = @projects.search(params[:filter_projects]) if params[:filter_projects].present?
@projects = @projects.page(params[:page]).per(PER_PAGE) if params[:filter_projects].blank?
respond_to do |format|
format.html
format.json do
render json: {
html: view_to_html_string("dashboard/projects/_projects", locals: { projects: @projects })
}
end
end
end
def starred
@starred_projects = ProjectsFinder.new.execute(current_user)
@starred_projects = @starred_projects.reorder('star_count DESC')
@starred_projects = @starred_projects.page(params[:page]).per(PER_PAGE)
@projects = ProjectsFinder.new.execute(current_user)
@projects = @projects.search(params[:filter_projects]) if params[:filter_projects].present?
@projects = @projects.reorder('star_count DESC')
@projects = @projects.page(params[:page]).per(PER_PAGE) if params[:filter_projects].blank?
respond_to do |format|
format.html
format.json do
render json: {
html: view_to_html_string("dashboard/projects/_projects", locals: { projects: @projects })
}
end
end
end
end

View file

@ -14,7 +14,7 @@ class GroupsController < Groups::ApplicationController
# Load group projects
before_action :load_projects, except: [:index, :new, :create, :projects, :edit, :update, :autocomplete]
before_action :event_filter, only: :show
before_action :event_filter, only: [:show, :events]
layout :determine_layout
@ -41,13 +41,16 @@ class GroupsController < Groups::ApplicationController
def show
@last_push = current_user.recent_push if current_user
@projects = @projects.includes(:namespace)
@projects = @projects.search(params[:filter_projects]) if params[:filter_projects].present?
@projects = @projects.page(params[:page]).per(PER_PAGE) if params[:filter_projects].blank?
respond_to do |format|
format.html
format.json do
load_events
pager_json("events/_events", @events.count)
render json: {
html: view_to_html_string("dashboard/projects/_projects", locals: { projects: @projects })
}
end
format.atom do
@ -57,6 +60,15 @@ class GroupsController < Groups::ApplicationController
end
end
def events
respond_to do |format|
format.json do
load_events
pager_json("events/_events", @events.count)
end
end
end
def edit
end

View file

@ -1,4 +1,5 @@
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
include AuthenticatesWithTwoFactor
protect_from_forgery except: [:kerberos, :saml, :cas3]
@ -29,8 +30,12 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
# Do additional LDAP checks for the user filter and EE features
if ldap_user.allowed?
log_audit_event(@user, with: :ldap)
sign_in_and_redirect(@user)
if @user.two_factor_enabled?
prompt_for_two_factor(@user)
else
log_audit_event(@user, with: :ldap)
sign_in_and_redirect(@user)
end
else
flash[:alert] = "Access denied for your LDAP account."
redirect_to new_user_session_path

View file

@ -28,6 +28,11 @@ class Projects::ApplicationController < ApplicationController
private
def apply_diff_view_cookie!
view = params[:view] || cookies[:diff_view]
cookies.permanent[:diff_view] = params[:view] = view if view
end
def builds_enabled
return render_404 unless @project.builds_enabled?
end

View file

@ -1,6 +1,6 @@
class Projects::ArtifactsController < Projects::ApplicationController
layout 'project'
before_action :authorize_read_build_artifacts!
before_action :authorize_read_build!
def download
unless artifacts_file.file_storage?
@ -43,14 +43,4 @@ class Projects::ArtifactsController < Projects::ApplicationController
def artifacts_file
@artifacts_file ||= build.artifacts_file
end
def authorize_read_build_artifacts!
unless can?(current_user, :read_build_artifacts, @project)
if current_user.nil?
return authenticate_user!
else
return render_404
end
end
end
end

View file

@ -2,11 +2,10 @@ class Projects::AvatarsController < Projects::ApplicationController
before_action :project
def show
repository = @project.repository
@blob = 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'
headers.store(*Gitlab::Workhorse.send_git_blob(repository, @blob))
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

View file

@ -0,0 +1,11 @@
class Projects::BadgesController < Projects::ApplicationController
def build
respond_to do |format|
format.html { render_404 }
format.svg do
image = Ci::ImageForBuildService.new.execute(project, ref: params[:ref])
send_file(image.path, filename: image.name, disposition: 'inline', type: 'image/svg+xml')
end
end
end
end

View file

@ -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)

View file

@ -1,9 +1,8 @@
class Projects::BuildsController < Projects::ApplicationController
before_action :build, except: [:index, :cancel_all]
before_action :authorize_manage_builds!, except: [:index, :show, :status]
layout "project"
before_action :authorize_read_build!, except: [:cancel, :cancel_all, :retry]
before_action :authorize_update_build!, except: [:index, :show, :status]
layout 'project'
def index
@scope = params[:scope]
@ -23,7 +22,6 @@ class Projects::BuildsController < Projects::ApplicationController
def cancel_all
@project.builds.running_or_pending.each(&:cancel)
redirect_to namespace_project_builds_path(project.namespace, project)
end
@ -46,20 +44,18 @@ class Projects::BuildsController < Projects::ApplicationController
end
build = Ci::Build.retry(@build)
redirect_to build_path(build)
end
def status
render json: @build.to_json(only: [:status, :id, :sha, :coverage], methods: :sha)
end
def cancel
@build.cancel
redirect_to build_path(@build)
end
def status
render json: @build.to_json(only: [:status, :id, :sha, :coverage], methods: :sha)
end
private
def build
@ -69,10 +65,4 @@ class Projects::BuildsController < Projects::ApplicationController
def build_path(build)
namespace_project_build_path(build.project.namespace, build.project, build)
end
def authorize_manage_builds!
unless can?(current_user, :manage_builds, project)
return render_404
end
end
end

View file

@ -4,14 +4,14 @@
class Projects::CommitController < Projects::ApplicationController
# Authorize
before_action :require_non_empty_project
before_action :authorize_download_code!, except: [:cancel_builds]
before_action :authorize_manage_builds!, only: [:cancel_builds]
before_action :authorize_download_code!, except: [:cancel_builds, :retry_builds]
before_action :authorize_update_build!, only: [:cancel_builds, :retry_builds]
before_action :authorize_read_commit_status!, only: [:builds]
before_action :commit
before_action :authorize_manage_builds!, only: [:cancel_builds, :retry_builds]
before_action :define_show_vars, only: [:show, :builds]
def show
return git_not_found! unless @commit
apply_diff_view_cookie!
@line_notes = commit.notes.inline
@note = @project.build_commit_note(commit)
@ -66,6 +66,8 @@ class Projects::CommitController < Projects::ApplicationController
end
def define_show_vars
return git_not_found! unless commit
if params[:w].to_i == 1
@diffs = commit.diffs({ ignore_whitespace_change: true })
else
@ -77,10 +79,4 @@ class Projects::CommitController < Projects::ApplicationController
@statuses = ci_commit.statuses if ci_commit
end
def authorize_manage_builds!
unless can?(current_user, :manage_builds, project)
return render_404
end
end
end

View file

@ -21,6 +21,9 @@ class Projects::CommitsController < Projects::ApplicationController
@note_counts = project.notes.where(commit_id: @commits.map(&:id)).
group(:commit_id).count
@merge_request = @project.merge_requests.opened.
find_by(source_project: @project, source_branch: @ref, target_branch: @repository.root_ref)
respond_to do |format|
format.html
format.json { pager_json("projects/commits/_commits", @commits.size) }

View file

@ -4,24 +4,23 @@ class Projects::CompareController < Projects::ApplicationController
# Authorize
before_action :require_non_empty_project
before_action :authorize_download_code!
before_action :assign_ref_vars, only: [:index, :show]
before_action :merge_request, only: [:index, :show]
def index
@ref = Addressable::URI.unescape(params[:to])
end
def show
base_ref = Addressable::URI.unescape(params[:from])
@ref = head_ref = Addressable::URI.unescape(params[:to])
diff_options = { ignore_whitespace_change: true } if params[:w] == '1'
compare_result = CompareService.new.
execute(@project, head_ref, @project, base_ref, diff_options)
execute(@project, @head_ref, @project, @base_ref, diff_options)
if compare_result
@commits = Commit.decorate(compare_result.commits, @project)
@diffs = compare_result.diffs
@commit = @project.commit(head_ref)
@base_commit = @project.merge_base_commit(base_ref, head_ref)
@commit = @project.commit(@head_ref)
@base_commit = @project.merge_base_commit(@base_ref, @head_ref)
@diff_refs = [@base_commit, @commit]
@line_notes = []
end
@ -31,4 +30,16 @@ class Projects::CompareController < Projects::ApplicationController
redirect_to namespace_project_compare_path(@project.namespace, @project,
params[:from], params[:to])
end
private
def assign_ref_vars
@base_ref = Addressable::URI.unescape(params[:from])
@ref = @head_ref = Addressable::URI.unescape(params[:to])
end
def merge_request
@merge_request ||= @project.merge_requests.opened.
find_by(source_project: @project, source_branch: @head_ref, target_branch: @base_ref)
end
end

View file

@ -57,6 +57,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
def diffs
apply_diff_view_cookie!
@commit = @merge_request.last_commit
@base_commit = @merge_request.diff_base_commit

View file

@ -11,11 +11,12 @@ class Projects::MilestonesController < Projects::ApplicationController
respond_to :html
def index
@milestones = case params[:state]
when 'all'; @project.milestones.order("state, due_date DESC")
when 'closed'; @project.milestones.closed.order("due_date DESC")
else @project.milestones.active.order("due_date ASC")
end
@milestones =
case params[:state]
when 'all' then @project.milestones.reorder(due_date: :desc, title: :asc)
when 'closed' then @project.milestones.closed.reorder(due_date: :desc, title: :asc)
else @project.milestones.active.reorder(due_date: :asc, title: :asc)
end
@milestones = @milestones.includes(:project)
@milestones = @milestones.page(params[:page]).per(PER_PAGE)

View file

@ -1,5 +1,5 @@
class Projects::RunnerProjectsController < Projects::ApplicationController
before_action :authorize_admin_project!
before_action :authorize_admin_build!
layout 'project_settings'

View file

@ -1,6 +1,6 @@
class Projects::RunnersController < Projects::ApplicationController
before_action :authorize_admin_build!
before_action :set_runner, only: [:edit, :update, :destroy, :pause, :resume, :show]
before_action :authorize_admin_project!
layout 'project_settings'

View file

@ -1,5 +1,5 @@
class Projects::TriggersController < Projects::ApplicationController
before_action :authorize_admin_project!
before_action :authorize_admin_build!
layout 'project_settings'

View file

@ -1,5 +1,5 @@
class Projects::VariablesController < Projects::ApplicationController
before_action :authorize_admin_project!
before_action :authorize_admin_build!
layout 'project_settings'

View file

@ -227,6 +227,7 @@ class ProjectsController < ApplicationController
:issues_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id, :default_branch,
:wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id, :avatar,
:builds_enabled, :build_allow_git_fetch, :build_timeout_in_minutes, :build_coverage_regex,
:public_builds,
)
end

View file

@ -4,8 +4,9 @@ class UsersController < ApplicationController
def show
@contributed_projects = contributed_projects.joined(@user).reject(&:forked?)
@projects = PersonalProjectsFinder.new(@user).execute(current_user)
@projects = @projects.page(params[:page]).per(PER_PAGE)
@groups = @user.groups.order_id_desc

View file

@ -169,18 +169,6 @@ module ApplicationHelper
Gitlab.config.extra
end
def search_placeholder
if @project && @project.persisted?
'Search'
elsif @snippet || @snippets || @show_snippets
'Search snippets'
elsif @group && @group.persisted?
'Search in this group'
else
'Search'
end
end
# Render a `time` element with Javascript-based relative date and tooltip
#
# time - Time object
@ -224,8 +212,7 @@ module ApplicationHelper
file_content
end
else
GitHub::Markup.render(file_name, file_content).
force_encoding(file_content.encoding).html_safe
other_markup(file_name, file_content)
end
rescue RuntimeError
simple_format(file_content)

View file

@ -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)

View file

@ -126,4 +126,16 @@ module BlobHelper
blob.size
end
end
def blob_svg?(blob)
blob.language && blob.language.name == 'SVG'
end
# SVGs can contain malicious JavaScript; only include whitelisted
# elements and attributes. Note that this whitelist is by no means complete
# and may omit some elements.
def sanitize_svg(blob)
blob.data = Loofah.scrub_fragment(blob.data, :strip).to_xml
blob
end
end

View file

@ -3,7 +3,7 @@ module BroadcastMessagesHelper
return unless message.present?
content_tag :div, class: 'broadcast-message', style: broadcast_message_style(message) do
icon('bullhorn') << ' ' << message.message
icon('bullhorn') << ' ' << render_broadcast_message(message.message)
end
end
@ -31,4 +31,8 @@ module BroadcastMessagesHelper
'Pending'
end
end
def render_broadcast_message(message)
Banzai.render(message, pipeline: :broadcast_message).html_safe
end
end

View file

@ -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

View file

@ -78,6 +78,21 @@ module GitlabMarkdownHelper
)
end
def other_markup(file_name, text)
Gitlab::OtherMarkup.render(
file_name,
text,
project: @project,
current_user: (current_user if defined?(current_user)),
# RelativeLinkFilter
project_wiki: @project_wiki,
requested_path: @path,
ref: @ref,
commit: @commit
)
end
# Return the first line of +text+, up to +max_chars+, after parsing the line
# as Markdown. HTML tags in the parsed output are not counted toward the
# +max_chars+ limit. If the length limit falls within a tag's contents, then

View file

@ -0,0 +1,37 @@
module IssuablesHelper
def sidebar_gutter_toggle_icon
sidebar_gutter_collapsed? ? icon('angle-double-left') : icon('angle-double-right')
end
def sidebar_gutter_collapsed_class
"right-sidebar-#{sidebar_gutter_collapsed? ? 'collapsed' : 'expanded'}"
end
def issuables_count(issuable)
base_issuable_scope(issuable).maximum(:iid)
end
def next_issuable_for(issuable)
base_issuable_scope(issuable).where('iid > ?', issuable.iid).last
end
def prev_issuable_for(issuable)
base_issuable_scope(issuable).where('iid < ?', issuable.iid).first
end
private
def sidebar_gutter_collapsed?
cookies[:collapsed_gutter] == 'true'
end
def base_issuable_scope(issuable)
issuable.project.send(issuable.class.table_name).send(issuable_state_scope(issuable))
end
def issuable_state_scope(issuable)
issuable.open? ? :opened : :closed
end
end

View file

@ -44,14 +44,14 @@ module IssuesHelper
end
def bulk_update_milestone_options
milestones = project_active_milestones.to_a
milestones = @project.milestones.active.reorder(due_date: :asc, title: :asc).to_a
milestones.unshift(Milestone::None)
options_from_collection_for_select(milestones, 'id', 'title', params[:milestone_id])
end
def milestone_options(object)
milestones = object.project.milestones.active.to_a
milestones = object.project.milestones.active.reorder(due_date: :asc, title: :asc).to_a
milestones.unshift(Milestone::None)
options_from_collection_for_select(milestones, 'id', 'title', object.milestone_id)
@ -69,7 +69,7 @@ module IssuesHelper
end
end
def issue_button_visibility(issue, closed)
def issue_button_visibility(issue, closed)
return 'hidden' if issue.closed? == closed
end

View file

@ -7,6 +7,8 @@ module LabelsHelper
# project - Project object which will be used as the context for the label's
# link. If omitted, defaults to `@project`, or the label's own
# project.
# type - The type of item the link will point to (:issue or
# :merge_request). If omitted, defaults to :issue.
# block - An optional block that will be passed to `link_to`, forming the
# body of the link element. If omitted, defaults to
# `render_colored_label`.
@ -23,14 +25,19 @@ module LabelsHelper
# # Force the generated link to use a provided project
# link_to_label(label, project: Project.last)
#
# # Force the generated link to point to merge requests instead of issues
# link_to_label(label, type: :merge_request)
#
# # Customize link body with a block
# link_to_label(label) { "My Custom Label Text" }
#
# Returns a String
def link_to_label(label, project: nil, &block)
def link_to_label(label, project: nil, type: :issue, &block)
project ||= @project || label.project
link = namespace_project_issues_path(project.namespace, project,
label_name: label.name)
link = send("namespace_project_#{type.to_s.pluralize}_path",
project.namespace,
project,
label_name: label.name)
if block_given?
link_to link, &block

View file

@ -19,6 +19,19 @@ module NavHelper
end
end
def page_gutter_class
if current_path?('merge_requests#show') ||
current_path?('merge_requests#diffs') ||
current_path?('merge_requests#commits') ||
current_path?('issues#show')
if cookies[:collapsed_gutter] == 'true'
"page-gutter right-sidebar-collapsed"
else
"page-gutter right-sidebar-expanded"
end
end
end
def nav_header_class
if nav_menu_collapsed?
"header-collapsed"

View file

@ -20,6 +20,12 @@ module ProjectsHelper
end
end
def link_to_member_avatar(author, opts = {})
default_opts = { avatar: true, name: true, size: 16, author_class: 'author', title: ":name" }
opts = default_opts.merge(opts)
image_tag(avatar_icon(author, opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]}", alt:'') if opts[:avatar]
end
def link_to_member(project, author, opts = {})
default_opts = { avatar: true, name: true, size: 16, author_class: 'author', title: ":name" }
opts = default_opts.merge(opts)
@ -53,14 +59,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" } do
link_output = simple_sanitize(project.name)
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
project_link += icon "chevron-down", class: "dropdown-toggle-caret js-projects-dropdown-toggle" if current_user
full_title = namespace_link + ' / ' + project_link
full_title += ' &middot; '.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)
@ -83,10 +98,6 @@ module ProjectsHelper
project_nav_tabs.include? name
end
def project_active_milestones
@project.milestones.active.order("due_date, title ASC")
end
def project_for_deploy_key(deploy_key)
if deploy_key.projects.include?(@project)
@project
@ -126,7 +137,7 @@ module ProjectsHelper
nav_tabs << :merge_requests
end
if project.builds_enabled? && can?(current_user, :read_build, project)
if can?(current_user, :read_build, project)
nav_tabs << :builds
end

View file

@ -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)}",

View file

@ -33,7 +33,7 @@ module SnippetsHelper
# surrounding code.
#
# @returns Array, unique and sorted.
def matching_lines(lined_content, surrounding_lines)
def matching_lines(lined_content, surrounding_lines, query)
used_lines = []
lined_content.each_with_index do |line, line_number|
used_lines.concat bounded_line_numbers(
@ -51,9 +51,9 @@ module SnippetsHelper
# surrounding_lines() worth of unmatching lines.
#
# @returns a hash with {snippet_object, snippet_chunks:{data,start_line}}
def chunk_snippet(snippet, surrounding_lines = 3)
def chunk_snippet(snippet, query, surrounding_lines = 3)
lined_content = snippet.content.split("\n")
used_lines = matching_lines(lined_content, surrounding_lines)
used_lines = matching_lines(lined_content, surrounding_lines, query)
snippet_chunk = []
snippet_chunks = []

View file

@ -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

View file

@ -3,26 +3,27 @@ module Emails
def build_fail_email(build_id, to)
@build = Ci::Build.find(build_id)
@project = @build.project
add_project_headers
add_build_headers
headers['X-GitLab-Build-Status'] = "failed"
add_build_headers('failed')
mail(to: to, subject: subject("Build failed for #{@project.name}", @build.short_sha))
end
def build_success_email(build_id, to)
@build = Ci::Build.find(build_id)
@project = @build.project
add_project_headers
add_build_headers
headers['X-GitLab-Build-Status'] = "success"
add_build_headers('success')
mail(to: to, subject: subject("Build success for #{@project.name}", @build.short_sha))
end
private
def add_build_headers
def add_build_headers(status)
headers['X-GitLab-Build-Id'] = @build.id
headers['X-GitLab-Build-Ref'] = @build.ref
headers['X-GitLab-Build-Status'] = status.to_s
end
end
end

View file

@ -5,17 +5,18 @@ class Ability
return [] unless user.is_a?(User)
return [] if user.blocked?
case subject.class.name
when "Project" then project_abilities(user, subject)
when "Issue" then issue_abilities(user, subject)
when "Note" then note_abilities(user, subject)
when "ProjectSnippet" then project_snippet_abilities(user, subject)
when "PersonalSnippet" then personal_snippet_abilities(user, subject)
when "MergeRequest" then merge_request_abilities(user, subject)
when "Group" then group_abilities(user, subject)
when "Namespace" then namespace_abilities(user, subject)
when "GroupMember" then group_member_abilities(user, subject)
when "ProjectMember" then project_member_abilities(user, subject)
case subject
when CommitStatus then commit_status_abilities(user, subject)
when Project then project_abilities(user, subject)
when Issue then issue_abilities(user, subject)
when Note then note_abilities(user, subject)
when ProjectSnippet then project_snippet_abilities(user, subject)
when PersonalSnippet then personal_snippet_abilities(user, subject)
when MergeRequest then merge_request_abilities(user, subject)
when Group then group_abilities(user, subject)
when Namespace then namespace_abilities(user, subject)
when GroupMember then group_member_abilities(user, subject)
when ProjectMember then project_member_abilities(user, subject)
else []
end.concat(global_abilities(user))
end
@ -25,6 +26,8 @@ class Ability
case true
when subject.is_a?(PersonalSnippet)
anonymous_personal_snippet_abilities(subject)
when subject.is_a?(CommitStatus)
anonymous_commit_status_abilities(subject)
when subject.is_a?(Project) || subject.respond_to?(:project)
anonymous_project_abilities(subject)
when subject.is_a?(Group) || subject.respond_to?(:group)
@ -52,16 +55,26 @@ class Ability
:read_project_member,
:read_merge_request,
:read_note,
:read_build,
:read_commit_status,
:download_code
]
# Allow to read builds by anonymous user if guests are allowed
rules << :read_build if project.public_builds?
rules - project_disabled_features_rules(project)
else
[]
end
end
def anonymous_commit_status_abilities(subject)
rules = anonymous_project_abilities(subject.project)
# If subject is Ci::Build which inherits from CommitStatus filter the abilities
rules = filter_build_abilities(rules) if subject.is_a?(Ci::Build)
rules
end
def anonymous_group_abilities(subject)
group = if subject.is_a?(Group)
subject
@ -113,6 +126,9 @@ class Ability
if project.public? || project.internal?
rules.push(*public_project_rules)
# Allow to read builds for internal projects
rules << :read_build if project.public_builds?
end
if project.owner == user || user.admin?
@ -134,7 +150,8 @@ class Ability
def public_project_rules
@public_project_rules ||= project_guest_rules + [
:download_code,
:fork_project
:fork_project,
:read_commit_status,
]
end
@ -149,7 +166,6 @@ class Ability
:read_project_member,
:read_merge_request,
:read_note,
:read_build,
:create_project,
:create_issue,
:create_note
@ -158,24 +174,26 @@ class Ability
def project_report_rules
@project_report_rules ||= project_guest_rules + [
:create_commit_status,
:read_commit_statuses,
:read_build_artifacts,
:download_code,
:fork_project,
:create_project_snippet,
:update_issue,
:admin_issue,
:admin_label
:admin_label,
:read_commit_status,
:read_build,
]
end
def project_dev_rules
@project_dev_rules ||= project_report_rules + [
:admin_merge_request,
:create_commit_status,
:update_commit_status,
:create_build,
:update_build,
:create_merge_request,
:create_wiki,
:manage_builds,
:push_code
]
end
@ -201,7 +219,9 @@ class Ability
:admin_merge_request,
:admin_note,
:admin_wiki,
:admin_project
:admin_project,
:admin_commit_status,
:admin_build
]
end
@ -240,6 +260,10 @@ class Ability
rules += named_abilities('wiki')
end
unless project.builds_enabled
rules += named_abilities('build')
end
rules
end
@ -376,6 +400,22 @@ class Ability
rules
end
def commit_status_abilities(user, subject)
rules = project_abilities(user, subject.project)
# If subject is Ci::Build which inherits from CommitStatus filter the abilities
rules = filter_build_abilities(rules) if subject.is_a?(Ci::Build)
rules
end
def filter_build_abilities(rules)
# If we can't read build we should also not have that
# ability when looking at this in context of commit_status
%w(read create update admin).each do |rule|
rules.delete(:"#{rule}_commit_status") unless rules.include?(:"#{rule}_build")
end
rules
end
def abilities
@abilities ||= begin
abilities = Six.new

View file

@ -43,6 +43,7 @@
# metrics_port :integer default(8089)
# sentry_enabled :boolean default(FALSE)
# sentry_dsn :string
# email_author_in_body :boolean default(FALSE)
#
class ApplicationSetting < ActiveRecord::Base
@ -70,8 +71,8 @@ class ApplicationSetting < ActiveRecord::Base
url: true
validates :admin_notification_email,
allow_blank: true,
email: true
email: true,
allow_blank: true
validates :two_factor_grace_period,
numericality: { greater_than_or_equal_to: 0 }
@ -88,6 +89,14 @@ class ApplicationSetting < ActiveRecord::Base
presence: true,
if: :sentry_enabled
validates :akismet_api_key,
presence: true,
if: :akismet_enabled
validates :max_attachment_size,
presence: true,
numericality: { only_integer: true, greater_than: 0 }
validates_each :restricted_visibility_levels do |record, attr, value|
unless value.nil?
value.each do |level|
@ -143,7 +152,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

View file

@ -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

View file

@ -126,17 +126,17 @@ module Issuable
end
def to_hook_data(user)
{
hook_data = {
object_kind: self.class.name.underscore,
user: user.hook_attrs,
repository: {
name: project.name,
url: project.url_to_repo,
description: project.description,
homepage: project.web_url
},
object_attributes: hook_attrs
project: project.hook_attrs,
object_attributes: hook_attrs,
# DEPRECATED
repository: project.hook_attrs.slice(:name, :url, :description, :homepage)
}
hook_data.merge!(assignee: assignee.hook_attrs) if assignee
hook_data
end
def label_names

View file

@ -15,7 +15,7 @@ class Email < ActiveRecord::Base
belongs_to :user
validates :user_id, presence: true
validates :email, presence: true, email: { strict_mode: true }, uniqueness: true
validates :email, presence: true, uniqueness: true, email: true
validate :unique_email, if: ->(email) { email.email_changed? }
before_validation :cleanup_email

View file

@ -49,7 +49,7 @@ class Event < ActiveRecord::Base
scope :code_push, -> { where(action: PUSHED) }
scope :in_projects, ->(projects) do
where(project_id: projects.select(:id).reorder(nil)).recent
where(project_id: projects.map(&:id)).recent
end
scope :with_associations, -> { includes(project: :namespace) }

View file

@ -39,7 +39,6 @@ class Member < ActiveRecord::Base
if: :invite?
},
email: {
strict_mode: true,
allow_nil: true
},
uniqueness: {

View file

@ -137,6 +137,7 @@ class MergeRequest < ActiveRecord::Base
scope :by_milestone, ->(milestone) { where(milestone_id: milestone) }
scope :in_projects, ->(project_ids) { where("source_project_id in (:project_ids) OR target_project_id in (:project_ids)", project_ids: project_ids) }
scope :of_projects, ->(ids) { where(target_project_id: ids) }
scope :opened, -> { with_state(:opened) }
scope :merged, -> { with_state(:merged) }
scope :closed, -> { with_state(:closed) }
scope :closed_and_merged, -> { with_states(:closed, :merged) }
@ -240,7 +241,7 @@ class MergeRequest < ActiveRecord::Base
return unless unchecked?
can_be_merged =
project.repository.can_be_merged?(source_sha, target_branch)
!broken? && project.repository.can_be_merged?(source_sha, target_branch)
if can_be_merged
mark_as_mergeable
@ -258,7 +259,7 @@ class MergeRequest < ActiveRecord::Base
end
def work_in_progress?
!!(title =~ /\A\[?WIP\]?:? /i)
!!(title =~ /\A\[?WIP(\]|:| )/i)
end
def mergeable?
@ -284,7 +285,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
@ -346,10 +348,10 @@ class MergeRequest < ActiveRecord::Base
# Return the set of issues that will be closed if this merge request is accepted.
def closes_issues(current_user = self.author)
if target_branch == project.default_branch
issues = commits.flat_map { |c| c.closes_issues(current_user) }
issues.push(*Gitlab::ClosingIssueExtractor.new(project, current_user).
closed_by_message(description))
issues.uniq(&:id)
messages = commits.map(&:safe_message) << description
Gitlab::ClosingIssueExtractor.new(project, current_user).
closed_by_message(messages.join("\n"))
else
[]
end

View file

@ -34,7 +34,7 @@ class Milestone < ActiveRecord::Base
scope :closed, -> { with_state(:closed) }
scope :of_projects, ->(ids) { where(project_id: ids) }
validates :title, presence: true
validates :title, presence: true, uniqueness: { scope: :project_id }
validates :project, presence: true
strip_attributes :title

View file

@ -342,7 +342,7 @@ class Project < ActiveRecord::Base
end
def repository
@repository ||= Repository.new(path_with_namespace, nil, self)
@repository ||= Repository.new(path_with_namespace, self)
end
def commit(id = 'HEAD')
@ -738,11 +738,20 @@ class Project < ActiveRecord::Base
def hook_attrs
{
name: name,
ssh_url: ssh_url_to_repo,
http_url: http_url_to_repo,
description: description,
web_url: web_url,
avatar_url: avatar_url,
git_ssh_url: ssh_url_to_repo,
git_http_url: http_url_to_repo,
namespace: namespace.name,
visibility_level: visibility_level
visibility_level: visibility_level,
path_with_namespace: path_with_namespace,
default_branch: default_branch,
# Backward compatibility
homepage: web_url,
url: url_to_repo,
ssh_url: ssh_url_to_repo,
http_url: http_url_to_repo
}
end
@ -790,6 +799,8 @@ class Project < ActiveRecord::Base
def change_head(branch)
# Cached divergent commit counts are based on repository head
repository.expire_branch_cache
repository.expire_root_ref_cache
gitlab_shell.update_repository_head(self.path_with_namespace, branch)
reload_default_branch
end

View file

@ -112,7 +112,7 @@ class PushoverService < Service
priority: priority,
title: "#{project.name_with_namespace}",
message: message,
url: data[:repository][:homepage],
url: data[:project][:web_url],
url_title: "See project #{project.name_with_namespace}"
}

View file

@ -123,7 +123,7 @@ class ProjectWiki
end
def repository
Repository.new(path_with_namespace, default_branch, @project)
Repository.new(path_with_namespace, @project)
end
def default_branch

View file

@ -15,7 +15,7 @@ class Repository
Gitlab::Popen.popen(%W(find #{repository_downloads_path} -not -path #{repository_downloads_path} -mmin +120 -delete))
end
def initialize(path_with_namespace, default_branch = nil, project = nil)
def initialize(path_with_namespace, project)
@path_with_namespace = path_with_namespace
@project = project
end
@ -44,7 +44,9 @@ class Repository
end
def empty?
raw_repository.empty?
return @empty unless @empty.nil?
@empty = cache.fetch(:empty?) { raw_repository.empty? }
end
#
@ -57,7 +59,11 @@ class Repository
# This method return true if repository contains some content visible in project page.
#
def has_visible_content?
raw_repository.branch_count > 0
return @has_visible_content unless @has_visible_content.nil?
@has_visible_content = cache.fetch(:has_visible_content?) do
raw_repository.branch_count > 0
end
end
def commit(id = 'HEAD')
@ -78,7 +84,8 @@ class Repository
offset: offset,
# --follow doesn't play well with --skip. See:
# https://gitlab.com/gitlab-org/gitlab-ce/issues/3574#note_3040520
follow: false
follow: false,
skip_merges: skip_merges
}
commits = Gitlab::Git::Commit.where(options)
@ -184,8 +191,11 @@ class Repository
cache.fetch(:"diverging_commit_counts_#{branch.name}") do
# Rugged seems to throw a `ReferenceError` when given branch_names rather
# than SHA-1 hashes
number_commits_behind = commits_between(branch.target, root_ref_hash).size
number_commits_ahead = commits_between(root_ref_hash, branch.target).size
number_commits_behind = raw_repository.
count_commits_between(branch.target, root_ref_hash)
number_commits_ahead = raw_repository.
count_commits_between(root_ref_hash, branch.target)
{ behind: number_commits_behind, ahead: number_commits_ahead }
end
@ -196,12 +206,6 @@ class Repository
readme version contribution_guide changelog license)
end
def branch_cache_keys
branches.map do |branch|
:"diverging_commit_counts_#{branch.name}"
end
end
def build_cache
cache_keys.each do |key|
unless cache.exist?(key)
@ -226,20 +230,39 @@ class Repository
@branches = nil
end
def expire_cache
def expire_cache(branch_name = nil)
cache_keys.each do |key|
cache.expire(key)
end
expire_branch_cache
expire_branch_cache(branch_name)
end
def expire_branch_cache
branches.each do |branch|
cache.expire(:"diverging_commit_counts_#{branch.name}")
def expire_branch_cache(branch_name = nil)
# When we push to the root branch we have to flush the cache for all other
# branches as their statistics are based on the commits relative to the
# root branch.
if !branch_name || branch_name == root_ref
branches.each do |branch|
cache.expire(:"diverging_commit_counts_#{branch.name}")
end
# In case a commit is pushed to a non-root branch we only have to flush the
# cache for said branch.
else
cache.expire(:"diverging_commit_counts_#{branch_name}")
end
end
def expire_root_ref_cache
cache.expire(:root_ref)
@root_ref = nil
end
def expire_has_visible_content_cache
cache.expire(:has_visible_content?)
@has_visible_content = nil
end
def rebuild_cache
cache_keys.each do |key|
cache.expire(key)
@ -477,7 +500,7 @@ class Repository
end
def root_ref
@root_ref ||= raw_repository.root_ref
@root_ref ||= cache.fetch(:root_ref) { raw_repository.root_ref }
end
def commit_dir(user, path, message, branch)

10
app/models/spam_log.rb Normal file
View file

@ -0,0 +1,10 @@
class SpamLog < ActiveRecord::Base
belongs_to :user
validates :user, presence: true
def remove_user
user.block
user.destroy
end
end

View file

@ -0,0 +1,5 @@
class SpamReport < ActiveRecord::Base
belongs_to :user
validates :user, presence: true
end

View file

@ -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

View file

@ -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'
@ -145,11 +146,8 @@ class User < ActiveRecord::Base
# Validations
#
validates :name, presence: true
# Note that a 'uniqueness' and presence check is provided by devise :validatable for email. We do not need to
# duplicate that here as the validation framework will have duplicate errors in the event of a failure.
validates :email, presence: true, email: { strict_mode: true }
validates :notification_email, presence: true, email: { strict_mode: true }
validates :public_email, presence: true, email: { strict_mode: true }, allow_blank: true, uniqueness: true
validates :notification_email, presence: true, email: true
validates :public_email, presence: true, uniqueness: true, email: true, allow_blank: true
validates :bio, length: { maximum: 255 }, allow_blank: true
validates :projects_limit, presence: true, numericality: { greater_than_or_equal_to: 0 }
validates :username,

Some files were not shown because too many files have changed in this diff Show more