Commit 82d2e5c3 authored by Zeger-Jan van de Weg's avatar Zeger-Jan van de Weg

Merge branch 'master' into copying-file-seen-as-licence

parents 5fd280f3 4e5897f5

Too many changes to show.

To preserve performance only 1000 of 1000+ files are displayed.

......@@ -37,8 +37,10 @@ nohup.out
public/assets/
public/uploads.*
public/uploads/
shared/artifacts/
rails_best_practices_output.html
/tags
tmp/
vendor/bundle/*
builds/*
shared/*
......@@ -8,7 +8,7 @@ before_script:
- touch log/application.log
- touch log/test.log
- bundle install --without postgres production --jobs $(nproc) "${FLAGS[@]}"
- bundle exec rake db:create RAILS_ENV=test
- bundle exec rake db:reset db:create RAILS_ENV=test
spec:feature:
script:
......@@ -24,6 +24,27 @@ spec:api:
- ruby
- mysql
spec:models:
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:models
tags:
- ruby
- mysql
spec:lib:
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:lib
tags:
- ruby
- mysql
spec:services:
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:services
tags:
- ruby
- mysql
spec:benchmark:
script:
- RAILS_ENV=test bundle exec rake spec:benchmark
......@@ -39,9 +60,16 @@ spec:other:
- ruby
- mysql
spinach:project:
spinach:project:half:
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:half
tags:
- ruby
- mysql
spinach:project:rest:
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:rest
tags:
- ruby
- mysql
......@@ -73,3 +101,25 @@ brakeman:
tags:
- ruby
- mysql
flog:
script:
- bundle exec rake flog
tags:
- ruby
- mysql
flay:
script:
- bundle exec rake flay
tags:
- ruby
- mysql
bundler:audit:
script:
- "bundle exec bundle-audit update"
- "bundle exec bundle-audit check"
tags:
- ruby
- mysql
......@@ -888,7 +888,7 @@ Lint/RequireParentheses:
Lint/RescueException:
Description: 'Avoid rescuing the Exception class.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-blind-rescues'
Enabled: false
Enabled: true
Lint/ShadowingOuterLocalVariable:
Description: >-
......
Please view this file on the master branch, on stable branches it's out of date.
v 8.2.0 (unreleased)
- Remove CSS property preventing hard tabs from rendering in Chromium 45 (Stan Hu)
v 8.3.0 (unreleased)
- Merge when build succeeds (Zeger-Jan van de Weg)
- Bump gollum-lib to 4.1.0 (Stan Hu)
- Fix broken group avatar upload under "New group" (Stan Hu)
- Update project repositorize size and commit count during import:repos task (Stan Hu)
- Fix API setting of 'public' attribute to false will make a project private (Stan Hu)
- Handle and report SSL errors in Web hook test (Stan Hu)
- Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera)
- Add rake tasks for git repository maintainance (Zeger-Jan van de Weg)
- Fix 500 error when update group member permission
- Trim leading and trailing whitespace of milestone and issueable titles (Jose Corcuera)
- Recognize issue/MR/snippet/commit links as references
- Add ignore whitespace change option to commit view
- Fire update hook from GitLab
- Style warning about mentioning many people in a comment
- Fix: sort milestones by due date once again (Greg Smethells)
- Don't show project fork event as "imported"
- Add API endpoint to fetch merge request commits list
- Expose events API with comment information and author info
- Fix: Ensure "Remove Source Branch" button is not shown when branch is being deleted. #3583
- Run custom Git hooks when branch is created or deleted.
- Fix bug when simultaneously accepting multiple MRs results in MRs that are of "merged" status, but not merged to the target branch
- Add languages page to graphs
- Block LDAP user when they are no longer found in the LDAP server
- Improve wording on project visibility levels (Zeger-Jan van de Weg)
- Automatically select default clone protocol based on user preferences (Eirik Lygre)
- Make Network page as sub tab of Commits
- Add copy-to-clipboard button for Snippets
v 8.2.3
- Fix application settings cache not expiring after changes (Stan Hu)
- Fix Error 500s when creating global milestones with Unicode characters (Stan Hu)
- Update documentation for "Guest" permissions
- Properly convert Emoji-only comments into Award Emojis
- Enable devise paranoid mode to prevent user enumeration attack
v 8.2.3
- Webhook payload has an added, modified and removed properties for each commit
- Fix 500 error when creating a merge request that removes a submodule
v 8.2.2
- Fix 404 in redirection after removing a project (Stan Hu)
- Ensure cached application settings are refreshed at startup (Stan Hu)
- Fix Error 500 when viewing user's personal projects from admin page (Stan Hu)
- Fix: Raw private snippets access workflow
- Prevent "413 Request entity too large" errors when pushing large files with LFS
- Fix: As an admin, cannot add oneself as a member to a group/project
- Fix invalid links within projects dashboard header
- Make current user the first user in assignee dropdown in issues detail page (Stan Hu)
- Fix: duplicate email notifications on issue comments
v 8.2.1
- Forcefully update builds that didn't want to update with state machine
- Fix: saving GitLabCiService as Admin Template
v 8.2.0
- Improved performance of finding projects and groups in various places
- Improved performance of rendering user profile pages and Atom feeds
- Expose build artifacts path as config option
- Fix grouping of contributors by email in graph.
- Improved performance of finding issues with/without labels
- Fix Drone CI service template not saving properly (Stan Hu)
- Fix avatars not showing in Atom feeds and project issues when Gravatar disabled (Stan Hu)
- Added a GitLab specific profiling tool called "Sherlock" (see GitLab CE merge request #1749)
- Upgrade gitlab_git to 7.2.20 and rugged to 0.23.3 (Stan Hu)
- Improved performance of finding users by one of their Email addresses
- Add allow_failure field to commit status API (Stan Hu)
- Commits without .gitlab-ci.yml are marked as skipped
- Save detailed error when YAML syntax is invalid
- Since GitLab CI is enabled by default, remove enabling it by pushing .gitlab-ci.yml
- Added build artifacts
- Improved performance of replacing references in comments
- Show last project commit to default branch on project home page
- Highlight comment based on anchor in URL
......@@ -20,25 +84,40 @@ v 8.2.0 (unreleased)
- Send build name and stage in CI notification e-mail
- Extend yml syntax for only and except to support specifying repository path
- Enable shared runners to all new projects
- Bump GitLab-Workhorse to 0.4.1
- Allow to define cache in `.gitlab-ci.yml`
- Fix: 500 error returned if destroy request without HTTP referer (Kazuki Shimizu)
- Remove deprecated CI events from project settings page
- Improve personal snippet access workflow (Douglas Alexandre)
- [API] Add ability to fetch the commit ID of the last commit that actually touched a file
- Fix omniauth documentation setting for omnibus configuration (Jon Cairns)
- Add "New file" link to dropdown on project page
- Include commit logs in project search
- Add "added", "modified" and "removed" properties to commit object in webhook
- Rename "Back to" links to "Go to" because its not always a case it point to place user come from
- Allow groups to appear in the search results if the group owner allows it
- Add email notification to former assignee upon unassignment (Adam Lieskovský)
- New design for project graphs page
- Fix incoming email config defaults
- Remove deprecated dumped yaml file generated from previous job definitions
- Show specific runners from projects where user is master or owner
- MR target branch is now visible on a list view when it is different from project's default one
- Improve Continuous Integration graphs page
- Accept COPYING,COPYING.lesser, and licence as license file (Zeger-Jan van de Weg)
- Make color of "Accept Merge Request" button consistent with current build status
- Add ignore white space option in merge request diff and commit and compare view
- Ability to add release notes (markdown text and attachments) to git tags (aka Releases)
- Relative links from a repositories README.md now link to the default branch
- Fix trailing whitespace issue in merge request/issue title
- Fix bug when milestone/label filter was empty for dashboard issues page
- Add ability to create milestone in group projects from single form
- Add option to create merge request when editing/creating a file (Dirceu Tiegs)
- Prevent the last owner of a group from being able to delete themselves by 'adding' themselves as a master (James Lopez)
- Add Award Emoji to issue and merge request pages
v 8.1.4
- Fix bug where manually merged branches in a MR would end up with an empty diff (Stan Hu)
- Prevent redirect loop when home_page_url is set to the root URL
- Fix incoming email config defaults
- Make color of "Accept Merge Request" button consistent with current build status
- Remove CSS property preventing hard tabs from rendering in Chromium 45 (Stan Hu)
v 8.1.3
......@@ -178,7 +257,6 @@ v 8.0.2
- Allow AWS S3 Server-Side Encryption with Amazon S3-Managed Keys for backups (Paul Beattie)
v 8.0.1
- Remove git refs used internally by GitLab from network graph (Stan Hu)
- Improve CI migration procedure and documentation
v 8.0.0
......
......@@ -10,7 +10,7 @@ By submitting code as an individual you agree to the [individual contributor lic
## Security vulnerability disclosure
Please report suspected security vulnerabilities in private to support@gitlab.com, also see the [disclosure section on the GitLab.com website](http://about.gitlab.com/disclosure/). Please do NOT create publicly viewable issues for suspected security vulnerabilities.
Please report suspected security vulnerabilities in private to support@gitlab.com, also see the [disclosure section on the GitLab.com website](https://about.gitlab.com/disclosure/). Please do NOT create publicly viewable issues for suspected security vulnerabilities.
## Closing policy for issues and merge requests
......@@ -23,10 +23,20 @@ Issues and merge requests should be in English and contain appropriate language
## Helping others
Please help other GitLab users when you can.
The channnels people will reach out on can be found on the [getting help page](https://about.gitlab.com/getting-help/).
Sign up for the mailinglist, answer GitLab questions on StackOverflow or respond in the irc channel.
The channels people will reach out on can be found on the [getting help page](https://about.gitlab.com/getting-help/).
Sign up for the mailinglist, answer GitLab questions on StackOverflow or respond in the IRC channel.
You can also sign up on [CodeTriage](http://www.codetriage.com/gitlabhq/gitlabhq) to help with one issue every day.
## I want to contribute!
If you want to contribute to GitLab, but are not sure where to start,
look for [issues](https://gitlab.com/gitlab-org/gitlab-ce/issues?milestone_id=&scope=all&sort=created_desc&state=opened&utf8=%E2%9C%93&assignee_id=&author_id=&milestone_title=&label_name=up-for-grabs)
with the label `up-for-grabs`.
These issues will be of reasonable size and challenge, for anyone to start
contributing to GitLab.
This was inspired by [an article by Kent C. Dodds](https://medium.com/@kentcdodds/first-timers-only-78281ea47455#.i2f363mx4).
## Issue tracker
To get support for your particular problem please use the [getting help channels](https://about.gitlab.com/getting-help/).
......@@ -35,7 +45,7 @@ The [GitLab CE issue tracker on GitLab.com](https://gitlab.com/gitlab-org/gitlab
Do not use the issue tracker for feature requests. We have a specific [feature request forum](http://feedback.gitlab.com) for this purpose. Please keep feature requests as small and simple as possible, complex ones might be edited to make them small and simple.
Please send a merge request with a tested solution or a merge request with a failing test instead of opening an issue if you can. If you're unsure where to post, post to the [mailing list](https://groups.google.com/forum/#!forum/gitlabhq) or [Stack Overflow](http://stackoverflow.com/questions/tagged/gitlab) first. There are a lot of helpful GitLab users there who may be able to help you quickly. If your particular issue turns out to be a bug, it will find its way from there.
Please send a merge request with a tested solution or a merge request with a failing test instead of opening an issue if you can. If you're unsure where to post, post to the [mailing list](https://groups.google.com/forum/#!forum/gitlabhq) or [Stack Overflow](https://stackoverflow.com/questions/tagged/gitlab) first. There are a lot of helpful GitLab users there who may be able to help you quickly. If your particular issue turns out to be a bug, it will find its way from there.
### Issue tracker guidelines
......@@ -47,10 +57,10 @@ Please send a merge request with a tested solution or a merge request with a fai
1. **Observed behavior**
1. **Relevant logs and/or screenshots:** Please use code blocks (\`\`\`) to format console output, logs, and code as it's very hard to read otherwise.
1. **Output of checks**
* Results of GitLab [Application Check](doc/install/installation.md#check-application-status) (`sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production SANITIZE=true`); we will only investigate if the tests are passing
* Results of GitLab [Application Check](doc/install/installation.md#check-application-status) (For installations with omnibus-gitlab package: `sudo gitlab-rake gitlab:check SANITIZE=true`); For installations from source: `sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production SANITIZE=true`); we will only investigate if the tests are passing
* Version of GitLab you are running; we will only investigate issues in the latest stable and development releases as per the [maintenance policy](MAINTENANCE.md)
* Add the last commit SHA-1 of the GitLab version you used to replicate the issue (obtainable from the help page)
* Describe your setup (use relevant parts from `sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production`)
* Describe your setup (use relevant parts from the env info: For installations with omnibus-gitlab package: `sudo gitlab-rake gitlab:env:info`; For installations from source: `sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production`)
1. **Possible fixes**: If you can, link to the line of code that might be responsible for the problem
## Merge requests
......@@ -59,7 +69,7 @@ We welcome merge requests with fixes and improvements to GitLab code, tests, and
Merge requests can be filed either at [gitlab.com](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests) or [github.com](https://github.com/gitlabhq/gitlabhq/pulls).
If you are new to GitLab development (or web development in general), search for the label `easyfix` ([gitlab.com](https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=easyfix), [github](https://github.com/gitlabhq/gitlabhq/labels/easyfix)). Those are issues easy to fix, marked by the GitLab core-team. If you are unsure how to proceed but want to help, mention one of the core-team members to give you a hint.
If you are new to GitLab development (or web development in general), search for the label `easyfix` ([GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=easyfix), [GitHub](https://github.com/gitlabhq/gitlabhq/labels/easyfix)). Those are issues easy to fix, marked by the GitLab core-team. If you are unsure how to proceed but want to help, mention one of the core-team members to give you a hint.
To start with GitLab download the [GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit) and see [Development section](doc/development/README.md) in the help file.
......@@ -72,7 +82,7 @@ If you can, please submit a merge request with the fix or improvements including
1. Write [tests](https://gitlab.com/gitlab-org/gitlab-development-kit#running-the-tests) and code
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
1. If you have multiple commits please combine them into one commit by [squashing them](http://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits)
1. If you have multiple commits please combine them into one commit by [squashing them](https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits)
1. Push the commit 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
......@@ -99,7 +109,7 @@ If you contribute to GitLab please know that changes involve more than just code
We have the following [definition of done](http://guide.agilealliance.org/guide/definition-of-done.html).
Please ensure you support the feature you contribute through all of these steps.
1. Description explaning the relevancy (see following item)
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
......@@ -163,7 +173,7 @@ If you add a dependency in GitLab (such as an operating system package) please c
1. [Markdown](http://www.cirosantilli.com/markdown-styleguide)
1. [Database Migrations](doc/development/migration_style_guide.md)
1. [Documentation styleguide](doc_styleguide.md)
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 of "prohibited this user from being saved due to the following errors:" the text should be "sorry, we could not create your account because:". Also these [excellent writing guidelines](https://github.com/NARKOZ/guides#writing).
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 of "prohibited this user from being saved due to the following errors:" the text should be "sorry, we could not create your account because:". Also these [excellent writing guidelines](https://github.com/NARKOZ/guides#writing).
This is also the style used by linting tools such as [RuboCop](https://github.com/bbatsov/rubocop), [PullReview](https://www.pullreview.com/) and [Hound CI](https://houndci.com).
......@@ -181,4 +191,4 @@ This code of conduct applies both within project spaces and in public spaces whe
Instances of abusive, harassing, or otherwise unacceptable behavior can be reported by emailing contact@gitlab.com
This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.1.0, available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/)
This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.1.0, available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/)
source "https://rubygems.org"
gem 'rails', '4.1.12'
gem 'rails', '4.2.4'
gem 'rails-deprecated_sanitizer', '~> 1.0.3'
# Responders respond_to and respond_with
gem 'responders', '~> 2.0'
# Specify a sprockets version due to security issue
# See https://groups.google.com/forum/#!topic/rubyonrails-security/doAVp0YaTqY
......@@ -16,7 +20,7 @@ gem "pg", '~> 0.18.2', group: :postgres
# Authentication libraries
gem 'devise', '~> 3.5.2'
gem 'devise-async', '~> 0.9.0'
gem 'doorkeeper', '~> 2.1.3'
gem 'doorkeeper', '~> 2.2.0'
gem 'omniauth', '~> 1.2.2'
gem 'omniauth-bitbucket', '~> 0.0.2'
gem 'omniauth-facebook', '~> 3.0.0'
......@@ -28,7 +32,7 @@ gem 'omniauth-saml', '~> 1.4.0'
gem 'omniauth-shibboleth', '~> 1.2.0'
gem 'omniauth-twitter', '~> 1.2.0'
gem 'omniauth_crowd'
gem 'rack-oauth2', '~> 1.0.5'
gem 'rack-oauth2', '~> 1.2.1'
# Two-factor authentication
gem 'devise-two-factor', '~> 2.0.0'
......@@ -48,13 +52,13 @@ gem "gitlab_git", '~> 7.2.20'
gem 'gitlab_omniauth-ldap', '~> 1.2.1', require: "omniauth-ldap"
# Git Wiki
gem 'gollum-lib', '~> 4.0.2'
gem 'gollum-lib', '~> 4.1.0'
# Language detection
gem "github-linguist", "~> 4.7.0", require: "linguist"
# API
gem 'grape', '~> 0.6.1'
gem 'grape', '~> 0.13.0'
gem 'grape-entity', '~> 0.4.2'
gem 'rack-cors', '~> 0.4.0', require: 'rack/cors'
......@@ -62,9 +66,6 @@ gem 'rack-cors', '~> 0.4.0', require: 'rack/cors'
# based on human-friendly examples
gem "stamp", '~> 0.6.0'
# Enumeration fields
gem 'enumerize', '~> 0.7.0'
# Pagination
gem "kaminari", "~> 0.16.3"
......@@ -92,12 +93,12 @@ gem 'html-pipeline', '~> 1.11.0'
gem 'task_list', '~> 1.0.2', require: 'task_list/railtie'
gem 'github-markup', '~> 1.3.1'
gem 'redcarpet', '~> 3.3.3'
gem 'RedCloth', '~> 4.2.9'
gem 'rdoc', '~>3.6'
gem 'org-ruby', '~> 0.9.12'
gem 'creole', '~>0.3.6'
gem 'creole', '~> 0.5.0'
gem 'wikicloth', '0.8.1'
gem 'asciidoctor', '~> 1.5.2'
gem 'rouge', '~> 1.10.1'
# Diffs
gem 'diffy', '~> 3.0.3'
......@@ -118,15 +119,14 @@ gem 'acts-as-taggable-on', '~> 3.4'
# Background jobs
gem 'sinatra', '~> 1.4.4', require: nil
gem 'sidekiq', '3.3.0'
gem 'sidetiq', '~> 0.6.3'
gem 'sidekiq', '~> 3.5.0'
gem 'sidekiq-cron', '~> 0.3.0'
# HTTP requests
gem "httparty", '~> 0.13.3'
# Colored output to console
gem "colored", '~> 1.2'
gem "colorize", '~> 0.5.8'
gem "colorize", '~> 0.7.0'
# GitLab settings
gem 'settingslogic', '~> 2.0.9'
......@@ -154,7 +154,7 @@ gem "gemnasium-gitlab-service", "~> 0.2"
gem "slack-notifier", "~> 1.2.0"
# Asana integration
gem 'asana', '~> 0.0.6'
gem 'asana', '~> 0.4.0'
# FogBugz integration
gem 'ruby-fogbugz', '~> 0.2.1'
......@@ -170,6 +170,7 @@ gem "underscore-rails", "~> 1.4.4"
# Sanitize user input
gem "sanitize", '~> 2.0'
gem 'babosa', '~> 1.0.2'
# Protect against bruteforcing
gem "rack-attack", '~> 4.3.0'
......@@ -187,13 +188,13 @@ gem "sass-rails", '~> 4.0.5'
gem "coffee-rails", '~> 4.1.0'
gem "uglifier", '~> 2.7.2'
gem 'turbolinks', '~> 2.5.0'
gem 'jquery-turbolinks', '~> 2.0.1'
gem 'jquery-turbolinks', '~> 2.1.0'
gem 'addressable', '~> 2.3.8'
gem 'bootstrap-sass', '~> 3.0'
gem 'font-awesome-rails', '~> 4.2'
gem 'gitlab_emoji', '~> 0.1'
gem 'gon', '~> 5.0.0'
gem 'gon', '~> 6.0.1'
gem 'jquery-atwho-rails', '~> 1.3.2'
gem 'jquery-rails', '~> 3.1.3'
gem 'jquery-scrollto-rails', '~> 1.4.3'
......@@ -203,6 +204,7 @@ gem 'raphael-rails', '~> 2.1.2'
gem 'request_store', '~> 1.2.0'
gem 'select2-rails', '~> 3.5.9'
gem 'virtus', '~> 1.0.1'
gem 'net-ssh', '~> 3.0.1'
group :development do
gem "foreman"
......@@ -214,6 +216,7 @@ group :development do
gem 'rerun', '~> 0.10.0'
gem 'bullet', require: false
gem 'rblineprof', platform: :mri, require: false
gem 'web-console', '~> 2.0'
# Better errors handler
gem 'better_errors', '~> 1.0.1'
......@@ -259,6 +262,9 @@ group :development, :test do
gem 'rubocop', '~> 0.28.0', require: false
gem 'coveralls', '~> 0.8.2', require: false
gem 'simplecov', '~> 0.10.0', require: false
gem 'flog', require: false
gem 'flay', require: false
gem 'bundler-audit', require: false
gem 'benchmark-ips', require: false
end
......@@ -267,7 +273,7 @@ group :test do
gem 'shoulda-matchers', '~> 2.8.0', require: false
gem 'email_spec', '~> 1.6.0'
gem 'webmock', '~> 1.21.0'
gem 'test_after_commit', '~> 0.2.2'
gem 'test_after_commit', '~> 0.4.2'
gem 'sham_rack'
end
......
This diff is collapsed.
......@@ -34,13 +34,19 @@ The most important thing is making sure valid issues receive feedback from the d
## Workflow labels
Workflow labels are purposely not very detailed since that would be hard to keep updated as you would need to re-evaluate them after every comment. We optionally use functional labels on demand when want to group related issues to get an overview (for example all issues related to RVM, to tackle them in one go) and to add details to the issue.
Workflow labels are purposely not very detailed since that would be hard to keep updated as you would need to re-evaluate them after every comment. We optionally use functional labels on demand when want to group related issues to get an overview (for example all issues related to RVM, to tackle them in one go) and to add details to the issue.
- *Awaiting feedback*: Feedback pending from the reporter
- *Awaiting confirmation of fix*: The issue should already be solved in **master** (generally you can avoid this workflow item and just close the issue right away)
- *Attached MR*: There is a MR attached and the discussion should happen there
- We need to let issues stay in sync with the MR's. We can do this with a "Closing #XXXX" or "Fixes #XXXX" comment in the MR. We can't close the issue when there is a merge request because sometimes a MR is not good and we just close the MR, then the issue must stay.
- *Awaiting developer action/feedback*: Issue needs to be fixed or clarified by a developer
- *Developer*: needs help from a developer
- *UX* needs needs help from a UX designer
- *Frontend* needs help from a Front-end engineer
- *Graphics* needs help from a Graphics designer
- *up-for-grabs* is an issue suitable for first-time contributors, of reasonable difficulty and size. Not exclusive with other labels.
Example workflow: when a UX designer provided a design but it needs frontend work they remove the UX label and add the frontend label.
## Functional labels
......
# For DEVELOPMENT only. Production uses Runit in
# https://gitlab.com/gitlab-org/omnibus-gitlab or the init scripts in
# lib/support/init.d, which call scripts in bin/ .
#
web: bundle exec unicorn_rails -p ${PORT:="3000"} -E ${RAILS_ENV:="development"} -c ${UNICORN_CONFIG:="config/unicorn.rb"}
worker: bundle exec sidekiq -q post_receive -q mailer -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q incoming_email -q runner -q common -q default
worker: bundle exec sidekiq -q post_receive -q mailers -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q incoming_email -q runner -q common -q default
# mail_room: bundle exec mail_room -q -c config/mail_room.yml
......@@ -27,8 +27,6 @@ There are two editions of GitLab:
- GitLab Community Edition (CE) is available freely under the MIT Expat license.
- GitLab Enterprise Edition (EE) includes [extra features](https://about.gitlab.com/features/#compare) that are more useful for organizations with more than 100 users. To use EE and get official support please [become a subscriber](https://about.gitlab.com/pricing/).
Included with the GitLab Omnibus Packages is [GitLab CI](https://about.gitlab.com/gitlab-ci/) that can easily build, test and deploy code.
## Website
On [about.gitlab.com](https://about.gitlab.com/) you can find more information about:
......@@ -82,7 +80,7 @@ There are a lot of [third-party applications integrating with GitLab](https://ab
## GitLab release cycle
Since 2011 a minor or major version of GitLab is released on the 22nd of every month. Patch and security releases are published when needed. New features are detailed on the [blog](https://about.gitlab.com/blog/) and in the [changelog](CHANGELOG). For more information about the release process see the [release documentation](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/release). Features that will likely be in the next releases can be found on the [feature request forum](http://feedback.gitlab.com/forums/176466-general) with the status [started](http://feedback.gitlab.com/forums/176466-general/status/796456) and [completed](http://feedback.gitlab.com/forums/176466-general/status/796457).
For more information about the release process see the [release documentation](http://doc.gitlab.com/ce/release/).
## Upgrading
......
8.1.0.pre
8.3.0.pre
app/assets/images/icon-link.png

726 Bytes | W: | H:

app/assets/images/icon-link.png

1.1 KB | W: | H:

app/assets/images/icon-link.png
app/assets/images/icon-link.png
app/assets/images/icon-link.png
app/assets/images/icon-link.png
  • 2-up
  • Swipe
  • Onion skin
......@@ -2,6 +2,8 @@
groups_path: "/api/:version/groups.json"
group_path: "/api/:version/groups/:id.json"
namespaces_path: "/api/:version/namespaces.json"
group_projects_path: "/api/:version/groups/:id/projects.json"
projects_path: "/api/:version/projects.json"
group: (group_id, callback) ->
url = Api.buildUrl(Api.group_path)
......@@ -44,6 +46,35 @@
).done (namespaces) ->
callback(namespaces)
# Return projects list. Filtered by query
projects: (query, callback) ->
url = Api.buildUrl(Api.projects_path)
$.ajax(
url: url
data:
private_token: gon.api_token
search: query
per_page: 20
dataType: "json"
).done (projects) ->
callback(projects)
# Return group projects list. Filtered by query
groupProjects: (group_id, query, callback) ->
url = Api.buildUrl(Api.group_projects_path)
url = url.replace(':id', group_id)
$.ajax(
url: url
data:
private_token: gon.api_token
search: query
per_page: 20
dataType: "json"
).done (projects) ->
callback(projects)
buildUrl: (url) ->
url = gon.relative_url_root + url if gon.relative_url_root?
return url.replace(':version', gon.api_version)
......@@ -135,17 +135,25 @@ $ ->
), 1
# Initialize tooltips
$('body').tooltip({
selector: '.has_tooltip, [data-toggle="tooltip"], .page-sidebar-collapsed .nav-sidebar a'
$('body').tooltip(
selector: '.has_tooltip, [data-toggle="tooltip"]'
placement: (_, el) ->
$el = $(el)
if $el.attr('id') == 'js-shortcuts-home'
# Place the logo tooltip on the right when collapsed, bottom when expanded
$el.parents('header').hasClass('header-collapsed') and 'right' or 'bottom'
else
# Otherwise use the data-placement attribute, or 'bottom' if undefined
$el.data('placement') or 'bottom'
})
$el.data('placement') || 'bottom'
)
$('.header-logo .home').tooltip(
placement: (_, el) ->
$el = $(el)
if $('.page-with-sidebar').hasClass('page-sidebar-collapsed') then 'right' else 'bottom'
container: 'body'
)
$('.page-with-sidebar').tooltip(
selector: '.sidebar-collapsed .nav-sidebar a, .sidebar-collapsed a.sidebar-user'
placement: 'right'
container: 'body'
)
# Form submitter
$('.trigger-submit').on 'change', ->
......
class @AwardsHandler
constructor: (@post_emoji_url, @noteable_type, @noteable_id) ->
addAward: (emoji) ->
@postEmoji emoji, =>
@addAwardToEmojiBar(emoji)
addAwardToEmojiBar: (emoji, custom_path = '') ->
if @exist(emoji)
if @isActive(emoji)
@decrementCounter(emoji)
else
counter = @findEmojiIcon(emoji).siblings(".counter")
counter.text(parseInt(counter.text()) + 1)
counter.parent().addClass("active")
@addMeToAuthorList(emoji)
else
@createEmoji(emoji, custom_path)
exist: (emoji) ->
@findEmojiIcon(emoji).length > 0
isActive: (emoji) ->
@findEmojiIcon(emoji).parent().hasClass("active")
decrementCounter: (emoji) ->
counter = @findEmojiIcon(emoji).siblings(".counter")
if parseInt(counter.text()) > 1
counter.text(parseInt(counter.text()) - 1)
counter.parent().removeClass("active")
@removeMeFromAuthorList(emoji)
else
award = counter.parent()
award.tooltip("destroy")
award.remove()
removeMeFromAuthorList: (emoji) ->
award_block = @findEmojiIcon(emoji).parent()
authors = award_block.attr("data-original-title").split(", ")
authors = _.without(authors, "me").join(", ")
award_block.attr("title", authors)
@resetTooltip(award_block)
addMeToAuthorList: (emoji) ->
award_block = @findEmojiIcon(emoji).parent()
authors = award_block.attr("data-original-title").split(", ")
authors.push("me")
award_block.attr("title", authors.join(", "))
@resetTooltip(award_block)
resetTooltip: (award) ->
award.tooltip("destroy")
# "destroy" call is asynchronous, this is why we need to set timeout.
setTimeout (->
award.tooltip()
), 200
createEmoji: (emoji, custom_path) ->
nodes = []
nodes.push("<div class='award active' title='me'>")
nodes.push("<div class='icon' data-emoji='" + emoji + "'>")
nodes.push(@getImage(emoji, custom_path))
nodes.push("</div>")
nodes.push("<div class='counter'>1")
nodes.push("</div></div>")
$(".awards-controls").before(nodes.join("\n"))
$(".award").tooltip()
getImage: (emoji, custom_path) ->
if custom_path
$("<img>").attr({src: custom_path, width: 20, height: 20}).wrap("<div>").parent().html()
else
$("li[data-emoji='" + emoji + "']").html()
postEmoji: (emoji, callback) ->
$.post @post_emoji_url, { note: {
note: ":" + emoji + ":"
noteable_type: @noteable_type
noteable_id: @noteable_id
}},(data) ->
if data.ok
callback.call()
findEmojiIcon: (emoji) ->
$(".icon[data-emoji='" + emoji + "']")
scrollToAwards: ->
$('body, html').animate({
scrollTop: $('.awards').offset().top - 80
}, 200)
......@@ -23,18 +23,6 @@ class @BlobFileDropzone
init: ->
this.on 'addedfile', (file) ->
$('.dropzone-alerts').html('').hide()
commit_message = form.find('#commit_message')[0]
if /^Upload/.test(commit_message.placeholder)
commit_message.placeholder = 'Upload ' + file.name
return
this.on 'removedfile', (file) ->
commit_message = form.find('#commit_message')[0]
if /^Upload/.test(commit_message.placeholder)
commit_message.placeholder = 'Upload new file'
return
......@@ -47,8 +35,9 @@ class @BlobFileDropzone
return
this.on 'sending', (file, xhr, formData) ->
formData.append('new_branch', form.find('#new_branch').val())
formData.append('commit_message', form.find('#commit_message').val())
formData.append('new_branch', form.find('.js-new-branch').val())
formData.append('create_merge_request', form.find('.js-create-merge-request').val())
formData.append('commit_message', form.find('.js-commit-message').val())
return
# Override behavior of adding error underneath preview
......
#= require clipboard
$ ->
clipboard = new Clipboard '.js-clipboard-trigger',
text: (trigger) ->
$target = $(trigger.nextElementSibling || trigger.previousElementSibling)
$target.data('clipboard-text') || $target.text().trim()
genericSuccess = (e) ->
showTooltip(e.trigger, 'Copied!')
# Clear the selection and blur the trigger so it loses its border
e.clearSelection()
$(e.trigger).blur()
clipboard.on 'success', (e) ->
$(e.trigger).
tooltip(trigger: 'manual', placement: 'auto bottom', title: 'Copied!').
tooltip('show')
# Safari doesn't support `execCommand`, so instead we inform the user to
# copy manually.
#
# See http://clipboardjs.com/#browser-support
genericError = (e) ->
if /Mac/i.test(navigator.userAgent)
key = '&#8984;' # Command
else
key = 'Ctrl'
# Clear the selection and blur the trigger so it loses its border
e.clearSelection()
$(e.trigger).blur()
showTooltip(e.trigger, "Press #{key}-C to copy")
# Manually hide the tooltip after 1 second
setTimeout(->
$(e.trigger).tooltip('hide')
, 1000)
showTooltip = (target, title) ->
$(target).
tooltip(
container: 'body'
html: 'true'
placement: 'auto bottom'
title: title
trigger: 'manual'
).
tooltip('show').
one('mouseleave', -> $(this).tooltip('hide'))
$ ->
clipboard = new Clipboard '[data-clipboard-target], [data-clipboard-text]'
clipboard.on 'success', genericSuccess
clipboard.on 'error', genericError
......@@ -28,6 +28,8 @@ class Dispatcher
when 'projects:milestones:new', 'projects:milestones:edit'
new ZenMode()
new DropzoneInput($('.milestone-form'))
when 'groups:milestones:new'
new ZenMode()
when 'projects:compare:show'
new Diff()
when 'projects:issues:new','projects:issues:edit'
......@@ -81,7 +83,7 @@ class Dispatcher
when 'projects:project_members:index'
new ProjectMembers()
new UsersSelect()
when 'groups:new', 'groups:edit', 'admin:groups:edit'
when 'groups:new', 'groups:edit', 'admin:groups:edit', 'admin:groups:new'
new GroupAvatar()
when 'projects:tree:show'
new TreeView()
......
#= require markdown_preview
class @DropzoneInput
constructor: (form) ->
Dropzone.autoDiscover = false
......@@ -11,17 +13,14 @@ class @DropzoneInput
uploadProgress = $("<div class=\"div-dropzone-progress\"></div>")
btnAlert = "<button type=\"button\"" + alertAttr + ">&times;</button>"
project_uploads_path = window.project_uploads_path or null
markdown_preview_path = window.markdown_preview_path or null
max_file_size = gon.max_file_size or 10
form_textarea = $(form).find("textarea.markdown-area")
form_textarea.wrap "<div class=\"div-dropzone\"></div>"
form_textarea.on 'paste', (event) =>
handlePaste(event)
form_textarea.on "input", ->
hideReferencedUsers()
form_textarea.on "blur", ->
renderMarkdown()
$(form).setupMarkdownPreview()
form_dropzone = $(form).find('.div-dropzone')
form_dropzone.parent().addClass "div-dropzone-wrapper"
......@@ -34,42 +33,6 @@ class @DropzoneInput
"opacity": 0
"display": "none"
# Preview button
$(document).off "click", ".js-md-preview-button"
$(document).on "click", ".js-md-preview-button", (e) ->
###
Shows the Markdown preview.
Lets the server render GFM into Html and displays it.
###
e.preventDefault()
form = $(this).closest("form")
# toggle tabs
form.find(".js-md-write-button").parent().removeClass "active"
form.find(".js-md-preview-button").parent().addClass "active"
# toggle content
form.find(".md-write-holder").hide()
form.find(".md-preview-holder").show()
renderMarkdown()
# Write button
$(document).off "click", ".js-md-write-button"
$(document).on "click", ".js-md-write-button", (e) ->
###
Shows the Markdown textarea.
###
e.preventDefault()
form = $(this).closest("form")
# toggle tabs
form.find(".js-md-write-button").parent().addClass "active"
form.find(".js-md-preview-button").parent().removeClass "active"
# toggle content
form.find(".md-write-holder").show()
form.find(".md-preview-holder").hide()
dropzone = form_dropzone.dropzone(
url: project_uploads_path
dictDefaultMessage: ""
......@@ -136,41 +99,6 @@ class @DropzoneInput
child = $(dropzone[0]).children("textarea")
hideReferencedUsers = ->
referencedUsers = form.find(".referenced-users")
referencedUsers.hide()
renderReferencedUsers = (users) ->
referencedUsers = form.find(".referenced-users")
if referencedUsers.length
if users.length >= 10
referencedUsers.show()
referencedUsers.find(".js-referenced-users-count").text users.length
else
referencedUsers.hide()
renderMarkdown = ->
preview = form.find(".js-md-preview")
mdText = form.find(".markdown-area").val()
if mdText.trim().length is 0
preview.text "Nothing to preview."
hideReferencedUsers()
else
preview.text "Loading..."
$.ajax(
type: "POST",
url: markdown_preview_path,
data: {
text: mdText
},
dataType: "json"
).success (data) ->
preview.html data.body
preview.syntaxHighlight()
renderReferencedUsers data.references.users
formatLink = (link) ->
text = "[#{link.alt}](#{link.url})"
text = "!#{text}" if link.is_image
......
class @Flash
constructor: (message, type)->
flash = $(".flash-container")
flash.html("")
@flash = $(".flash-container")
@flash.html("")
$('<div/>',
innerDiv = $('<div/>',
class: "flash-#{type}",
text: message
).appendTo(".flash-container")
)
innerDiv.appendTo(".flash-container")
flash.click -> $(@).fadeOut()
flash.show()
@flash.click -> $(@).fadeOut()
@flash.show()
pin: ->
@flash.addClass('flash-pinned flash-raised')
......@@ -29,7 +29,7 @@
$('#filter_issue_search').val($('#issue_search').val())
initSelects: ->
$("select#update_status").select2(width: 'resolve', dropdownAutoWidth: true)
$("select#update_state_event").select2(width: 'resolve', dropdownAutoWidth: true)
$("select#update_assignee_id").select2(width: 'resolve', dropdownAutoWidth: true)
$("select#update_milestone_id").select2(width: 'resolve', dropdownAutoWidth: true)
$("select#label_name").select2(width: 'resolve', dropdownAutoWidth: true)
......
# MarkdownPreview
#
# Handles toggling the "Write" and "Preview" tab clicks, rendering the preview,
# and showing a warning when more than `x` users are referenced.
#
class @MarkdownPreview
# Minimum number of users referenced before triggering a warning
referenceThreshold: 10
showPreview: (form) ->
preview = form.find('.js-md-preview')
mdText = form.find('textarea.markdown-area').val()
if mdText.trim().length == 0
preview.text('Nothing to preview.')
@hideReferencedUsers(form)
else
preview.text('Loading...')
@renderMarkdown mdText, (response) =>
preview.html(response.body)
preview.syntaxHighlight()
@renderReferencedUsers(response.references.users, form)
renderMarkdown: (text, success) ->
return unless window.markdown_preview_path
$.ajax
type: 'POST'
url: window.markdown_preview_path
data: { text: text }
dataType: 'json'
success: success
hideReferencedUsers: (form) ->
referencedUsers = form.find('.referenced-users')
referencedUsers.hide()
renderReferencedUsers: (users, form) ->
referencedUsers = form.find('.referenced-users')
if referencedUsers.length
if users.length >= @referenceThreshold
referencedUsers.show()
referencedUsers.find('.js-referenced-users-count').text(users.length)
else
referencedUsers.hide()
markdownPreview = new MarkdownPreview()
previewButtonSelector = '.js-md-preview-button'
writeButtonSelector = '.js-md-write-button'
$.fn.setupMarkdownPreview = ->
$form = $(this)
form_textarea = $form.find('textarea.markdown-area')
form_textarea.on 'input', -> markdownPreview.hideReferencedUsers($form)
form_textarea.on 'blur', -> markdownPreview.showPreview($form)
$(document).on 'click', previewButtonSelector, (e) ->
e.preventDefault()
$form = $(this).closest('form')
# toggle tabs
$form.find(writeButtonSelector).parent().removeClass('active')
$form.find(previewButtonSelector).parent().addClass('active')
# toggle content
$form.find('.md-write-holder').hide()
$form.find('.md-preview-holder').show()
markdownPreview.showPreview($form)
$(document).on 'click', writeButtonSelector, (e) ->
e.preventDefault()
$form = $(this).closest('form')
# toggle tabs
$form.find(writeButtonSelector).parent().addClass('active')
$form.find(previewButtonSelector).parent().removeClass('active')
# toggle content
$form.find('.md-write-holder').show()
$form.find('.md-preview-holder').hide()
......@@ -43,6 +43,7 @@
#
class @MergeRequestTabs
diffsLoaded: false
buildsLoaded: false
commitsLoaded: false
constructor: (@opts = {}) ->
......@@ -54,6 +55,12 @@ class @MergeRequestTabs
bindEvents: ->
$(document).on 'shown.bs.tab', '.merge-request-tabs a[data-toggle="tab"]', @tabShown
$(document).on 'click', '.js-show-tab', @showTab
showTab: (event) =>
event.preventDefault()
@activateTab $(event.target).data('action')
tabShown: (event) =>
$target = $(event.target)
......@@ -63,6 +70,8 @@ class @MergeRequestTabs
@loadCommits($target.attr('href'))
else if action == 'diffs'
@loadDiff($target.attr('href'))
else if action == 'builds'
@loadBuilds($target.attr('href'))
@setCurrentAction(action)
......@@ -101,7 +110,7 @@ class @MergeRequestTabs
action = 'notes' if action == 'show'
# Remove a trailing '/commits' or '/diffs'
new_state = @_location.pathname.replace(/\/(commits|diffs)(\.html)?\/?$/, '')
new_state = @_location.pathname.replace(/\/(commits|diffs|builds)(\.html)?\/?$/, '')
# Append the new action if we're on a tab other than 'notes'
unless action == 'notes'
......@@ -139,6 +148,17 @@ class @MergeRequestTabs
@diffsLoaded = true
@scrollToElement("#diffs")
loadBuilds: (source) ->
return if @buildsLoaded
@_get
url: "#{source}.json"
success: (data) =>
document.getElementById('builds').innerHTML = data.html
$('.js-timeago').timeago()
@buildsLoaded = true
@scrollToElement("#builds")
# Show or hide the loading spinner
#
# status - Boolean, true to show, false to hide
......
......@@ -10,17 +10,20 @@ class @MergeRequestWidget
constructor: (@opts) ->
modal = $('#modal_merge_info').modal(show: false)
mergeInProgress: ->
mergeInProgress: (deleteSourceBranch = false)->
$.ajax
type: 'GET'
url: $('.merge-request').data('url')
success: (data) =>
if data.state == "merged"
location.reload()
urlSuffix = if deleteSourceBranch then '?delete_source=true' else ''
window.location.href = window.location.href + urlSuffix
else if data.merge_error
$('.mr-widget-body').html("<h4>" + data.merge_error + "</h4>")
else
setTimeout(merge_request_widget.mergeInProgress, 2000)
callback = -> merge_request_widget.mergeInProgress(deleteSourceBranch)
setTimeout(callback, 2000)
dataType: 'json'
getMergeStatus: ->
......
class @NewCommitForm
constructor: (form) ->
@newBranch = form.find('.js-new-branch')
@originalBranch = form.find('.js-original-branch')
@createMergeRequest = form.find('.js-create-merge-request')
@createMergeRequestContainer = form.find('.js-create-merge-request-container')
@renderDestination()
@newBranch.keyup @renderDestination
renderDestination: =>
different = @newBranch.val() != @originalBranch.val()
if different
@createMergeRequestContainer.show()
@createMergeRequest.prop('checked', true) unless @wasDifferent
else
@createMergeRequestContainer.hide()
@createMergeRequest.prop('checked', false)
@wasDifferent = different
......@@ -111,15 +111,25 @@ class @Notes
Note: for rendering inline notes use renderDiscussionNote
###
renderNote: (note) ->
unless note.valid
if note.award
flash = new Flash('You have already used this award emoji!', 'alert')
flash.pin()
return
# render note if it not present in loaded list
# or skip if rendered
if @isNewNote(note)
if @isNewNote(note) && !note.award
@note_ids.push(note.id)
$('ul.main-notes-list').
append(note.html).
syntaxHighlight()
@initTaskList()
if note.award
awards_handler.addAwardToEmojiBar(note.note, note.emoji_path)
awards_handler.scrollToAwards()
###
Check if note does not exists on page
###
......@@ -255,7 +265,6 @@ class @Notes
###
addNote: (xhr, note, status) =>
@renderNote(note)
@updateVotes()
###
Called in response to the new note form being submitted
......@@ -360,8 +369,8 @@ class @Notes
note = $(this).closest(".note")
note.find(".note-attachment").remove()
note.find(".note-body > .note-text").show()
note.find(".js-note-attachment-delete").hide()
note.find(".note-edit-form").hide()
note.find(".note-header").show()
note.find(".current-note-edit-form").remove()
###
Called when clicking on the "reply" button for a diff line.
......@@ -473,9 +482,6 @@ class @Notes
form = $(e.target).closest(".js-discussion-note-form")
@removeDiscussionNoteForm(form)
updateVotes: ->
true
###
Called after an attachment file has been selected.
......
class @Project
constructor: ->
# Git clone panel switcher
cloneHolder = $('.git-clone-holder')
if cloneHolder.length
$('a, button', cloneHolder).click ->
$('a, button', cloneHolder).removeClass 'active'
$(@).addClass 'active'
$('#project_clone', cloneHolder).val $(@).data 'clone'
$(".clone").text("").append $(@).data 'clone'
# Git protocol switcher
$('.js-protocol-switch').click ->
return if $(@).hasClass('active')
# Toggle 'active' for both buttons
$('.js-protocol-switch').toggleClass('active')
url = $(@).data('clone')
# Update the input field
$('#project_clone').val(url)
# Update the command line instructions
$('.clone').text(url)
# Ref switcher
$('.project-refs-select').on 'change', ->
......@@ -39,4 +45,4 @@ class @Project
when 4 then label = ' On Mention '
$('#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'
\ No newline at end of file
$(@).parent().addClass 'active'
class @ProjectSelect
constructor: ->
$('.ajax-project-select').each (i, select) ->
@groupId = $(select).data('group-id')
@includeGroups = $(select).data('include-groups')
placeholder = "Search for project"
placeholder += " or group" if @includeGroups
$(select).select2
placeholder: placeholder
minimumInputLength: 0
query: (query) =>
finalCallback = (projects) ->
data = { results: projects }
query.callback(data)
if @includeGroups
projectsCallback = (projects) ->
groupsCallback = (groups) ->
data = groups.concat(projects)
finalCallback(data)
Api.groups query.term, false, groupsCallback
else
projectsCallback = finalCallback
if @groupId
Api.groupProjects @groupId, query.term, projectsCallback
else
Api.projects query.term, projectsCallback
id: (project) ->
project.web_url
text: (project) ->
project.name_with_namespace || project.name
dropdownCssClass: "ajax-project-dropdown"
......@@ -5,6 +5,7 @@ $(document).on("click", '.toggle-nav-collapse', (e) ->
$('.page-with-sidebar').toggleClass("#{collapsed} #{expanded}")
$('header').toggleClass("header-collapsed header-expanded")
$('.sidebar-wrapper').toggleClass("sidebar-collapsed sidebar-expanded")
$('.toggle-nav-collapse i').toggleClass("fa-angle-right fa-angle-left")
$.cookie("collapsed_nav", $('.page-with-sidebar').hasClass(collapsed), { path: '/' })
)
......@@ -6,7 +6,7 @@ window.ContributorsStatGraphUtil =
for entry in log
@add_date(entry.date, total) unless total[entry.date]?
data = by_author[entry.author_name] #|| by_email[entry.author_email]
data = by_author[entry.author_name] || by_email[entry.author_email]
data ?= @add_author(entry, by_author, by_email)
@add_date(entry.date, data) unless data[entry.date]
......@@ -95,5 +95,4 @@ window.ContributorsStatGraphUtil =
if date_range is null || date_range[0] <= new Date(date) <= date_range[1]
true
else
false
false
\ No newline at end of file
......@@ -2,3 +2,9 @@ class @User
constructor: ->
$('.profile-groups-avatars').tooltip("placement": "top")
new ProjectsList()
$('.hide-project-limit-message').on 'click', (e) ->
path = '/'
$.cookie('hide_project_limit_message', 'false', { path: path })
$(@).parents('.project-limit-message').remove()
e.preventDefault()
......@@ -32,17 +32,15 @@ class @UsersSelect
if showNullUser
nullUser = {
name: 'Unassigned',
avatar: null,
username: 'none',
id: 0
}
data.results.unshift(nullUser)
if showAnyUser
name = showAnyUser
name = 'Any User' if name == true
anyUser = {
name: 'Any',
avatar: null,
username: 'none',
name: name,
id: null
}
data.results.unshift(anyUser)
......@@ -50,7 +48,6 @@ class @UsersSelect
if showEmailUser && data.results.length == 0 && query.term.match(/^[^@]+@[^@]+$/)
emailUser = {
name: "Invite \"#{query.term}\"",
avatar: null,
username: query.term,
id: query.term
}
......@@ -58,11 +55,8 @@ class @UsersSelect
query.callback(data)
initSelection: (element, callback) =>
id = $(element).val()
if id != "" && id != "0"
@user(id, callback)
initSelection: (args...) =>
@initSelection(args...)
formatResult: (args...) =>
@formatResult(args...)
formatSelection: (args...) =>
......@@ -71,16 +65,24 @@ class @UsersSelect
escapeMarkup: (m) -> # we do not want to escape markup since we are displaying html in results
m
initSelection: (element, callback) ->
id = $(element).val()
if id == "0"
nullUser = { name: 'Unassigned' }
callback(nullUser)
else if id != ""
@user(id, callback)
formatResult: (user) ->
if user.avatar_url
avatar = user.avatar_url
else
avatar = gon.default_avatar_url
"<div class='user-result'>
"<div class='user-result #{'no-username' unless user.username}'>
<div class='user-image'><img class='avatar s24' src='#{avatar}'></div>
<div class='user-name'>#{user.name}</div>
<div class='user-username'>#{user.username}</div>
<div class='user-username'>#{user.username || ""}</div>
</div>"
formatSelection: (user) ->
......
@import "framework/fonts";
@import "framework/variables";
@import "framework/mixins";
@import "framework/layout";
@import 'framework/tw_bootstrap_variables';
@import 'framework/tw_bootstrap';
@import "framework/layout";
@import "framework/avatar.scss";
@import "framework/blocks.scss";
......@@ -25,6 +25,7 @@
@import "framework/markdown_area.scss";
@import "framework/mobile.scss";
@import "framework/pagination.scss";
@import "framework/panels.scss";
@import "framework/selects.scss";
@import "framework/sidebar.scss";
@import "framework/tables.scss";
......
......@@ -68,6 +68,10 @@
.oneline {
line-height: 42px;
}
> p:last-child {
margin-bottom: 0;
}
}
.cover-block {
......@@ -112,5 +116,14 @@
position: absolute;
top: 10px;
right: 10px;
&.left {
left: 10px;
right: auto;
}
}
}
.block-connector {
margin-top: -1px;
}
......@@ -7,8 +7,8 @@
/* Common styles for all types */
.bs-callout {
margin: 20px 0;
padding: 20px;
margin: $gl-padding 0;
padding: $gl-padding;
border-left: 3px solid $border-color;
color: $text-color;
background: $background-color;
......@@ -42,4 +42,3 @@
border-color: #5cA64d;
color: #3c763d;
}
......@@ -7,7 +7,7 @@
/** COMMON CLASSES **/
.prepend-top-10 { margin-top:10px }
.prepend-top-default { margin-top: $gl-padding; }
.prepend-top-default { margin-top: $gl-padding !important; }
.prepend-top-20 { margin-top:20px }
.prepend-left-10 { margin-left:10px }
.prepend-left-20 { margin-left:20px }
......@@ -52,6 +52,10 @@ pre {
}
}
hr {
margin: $gl-padding 0;
}
.dropdown-menu > li > a {
text-shadow: none;
}
......@@ -64,7 +68,7 @@ pre {
.dropdown-menu > li > a:hover,
.dropdown-menu > li > a:focus {
background: $gl-primary;
color: #FFF
color: #FFF;
}
.str-truncated {
......@@ -328,15 +332,15 @@ table {
}
}
.well {
margin-bottom: $gl-padding;
}
.search_box {
@extend .well;
text-align: center;
}
.task-status {
margin-left: 10px;
}
#nprogress .spinner {
top: 15px !important;
right: 10px !important;
......@@ -375,9 +379,8 @@ table {
text-align: center;
margin-top: 5px;
margin-bottom: $gl-padding;
height: 56px;
height: auto;
margin-top: -$gl-padding;
padding-top: $gl-padding;
&.no-bottom {
margin-bottom: 0;
......@@ -386,6 +389,18 @@ table {
&.no-top {
margin-top: 0;
}
li a {
display: inline-block;
padding-top: $gl-padding;
padding-bottom: 11px;
margin-bottom: -1px;
}
&.bottom-border {
border-bottom: 1px solid $border-color;
height: 57px;
}
}
.center-middle-menu {
......@@ -429,3 +444,20 @@ table {
.space-right {
margin-right: 10px;
}
.alert, .progress {
margin-bottom: $gl-padding;
}
.new-project-item-select-holder {
display: inline-block;
position: relative;
.new-project-item-select {
position: absolute;
top: 0;
right: 0;
width: 250px !important;
visibility: hidden;
}
}
......@@ -8,7 +8,6 @@
border: none;
border-top: 1px solid #E7E9EE;
border-bottom: 1px solid #E7E9EE;
margin-bottom: 1em;
&.readme-holder {
border-bottom: 0;
......@@ -22,10 +21,9 @@
position: relative;
background: $background-color;
border-bottom: 1px solid $border-color;
text-shadow: 0 1px 1px #fff;
margin: 0;
text-align: left;
padding: 10px 15px;
padding: 10px $gl-padding;
.file-actions {
float: right;
......@@ -171,4 +169,3 @@
}
}
}
......@@ -15,3 +15,13 @@
@extend .alert-danger;
}
}
.flash-pinned {
position: fixed;
top: 80px;
width: 80%;
}
.flash-raised {
z-index: 1000;
}
......@@ -22,9 +22,10 @@ input[type='text'].danger {
}
.form-actions {
padding: 17px 20px 18px;
margin-top: 18px;
margin-bottom: 18px;
margin: -$gl-padding;
margin-top: 0;
margin-bottom: -$gl-padding;
padding: $gl-padding;
background-color: $background-color;
border-top: 1px solid $border-color;
}
......@@ -73,6 +74,8 @@ label {
.form-control {
@include box-shadow(none);
height: 42px;
padding: 8px $gl-padding;
}
.wiki-content {
......@@ -88,7 +91,19 @@ label {
}
.input-group {
.select2-container {
display: table-cell;
width: 200px !important;
}
.input-group-addon {
background-color: #f7f8fa;
}
.input-group-addon:not(:first-child):not(:last-child) {
border-left: 0;
border-right: 0;
}
}
.help-block {
margin-bottom: 0;
}
......@@ -6,15 +6,17 @@ header {
transition-duration: .3s;
&.navbar-empty {
height: 58px;
background: #FFF;
border-bottom: 1px solid #EEE;
.center-logo {
margin: 8px 0;
margin: 11px 0;
text-align: center;
img {
height: 32px;
#tanuki-logo, img {
width: 36px;
height: 36px;
}
}
}
......@@ -118,6 +120,10 @@ header {
}
}
}
.impersonation i {
color: $red-normal;
}
}
@mixin collapsed-header {
......
......@@ -7,8 +7,9 @@
.issue-box {
@include border-radius(2px);
display: inline-block;
padding: 10px $gl-padding;
display: block;
float: left;
padding: 0 $gl-padding;
font-weight: normal;
margin-right: 10px;
font-size: $gl-font-size;
......
......@@ -2,9 +2,13 @@ html {
overflow-y: scroll;
&.touch .tooltip { display: none !important; }
}
body {
background-color: #EAEBEC !important;
body {
padding-top: $header-height;
&.navless {
background-color: white !important;
}
}
......@@ -18,7 +22,8 @@ html {
}
.navless-container {
margin-top: 30px;
margin-top: $header-height;
padding-top: $gl-padding * 2;
}
.container-limited {
......
......@@ -7,7 +7,7 @@
padding: 0;
list-style: none;
li {
> li {
padding: 10px 15px;
min-height: 20px;
border-bottom: 1px solid #eee;
......@@ -72,13 +72,6 @@
}
}
ol, ul {
&.styled {
li {
padding: 2px;
}
}
}
/** light list with border-bottom between li **/
ul.bordered-list {
......@@ -95,8 +88,14 @@ ul.bordered-list {
}
}
li.task-list-item {
list-style-type: none;
ul.task-list {
li.task-list-item {
list-style-type: none;
}
ul:not(.task-list) {
padding-left: 1.3em;
}
}
ul.content-list {
......@@ -127,3 +126,32 @@ ul.content-list {
}
}
.panel > .content-list {
li {
margin: 0;
}
}
ul.controls {
padding-top: 1px;
float: right;
list-style: none;
.btn {
padding: 10px 14px;
}
> li {
float: left;
padding-right: 10px;
.author_link {
display: inline-block;
.avatar-inline {
margin-left: 0;
margin-right: 0;
}
}
}
}
......@@ -73,11 +73,8 @@
}
.referenced-users {
padding: 10px 0;
color: #999;
margin-left: 10px;
margin-top: 1px;
margin-right: 130px;
color: #4c4e54;
padding-top: 10px;
}
.md-preview-holder {
......
......@@ -82,9 +82,6 @@
}
.center-top-menu {
height: 45px;
margin-bottom: 30px;
li a {
font-size: 14px;
padding: 19px 10px;
......
......@@ -32,3 +32,7 @@
}
}
}
.panel > .gl-pagination {
margin: 0;
}
.panel {
margin-bottom: $gl-padding;
.panel-heading {
padding: 7px $gl-padding;
line-height: 42px !important;
}
.panel-body {
padding: $gl-padding;
.form-actions {
margin: -$gl-padding;
margin-top: $gl-padding;
}
}
}
......@@ -15,6 +15,16 @@
border-left: none;
padding-top: 5px;
}
.select2-chosen {
color: $gl-text-color;
}
&.select2-default {
.select2-chosen {
color: #999;
}
}
}
}
......@@ -23,6 +33,7 @@
border: 1px solid #e7e9ed;
}
.select2-drop {
@include box-shadow(rgba(76, 86, 103, 0.247059) 0px 0px 1px 0px, rgba(31, 37, 50, 0.317647) 0px 2px 18px 0px);
@include border-radius (0px);
......@@ -48,17 +59,38 @@
color: #313236;
}
.select2-container-multi {
.select2-choices {
@include border-radius(2px);
border-color: $input-border;
background: white;
padding-left: $gl-padding / 2;
.select2-search-field input {
padding: $gl-padding / 2;
font-size: 13px;
height: auto;
font-family: inherit;
font-size: inherit;
}
.select2-container-multi .select2-choices {
@include border-radius(2px);
border-color: #CCC;
}
.select2-container-multi .select2-choices .select2-search-field input {
padding: 8px 14px;
font-size: 13px;
line-height: 18px;
height: auto;
.select2-search-choice {
margin: 8px 0 0 8px;
background: white;
box-shadow: none;
border-color: $input-border;
color: $gl-text-color;
line-height: 15px;
.select2-search-choice-close {
top: 5px;
}
&.select2-search-choice-focus {
border-color: $gl-text-color;
}
}
}
}
.select2-drop-active {
......@@ -123,10 +155,16 @@
}
.user-result {
min-height: 24px;
.user-image {
float: left;
}
.user-name {
&.no-username {
.user-name {
line-height: 24px;
}
}
}
......
.page-with-sidebar {
padding-top: $header-height;
transition-duration: .3s;
.sidebar-wrapper {
position: fixed;
top: 0;
......@@ -14,19 +17,15 @@
.sidebar-wrapper {
z-index: 99;
background: $background-color;
transition-duration: .3s;
}
.content-wrapper {
min-height: 100vh;
width: 100%;
padding: 20px;
background: #EAEBEC;
.container-fluid {
background: #FFF;
padding: $gl-padding;
min-height: 90vh;
&.container-blank {
background: none;
......@@ -36,6 +35,83 @@
}
}
.sidebar-wrapper {
.header-logo {
border-bottom: 1px solid transparent;
float: left;
height: $header-height;
width: $sidebar_width;
position: fixed;
z-index: 999;
overflow: hidden;
transition-duration: .3s;
a {
float: left;
height: $header-height;
width: 100%;
padding: 11px 0 11px 22px;
overflow: hidden;
outline: none;
transition-duration: .3s;
img {
width: 36px;
height: 36px;
}
#tanuki-logo, img {
float: left;
}
.gitlab-text-container {
width: 230px;
h3 {
width: 158px;
float: left;
margin: 0;
margin-left: 14px;
font-size: 19px;
line-height: 41px;
font-weight: normal;
}
}
}
&:hover {
background-color: #EEE;
}
}
.sidebar-user {
padding: 9px 22px;
position: fixed;
bottom: 40px;
width: $sidebar_width;
overflow: hidden;
transition-duration: .3s;
.username {
margin-left: 10px;
width: $sidebar_width - 2 * 10px;
font-size: 16px;
line-height: 34px;
}
}
}
.tanuki-shape {
transition: all 0.8s;
&:hover {
fill: rgb(255, 255, 255);
transition: all 0.1s;
}
}
.nav-sidebar {
margin-top: 14 + $header-height;
margin-bottom: 100px;
......@@ -62,7 +138,7 @@
color: $gray;
display: block;
text-decoration: none;
padding-left: 22px;
padding-left: 23px;
font-weight: normal;
outline: none;
......@@ -86,6 +162,10 @@
padding: 0px 8px;
@include border-radius(6px);
}
&.back-link i {
transition-duration: .3s;
}
}
}
}
......@@ -101,7 +181,6 @@
@mixin expanded-sidebar {
padding-left: $sidebar_width;
transition-duration: .3s;
.sidebar-wrapper {
width: $sidebar_width;
......@@ -115,16 +194,15 @@
&.back-link {
i {
visibility: hidden;
opacity: 0;
}
}
}
}
}
@mixin folded-sidebar {
padding-left: 60px;
transition-duration: .3s;
@mixin collapsed-sidebar {
padding-left: $sidebar_collapsed_width;
.sidebar-wrapper {
width: $sidebar_collapsed_width;
......@@ -133,7 +211,7 @@
width: $sidebar_collapsed_width;
a {
padding-left: 12px;
padding-left: ($sidebar_collapsed_width - 36) / 2;
.gitlab-text-container {
display: none;
......@@ -144,9 +222,13 @@
.nav-sidebar {
width: $sidebar_collapsed_width;
li a {
span {
display: none;
li {
width: auto;
a {
span {
display: none;
}
}
}
}
......@@ -156,7 +238,7 @@
}
.sidebar-user {
padding-left: 12px;
padding-left: ($sidebar_collapsed_width - 36) / 2;
width: $sidebar_collapsed_width;
.username {
......@@ -187,11 +269,11 @@
@media (max-width: $screen-md-max) {
.page-sidebar-collapsed {
@include folded-sidebar;
@include collapsed-sidebar;
}
.page-sidebar-expanded {
@include folded-sidebar;
@include collapsed-sidebar;
}
.collapse-nav {
......@@ -201,83 +283,10 @@
@media(min-width: $screen-md-max) {
.page-sidebar-collapsed {
@include folded-sidebar;
@include collapsed-sidebar;
}
.page-sidebar-expanded {
@include expanded-sidebar;
}
}
.sidebar-user {
padding: 9px 22px;
position: fixed;
bottom: 40px;
width: $sidebar_width;
overflow: hidden;
transition-duration: .3s;
.username {
margin-left: 10px;
width: $sidebar_width - 2 * 10px;
font-size: 16px;
line-height: 34px;
}
}
.sidebar-wrapper {
.header-logo {
border-bottom: 1px solid transparent;
float: left;
height: $header-height;
width: $sidebar_width;
overflow: hidden;
transition-duration: .3s;
a {
float: left;
height: $header-height;
width: 100%;
padding: 10px 22px;
overflow: hidden;
outline: none;
img {
width: 36px;
height: 36px;
}
#tanuki-logo, img {
float: left;
}
.gitlab-text-container {
width: 230px;
h3 {
width: 158px;
float: left;
margin: 0;
margin-left: 14px;
font-size: 19px;
line-height: 41px;
font-weight: normal;
}
}
}
&:hover {
background-color: #EEE;
}
}
}
.tanuki-shape {
transition: all 0.8s;
&:hover {
fill: rgb(255, 255, 255);
transition: all 0.1s;
}
}
......@@ -6,6 +6,8 @@
table {
&.table {
margin-bottom: $gl-padding;
.dropdown-menu a {
text-decoration: none;
}
......
......@@ -172,7 +172,7 @@
}
.panel-body {
form {
form, pre {
margin: 0;
}
......@@ -190,6 +190,10 @@
.btn {
min-width: 124px;
}
.btn-clipboard {
min-width: 0px;
}
}
&.panel-small {
......
......@@ -181,6 +181,10 @@ body {
line-height: 1.3;
font-size: 1.25em;
font-weight: 600;
&:last-child {
margin-bottom: 0;
}
}
.page-title-empty {
......@@ -216,6 +220,7 @@ pre {
.monospace {
font-family: $monospace_font;
font-size: 90%;
}
code {
......@@ -256,3 +261,9 @@ textarea.js-gfm-input {
.strikethrough {
text-decoration: line-through;
}
h1, h2, h3, h4 {
small {
color: $gl-gray;
}
}
......@@ -21,7 +21,7 @@
.autoscroll-container {
position: fixed;
bottom: 10px;
bottom: 20px;
right: 20px;
z-index: 100;
}
......@@ -34,7 +34,7 @@
a {
display: block;
margin-bottom: 5px;
margin-bottom: 10px;
}
}
......@@ -67,9 +67,4 @@
color: #3084bb !important;
}
}
.build-top-menu {
margin-top: 0;
margin-bottom: 2px;
}
}
......@@ -2,10 +2,6 @@
display: block;
}
.commit-title{
margin-bottom: 10px;
}
.commit-author, .commit-committer{
display: block;
color: #999;
......@@ -41,6 +37,8 @@
.commit-box {
.commit-title {
margin: 0;
font-size: 23px;
color: #313236;
}
.commit-description {
......@@ -56,6 +54,7 @@
li {
padding: 3px 0px;
line-height: 20px;
}
}
.new-file {
......@@ -107,16 +106,3 @@
z-index: 2;
}
}
.commit-ci-menu {
padding: 0;
margin: 0;
list-style: none;
margin-top: 5px;
height: 56px;
margin: -16px;
padding: 16px;
text-align: center;
margin-top: 0px;
margin-bottom: 2px;
}
......@@ -19,48 +19,38 @@
color: #B94A48;
}
}
.commit-button-annotation {
display: inline-block;
margin: 0;
padding: 2px;
> * {
float: left;
}
.message {
display: inline-block;
margin: 5px 8px 0 8px;
}
}
.file-title {
@extend .monospace;
line-height: 42px;
padding-top: 7px;
padding-bottom: 7px;
}
.editor-ref {
background: $background-color;
padding: 11px 15px;
padding-right: $gl-padding;
border-right: 1px solid $border-color;
display: inline-block;
margin: -5px -5px;
display: block;
float: left;
margin-right: 10px;
}
.editor-file-name {
.new-file-name {
display: inline-block;
width: 450px;
}
@extend .monospace;
float: left;
margin-right: 10px;
}
.form-control {
margin-top: -3px;
}
.new-file-name {
display: inline-block;
width: 450px;
float: left;
}
.form-actions {
margin: -$gl-padding;
margin-top: 0;
padding: $gl-padding
.select2 {
float: right;
}
}
.new-group-member-holder {
margin-top: 50px;
padding-top: 20px;
}
.member-search-form {
float: left;
}
......@@ -15,4 +10,4 @@
.form-control {
height: 42px;
}
}
\ No newline at end of file
}
......@@ -51,11 +51,12 @@
.issuable-details {
.page-title {
margin-top: -15px;
padding: 10px 0;
margin-top: -$gl-padding;
padding: 7px 0;
margin-bottom: 0;
color: #5c5d5e;
font-size: 16px;
line-height: 42px;
.author {
color: #5c5d5e;
......@@ -89,6 +90,17 @@
}
}
.issuable-show-labels {
a {
margin-right: 5px;
margin-bottom: 5px;
display: inline-block;
.color-label {
padding: 6px 10px;
}
}
}
.cross-project-reference {
text-align: center;
width: 100%;
......@@ -101,3 +113,72 @@
background-color: $background-color;
}
}
.awards {
@include clearfix;
line-height: 34px;
margin: 2px 0;
.award {
@include border-radius(5px);
border: 1px solid;
padding: 0px 10px;
float: left;
margin: 0 5px;
border-color: $border-color;
cursor: pointer;
&.active {
border-color: $border-gray-light;
background-color: $gray-light;
.counter {
font-weight: bold;
}
}
.icon {
float: left;
margin-right: 10px;
}
.counter {
float: left;
}
}
.awards-controls {
margin-left: 10px;
float: left;
.add-award {
font-size: 24px;
color: $gl-gray;
position: relative;
top: 2px;
&:hover,
&:link {
text-decoration: none;
}
}
.awards-menu {
padding: $gl-padding;
min-width: 214px;
> li {
cursor: pointer;
margin: 5px;
}
}
}
.awards-menu{
li {
float: left;
margin: 3px;
}
}
}
......@@ -56,17 +56,6 @@
}
}
.issue-show-labels {
a {
margin-right: 5px;
margin-bottom: 5px;
display: inline-block;
.color-label {
padding: 6px 10px;
}
}
}
form.edit-issue {
margin: 0;
}
......
......@@ -19,6 +19,7 @@
h1:first-child {
font-weight: normal;
margin-bottom: 30px;
margin-top: 0;
}
img {
......
......@@ -4,7 +4,6 @@
*/
.mr-state-widget {
background: #F7F8FA;
margin-bottom: 20px;
color: $gl-gray;
border: 1px solid #dce0e6;
@include border-radius(2px);
......@@ -19,6 +18,7 @@
.accept-merge-holder {
.accept-action {
display: inline-block;
float: left;
.accept_merge_request {
&.ci-pending,
......@@ -37,14 +37,15 @@
.accept-control {
display: inline-block;
float: left;
margin: 0;
margin-left: 20px;
padding: 5px;
padding-top: 12px;
line-height: 20px;
&.right {
float: right;
padding-top: 12px;
a {
color: $gl-gray;
}
......@@ -82,12 +83,16 @@
&.ci-error {
color: $gl-danger;
}
a.monospace {
color: inherit;
}
}
.mr-widget-body,
.ci_widget,
.mr-widget-footer {
padding: 15px;
padding: $gl-padding;
}
.normal {
......@@ -116,26 +121,8 @@
}
}
.merge-request .merge-request-tabs {
@include nav-menu;
margin: -$gl-padding;
padding: $gl-padding;
text-align: center;
margin-bottom: 1px;
}
// Mobile
@media (max-width: 480px) {
.merge-request .merge-request-tabs {
margin: 0;
padding: 0;
li {
a {
padding: 0;
}
}
}
.merge-request-details {
margin-bottom: $gl-padding;
}
.mr_source_commit,
......@@ -155,7 +142,7 @@
font-family: $monospace_font;
font-weight: bold;
overflow: hidden;
font-size: 14px;
font-size: 90%;
margin: 0 3px;
}
......@@ -192,27 +179,12 @@
line-height: 1.1;
}
.merge-request-form-info {
padding-top: 15px;
}
// hide mr close link for inline diff comment form
.diff-file .close-mr-link,
.diff-file .reopen-mr-link {
display: none;
}
.merge-request-show-labels {
a {
margin-right: 5px;
margin-bottom: 5px;
display: inline-block;
.color-label {
padding: 6px 10px;
}
}
}
.merge-request-form .select2-container {
width: 250px !important;
}
......
......@@ -7,6 +7,7 @@
}
.reply-btn {
@extend .btn-primary;
margin: 10px $gl-padding;
}
.diff-file .diff-content {
tr.line_holder:hover {
......@@ -38,9 +39,8 @@
}
.new_note, .edit_note {
.buttons {
margin-top: 8px;
margin-bottom: 3px;
.note-form-actions {
margin-top: $gl-padding;
}
.note-preview-holder {
......@@ -79,8 +79,8 @@
padding: $gl-padding;
margin-left: -$gl-padding;
margin-right: -$gl-padding;
border-right: 1px solid #ECEEF1;
border-top: 1px solid #ECEEF1;
border-right: 1px solid $border-color;
border-top: 1px solid $border-color;
margin-bottom: -$gl-padding;
}
......@@ -150,7 +150,6 @@
.discussion-reply-holder {
background: $background-color;
padding: 10px 15px;
border-top: 1px solid $border-color;
}
}
......
......@@ -109,13 +109,9 @@ ul.notes {
}
}
// Reduce left padding of first task list ul element
ul.task-list:first-child {
padding-left: 10px;
// sub-tasks should be padded normally
ul {
padding-left: 20px;
ul.task-list {
ul:not(.task-list) {
padding-left: 1.3em;
}
}
......
......@@ -5,12 +5,6 @@
}
}
.btn-build-token {
float: left;
padding: 6px 20px;
margin-right: 12px;
}
.profile-avatar-form-option {
hr {
margin: 10px 0;
......
......@@ -5,7 +5,7 @@
font-weight: normal;
}
}
.no-ssh-key-message {
.no-ssh-key-message, .project-limit-message {
background-color: #f28d35;
margin-bottom: 16px;
}
......@@ -18,10 +18,6 @@
}
}
.project-edit-content {
padding: 7px;
}
.project-name-holder {
.help-inline {
vertical-align: top;
......@@ -30,12 +26,6 @@
}
.project-home-panel {
text-align: center;
background: #f7f8fa;
margin: -$gl-padding;
padding: $gl-padding;
padding: 44px 0 17px 0;
.project-identicon-holder {
margin-bottom: 16px;
......@@ -90,7 +80,12 @@
}
.visibility-level-label {
@extend .btn;
@extend .btn-gray;
color: $gray;
cursor: default;
i {
color: inherit;
}
......@@ -100,7 +95,6 @@
display: inline-table;
position: relative;
top: 17px;
margin-bottom: 44px;
}
.project-repo-buttons {
......@@ -178,6 +172,11 @@
&:active {
outline: none;
}
&.btn-clipboard {
padding-left: 15px;
padding-right: 15px;
}
}
.active {
......@@ -366,7 +365,7 @@ table.table.protected-branches-list tr.no-border {
.project-stats {
text-align: center;
margin-top: 15px;
margin-top: $gl-padding;
margin-bottom: 0;
padding-top: 10px;
padding-bottom: 4px;
......@@ -552,4 +551,4 @@ pre.light-well {
z-index: 100;
position: relative;
}
}
\ No newline at end of file
}
......@@ -27,56 +27,28 @@
}
.snippet-holder {
.snippet-details {
.page-title {
margin-top: -15px;
padding: 10px 0;
margin-bottom: 0;
color: #5c5d5e;
font-size: 16px;
.author {
color: #5c5d5e;
}
.snippet-id {
color: #5c5d5e;
}
}
.snippet-title {
margin: 0;
font-size: 23px;
color: #313236;
}
@media (max-width: $screen-md-max) {
.new-snippet-link {
display: none;
}
}
@media (max-width: $screen-sm-max) {
.creator,
.page-title .btn-close {
display: none;
}
}
}
margin-bottom: -$gl-padding;
.file-holder {
border-top: 0;
}
}
.file-actions {
.btn-clipboard {
@extend .btn;
}
}
}
.snippet-box {
@include border-radius(2px);
display: inline-block;
padding: 10px $gl-padding;
display: block;
float: left;
padding: 0 $gl-padding;
font-weight: normal;
margin-right: 10px;
font-size: $gl-font-size;
border: 1px solid;
line-height: 40px;
}
.gitlab-ui-dev-kit {
> h2 {
font-size: 27px;
border-bottom: 1px solid #CCC;
color: #666;
margin: 30px 0;
margin: 35px 0 20px;
font-weight: bold;
}
}
......@@ -4,3 +4,8 @@
margin-right: auto;
padding-right: 7px;
}
.wiki-last-edit-by {
font-size: 80%;
font-weight: normal;
}
......@@ -10,7 +10,7 @@ class AbuseReportsController < ApplicationController
if @abuse_report.save
if current_application_settings.admin_notification_email.present?
AbuseReportMailer.delay.notify(@abuse_report.id)
AbuseReportMailer.notify(@abuse_report.id).deliver_later
end
message = "Thank you for your report. A GitLab administrator will look into it shortly."
......
......@@ -8,4 +8,10 @@ class Admin::ApplicationController < ApplicationController
def authenticate_admin!
return render_404 unless current_user.is_admin?
end
def authorize_impersonator!
if session[:impersonator_id]
User.find_by!(username: session[:impersonator_id]).admin?
end
end
end
......@@ -58,6 +58,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:admin_notification_email,
:user_oauth_applications,
:shared_runners_enabled,
:max_artifacts_size,
restricted_visibility_levels: [],
import_sources: []
)
......
class Admin::ImpersonationController < Admin::ApplicationController
skip_before_action :authenticate_admin!, only: :destroy
before_action :user
before_action :authorize_impersonator!
def create
if @user.blocked?
flash[:alert] = "You cannot impersonate a blocked user"
redirect_to admin_user_path(@user)
else
session[:impersonator_id] = current_user.username
session[:impersonator_return_to] = admin_user_path(@user)
warden.set_user(user, scope: 'user')
flash[:alert] = "You are impersonating #{user.username}."
redirect_to root_path
end
end
def destroy
redirect = session[:impersonator_return_to]
warden.set_user(user, scope: 'user')
session[:impersonator_return_to] = nil
session[:impersonator_id] = nil
redirect_to redirect || root_path
end
def user
@user ||= User.find_by!(username: params[:id] || session[:impersonator_id])
end
end
......@@ -63,12 +63,6 @@ class Admin::UsersController < Admin::ApplicationController
end
end
def login_as
sign_in(user)
flash[:alert] = "Logged in as #{user.username}"
redirect_to root_path
end
def disable_two_factor
user.disable_two_factor!
redirect_to admin_user_path(user),
......
class AutocompleteController < ApplicationController
skip_before_action :authenticate_user!, only: [:users]
before_action :find_users, only: [:users]
def users
begin
@users =
if params[:project_id].present?
project = Project.find(params[:project_id])
if can?(current_user, :read_project, project)
project.team.users
end
elsif params[:group_id]
group = Group.find(params[:group_id])
if can?(current_user, :read_group, group)
group.users
end
elsif current_user
User.all
end
rescue ActiveRecord::RecordNotFound
if current_user
return render json: {}, status: 404
end
end
if @users.nil? && current_user.nil?
authenticate_user!
end
@users ||= User.none
@users = @users.search(params[:search]) if params[:search].present?
@users = @users.active
@users = @users.reorder(:name)
@users = @users.page(params[:page]).per(PER_PAGE)
unless params[:search].present?
if params[:search].blank?
# Include current user if available to filter by "Me"
if params[:current_user] && current_user
@users = [*@users, current_user].uniq
......@@ -49,4 +23,25 @@ class AutocompleteController < ApplicationController
@user = User.find(params[:id])
render json: @user, only: [:name, :username, :id], methods: [:avatar_url]
end
private
def find_users
@users =
if params[:project_id].present?
project = Project.find(params[:project_id])
return render_404 unless can?(current_user, :read_project, project)
project.team.users
elsif params[:group_id].present?
group = Group.find(params[:group_id])
return render_404 unless can?(current_user, :read_group, group)
group.users
elsif current_user
User.all
else
User.none
end
end
end
......@@ -15,10 +15,10 @@ module Ci
@builds = @config_processor.builds
@status = true
end
rescue Ci::GitlabCiYamlProcessor::ValidationError => e
rescue Ci::GitlabCiYamlProcessor::ValidationError, Psych::SyntaxError => e
@error = e.message
@status = false
rescue Exception
rescue
@error = "Undefined error"
@status = false
end
......
......@@ -26,10 +26,6 @@ module Ci
redirect_to namespace_project_runners_path(project.gl_project.namespace, project.gl_project)
end
def dumped_yaml
send_data @project.generated_yaml_config, filename: '.gitlab-ci.yml'
end
protected
def project
......
module CreatesMergeRequestForCommit
extend ActiveSupport::Concern
def new_merge_request_path
if @project.forked?
target_project = @project.forked_from_project || @project
target_branch = target_project.repository.root_ref
else
target_project = @project
target_branch = @ref
end
new_namespace_project_merge_request_path(
@project.namespace,
@project,
merge_request: {
source_project_id: @project.id,
target_project_id: target_project.id,
source_branch: @new_branch,
target_branch: target_branch
}
)
end
def create_merge_request?
params[:create_merge_request] && @new_branch != @ref
end
end
module GlobalMilestones
extend ActiveSupport::Concern
def milestones
epoch = DateTime.parse('1970-01-01')
@milestones = MilestonesFinder.new.execute(@projects, params)
@milestones = GlobalMilestone.build_collection(@milestones)
@milestones = @milestones.sort_by { |x| x.due_date.nil? ? epoch : x.due_date }
@milestones = Kaminari.paginate_array(@milestones).page(params[:page]).per(ApplicationController::PER_PAGE)
end
def milestone
milestones = Milestone.of_projects(@projects).where(title: params[:title])
if milestones.present?
@milestone = GlobalMilestone.new(params[:title], milestones)
else
render_404
end
end
end
module IssuesAction
extend ActiveSupport::Concern
def issues
@issues = get_issues_collection
@issues = @issues.page(params[:page]).per(ApplicationController::PER_PAGE)
@issues = @issues.preload(:author, :project)
respond_to do |format|
format.html
format.atom { render layout: false }
end
end
end
module MergeRequestsAction
extend ActiveSupport::Concern
def merge_requests
@merge_requests = get_merge_requests_collection
@merge_requests = @merge_requests.page(params[:page]).per(ApplicationController::PER_PAGE)
@merge_requests = @merge_requests.preload(:author, :target_project)
end
end
class Dashboard::MilestonesController < Dashboard::ApplicationController
before_action :load_projects
include GlobalMilestones
before_action :projects
before_action :milestones, only: [:index]
before_action :milestone, only: [:show]
def index
project_milestones = case params[:state]
when 'all'; state
when 'closed'; state('closed')
else state('active')
end
@dashboard_milestones = Milestones::GroupService.new(project_milestones).execute
@dashboard_milestones = Kaminari.paginate_array(@dashboard_milestones).page(params[:page]).per(PER_PAGE)
end
def show
project_milestones = Milestone.where(project_id: @projects).order("due_date ASC")
@dashboard_milestone = Milestones::GroupService.new(project_milestones).milestone(title)
end
private
def load_projects
@projects = current_user.authorized_projects.sorted_by_activity.non_archived
end
def title
params[:title]
end
def state(state = nil)
conditions = { project_id: @projects }
conditions.reverse_merge!(state: state) if state
Milestone.where(conditions).order("title ASC")
def projects
@projects ||= current_user.authorized_projects.sorted_by_activity.non_archived
end
end
class DashboardController < Dashboard::ApplicationController
include IssuesAction
include MergeRequestsAction
before_action :event_filter, only: :activity
before_action :projects, only: [:issues, :merge_requests]
respond_to :html
def merge_requests
@merge_requests = get_merge_requests_collection
@merge_requests = @merge_requests.page(params[:page]).per(PER_PAGE)
@merge_requests = @merge_requests.preload(:author, :target_project)
end
def issues
@issues = get_issues_collection
@issues = @issues.page(params[:page]).per(PER_PAGE)
@issues = @issues.preload(:author, :project)
respond_to do |format|
format.html
format.atom { render layout: false }
end
end
def activity
@last_push = current_user.recent_push
......@@ -47,4 +34,8 @@ class DashboardController < Dashboard::ApplicationController
@events = @event_filter.apply_filter(@events).with_associations
@events = @events.limit(20).offset(params[:offset] || 0)
end
def projects
@projects ||= current_user.authorized_projects.sorted_by_activity.non_archived
end
end
class Groups::ApplicationController < ApplicationController
layout 'group'
before_action :group
private
def group
@group ||= Group.find_by(path: params[:group_id])
end
def authorize_read_group!
unless @group and can?(current_user, :read_group, @group)
if current_user.nil?
......@@ -12,13 +17,13 @@ class Groups::ApplicationController < ApplicationController
end
end
end
def authorize_admin_group!
unless can?(current_user, :admin_group, group)
return render_404
end
end
def authorize_admin_group_member!
unless can?(current_user, :admin_group_member, group)
return render_403
......
class Groups::AvatarsController < ApplicationController
class Groups::AvatarsController < Groups::ApplicationController
def destroy
@group = Group.find_by(path: params[:group_id])
@group.remove_avatar!
@group.save
redirect_to edit_group_path(@group)
......
class Groups::GroupMembersController < Groups::ApplicationController
skip_before_action :authenticate_user!, only: [:index]
before_action :group
# Authorize
before_action :authorize_read_group!
before_action :authorize_admin_group!, except: [:index, :leave]
before_action :authorize_admin_group_member!, only: [:create, :resend_invite]
before_action :authorize_admin_group_member!, except: [:index, :leave]
def index
@project = @group.projects.find(params[:project_id]) if params[:project_id]
......@@ -18,7 +16,8 @@ class Groups::GroupMembersController < Groups::ApplicationController
end
@members = @members.order('access_level DESC').page(params[:page]).per(50)
@group_member = GroupMember.new
@group_member = @group.group_members.new
end
def create
......@@ -28,24 +27,23 @@ class Groups::GroupMembersController < Groups::ApplicationController
end
def update
@member = @group.group_members.find(params[:id])
@group_member = @group.group_members.find(params[:id])
return render_403 unless can?(current_user, :update_group_member, @member)
return render_403 unless can?(current_user, :update_group_member, @group_member)
@member.update_attributes(member_params)
@group_member.update_attributes(member_params)
end
def destroy
@group_member = @group.group_members.find(params[:id])
if can?(current_user, :destroy_group_member, @group_member) # May fail if last owner.
@group_member.destroy
respond_to do |format|
format.html { redirect_to group_group_members_path(@group), notice: 'User was successfully removed from group.' }
format.js { render nothing: true }
end
else
return render_403
return render_403 unless can?(current_user, :destroy_group_member, @group_member)
@group_member.destroy
respond_to do |format|
format.html { redirect_to group_group_members_path(@group), notice: 'User was successfully removed from group.' }
format.js { render nothing: true }
end
end
......@@ -64,10 +62,11 @@ class Groups::GroupMembersController < Groups::ApplicationController
end
def leave
@group_member = @group.group_members.where(user_id: current_user.id).first
@group_member = @group.group_members.find_by(user_id: current_user)
if can?(current_user, :destroy_group_member, @group_member)
@group_member.destroy
redirect_to(dashboard_groups_path, notice: "You left #{group.name} group.")
else
if @group.last_owner?(current_user)
......@@ -80,10 +79,6 @@ class Groups::GroupMembersController < Groups::ApplicationController
protected
def group
@group ||= Group.find_by(path: params[:group_id])
end
def member_params
params.require(:group_member).permit(:access_level, :user_id)
end
......
class Groups::MilestonesController < Groups::ApplicationController
before_action :authorize_group_milestone!, only: :update
include GlobalMilestones
before_action :projects
before_action :milestones, only: [:index]
before_action :milestone, only: [:show, :update]
before_action :authorize_group_milestone!, only: [:create, :update]
def index
project_milestones = case params[:state]
when 'all'; state
when 'closed'; state('closed')
else state('active')
end
@group_milestones = Milestones::GroupService.new(project_milestones).execute
@group_milestones = Kaminari.paginate_array(@group_milestones).page(params[:page]).per(PER_PAGE)
end
def show
project_milestones = Milestone.where(project_id: group.projects).order("due_date ASC")
@group_milestone = Milestones::GroupService.new(project_milestones).milestone(title)
def new
@milestone = Milestone.new
end
def update
project_milestones = Milestone.where(project_id: group.projects).order("due_date ASC")
@group_milestones = Milestones::GroupService.new(project_milestones).milestone(title)
def create
project_ids = params[:milestone][:project_ids]
title = milestone_params[:title]
@group_milestones.milestones.each do |milestone|
Milestones::UpdateService.new(milestone.project, current_user, params[:milestone]).execute(milestone)
@group.projects.where(id: project_ids).each do |project|
Milestones::CreateService.new(project, current_user, milestone_params).execute
end
respond_to do |format|
format.js
format.html do
redirect_to group_milestones_path(group)
end
redirect_to milestone_path(title)
end
def show
end
def update
@milestone.milestones.each do |milestone|
Milestones::UpdateService.new(milestone.project, current_user, milestone_params).execute(milestone)
end
redirect_back_or_default(default: milestone_path(@milestone.title))
end
private
def group
@group ||= Group.find_by(path: params[:group_id])
def authorize_group_milestone!
return render_404 unless can?(current_user, :admin_milestones, group)
end
def title
params[:title]
def milestone_params
params.require(:milestone).permit(:title, :description, :due_date, :state_event)
end
def state(state = nil)
conditions = { project_id: group.projects }
conditions.reverse_merge!(state: state) if state
Milestone.where(conditions).order("title ASC")
def milestone_path(title)
group_milestone_path(@group, title.to_slug.to_s, title: title)
end
def authorize_group_milestone!
return render_404 unless can?(current_user, :admin_group, group)
def projects
@projects ||= @group.projects
end
end
class GroupsController < Groups::ApplicationController
include IssuesAction
include MergeRequestsAction
skip_before_action :authenticate_user!, only: [:show, :issues, :merge_requests]
respond_to :html
before_action :group, except: [:new, :create]
......@@ -53,23 +56,6 @@ class GroupsController < Groups::ApplicationController
end
end
def merge_requests
@merge_requests = get_merge_requests_collection
@merge_requests = @merge_requests.page(params[:page]).per(PER_PAGE)
@merge_requests = @merge_requests.preload(:author, :target_project)
end
def issues
@issues = get_issues_collection
@issues = @issues.page(params[:page]).per(PER_PAGE)
@issues = @issues.preload(:author, :project)
respond_to do |format|
format.html
format.atom { render layout: false }
end
end
def edit
end
......
......@@ -40,7 +40,9 @@ class PasswordsController < Devise::PasswordsController
def throttle_reset
return unless resource && resource.recently_sent_password_reset?
redirect_to new_password_path(resource_name),
alert: I18n.t('devise.passwords.recently_reset')
# Throttle reset attempts, but return a normal message to
# avoid user enumeration attack.
redirect_to new_user_session_path,
notice: I18n.t('devise.passwords.send_paranoid_instructions')
end
end
......@@ -70,6 +70,7 @@ class ProfilesController < Profiles::ApplicationController
:email,
:hide_no_password,
:hide_no_ssh_key,
:hide_project_limit,
:linkedin,
:location,
:name,
......
......@@ -21,15 +21,15 @@ class Projects::ApplicationController < ApplicationController
unless @repository.branch_names.include?(@ref)
redirect_to(
namespace_project_tree_path(@project.namespace, @project, @ref),
notice: "This action is not allowed unless you are on top of a branch"
notice: "This action is not allowed unless you are on a branch"
)
end
end
private
def ci_enabled
return render_404 unless @project.gitlab_ci?
def builds_enabled
return render_404 unless @project.builds_enabled?
end
def ci_project
......
# Controller for viewing a file's blame
class Projects::BlobController < Projects::ApplicationController
include ExtractsPath
include CreatesMergeRequestForCommit
include ActionView::Helpers::SanitizeHelper
# Raised when given an invalid file path
......@@ -22,21 +23,9 @@ class Projects::BlobController < Projects::ApplicationController
end
def create
result = Files::CreateService.new(@project, current_user, @commit_params).execute
if result[:status] == :success
flash[:notice] = "The changes have been successfully committed"
respond_to do |format|
format.html { redirect_to namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @file_path)) }
format.json { render json: { message: "success", filePath: namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @file_path)) } }
end
else
flash[:alert] = result[:message]
respond_to do |format|
format.html { render :new }
format.json { render json: { message: "failed", filePath: namespace_project_blob_path(@project.namespace, @project, @id) } }
end
end
create_commit(Files::CreateService, success_path: after_create_path,
failure_view: :new,
failure_path: namespace_project_new_blob_path(@project.namespace, @project, @ref))
end
def show
......@@ -47,21 +36,9 @@ class Projects::BlobController < Projects::ApplicationController
end
def update
result = Files::UpdateService.new(@project, current_user, @commit_params).execute
if result[:status] == :success
flash[:notice] = "Your changes have been successfully committed"
respond_to do |format|
format.html { redirect_to after_edit_path }
format.json { render json: { message: "success", filePath: after_edit_path } }
end
else
flash[:alert] = result[:message]
respond_to do |format|
format.html { render :edit }
format.json { render json: { message: "failed", filePath: namespace_project_new_blob_path(@project.namespace, @project, @id) } }
end
end
create_commit(Files::UpdateService, success_path: after_edit_path,
failure_view: :edit,
failure_path: namespace_project_blob_path(@project.namespace, @project, @id))
end
def preview
......@@ -77,7 +54,7 @@ class Projects::BlobController < Projects::ApplicationController
if result[:status] == :success
flash[:notice] = "Your changes have been successfully committed"
redirect_to namespace_project_tree_path(@project.namespace, @project, @target_branch)
redirect_to after_destroy_path
else
flash[:alert] = result[:message]
render :show
......@@ -131,15 +108,51 @@ class Projects::BlobController < Projects::ApplicationController
render_404
end
def create_commit(service, success_path:, failure_view:, failure_path:)
result = service.new(@project, current_user, @commit_params).execute
if result[:status] == :success
flash[:notice] = "Your changes have been successfully committed"
respond_to do |format|
format.html { redirect_to success_path }
format.json { render json: { message: "success", filePath: success_path } }
end
else
flash[:alert] = result[:message]
respond_to do |format|
format.html { render failure_view }
format.json { render json: { message: "failed", filePath: failure_path } }
end
end
end
def after_create_path
@after_create_path ||=
if create_merge_request?
new_merge_request_path
else
namespace_project_blob_path(@project.namespace, @project, File.join(@new_branch, @file_path))
end
end
def after_edit_path
@after_edit_path ||=
if from_merge_request
if create_merge_request?
new_merge_request_path
elsif from_merge_request && @new_branch == @ref
diffs_namespace_project_merge_request_path(from_merge_request.target_project.namespace, from_merge_request.target_project, from_merge_request) +
"#file-path-#{hexdigest(@path)}"
elsif @target_branch.present?
namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @path))
else
namespace_project_blob_path(@project.namespace, @project, @id)
namespace_project_blob_path(@project.namespace, @project, File.join(@new_branch, @path))
end
end
def after_destroy_path
@after_destroy_path ||=
if create_merge_request?
new_merge_request_path
else
namespace_project_tree_path(@project.namespace, @project, @new_branch)
end
end
......@@ -149,12 +162,20 @@ class Projects::BlobController < Projects::ApplicationController
end
def sanitized_new_branch_name
@new_branch ||= sanitize(strip_tags(params[:new_branch]))
sanitize(strip_tags(params[:new_branch]))
end
def editor_variables
@current_branch = @ref
@target_branch = params[:new_branch].present? ? sanitized_new_branch_name : @ref
@new_branch =
if params[:new_branch].present?
sanitized_new_branch_name
elsif ::Gitlab::GitAccess.new(current_user, @project).can_push_to_branch?(@ref)
@ref
else
@repository.next_patch_branch
end
@file_path =
if action_name.to_s == 'create'
......@@ -174,7 +195,7 @@ class Projects::BlobController < Projects::ApplicationController
@commit_params = {
file_path: @file_path,
current_branch: @current_branch,
target_branch: @target_branch,
target_branch: @new_branch,
commit_message: params[:commit_message],
file_content: params[:content],
file_content_encoding: params[:encoding]
......
......@@ -3,7 +3,7 @@ class Projects::BranchesController < Projects::ApplicationController
# Authorize
before_action :require_non_empty_project
before_action :authorize_download_code!
before_action :authorize_push_code!, only: [:create, :destroy]
before_action :authorize_push_code!, only: [:new, :create, :destroy]
def index
@sort = params[:sort] || 'name'
......
......@@ -3,6 +3,7 @@ class Projects::BuildsController < Projects::ApplicationController
before_action :build, except: [:index, :cancel_all]
before_action :authorize_manage_builds!, except: [:index, :show, :status]
before_action :authorize_download_build_artifacts!, only: [:download]
layout "project"
......@@ -51,6 +52,18 @@ class Projects::BuildsController < Projects::ApplicationController
redirect_to build_path(build)
end
def download
unless artifacts_file.file_storage?
return redirect_to artifacts_file.url
end
unless artifacts_file.exists?
return not_found!
end
send_file artifacts_file.path, disposition: 'attachment'
end
def status
render json: @build.to_json(only: [:status, :id, :sha, :coverage], methods: :sha)
end
......@@ -67,6 +80,10 @@ class Projects::BuildsController < Projects::ApplicationController
@build ||= ci_project.builds.unscoped.find_by!(id: params[:id])
end
def artifacts_file
build.artifacts_file
end
def build_path(build)
namespace_project_build_path(build.gl_project.namespace, build.gl_project, build)
end
......@@ -76,4 +93,14 @@ class Projects::BuildsController < Projects::ApplicationController
return page_404
end
end
def authorize_download_build_artifacts!
unless can?(current_user, :download_build_artifacts, @project)
if current_user.nil?
return authenticate_user!
else
return render_404
end
end
end
end
......@@ -37,7 +37,7 @@ class Projects::CommitController < Projects::ApplicationController
def cancel_builds
ci_commit.builds.running_or_pending.each(&:cancel)
redirect_to builds_namespace_project_commit_path(project.namespace, project, commit.sha)
redirect_back_or_default default: builds_namespace_project_commit_path(project.namespace, project, commit.sha)
end
def retry_builds
......@@ -47,7 +47,7 @@ class Projects::CommitController < Projects::ApplicationController
end
end
redirect_to builds_namespace_project_commit_path(project.namespace, project, commit.sha)
redirect_back_or_default default: builds_namespace_project_commit_path(project.namespace, project, commit.sha)
end
def branches
......@@ -67,10 +67,15 @@ class Projects::CommitController < Projects::ApplicationController
end
def define_show_vars
@diffs = commit.diffs
if params[:w].to_i == 1
@diffs = commit.diffs({ ignore_whitespace_change: true })
else
@diffs = commit.diffs
end
@notes_count = commit.notes.count
@builds = ci_commit.builds if ci_commit
@statuses = ci_commit.statuses if ci_commit
end
def authorize_manage_builds!
......
......@@ -12,15 +12,16 @@ class Projects::CompareController < Projects::ApplicationController
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)
execute(@project, head_ref, @project, base_ref, diff_options)
if compare_result
@commits = Commit.decorate(compare_result.commits, @project)
@diffs = compare_result.diffs
@commit = @commits.last
@first_commit = @commits.first
@commit = @project.commit(head_ref)
@first_commit = @project.commit(base_ref)
@line_notes = []
end
end
......
......@@ -5,7 +5,7 @@ class Projects::GraphsController < Projects::ApplicationController
before_action :require_non_empty_project
before_action :assign_ref_vars
before_action :authorize_download_code!
before_action :ci_enabled, only: :ci
before_action :builds_enabled, only: :ci
def show
respond_to do |format|
......@@ -34,6 +34,26 @@ class Projects::GraphsController < Projects::ApplicationController
@charts[:build_times] = Ci::Charts::BuildTime.new(ci_project)
end
def languages
@languages = Linguist::Repository.new(@repository.rugged, @repository.rugged.head.target_id).languages
total = @languages.map(&:last).sum
@languages = @languages.map do |language|
name, share = language
color = Digest::SHA256.hexdigest(name)[0...6]
{
value: (share.to_f * 100 / total).round(2),
label: name,
color: "##{color}",
highlight: "##{color}"
}
end
@languages.sort! do |x, y|
y[:value] <=> x[:value]
end
end
private
def fetch_graph
......
......@@ -25,13 +25,12 @@ class Projects::HooksController < Projects::ApplicationController
def test
if !@project.empty_repo?
status = TestHookService.new.execute(hook, current_user)
status, message = TestHookService.new.execute(hook, current_user)
if status
flash[:notice] = 'Hook successfully executed.'
else
flash[:alert] = 'Hook execution failed. '\
'Ensure hook URL is correct and service is up.'
flash[:alert] = "Hook execution failed: #{message}"
end
else
flash[:alert] = 'Hook execution failed. Ensure the project has commits.'
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
File mode changed from 100644 to 100755
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
File mode changed from 100644 to 100755
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment