Commit 579ab088 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'ce-to-ee' into 'master'

CE to EE

@rspeicher @douwe reference extraction / markdown parsing was refactored and improved again and again. And every time I merge CE into EE I has conflicts in this code. Can we have SAME code  CE and EE responsible for reference extraction?

Problem files:

* lib/gitlab/markdown/external_issue_reference_filter.rb
* lib/gitlab/reference_extractor.rb

See merge request !411
parents 1498d760 c9f1fb57
before_script:
- export PATH=$HOME/bin:/usr/local/bin:/usr/bin:/bin
- ruby -v
- which ruby
- gem install bundler
- which bundle
- echo $PATH
- cp config/database.yml.mysql config/database.yml
- cp config/gitlab.yml.example config/gitlab.yml
- ! 'sed "s/username\:.*$/username\: runner/" -i config/database.yml'
- ! 'sed "s/password\:.*$/password\: ''password''/" -i config/database.yml'
- sed "s/gitlabhq_test/gitlabhq_test_$((RANDOM/5000))/" -i config/database.yml
- touch log/application.log
- touch log/test.log
- bundle install --without postgres production --jobs $(nproc)
- bundle exec rake db:create RAILS_ENV=test
jobs:
- script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec
name: Rspec
runner: ruby,mysql
- script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach
name: Spinach
runner: ruby,mysql
- script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake jasmine:ci
name: Jasmine
runner: ruby,mysql
- script:
- bundle exec rubocop
name: Rubocop
runner: ruby,mysql
- script:
- bundle exec rake brakeman
name: Brakeman
runner: ruby,mysql
deploy_jobs: []
skip_refs: ''
Please view this file on the master branch, on stable branches it's out of date. Please view this file on the master branch, on stable branches it's out of date.
v 7.11.0 (unreleased) v 7.12.0 (unreleased)
- Don't notify users mentioned in code blocks or blockquotes.
- Omit link to generate labels if user does not have access to create them (Stan Hu)
- Disable changing of the source branch in merge request update API (Stan Hu)
- Shorten merge request WIP text.
- Add option to disallow users from registering any application to use GitLab as an OAuth provider
- Support editing target branch of merge request (Stan Hu)
- Refactor permission checks with issues and merge requests project settings (Stan Hu)
- Fix Markdown preview not working in Edit Milestone page (Stan Hu)
- Fix Zen Mode not closing with ESC key (Stan Hu)
- Allow HipChat API version to be blank and default to v2 (Stan Hu)
- Add file attachment support in Milestone description (Stan Hu)
- Fix milestone "Browse Issues" button.
- Set milestone on new issue when creating issue from index with milestone filter active.
- Make namespace API available to all users (Stan Hu)
- Add web hook support for note events (Stan Hu)
- Disable "New Issue" and "New Merge Request" buttons when features are disabled in project settings (Stan Hu)
- Remove Rack Attack monkey patches and bump to version 4.3.0 (Stan Hu)
- Fix clone URL losing selection after a single click in Safari and Chrome (Stan Hu)
- Fix git blame syntax highlighting when different commits break up lines (Stan Hu)
- Add "Resend confirmation e-mail" link in profile settings (Stan Hu)
- Allow to configure location of the `.gitlab_shell_secret` file. (Jakub Jirutka)
- Disabled expansion of top/bottom blobs for new file diffs
- Update Asciidoctor gem to version 1.5.2. (Jakub Jirutka)
- Fix resolving of relative links to repository files in AsciiDoc documents. (Jakub Jirutka)
- Use the user list from the target project in a merge request (Stan Hu)
- Default extention for wiki pages is now .md instead of .markdown (Jeroen van Baarsen)
- Add validation to wiki page creation (only [a-zA-Z0-9/_-] are allowed) (Jeroen van Baarsen)
- Fix new/empty milestones showing 100% completion value (Jonah Bishop)
- Add a note when an Issue or Merge Request's title changes
- Consistently refer to MRs as either Accepted or Rejected.
- Add Accepted and Rejected tabs to MR lists.
- Prefix EmailsOnPush email subject with `[Git]`.
- Group project contributions by both name and email.
- Clarify navigation labels for Project Settings and Group Settings.
- Move user avatar and logout button to sidebar
- You can not remove user if he/she is an only owner of group
- User should be able to leave group. If not - show him proper message
- User has ability to leave project
- Add SAML support as an omniauth provider
- Allow to configure a URL to show after sign out
- Add an option to automatically sign-in with an Omniauth provider
- Better performance for web editor (switched from satellites to rugged)
- GitLab CI service sends .gitlab-ci.yaml in each push call
- When remove project - move repository and schedule it removal
- Improve group removing logic
- Trigger create-hooks on backup restore task
v 7.11.4
- Fix missing bullets when creating lists
- Set rel="nofollow" on external links
v 7.11.3
- no changes
- Fix upgrader script (Martins Polakovs)
v 7.11.2
- no changes
v 7.11.1
- no changes
v 7.11.0
- Fall back to Plaintext when Syntaxhighlighting doesn't work. Fixes some buggy lexers (Hannes Rosenögger)
- Get editing comments to work in Chrome 43 again.
- Allow special character in users bio. I.e.: I <3 GitLab
v 7.11.0
- Fix broken view when viewing history of a file that includes a path that used to be another file (Stan Hu) - Fix broken view when viewing history of a file that includes a path that used to be another file (Stan Hu)
- Don't show duplicate deploy keys - Don't show duplicate deploy keys
- Fix commit time being displayed in the wrong timezone in some cases (Hannes Rosenögger) - Fix commit time being displayed in the wrong timezone in some cases (Hannes Rosenögger)
...@@ -11,8 +78,6 @@ v 7.11.0 (unreleased) ...@@ -11,8 +78,6 @@ v 7.11.0 (unreleased)
- Don't allow a merge request to be merged when its title starts with "WIP". - Don't allow a merge request to be merged when its title starts with "WIP".
- Add a page title to every page. - Add a page title to every page.
- Allow primary email to be set to an email that you've already added. - Allow primary email to be set to an email that you've already added.
- Fix Error 500 when searching Wiki pages (Stan Hu)
- Get Gitorious importer to work again.
- Fix clone URL field and X11 Primary selection (Dmitry Medvinsky) - Fix clone URL field and X11 Primary selection (Dmitry Medvinsky)
- Ignore invalid lines in .gitmodules - Ignore invalid lines in .gitmodules
- Fix "Cannot move project" error message from popping up after a successful transfer (Stan Hu) - Fix "Cannot move project" error message from popping up after a successful transfer (Stan Hu)
...@@ -21,7 +86,6 @@ v 7.11.0 (unreleased) ...@@ -21,7 +86,6 @@ v 7.11.0 (unreleased)
- Fix "Revspec not found" errors when viewing diffs in a forked project with submodules (Stan Hu) - Fix "Revspec not found" errors when viewing diffs in a forked project with submodules (Stan Hu)
- Improve project page UI - Improve project page UI
- Fix broken file browsing with relative submodule in personal projects (Stan Hu) - Fix broken file browsing with relative submodule in personal projects (Stan Hu)
- Fix DB error when trying to tag a repository (Stan Hu)
- Add "Reply quoting selected text" shortcut key (`r`) - Add "Reply quoting selected text" shortcut key (`r`)
- Fix bug causing `@whatever` inside an issue's first code block to be picked up as a user mention. - Fix bug causing `@whatever` inside an issue's first code block to be picked up as a user mention.
- Fix bug causing `@whatever` inside an inline code snippet (backtick-style) to be picked up as a user mention. - Fix bug causing `@whatever` inside an inline code snippet (backtick-style) to be picked up as a user mention.
...@@ -32,20 +96,19 @@ v 7.11.0 (unreleased) ...@@ -32,20 +96,19 @@ v 7.11.0 (unreleased)
- Show Atom feed buttons everywhere where applicable. - Show Atom feed buttons everywhere where applicable.
- Add project activity atom feed. - Add project activity atom feed.
- Don't crash when an MR from a fork has a cross-reference comment from the target project on one of its commits. - Don't crash when an MR from a fork has a cross-reference comment from the target project on one of its commits.
- Explain how to get a new password reset token in welcome emails
- Include commit comments in MR from a forked project. - Include commit comments in MR from a forked project.
- Fix adding new group members from admin area
- Group milestones by title in the dashboard and all other issue views. - Group milestones by title in the dashboard and all other issue views.
- Query issues, merge requests and milestones with their IID through API (Julien Bianchi) - Query issues, merge requests and milestones with their IID through API (Julien Bianchi)
- Add default project and snippet visibility settings to the admin web UI. - Add default project and snippet visibility settings to the admin web UI.
- Show incompatible projects in Google Code import status (Stan Hu) - Show incompatible projects in Google Code import status (Stan Hu)
- Fix bug where commit data would not appear in some subdirectories (Stan Hu) - Fix bug where commit data would not appear in some subdirectories (Stan Hu)
- Unescape branch names in compare commit (Stan Hu)
- Task lists are now usable in comments, and will show up in Markdown previews. - Task lists are now usable in comments, and will show up in Markdown previews.
- Fix bug where avatar filenames were not actually deleted from the database during removal (Stan Hu) - Fix bug where avatar filenames were not actually deleted from the database during removal (Stan Hu)
- Fix bug where Slack service channel was not saved in admin template settings. (Stan Hu) - Fix bug where Slack service channel was not saved in admin template settings. (Stan Hu)
- Protect OmniAuth request phase against CSRF. - Protect OmniAuth request phase against CSRF.
- - Don't send notifications to mentioned users that don't have access to the project in question.
- - Add search issues/MR by number
- Move snippets UI to fluid layout - Move snippets UI to fluid layout
- Improve UI for sidebar. Increase separation between navigation and content - Improve UI for sidebar. Increase separation between navigation and content
- Improve new project command options (Ben Bodenmiller) - Improve new project command options (Ben Bodenmiller)
...@@ -66,6 +129,17 @@ v 7.11.0 (unreleased) ...@@ -66,6 +129,17 @@ v 7.11.0 (unreleased)
- Add style for <kbd> element in markdown - Add style for <kbd> element in markdown
- Spin spinner icon next to "Checking for CI status..." on MR page. - Spin spinner icon next to "Checking for CI status..." on MR page.
- Fix reference links in dashboard activity and ATOM feeds. - Fix reference links in dashboard activity and ATOM feeds.
- Ensure that the first added admin performs repository imports
v 7.10.4
- Fix migrations broken in 7.10.2
- Make tags for GitLab installations running on MySQL case sensitive
- Get Gitorious importer to work again.
- Fix adding new group members from admin area
- Fix DB error when trying to tag a repository (Stan Hu)
- Fix Error 500 when searching Wiki pages (Stan Hu)
- Unescape branch names in compare commit (Stan Hu)
- Order commit comments chronologically in API.
v 7.10.2 v 7.10.2
- Fix CI links on MR page - Fix CI links on MR page
...@@ -150,12 +224,12 @@ v 7.9.0 (unreleased) ...@@ -150,12 +224,12 @@ v 7.9.0 (unreleased)
- Ability to skip some items from backup (database, respositories or uploads) - Ability to skip some items from backup (database, respositories or uploads)
- Archive repositories in background worker. - Archive repositories in background worker.
- Import GitHub, Bitbucket or GitLab.com projects owned by authenticated user into current namespace. - Import GitHub, Bitbucket or GitLab.com projects owned by authenticated user into current namespace.
- Project labels are now available over the API under the "tag_list" field (Cristian Medina) - Project labels are now available over the API under the "tag_list" field (Cristian Medina)
- Fixed link paths for HTTP and SSH on the admin project view (Jeremy Maziarz) - Fixed link paths for HTTP and SSH on the admin project view (Jeremy Maziarz)
- Fix and improve help rendering (Sullivan Sénéchal) - Fix and improve help rendering (Sullivan Sénéchal)
- Fix final line in EmailsOnPush email diff being rendered as error. - Fix final line in EmailsOnPush email diff being rendered as error.
- Prevent duplicate Buildkite service creation. - Prevent duplicate Buildkite service creation.
- Fix git over ssh errors 'fatal: protocol error: bad line length character' - Fix git over ssh errors 'fatal: protocol error: bad line length character'
- Automatically setup GitLab CI project for forks if origin project has GitLab CI enabled - Automatically setup GitLab CI project for forks if origin project has GitLab CI enabled
- Bust group page project list cache when namespace name or path changes. - Bust group page project list cache when namespace name or path changes.
- Explicitly set image alt-attribute to prevent graphical glitches if gravatars could not be loaded - Explicitly set image alt-attribute to prevent graphical glitches if gravatars could not be loaded
...@@ -164,7 +238,7 @@ v 7.9.0 (unreleased) ...@@ -164,7 +238,7 @@ v 7.9.0 (unreleased)
- Fix stuck Merge Request merging events from old installations (Ben Bodenmiller) - Fix stuck Merge Request merging events from old installations (Ben Bodenmiller)
- Fix merge request comments on files with multiple commits - Fix merge request comments on files with multiple commits
- Fix Resource Owner Password Authentication Flow - Fix Resource Owner Password Authentication Flow
v 7.9.4 v 7.9.4
- Security: Fix project import URL regex to prevent arbitary local repos from being imported - Security: Fix project import URL regex to prevent arbitary local repos from being imported
- Fixed issue where only 25 commits would load in file listings - Fixed issue where only 25 commits would load in file listings
...@@ -470,6 +544,12 @@ v 7.5.0 ...@@ -470,6 +544,12 @@ v 7.5.0
- Use secret token with GitLab internal API. - Use secret token with GitLab internal API.
- Add missing timestamps to 'members' table - Add missing timestamps to 'members' table
v 7.4.5
- Bump gitlab_git to 7.0.0.rc12 (includes Rugged 0.21.2)
v 7.4.4
- No changes
v 7.4.3 v 7.4.3
- Fix raw snippets view - Fix raw snippets view
- Fix security issue for member api - Fix security issue for member api
......
...@@ -29,11 +29,9 @@ You can also sign up on [CodeTriage](http://www.codetriage.com/gitlabhq/gitlabhq ...@@ -29,11 +29,9 @@ You can also sign up on [CodeTriage](http://www.codetriage.com/gitlabhq/gitlabhq
## Issue tracker ## Issue tracker
To get support for your particular problem please use the channels as detailed in the [getting help section of the readme](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/README.md#getting-help). Professional [support subscriptions](http://about.gitlab.com/subscription/) and [consulting services](http://about.gitlab.com/consultancy/) are available from [GitLab.com](http://about.gitlab.com/). To get support for your particular problem please use the [getting help channels](https://about.gitlab.com/getting-help/).
The [issue tracker](https://gitlab.com/gitlab-org/gitlab-ce/issues) is only for obvious errors in the latest [stable or development release of GitLab](MAINTENANCE.md). If something is wrong but it is not a regression compared to older versions of GitLab please do not open an issue but a feature request. When submitting an issue please conform to the issue submission guidelines listed below. Not all issues will be addressed and your issue is more likely to be addressed if you submit a merge request which partially or fully addresses the issue. The [GitLab CE issue tracker on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/issues) is only for obvious errors in the latest [stable or development release of GitLab](MAINTENANCE.md). If something is wrong but it is not a regression compared to older versions of GitLab please do not open an issue but a feature request. When submitting an issue please conform to the issue submission guidelines listed below. Not all issues will be addressed and your issue is more likely to be addressed if you submit a merge request which partially or fully addresses the issue.
Issues can be filed either at [gitlab.com](https://gitlab.com/gitlab-org/gitlab-ce/issues) or [github.com](https://github.com/gitlabhq/gitlabhq/issues).
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. 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.
...@@ -86,7 +84,9 @@ If you can, please submit a merge request with the fix or improvements including ...@@ -86,7 +84,9 @@ If you can, please submit a merge request with the fix or improvements including
1. If your MR touches code that executes shell commands, make sure it adheres to the [shell command guidelines]( doc/development/shell_commands.md). 1. If your MR touches code that executes shell commands, make sure it adheres to the [shell command guidelines]( doc/development/shell_commands.md).
1. Also have a look at the [shell command guidelines](doc/development/shell_commands.md) if your code reads or opens files, or handles paths to files on disk. 1. Also have a look at the [shell command guidelines](doc/development/shell_commands.md) if your code reads or opens files, or handles paths to files on disk.
The **official merge window** is in the beginning of the month from the 1st to the 7th day of the month. The best time to submit a MR and get feedback fast. Before this time the GitLab B.V. team is still dealing with work that is created by the monthly release such as assisting subscribers with upgrade issues, the release of Enterprise Edition and the upgrade of GitLab Cloud. After the 7th it is already getting closer to the release date of the next version. This means there is less time to fix the issues created by merging large new features. The **official merge window** is in the beginning of the month from the 1st to the 7th day of the month. The best time to submit a MR and get feedback fast.
Before this time the GitLab B.V. team is still dealing with work that is created by the monthly release such as regressions requiring patch releases.
After the 7th it is already getting closer to the release date of the next version. This means there is less time to fix the issues created by merging large new features.
Please keep the change in a single MR **as small as possible**. If you want to contribute a large feature think very hard what the minimum viable change is. Can you split functionality? Can you only submit the backend/API code? Can you start with a very simple UI? Can you do part of the refactor? The increased reviewability of small MR's that leads to higher code quality is more important to us than having a minimal commit log. The smaller a MR is the more likely it is it will be merged (quickly), after that you can send more MR's to enhance it. Please keep the change in a single MR **as small as possible**. If you want to contribute a large feature think very hard what the minimum viable change is. Can you split functionality? Can you only submit the backend/API code? Can you start with a very simple UI? Can you do part of the refactor? The increased reviewability of small MR's that leads to higher code quality is more important to us than having a minimal commit log. The smaller a MR is the more likely it is it will be merged (quickly), after that you can send more MR's to enhance it.
...@@ -160,8 +160,9 @@ If you add a dependency in GitLab (such as an operating system package) please c ...@@ -160,8 +160,9 @@ If you add a dependency in GitLab (such as an operating system package) please c
1. [CoffeeScript](https://github.com/thoughtbot/guides/tree/master/style#coffeescript) 1. [CoffeeScript](https://github.com/thoughtbot/guides/tree/master/style#coffeescript)
1. [Shell commands](doc/development/shell_commands.md) created by GitLab contributors to enhance security 1. [Shell commands](doc/development/shell_commands.md) created by GitLab contributors to enhance security
1. [Markdown](http://www.cirosantilli.com/markdown-styleguide) 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).
1. [Migrations](doc/development/migration_style_guide.md)
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). 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).
...@@ -177,4 +178,4 @@ Project maintainers have the right and responsibility to remove, edit, or reject ...@@ -177,4 +178,4 @@ Project maintainers have the right and responsibility to remove, edit, or reject
Instances of abusive, harassing, or otherwise unacceptable behavior can be Instances of abusive, harassing, or otherwise unacceptable behavior can be
reported by emailing contact@gitlab.com reported by emailing contact@gitlab.com
This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/) This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
\ No newline at end of file
source "https://rubygems.org" source "https://rubygems.org"
def darwin_only(require_as)
RUBY_PLATFORM.include?('darwin') && require_as
end
def linux_only(require_as)
RUBY_PLATFORM.include?('linux') && require_as
end
gem "rails", "~> 4.1.0" gem "rails", "~> 4.1.0"
# Make links from text
gem 'rails_autolink', '~> 1.1'
# Default values for AR models # Default values for AR models
gem "default_value_for", "~> 3.0.0" gem "default_value_for", "~> 3.0.0"
...@@ -31,6 +20,7 @@ gem 'omniauth-shibboleth' ...@@ -31,6 +20,7 @@ gem 'omniauth-shibboleth'
gem 'omniauth-kerberos', group: :kerberos gem 'omniauth-kerberos', group: :kerberos
gem 'omniauth-gitlab' gem 'omniauth-gitlab'
gem 'omniauth-bitbucket' gem 'omniauth-bitbucket'
gem 'omniauth-saml'
gem 'doorkeeper', '2.1.3' gem 'doorkeeper', '2.1.3'
gem "rack-oauth2", "~> 1.0.5" gem "rack-oauth2", "~> 1.0.5"
...@@ -44,12 +34,16 @@ gem "browser" ...@@ -44,12 +34,16 @@ gem "browser"
# Extracting information from a git repository # Extracting information from a git repository
# Provide access to Gitlab::Git library # Provide access to Gitlab::Git library
gem "gitlab_git", '~> 7.1.12' gem "gitlab_git", '~> 7.2.2'
# Ruby/Rack Git Smart-HTTP Server Handler # Ruby/Rack Git Smart-HTTP Server Handler
# GitLab fork with a lot of changes (improved thread-safety, better memory usage etc)
# For full list of changes see https://github.com/SaitoWu/grack/compare/master...gitlabhq:master
gem 'gitlab-grack', '~> 2.0.2', require: 'grack' gem 'gitlab-grack', '~> 2.0.2', require: 'grack'
# LDAP Auth # LDAP Auth
# GitLab fork with several improvements to original library. For full list of changes
# see https://github.com/intridea/omniauth-ldap/compare/master...gitlabhq:master
gem 'gitlab_omniauth-ldap', '1.2.1', require: "omniauth-ldap" gem 'gitlab_omniauth-ldap', '1.2.1', require: "omniauth-ldap"
gem 'net-ldap' gem 'net-ldap'
...@@ -57,6 +51,10 @@ gem 'net-ldap' ...@@ -57,6 +51,10 @@ gem 'net-ldap'
gem 'gollum-lib', '~> 4.0.2' gem 'gollum-lib', '~> 4.0.2'
# Language detection # Language detection
# GitLab fork of linguist does not require pygments/python dependency.
# New version of original gem also dropped pygments support but it has strict
# dependency to unstable rugged version. We have internal issue for replacing
# fork with original gem when we meet on same rugged version - https://dev.gitlab.org/gitlab/gitlabhq/issues/2052.
gem "gitlab-linguist", "~> 3.0.1", require: "linguist" gem "gitlab-linguist", "~> 3.0.1", require: "linguist"
# API # API
...@@ -95,7 +93,7 @@ gem "seed-fu" ...@@ -95,7 +93,7 @@ gem "seed-fu"
# Markdown and HTML processing # Markdown and HTML processing
gem 'html-pipeline', '~> 1.11.0' gem 'html-pipeline', '~> 1.11.0'
gem 'task_list', '~> 1.0.0', require: 'task_list/railtie' gem 'task_list', '1.0.2', require: 'task_list/railtie'
gem 'github-markup' gem 'github-markup'
gem 'redcarpet', '~> 3.2.3' gem 'redcarpet', '~> 3.2.3'
gem 'RedCloth' gem 'RedCloth'
...@@ -103,7 +101,7 @@ gem 'rdoc', '~>3.6' ...@@ -103,7 +101,7 @@ gem 'rdoc', '~>3.6'
gem 'org-ruby', '= 0.9.12' gem 'org-ruby', '= 0.9.12'
gem 'creole', '~>0.3.6' gem 'creole', '~>0.3.6'
gem 'wikicloth', '=0.8.1' gem 'wikicloth', '=0.8.1'
gem 'asciidoctor', '= 0.1.4' gem 'asciidoctor', '~> 1.5.2'
# Diffs # Diffs
gem 'diffy', '~> 3.0.3' gem 'diffy', '~> 3.0.3'
...@@ -173,7 +171,7 @@ gem "underscore-rails", "~> 1.4.4" ...@@ -173,7 +171,7 @@ gem "underscore-rails", "~> 1.4.4"
gem "sanitize", '~> 2.0' gem "sanitize", '~> 2.0'
# Protect against bruteforcing # Protect against bruteforcing
gem "rack-attack" gem "rack-attack", '~> 4.3.0'
# Ace editor # Ace editor
gem 'ace-rails-ap' gem 'ace-rails-ap'
...@@ -187,23 +185,23 @@ gem 'charlock_holmes' ...@@ -187,23 +185,23 @@ gem 'charlock_holmes'
gem "sass-rails", '~> 4.0.2' gem "sass-rails", '~> 4.0.2'
gem "coffee-rails" gem "coffee-rails"
gem "uglifier" gem "uglifier"
gem 'turbolinks' gem 'turbolinks', '~> 2.5.0'
gem 'jquery-turbolinks' gem 'jquery-turbolinks'
gem 'select2-rails' gem 'addressable'
gem 'bootstrap-sass', '~> 3.0'
gem 'font-awesome-rails', '~> 4.2'
gem 'gitlab_emoji', '~> 0.1'
gem 'gon', '~> 5.0.0'
gem 'jquery-atwho-rails', '~> 1.0.0' gem 'jquery-atwho-rails', '~> 1.0.0'
gem "jquery-rails" gem 'jquery-rails', '3.1.2'
gem "jquery-ui-rails" gem 'jquery-scrollto-rails'
gem "jquery-scrollto-rails" gem 'jquery-ui-rails'
gem "raphael-rails", "~> 2.1.2"
gem 'bootstrap-sass', '~> 3.0'
gem "font-awesome-rails", '~> 4.2'
gem "gitlab_emoji", "~> 0.1"
gem "gon", '~> 5.0.0'
gem 'nprogress-rails' gem 'nprogress-rails'
gem 'raphael-rails', '~> 2.1.2'
gem 'request_store' gem 'request_store'
gem "virtus" gem 'select2-rails'
gem 'addressable' gem 'virtus'
gem "gitlab-license", "~> 0.0.2" gem "gitlab-license", "~> 0.0.2"
...@@ -242,25 +240,18 @@ group :development, :test do ...@@ -242,25 +240,18 @@ group :development, :test do
gem 'minitest', '~> 5.3.0' gem 'minitest', '~> 5.3.0'
# Generate Fake data # Generate Fake data
gem "ffaker" gem 'ffaker', '~> 2.0.0'
# Guard
gem 'guard-rspec'
gem 'guard-spinach'
# Notification
gem 'rb-fsevent', require: darwin_only('rb-fsevent')
gem 'growl', require: darwin_only('growl')
gem 'rb-inotify', require: linux_only('rb-inotify')
# PhantomJS driver for Capybara # PhantomJS driver for Capybara
gem 'poltergeist', '~> 1.5.1' gem 'poltergeist', '~> 1.5.1'
gem 'jasmine-rails' gem 'teaspoon', '~> 1.0.0'
gem 'teaspoon-jasmine'
gem "spring", '~> 1.3.1' gem 'spring', '~> 1.3.1'
gem "spring-commands-rspec", '1.0.4' gem 'spring-commands-rspec', '~> 1.0.0'
gem "spring-commands-spinach", '1.0.0' gem 'spring-commands-spinach', '~> 1.0.0'
gem 'spring-commands-teaspoon', '~> 0.0.2'
gem "byebug" gem "byebug"
end end
...@@ -280,4 +271,4 @@ end ...@@ -280,4 +271,4 @@ end
gem "newrelic_rpm" gem "newrelic_rpm"
gem 'octokit', '3.7.0' gem 'octokit', '3.7.0'
gem "rugments" gem "rugments", "~> 1.0.0.beta7"
...@@ -42,7 +42,7 @@ GEM ...@@ -42,7 +42,7 @@ GEM
arel (5.0.1.20140414130214) arel (5.0.1.20140414130214)
asana (0.0.6) asana (0.0.6)
activeresource (>= 3.2.3) activeresource (>= 3.2.3)
asciidoctor (0.1.4) asciidoctor (1.5.2)
ast (2.0.0) ast (2.0.0)
astrolabe (1.3.0) astrolabe (1.3.0)
parser (>= 2.2.0.pre.3, < 3.0) parser (>= 2.2.0.pre.3, < 3.0)
...@@ -101,13 +101,13 @@ GEM ...@@ -101,13 +101,13 @@ GEM
coderay (1.1.0) coderay (1.1.0)
coercible (1.0.0) coercible (1.0.0)
descendants_tracker (~> 0.0.1) descendants_tracker (~> 0.0.1)
coffee-rails (4.0.1) coffee-rails (4.1.0)
coffee-script (>= 2.2.0) coffee-script (>= 2.2.0)
railties (>= 4.0.0, < 5.0) railties (>= 4.0.0, < 5.0)
coffee-script (2.2.0) coffee-script (2.4.1)
coffee-script-source coffee-script-source
execjs execjs
coffee-script-source (1.6.3) coffee-script-source (1.9.1.1)
colored (1.2) colored (1.2)
colorize (0.5.8) colorize (0.5.8)
columnize (0.9.0) columnize (0.9.0)
...@@ -176,7 +176,7 @@ GEM ...@@ -176,7 +176,7 @@ GEM
faraday_middleware (0.9.0) faraday_middleware (0.9.0)
faraday (>= 0.7.4, < 0.9) faraday (>= 0.7.4, < 0.9)
fastercsv (1.5.5) fastercsv (1.5.5)
ffaker (1.22.1) ffaker (2.0.0)
ffi (1.9.8) ffi (1.9.8)
fog (1.21.0) fog (1.21.0)
fog-brightbox fog-brightbox
...@@ -226,11 +226,11 @@ GEM ...@@ -226,11 +226,11 @@ GEM
mime-types (~> 1.19) mime-types (~> 1.19)
gitlab_emoji (0.1.0) gitlab_emoji (0.1.0)
gemojione (~> 2.0) gemojione (~> 2.0)
gitlab_git (7.1.12) gitlab_git (7.2.2)
activesupport (~> 4.0) activesupport (~> 4.0)
charlock_holmes (~> 0.6) charlock_holmes (~> 0.6)
gitlab-linguist (~> 3.0) gitlab-linguist (~> 3.0)
rugged (~> 0.21.2) rugged (~> 0.22.2)
gitlab_meta (7.0) gitlab_meta (7.0)
gitlab_omniauth-ldap (1.2.1) gitlab_omniauth-ldap (1.2.1)
net-ldap (~> 0.9) net-ldap (~> 0.9)
...@@ -262,19 +262,6 @@ GEM ...@@ -262,19 +262,6 @@ GEM
grape-entity (0.4.2) grape-entity (0.4.2)
activesupport activesupport
multi_json (>= 1.3.2) multi_json (>= 1.3.2)
growl (1.0.3)
guard (2.2.4)
formatador (>= 0.2.4)
listen (~> 2.1)
lumberjack (~> 1.0)
pry (>= 0.9.12)
thor (>= 0.18.1)
guard-rspec (4.2.0)
guard (>= 2.1.1)
rspec (>= 2.14, < 4.0)
guard-spinach (0.0.2)
guard (>= 1.1)
spinach
haml (4.0.5) haml (4.0.5)
tilt tilt
haml-rails (0.5.3) haml-rails (0.5.3)
...@@ -301,14 +288,8 @@ GEM ...@@ -301,14 +288,8 @@ GEM
i18n (0.7.0) i18n (0.7.0)
ice_cube (0.11.1) ice_cube (0.11.1)
ice_nine (0.10.0) ice_nine (0.10.0)
jasmine-core (2.2.0)
jasmine-rails (0.10.8)
jasmine-core (>= 1.3, < 3.0)
phantomjs (>= 1.9)
railties (>= 3.2.0)
sprockets-rails
jquery-atwho-rails (1.0.1) jquery-atwho-rails (1.0.1)
jquery-rails (3.1.0) jquery-rails (3.1.2)
railties (>= 3.0, < 5.0) railties (>= 3.0, < 5.0)
thor (>= 0.14, < 2.0) thor (>= 0.14, < 2.0)
jquery-scrollto-rails (1.4.3) jquery-scrollto-rails (1.4.3)
...@@ -333,13 +314,14 @@ GEM ...@@ -333,13 +314,14 @@ GEM
celluloid (~> 0.16.0) celluloid (~> 0.16.0)
rb-fsevent (>= 0.9.3) rb-fsevent (>= 0.9.3)
rb-inotify (>= 0.9) rb-inotify (>= 0.9)
lumberjack (1.0.4) macaddr (1.7.1)
systemu (~> 2.6.2)
mail (2.6.3) mail (2.6.3)
mime-types (>= 1.16, < 3) mime-types (>= 1.16, < 3)
method_source (0.8.2) method_source (0.8.2)
mime-types (1.25.1) mime-types (1.25.1)
mimemagic (0.3.0) mimemagic (0.3.0)
mini_portile (0.6.1) mini_portile (0.6.2)
minitest (5.3.5) minitest (5.3.5)
mousetrap-rails (1.4.6) mousetrap-rails (1.4.6)
multi_json (1.10.1) multi_json (1.10.1)
...@@ -351,7 +333,7 @@ GEM ...@@ -351,7 +333,7 @@ GEM
net-ssh (>= 2.6.5) net-ssh (>= 2.6.5)
net-ssh (2.8.0) net-ssh (2.8.0)
newrelic_rpm (3.9.4.245) newrelic_rpm (3.9.4.245)
nokogiri (1.6.5) nokogiri (1.6.6.2)
mini_portile (~> 0.6.0) mini_portile (~> 0.6.0)
nprogress-rails (0.1.2.3) nprogress-rails (0.1.2.3)
oauth (0.4.7) oauth (0.4.7)
...@@ -390,6 +372,9 @@ GEM ...@@ -390,6 +372,9 @@ GEM
omniauth-oauth2 (1.1.1) omniauth-oauth2 (1.1.1)
oauth2 (~> 0.8.0) oauth2 (~> 0.8.0)
omniauth (~> 1.0) omniauth (~> 1.0)
omniauth-saml (1.3.1)
omniauth (~> 1.1)
ruby-saml (~> 0.8.1)
omniauth-shibboleth (1.1.1) omniauth-shibboleth (1.1.1)
omniauth (>= 1.0.0) omniauth (>= 1.0.0)
omniauth-twitter (1.0.1) omniauth-twitter (1.0.1)
...@@ -401,7 +386,6 @@ GEM ...@@ -401,7 +386,6 @@ GEM
parser (2.2.0.2) parser (2.2.0.2)
ast (>= 1.1, < 3.0) ast (>= 1.1, < 3.0)
pg (0.15.1) pg (0.15.1)
phantomjs (1.9.8.0)
poltergeist (1.5.1) poltergeist (1.5.1)
capybara (~> 2.1) capybara (~> 2.1)
cliver (~> 0.3.1) cliver (~> 0.3.1)
...@@ -419,10 +403,10 @@ GEM ...@@ -419,10 +403,10 @@ GEM
quiet_assets (1.0.2) quiet_assets (1.0.2)
railties (>= 3.1, < 5.0) railties (>= 3.1, < 5.0)
racc (1.4.10) racc (1.4.10)
rack (1.5.2) rack (1.5.3)
rack-accept (0.4.5) rack-accept (0.4.5)
rack (>= 0.4) rack (>= 0.4)
rack-attack (4.2.0) rack-attack (4.3.0)
rack rack
rack-cors (0.2.9) rack-cors (0.2.9)
rack-mini-profiler (0.9.0) rack-mini-profiler (0.9.0)
...@@ -451,8 +435,6 @@ GEM ...@@ -451,8 +435,6 @@ GEM
sprockets-rails (~> 2.0) sprockets-rails (~> 2.0)
rails-observers (0.1.2) rails-observers (0.1.2)
activemodel (~> 4.0) activemodel (~> 4.0)
rails_autolink (1.1.6)
rails (> 3.1)
railties (4.1.9) railties (4.1.9)
actionpack (= 4.1.9) actionpack (= 4.1.9)
activesupport (= 4.1.9) activesupport (= 4.1.9)
...@@ -498,10 +480,6 @@ GEM ...@@ -498,10 +480,6 @@ GEM
rqrcode (0.4.2) rqrcode (0.4.2)
rqrcode-rails3 (0.1.7) rqrcode-rails3 (0.1.7)
rqrcode (>= 0.4.2) rqrcode (>= 0.4.2)
rspec (2.99.0)
rspec-core (~> 2.99.0)
rspec-expectations (~> 2.99.0)
rspec-mocks (~> 2.99.0)
rspec-collection_matchers (1.1.2) rspec-collection_matchers (1.1.2)
rspec-expectations (>= 2.99.0.beta1) rspec-expectations (>= 2.99.0.beta1)
rspec-core (2.99.2) rspec-core (2.99.2)
...@@ -524,6 +502,9 @@ GEM ...@@ -524,6 +502,9 @@ GEM
rainbow (>= 1.99.1, < 3.0) rainbow (>= 1.99.1, < 3.0)
ruby-progressbar (~> 1.4) ruby-progressbar (~> 1.4)
ruby-progressbar (1.7.1) ruby-progressbar (1.7.1)
ruby-saml (0.8.2)
nokogiri (>= 1.5.0)
uuid (~> 2.3)
ruby2ruby (2.1.3) ruby2ruby (2.1.3)
ruby_parser (~> 3.1) ruby_parser (~> 3.1)
sexp_processor (~> 4.0) sexp_processor (~> 4.0)
...@@ -531,8 +512,8 @@ GEM ...@@ -531,8 +512,8 @@ GEM
sexp_processor (~> 4.1) sexp_processor (~> 4.1)
rubyntlm (0.5.0) rubyntlm (0.5.0)
rubypants (0.2.0) rubypants (0.2.0)
rugged (0.21.4) rugged (0.22.2)
rugments (1.0.0.beta6) rugments (1.0.0.beta7)
safe_yaml (0.9.7) safe_yaml (0.9.7)
sanitize (2.1.0) sanitize (2.1.0)
nokogiri (>= 1.4.4) nokogiri (>= 1.4.4)
...@@ -548,9 +529,9 @@ GEM ...@@ -548,9 +529,9 @@ GEM
sdoc (0.3.20) sdoc (0.3.20)
json (>= 1.1.3) json (>= 1.1.3)
rdoc (~> 3.10) rdoc (~> 3.10)
seed-fu (2.3.1) seed-fu (2.3.5)
activerecord (>= 3.1, < 4.2) activerecord (>= 3.1, < 4.3)
activesupport (>= 3.1, < 4.2) activesupport (>= 3.1, < 4.3)
select2-rails (3.5.2) select2-rails (3.5.2)
thor (~> 0.14) thor (~> 0.14)
settingslogic (2.0.9) settingslogic (2.0.9)
...@@ -590,11 +571,13 @@ GEM ...@@ -590,11 +571,13 @@ GEM
capybara (>= 2.0.0) capybara (>= 2.0.0)
railties (>= 3) railties (>= 3)
spinach (>= 0.4) spinach (>= 0.4)
spring (1.3.3) spring (1.3.6)
spring-commands-rspec (1.0.4) spring-commands-rspec (1.0.4)
spring (>= 0.9.1) spring (>= 0.9.1)
spring-commands-spinach (1.0.0) spring-commands-spinach (1.0.0)
spring (>= 0.9.1) spring (>= 0.9.1)
spring-commands-teaspoon (0.0.2)
spring (>= 0.9.1)
sprockets (2.11.0) sprockets (2.11.0)
hike (~> 1.2) hike (~> 1.2)
multi_json (~> 1.0) multi_json (~> 1.0)
...@@ -607,8 +590,13 @@ GEM ...@@ -607,8 +590,13 @@ GEM
stamp (0.5.0) stamp (0.5.0)
state_machine (1.2.0) state_machine (1.2.0)
stringex (2.5.2) stringex (2.5.2)
systemu (2.6.5)
task_list (1.0.2) task_list (1.0.2)
html-pipeline html-pipeline
teaspoon (1.0.2)
railties (>= 3.2.5, < 5)
teaspoon-jasmine (2.2.0)
teaspoon (>= 1.0.0)
temple (0.6.7) temple (0.6.7)
term-ansicolor (1.2.2) term-ansicolor (1.2.2)
tins (~> 0.8) tins (~> 0.8)
...@@ -634,7 +622,7 @@ GEM ...@@ -634,7 +622,7 @@ GEM
multi_json (~> 1.7) multi_json (~> 1.7)
twitter-stream (~> 0.1) twitter-stream (~> 0.1)
tins (0.13.1) tins (0.13.1)
turbolinks (2.0.0) turbolinks (2.5.3)
coffee-rails coffee-rails
twitter-stream (0.1.16) twitter-stream (0.1.16)
eventmachine (>= 0.12.8) eventmachine (>= 0.12.8)
...@@ -655,6 +643,8 @@ GEM ...@@ -655,6 +643,8 @@ GEM
raindrops (~> 0.7) raindrops (~> 0.7)
unicorn-worker-killer (0.4.2) unicorn-worker-killer (0.4.2)
unicorn (~> 4) unicorn (~> 4)
uuid (2.3.7)
macaddr (~> 1.0)
version_sorter (2.0.0) version_sorter (2.0.0)
virtus (1.0.1) virtus (1.0.1)
axiom-types (~> 0.0.5) axiom-types (~> 0.0.5)
...@@ -684,7 +674,7 @@ DEPENDENCIES ...@@ -684,7 +674,7 @@ DEPENDENCIES
addressable addressable
annotate (~> 2.6.0.beta2) annotate (~> 2.6.0.beta2)
asana (~> 0.0.6) asana (~> 0.0.6)
asciidoctor (= 0.1.4) asciidoctor (~> 1.5.2)
attr_encrypted (= 1.3.4) attr_encrypted (= 1.3.4)
awesome_print awesome_print
better_errors better_errors
...@@ -714,7 +704,7 @@ DEPENDENCIES ...@@ -714,7 +704,7 @@ DEPENDENCIES
email_spec email_spec
enumerize enumerize
factory_girl_rails factory_girl_rails
ffaker ffaker (~> 2.0.0)
fog (~> 1.14) fog (~> 1.14)
font-awesome-rails (~> 4.2) font-awesome-rails (~> 4.2)
foreman foreman
...@@ -725,23 +715,19 @@ DEPENDENCIES ...@@ -725,23 +715,19 @@ DEPENDENCIES
gitlab-license (~> 0.0.2) gitlab-license (~> 0.0.2)
gitlab-linguist (~> 3.0.1) gitlab-linguist (~> 3.0.1)
gitlab_emoji (~> 0.1) gitlab_emoji (~> 0.1)
gitlab_git (~> 7.1.12) gitlab_git (~> 7.2.2)
gitlab_meta (= 7.0) gitlab_meta (= 7.0)
gitlab_omniauth-ldap (= 1.2.1) gitlab_omniauth-ldap (= 1.2.1)
gollum-lib (~> 4.0.2) gollum-lib (~> 4.0.2)
gon (~> 5.0.0) gon (~> 5.0.0)
grape (~> 0.6.1) grape (~> 0.6.1)
grape-entity (~> 0.4.2) grape-entity (~> 0.4.2)
growl
guard-rspec
guard-spinach
haml-rails haml-rails
hipchat (~> 1.5.0) hipchat (~> 1.5.0)
html-pipeline (~> 1.11.0) html-pipeline (~> 1.11.0)
httparty httparty
jasmine-rails
jquery-atwho-rails (~> 1.0.0) jquery-atwho-rails (~> 1.0.0)
jquery-rails jquery-rails (= 3.1.2)
jquery-scrollto-rails jquery-scrollto-rails
jquery-turbolinks jquery-turbolinks
jquery-ui-rails jquery-ui-rails
...@@ -760,6 +746,7 @@ DEPENDENCIES ...@@ -760,6 +746,7 @@ DEPENDENCIES
omniauth-gitlab omniauth-gitlab
omniauth-google-oauth2 omniauth-google-oauth2
omniauth-kerberos omniauth-kerberos
omniauth-saml
omniauth-shibboleth omniauth-shibboleth
omniauth-twitter omniauth-twitter
org-ruby (= 0.9.12) org-ruby (= 0.9.12)
...@@ -767,15 +754,12 @@ DEPENDENCIES ...@@ -767,15 +754,12 @@ DEPENDENCIES
poltergeist (~> 1.5.1) poltergeist (~> 1.5.1)
pry-rails pry-rails
quiet_assets (~> 1.0.1) quiet_assets (~> 1.0.1)
rack-attack rack-attack (~> 4.3.0)
rack-cors rack-cors
rack-mini-profiler rack-mini-profiler
rack-oauth2 (~> 1.0.5) rack-oauth2 (~> 1.0.5)
rails (~> 4.1.0) rails (~> 4.1.0)
rails_autolink (~> 1.1)
raphael-rails (~> 2.1.2) raphael-rails (~> 2.1.2)
rb-fsevent
rb-inotify
rdoc (~> 3.6) rdoc (~> 3.6)
redcarpet (~> 3.2.3) redcarpet (~> 3.2.3)
redis-rails redis-rails
...@@ -784,7 +768,7 @@ DEPENDENCIES ...@@ -784,7 +768,7 @@ DEPENDENCIES
rqrcode-rails3 rqrcode-rails3
rspec-rails (= 2.99) rspec-rails (= 2.99)
rubocop (= 0.28.0) rubocop (= 0.28.0)
rugments rugments (~> 1.0.0.beta7)
sanitize (~> 2.0) sanitize (~> 2.0)
sass-rails (~> 4.0.2) sass-rails (~> 4.0.2)
sdoc sdoc
...@@ -801,15 +785,18 @@ DEPENDENCIES ...@@ -801,15 +785,18 @@ DEPENDENCIES
slim slim
spinach-rails spinach-rails
spring (~> 1.3.1) spring (~> 1.3.1)
spring-commands-rspec (= 1.0.4) spring-commands-rspec (~> 1.0.0)
spring-commands-spinach (= 1.0.0) spring-commands-spinach (~> 1.0.0)
spring-commands-teaspoon (~> 0.0.2)
stamp stamp
state_machine state_machine
task_list (~> 1.0.0) task_list (= 1.0.2)
teaspoon (~> 1.0.0)
teaspoon-jasmine
test_after_commit test_after_commit
thin thin
tinder (~> 1.9.2) tinder (~> 1.9.2)
turbolinks turbolinks (~> 2.5.0)
uglifier uglifier
underscore-rails (~> 1.4.4) underscore-rails (~> 1.4.4)
unf unf
......
# A sample Guardfile
# More info at https://github.com/guard/guard#readme
guard 'rspec', cmd: "spring rspec", all_on_start: false, all_after_pass: false do
watch(%r{^spec/.+_spec\.rb$})
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
watch(%r{^lib/api/(.+)\.rb$}) { |m| "spec/requests/api/#{m[1]}_spec.rb" }
watch('spec/spec_helper.rb') { "spec" }
# Rails example
watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
watch('config/routes.rb') { "spec/routing" }
watch('app/controllers/application_controller.rb') { "spec/controllers" }
# Capybara request specs
watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/requests/#{m[1]}_spec.rb" }
end
guard 'spinach', command_prefix: 'spring' do
watch(%r|^features/(.*)\.feature|)
watch(%r|^features/steps/(.*)([^/]+)\.rb|) do |m|
"features/#{m[1]}#{m[2]}.feature"
end
end
...@@ -45,7 +45,7 @@ There are two editions of GitLab. ...@@ -45,7 +45,7 @@ There are two editions of GitLab.
*GitLab [Community Edition](https://about.gitlab.com/features/) (CE)* is available without any costs under an MIT license. *GitLab [Community Edition](https://about.gitlab.com/features/) (CE)* is available without any costs under an MIT license.
*GitLab Enterprise Edition (EE)* includes [extra features](https://about.gitlab.com/features/#compare) that are most useful for organizations with more than 100 users. *GitLab Enterprise Edition (EE)* includes [extra features](https://about.gitlab.com/features/#compare) that are most useful for organizations with more than 100 users.
To get access to the EE and support please [become a subscriber](https://about.gitlab.com/pricing/). To use EE and get official support please [become a subscriber](https://about.gitlab.com/pricing/).
## Code status ## Code status
......
7.11.0.pre-ee 7.12.0.pre-ee
...@@ -49,8 +49,6 @@ window.slugify = (text) -> ...@@ -49,8 +49,6 @@ window.slugify = (text) ->
window.ajaxGet = (url) -> window.ajaxGet = (url) ->
$.ajax({type: "GET", url: url, dataType: "script"}) $.ajax({type: "GET", url: url, dataType: "script"})
window.showAndHide = (selector) ->
window.split = (val) -> window.split = (val) ->
return val.split( /,\s*/ ) return val.split( /,\s*/ )
...@@ -92,15 +90,7 @@ window.disableButtonIfAnyEmptyField = (form, form_selector, button_selector) -> ...@@ -92,15 +90,7 @@ window.disableButtonIfAnyEmptyField = (form, form_selector, button_selector) ->
window.sanitize = (str) -> window.sanitize = (str) ->
return str.replace(/<(?:.|\n)*?>/gm, '') return str.replace(/<(?:.|\n)*?>/gm, '')
window.linkify = (str) ->
exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig
return str.replace(exp,"<a href='$1'>$1</a>")
window.simpleFormat = (str) ->
linkify(sanitize(str).replace(/\n/g, '<br />'))
window.unbindEvents = -> window.unbindEvents = ->
$(document).unbind('scroll')
$(document).off('scroll') $(document).off('scroll')
window.shiftWindow = -> window.shiftWindow = ->
...@@ -118,7 +108,10 @@ $.timeago.settings.allowFuture = true ...@@ -118,7 +108,10 @@ $.timeago.settings.allowFuture = true
$ -> $ ->
# Click a .js-select-on-focus field, select the contents # Click a .js-select-on-focus field, select the contents
$(".js-select-on-focus").on "focusin", -> $(this).select() $(".js-select-on-focus").on "focusin", ->
# Prevent a mouseup event from deselecting the input
$(this).select().one 'mouseup', (e) ->
e.preventDefault()
$('.remove-row').bind 'ajax:success', -> $('.remove-row').bind 'ajax:success', ->
$(this).closest('li').fadeOut() $(this).closest('li').fadeOut()
...@@ -142,8 +135,8 @@ $ -> ...@@ -142,8 +135,8 @@ $ ->
# Place the logo tooltip on the right when collapsed, bottom when expanded # Place the logo tooltip on the right when collapsed, bottom when expanded
$el.parents('header').hasClass('header-collapsed') and 'right' or 'bottom' $el.parents('header').hasClass('header-collapsed') and 'right' or 'bottom'
else else
# Otherwise use the data-placement attribute like normal # Otherwise use the data-placement attribute, or 'bottom' if undefined
$el.data('placement') $el.data('placement') or 'bottom'
}) })
# Form submitter # Form submitter
...@@ -176,6 +169,10 @@ $ -> ...@@ -176,6 +169,10 @@ $ ->
$(@).next('table').show() $(@).next('table').show()
$(@).remove() $(@).remove()
$('.navbar-toggle').on 'click', ->
$('.header-content .title').toggle()
$('.header-content .navbar-collapse').toggle()
# Show/hide comments on diff # Show/hide comments on diff
$("body").on "click", ".js-toggle-diff-comments", (e) -> $("body").on "click", ".js-toggle-diff-comments", (e) ->
$(@).toggleClass('active') $(@).toggleClass('active')
...@@ -191,14 +188,3 @@ $ -> ...@@ -191,14 +188,3 @@ $ ->
new ConfirmDangerModal(form, text) new ConfirmDangerModal(form, text)
new Aside() new Aside()
(($) ->
# Disable an element and add the 'disabled' Bootstrap class
$.fn.extend disable: ->
$(@).attr('disabled', 'disabled').addClass('disabled')
# Enable an element and remove the 'disabled' Bootstrap class
$.fn.extend enable: ->
$(@).removeAttr('disabled').removeClass('disabled')
)(jQuery)
...@@ -27,6 +27,7 @@ class Dispatcher ...@@ -27,6 +27,7 @@ class Dispatcher
new Milestone() new Milestone()
when 'projects:milestones:new', 'projects:milestones:edit' when 'projects:milestones:new', 'projects:milestones:edit'
new ZenMode() new ZenMode()
new DropzoneInput($('.milestone-form'))
when 'projects:compare:show' when 'projects:compare:show'
new Diff() new Diff()
when 'projects:issues:new','projects:issues:edit' when 'projects:issues:new','projects:issues:edit'
......
$.fn.showAndHide = -> # Disable an element and add the 'disabled' Bootstrap class
$(@).show(). $.fn.extend disable: ->
delay(3000). $(@)
fadeOut() .attr('disabled', 'disabled')
.addClass('disabled')
$.fn.enableButton = ->
$(@).removeAttr('disabled').
removeClass('disabled')
$.fn.disableButton = ->
$(@).attr('disabled', 'disabled').
addClass('disabled')
# Enable an element and remove the 'disabled' Bootstrap class
$.fn.extend enable: ->
$(@)
.removeAttr('disabled')
.removeClass('disabled')
#= require jquery
#= require jquery.waitforimages #= require jquery.waitforimages
#= require task_list #= require task_list
......
#= require jquery #= require jquery.waitforimages
#= require bootstrap
#= require task_list #= require task_list
class @MergeRequest class @MergeRequest
# Initialize MergeRequest behavior
#
# Options:
# action - String, current controller action
# diffs_loaded - Boolean, have diffs been pre-rendered server-side?
# (default: true if `action` is 'diffs', otherwise false)
# commits_loaded - Boolean, have commits been pre-rendered server-side?
# (default: false)
#
# check_enable - Boolean, whether to check automerge status
# url_to_automerge_check - String, URL to use to check automerge status
# current_status - String, current automerge status
# ci_enable - Boolean, whether a CI service is enabled
# url_to_ci_check - String, URL to use to check CI status
#
constructor: (@opts) -> constructor: (@opts) ->
@initContextWidget() @initContextWidget()
this.$el = $('.merge-request') this.$el = $('.merge-request')
@diffs_loaded = if @opts.action == 'diffs' then true else false
@commits_loaded = false
this.activateTab(@opts.action) @diffs_loaded = @opts.diffs_loaded or @opts.action == 'diffs'
@commits_loaded = @opts.commits_loaded or false
this.bindEvents() this.bindEvents()
this.activateTabFromPath()
this.initMergeWidget() this.initMergeWidget()
this.$('.show-all-commits').on 'click', => this.$('.show-all-commits').on 'click', =>
...@@ -65,8 +79,18 @@ class @MergeRequest ...@@ -65,8 +79,18 @@ class @MergeRequest
, 'json' , 'json'
bindEvents: -> bindEvents: ->
this.$('.merge-request-tabs').on 'click', 'li', (event) => this.$('.merge-request-tabs a[data-toggle="tab"]').on 'shown.bs.tab', (e) =>
this.activateTab($(event.currentTarget).data('action')) $target = $(e.target)
tab_action = $target.data('action')
# Lazy-load diffs
if tab_action == 'diffs'
this.loadDiff() unless @diffs_loaded
$('.diff-header').trigger('sticky_kit:recalc')
# Skip tab-persisting behavior on MergeRequests#new
unless @opts.action == 'new'
@setCurrentAction(tab_action)
this.$('.accept_merge_request').on 'click', -> this.$('.accept_merge_request').on 'click', ->
$('.automerge_widget.can_be_merged').hide() $('.automerge_widget.can_be_merged').hide()
...@@ -84,21 +108,54 @@ class @MergeRequest ...@@ -84,21 +108,54 @@ class @MergeRequest
this.$('.remove_source_branch_in_progress').hide() this.$('.remove_source_branch_in_progress').hide()
this.$('.remove_source_branch_widget.failed').show() this.$('.remove_source_branch_widget.failed').show()
activateTab: (action) -> # Activate a tab based on the current URL path
this.$('.merge-request-tabs li').removeClass 'active' #
this.$('.tab-content').hide() # If the current action is 'show' or 'new' (i.e., initial page load),
switch action # activates the first tab, otherwise activates the tab corresponding to the
when 'diffs' # current action (diffs, commits).
this.$('.merge-request-tabs .diffs-tab').addClass 'active' activateTabFromPath: ->
this.loadDiff() unless @diffs_loaded if @opts.action == 'show' || @opts.action == 'new'
this.$('.diffs').show() this.$('.merge-request-tabs a[data-toggle="tab"]:first').tab('show')
$(".diff-header").trigger("sticky_kit:recalc") else
when 'commits' this.$(".merge-request-tabs a[data-action='#{@opts.action}']").tab('show')
this.$('.merge-request-tabs .commits-tab').addClass 'active'
this.$('.commits').show() # Replaces the current Merge Request-specific action in the URL with a new one
else #
this.$('.merge-request-tabs .notes-tab').addClass 'active' # If the action is "notes", the URL is reset to the standard
this.$('.notes').show() # `MergeRequests#show` route.
#
# Examples:
#
# location.pathname # => "/namespace/project/merge_requests/1"
# setCurrentAction('diffs')
# location.pathname # => "/namespace/project/merge_requests/1/diffs"
#
# location.pathname # => "/namespace/project/merge_requests/1/diffs"
# setCurrentAction('notes')
# location.pathname # => "/namespace/project/merge_requests/1"
#
# location.pathname # => "/namespace/project/merge_requests/1/diffs"
# setCurrentAction('commits')
# location.pathname # => "/namespace/project/merge_requests/1/commits"
setCurrentAction: (action) ->
# Normalize action, just to be safe
action = 'notes' if action == 'show'
# Remove a trailing '/commits' or '/diffs'
new_state = location.pathname.replace(/\/(commits|diffs)\/?$/, '')
# Append the new action if we're on a tab other than 'notes'
unless action == 'notes'
new_state += "/#{action}"
# Ensure parameters and hash come along for the ride
new_state += location.search + location.hash
# Replace the current history state with the new one without breaking
# Turbolinks' history.
#
# See https://github.com/rails/turbolinks/issues/363
history.replaceState {turbolinks: true, url: new_state}, '', new_state
showState: (state) -> showState: (state) ->
$('.automerge_widget').hide() $('.automerge_widget').hide()
......
#= require jquery
#= require autosave #= require autosave
#= require bootstrap
#= require dropzone #= require dropzone
#= require dropzone_input #= require dropzone_input
#= require gfm_auto_complete #= require gfm_auto_complete
...@@ -312,6 +310,14 @@ class @Notes ...@@ -312,6 +310,14 @@ class @Notes
form.show() form.show()
textarea = form.find("textarea") textarea = form.find("textarea")
textarea.focus() textarea.focus()
# HACK (rspeicher/DouweM): Work around a Chrome 43 bug(?).
# The textarea has the correct value, Chrome just won't show it unless we
# modify it, so let's clear it and re-set it!
value = textarea.val()
textarea.val ""
textarea.val value
disableButtonIfEmptyField textarea, form.find(".js-comment-button") disableButtonIfEmptyField textarea, form.find(".js-comment-button")
### ###
......
...@@ -12,11 +12,11 @@ class @Profile ...@@ -12,11 +12,11 @@ class @Profile
$(this).find('.update-failed').hide() $(this).find('.update-failed').hide()
$('.update-username form').on 'ajax:complete', -> $('.update-username form').on 'ajax:complete', ->
$(this).find('.btn-save').enableButton() $(this).find('.btn-save').enable()
$(this).find('.loading-gif').hide() $(this).find('.loading-gif').hide()
$('.update-notifications').on 'ajax:complete', -> $('.update-notifications').on 'ajax:complete', ->
$(this).find('.btn-save').enableButton() $(this).find('.btn-save').enable()
$('.js-choose-user-avatar-button').bind "click", -> $('.js-choose-user-avatar-button').bind "click", ->
......
#= require jquery
#= require mousetrap #= require mousetrap
#= require shortcuts_navigation #= require shortcuts_navigation
class @ShortcutsIssuable extends ShortcutsNavigation class @ShortcutsIssuable extends ShortcutsNavigation
......
#= require d3 #= require d3
#= require jquery
#= require stat_graph_contributors_util #= require stat_graph_contributors_util
class @ContributorsStatGraph class @ContributorsStatGraph
......
...@@ -2,11 +2,15 @@ window.ContributorsStatGraphUtil = ...@@ -2,11 +2,15 @@ window.ContributorsStatGraphUtil =
parse_log: (log) -> parse_log: (log) ->
total = {} total = {}
by_author = {} by_author = {}
by_email = {}
for entry in log for entry in log
@add_date(entry.date, total) unless total[entry.date]? @add_date(entry.date, total) unless total[entry.date]?
@add_author(entry, by_author) unless by_author[entry.author_name]?
@add_date(entry.date, by_author[entry.author_name]) unless by_author[entry.author_name][entry.date] data = by_author[entry.author_name] #|| by_email[entry.author_email]
@store_data(entry, total[entry.date], by_author[entry.author_name][entry.date]) data ?= @add_author(entry, by_author, by_email)
@add_date(entry.date, data) unless data[entry.date]
@store_data(entry, total[entry.date], data[entry.date])
total = _.toArray(total) total = _.toArray(total)
by_author = _.toArray(by_author) by_author = _.toArray(by_author)
total: total, by_author: by_author total: total, by_author: by_author
...@@ -15,10 +19,12 @@ window.ContributorsStatGraphUtil = ...@@ -15,10 +19,12 @@ window.ContributorsStatGraphUtil =
collection[date] = {} collection[date] = {}
collection[date].date = date collection[date].date = date
add_author: (author, by_author) -> add_author: (author, by_author, by_email) ->
by_author[author.author_name] = {} data = {}
by_author[author.author_name].author_name = author.author_name data.author_name = author.author_name
by_author[author.author_name].author_email = author.author_email data.author_email = author.author_email
by_author[author.author_name] = data
by_email[author.author_email] = data
store_data: (entry, total, by_author) -> store_data: (entry, total, by_author) ->
@store_commits(total, by_author) @store_commits(total, by_author)
......
class @Wikis class @Wikis
constructor: -> constructor: ->
$('.build-new-wiki').bind "click", -> $('.build-new-wiki').bind "click", (e) ->
$('[data-error~=slug]').addClass("hidden")
$('p.hint').show()
field = $('#new_wiki_path') field = $('#new_wiki_path')
slug = field.val() valid_slug_pattern = /^[\w\/-]+$/
path = field.attr('data-wikis-path')
if(slug.length > 0) slug = field.val()
location.href = path + "/" + slug if slug.match valid_slug_pattern
path = field.attr('data-wikis-path')
if(slug.length > 0)
location.href = path + "/" + slug
else
e.preventDefault()
$('p.hint').hide()
$('[data-error~=slug]').removeClass("hidden")
class @ZenMode #= require dropzone
@fullscreen_prefix = 'fullscreen_' #= require mousetrap
#= require mousetrap/pause
class @ZenMode
constructor: -> constructor: ->
@active_zen_area = null @active_zen_area = null
@active_checkbox = null @active_checkbox = null
...@@ -12,34 +14,31 @@ class @ZenMode ...@@ -12,34 +14,31 @@ class @ZenMode
$('body').on 'click', '.zen-enter-link', (e) => $('body').on 'click', '.zen-enter-link', (e) =>
e.preventDefault() e.preventDefault()
$(e.currentTarget).closest('.zennable').find('.zen-toggle-comment').prop('checked', true) $(e.currentTarget).closest('.zennable').find('.zen-toggle-comment').prop('checked', true).change()
$('body').on 'click', '.zen-leave-link', (e) => $('body').on 'click', '.zen-leave-link', (e) =>
e.preventDefault() e.preventDefault()
$(e.currentTarget).closest('.zennable').find('.zen-toggle-comment').prop('checked', false) $(e.currentTarget).closest('.zennable').find('.zen-toggle-comment').prop('checked', false).change()
$('body').on 'change', '.zen-toggle-comment', (e) => $('body').on 'change', '.zen-toggle-comment', (e) =>
checkbox = e.currentTarget checkbox = e.currentTarget
if checkbox.checked if checkbox.checked
# Disable other keyboard shortcuts in ZEN mode # Disable other keyboard shortcuts in ZEN mode
Mousetrap.pause() Mousetrap.pause()
@udpateActiveZenArea(checkbox) @updateActiveZenArea(checkbox)
else else
@exitZenMode() @exitZenMode()
$(document).on 'keydown', (e) => $(document).on 'keydown', (e) =>
if e.keyCode is $.ui.keyCode.ESCAPE if e.keyCode is 27 # Esc
@exitZenMode() @exitZenMode()
e.preventDefault() e.preventDefault()
$(window).on 'hashchange', @updateZenModeFromLocationHash updateActiveZenArea: (checkbox) =>
udpateActiveZenArea: (checkbox) =>
@active_checkbox = $(checkbox) @active_checkbox = $(checkbox)
@active_checkbox.prop('checked', true) @active_checkbox.prop('checked', true)
@active_zen_area = @active_checkbox.parent().find('textarea') @active_zen_area = @active_checkbox.parent().find('textarea')
@active_zen_area.focus() @active_zen_area.focus()
window.location.hash = ZenMode.fullscreen_prefix + @active_checkbox.prop('id')
exitZenMode: => exitZenMode: =>
if @active_zen_area isnt null if @active_zen_area isnt null
...@@ -47,21 +46,9 @@ class @ZenMode ...@@ -47,21 +46,9 @@ class @ZenMode
@active_checkbox.prop('checked', false) @active_checkbox.prop('checked', false)
@active_zen_area = null @active_zen_area = null
@active_checkbox = null @active_checkbox = null
window.location.hash = '' @restoreScroll(@scroll_position)
window.scrollTo(window.pageXOffset, @scroll_position)
# Enable dropzone when leaving ZEN mode # Enable dropzone when leaving ZEN mode
Dropzone.forElement('.div-dropzone').enable() Dropzone.forElement('.div-dropzone').enable()
checkboxFromLocationHash: (e) -> restoreScroll: (y) ->
id = $.trim(window.location.hash.replace('#' + ZenMode.fullscreen_prefix, '')) window.scrollTo(window.pageXOffset, y)
if id
return $('.zennable input[type=checkbox]#' + id)[0]
else
return null
updateZenModeFromLocationHash: (e) =>
checkbox = @checkboxFromLocationHash()
if checkbox
@udpateActiveZenArea(checkbox)
else
@exitZenMode()
$style_color: #474D57; $style_color: #474D57;
$hover: #FFF3EB; $hover: #FFFAF1;
$gl-text-color: #222222; $gl-text-color: #222222;
$gl-link-color: #446e9b; $gl-link-color: #446e9b;
$nprogress-color: #c0392b; $nprogress-color: #c0392b;
$gl-font-size: 14px; $gl-font-size: 14px;
$list-font-size: 15px; $list-font-size: 15px;
$sidebar_collapsed_width: 52px;
$sidebar_width: 230px; $sidebar_width: 230px;
$avatar_radius: 50%; $avatar_radius: 50%;
$code_font_size: 13px; $code_font_size: 13px;
......
...@@ -316,7 +316,7 @@ table { ...@@ -316,7 +316,7 @@ table {
} }
.btn-sign-in { .btn-sign-in {
margin-top: 5px; margin-top: 7px;
text-shadow: none; text-shadow: none;
} }
...@@ -351,9 +351,8 @@ table { ...@@ -351,9 +351,8 @@ table {
} }
#nprogress .spinner { #nprogress .spinner {
top: auto !important; top: 15px !important;
bottom: 20px !important; right: 10px !important;
left: 20px !important;
} }
.header-with-avatar { .header-with-avatar {
......
...@@ -89,7 +89,6 @@ label { ...@@ -89,7 +89,6 @@ label {
@include box-shadow(none); @include box-shadow(none);
} }
.issuable-description,
.wiki-content { .wiki-content {
margin-top: 35px; margin-top: 35px;
} }
...@@ -2,7 +2,14 @@ ...@@ -2,7 +2,14 @@
* Application Header * Application Header
* *
*/ */
$header-height: 46px;
header { header {
&.navbar-empty {
background: #FFF;
border-bottom: 1px solid #EEE;
}
&.navbar-gitlab { &.navbar-gitlab {
z-index: 100; z-index: 100;
margin-bottom: 0; margin-bottom: 0;
...@@ -11,161 +18,104 @@ header { ...@@ -11,161 +18,104 @@ header {
width: 100%; width: 100%;
.container { .container {
background: #FFF;
width: 100% !important; width: 100% !important;
padding: 0; padding: 0;
background: #FFF;
border-bottom: 1px solid #EEE;
filter: none; filter: none;
.title {
position: relative;
float: left;
margin: 0;
margin-left: 25px;
font-size: 18px;
line-height: 44px;
font-weight: bold;
color: #444;
@include str-truncated(37%);
a {
color: #444;
&:hover {
text-decoration: underline;
}
}
}
.app_logo {
border-bottom: 1px solid transparent;
margin-bottom: -1px;
a {
padding: 5px 8px;
img {
float: left;
}
h3 {
width: 158px;
float: left;
margin: 0;
margin-left: 20px;
font-size: 18px;
line-height: 34px;
font-weight: normal;
}
}
}
.nav > li > a { .nav > li > a {
color: #666; color: #888;
font-size: 14px; font-size: 14px;
line-height: 32px; line-height: 19px;
padding: 6px 10px; padding: 0;
background-color: #f5f5f5;
margin: 9px 0;
margin-left: 10px;
border-radius: 40px;
height: 26px;
width: 26px;
line-height: 26px;
text-align: center;
&:hover, &:focus, &:active { &:hover, &:focus, &:active {
background: none; background-color: #EEE;
} }
} }
/** NAV block with links and profile **/
.nav {
float: right;
margin-right: 0;
}
.navbar-toggle { .navbar-toggle {
color: #666; color: #666;
margin: 0; margin: 0;
border-radius: 0; border-radius: 0;
position: absolute;
right: 2px;
&:hover { &:hover {
background-color: #EEE; background-color: #EEE;
} }
} }
} }
.turbolink-spinner {
font-size: 20px;
margin-right: 10px;
}
@media (max-width: $screen-xs-max) {
border-width: 0;
font-size: 18px;
.title {
@include str-truncated(70%);
}
.navbar-collapse {
margin-top: 47px;
}
.navbar-nav {
margin: 5px 0;
.visible-xs, .visable-sm {
display: table-cell !important;
}
}
li {
display: table-cell;
width: 1%;
a {
text-align: center;
font-size: 18px !important;
}
}
}
} }
/** .header-logo {
* border-bottom: 1px solid transparent;
* Logo holder
*
*/
.app_logo {
float: left; float: left;
margin-right: 9px; height: $header-height;
width: $sidebar_width;
a { a {
float: left; float: left;
height: 46px; height: $header-height;
width: 100%; width: 100%;
padding: 5px 8px;
h3 {
width: 158px;
float: left;
margin: 0;
margin-left: 20px;
font-size: 18px;
line-height: 34px;
font-weight: normal;
}
img { img {
width: 36px; width: 36px;
height: 36px; height: 36px;
float: left;
} }
} }
&:hover { &:hover {
background-color: #EEE; background-color: #EEE;
} }
} }
.profile-pic { .header-content {
padding: 0px !important; border-bottom: 1px solid #EEE;
width: 46px; padding-right: 35px;
height: 46px; height: $header-height;
margin-left: 5px;
img { .title {
width: 46px; position: relative;
height: 46px; float: left;
margin: 0;
margin-left: 35px;
font-size: 18px;
line-height: 44px;
font-weight: bold;
color: #444;
@include str-truncated(37%);
a {
color: #444;
&:hover {
text-decoration: underline;
}
}
} }
} }
/**
*
* Search box
*
*/
.search { .search {
margin-right: 10px; margin-right: 10px;
margin-left: 10px; margin-left: 10px;
...@@ -177,6 +127,7 @@ header { ...@@ -177,6 +127,7 @@ header {
} }
.search-input { .search-input {
width: 220px;
background-image: image-url("icon-search.png"); background-image: image-url("icon-search.png");
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: 10px; background-position: 10px;
...@@ -184,56 +135,74 @@ header { ...@@ -184,56 +135,74 @@ header {
padding: 4px 6px; padding: 4px 6px;
padding-left: 25px; padding-left: 25px;
font-size: 13px; font-size: 13px;
@include border-radius(3px);
border: 1px solid #DDD;
box-shadow: none;
@include transition(all 0.15s ease-in 0s);
background-color: #f5f5f5; background-color: #f5f5f5;
border-color: #f5f5f5;
&:focus {
@include box-shadow(none);
outline: none;
border-color: #DDD;
background-color: #FFF;
}
} }
} }
} }
.search .search-input { @mixin collapsed-header {
width: 300px; .header-logo {
&:focus { width: $sidebar_collapsed_width;
width: 330px;
}
}
@media (max-width: 1200px) { h3 {
.search .search-input { display: none;
width: 200px;
&:focus {
width: 230px;
} }
} }
}
@media (max-width: $screen-xs-max) { .header-content {
#nprogress .spinner { .title {
right: 35px !important; margin-left: 30px;
}
} }
} }
@media (max-width: $screen-md-max) { @media (max-width: $screen-md-max) {
.header-collapsed, .header-expanded { header .container .title {
width: 52px; max-width: 43%;
}
h3 { .header-collapsed, .header-expanded {
display: none; @include collapsed-header;
}
} }
} }
@media(min-width: $screen-md-max) { @media(min-width: $screen-md-max) {
.header-collapsed { .header-collapsed {
width: 52px; @include collapsed-header;
h3 {
display: none;
}
} }
.header-expanded { .header-expanded {
} }
} }
@media (max-width: $screen-xs-max) {
header .container {
font-size: 18px;
.title {
max-width: 70%;
}
.navbar-nav {
margin: 0px;
float: none !important;
.visible-xs, .visable-sm {
display: table-cell !important;
}
}
li {
display: table-cell;
width: 1%;
}
}
}
...@@ -39,7 +39,6 @@ ...@@ -39,7 +39,6 @@
&:hover { &:hover {
background: $hover; background: $hover;
border-bottom: 1px solid darken($hover, 10%);
} }
&:last-child { &:last-child {
......
...@@ -57,7 +57,7 @@ ...@@ -57,7 +57,7 @@
} }
.container .title { .container .title {
margin-left: 6px !important; margin-left: 15px !important;
max-width: 70% !important; max-width: 70% !important;
} }
} }
......
.page-with-sidebar { .page-with-sidebar {
background: $background-color;
.sidebar-wrapper { .sidebar-wrapper {
position: fixed; position: fixed;
top: 0; top: 0;
...@@ -102,13 +100,13 @@ ...@@ -102,13 +100,13 @@
padding-left: 50px; padding-left: 50px;
.sidebar-wrapper { .sidebar-wrapper {
width: 52px; width: $sidebar_collapsed_width;
.nav-sidebar { .nav-sidebar {
margin-top: 29px; margin-top: 29px;
position: fixed; position: fixed;
top: 45px; top: 45px;
width: 52px; width: $sidebar_collapsed_width;
li a { li a {
padding-left: 18px; padding-left: 18px;
...@@ -125,7 +123,21 @@ ...@@ -125,7 +123,21 @@
.collapse-nav a { .collapse-nav a {
left: 0px; left: 0px;
width: 52px; width: $sidebar_collapsed_width;
}
.sidebar-user {
.username {
display: none;
}
.avatar {
margin-bottom: 10px;
}
.logout-holder {
text-align: center;
}
} }
} }
} }
...@@ -170,3 +182,15 @@ ...@@ -170,3 +182,15 @@
@include expanded-sidebar; @include expanded-sidebar;
} }
} }
.sidebar-user {
position: absolute;
bottom: 0;
width: 100%;
padding: 10px;
color: #fff;
.avatar {
margin-top: 5px;
}
}
...@@ -23,6 +23,13 @@ pre { ...@@ -23,6 +23,13 @@ pre {
font-family: $monospace_font; font-family: $monospace_font;
} }
code {
&.key-fingerprint {
background: $body-bg;
color: $text-color;
}
}
/** /**
* Wiki typography * Wiki typography
* *
......
.zennable { .zennable {
position: relative; position: relative;
input { .zen-toggle-comment {
display: none; display: none;
} }
...@@ -26,10 +26,12 @@ ...@@ -26,10 +26,12 @@
} }
} }
// Hide the Enter link when we're in Zen mode
input:checked ~ .zen-backdrop .zen-enter-link { input:checked ~ .zen-backdrop .zen-enter-link {
display: none; display: none;
} }
// Show the Leave link when we're in Zen mode
input:checked ~ .zen-backdrop .zen-leave-link { input:checked ~ .zen-backdrop .zen-leave-link {
display: block; display: block;
position: absolute; position: absolute;
...@@ -62,6 +64,9 @@ ...@@ -62,6 +64,9 @@
} }
} }
// Make the placeholder text in the standard textarea the same color as the
// background, effectively hiding it
.zen-backdrop textarea::-webkit-input-placeholder { .zen-backdrop textarea::-webkit-input-placeholder {
color: white; color: white;
} }
...@@ -78,6 +83,9 @@ ...@@ -78,6 +83,9 @@
color: white; color: white;
} }
// Make the color of the placeholder text in the Zenned-out textarea darker,
// so it becomes visible
input:checked ~ .zen-backdrop textarea::-webkit-input-placeholder { input:checked ~ .zen-backdrop textarea::-webkit-input-placeholder {
color: #999; color: #999;
} }
......
...@@ -123,38 +123,31 @@ ...@@ -123,38 +123,31 @@
.mr-state-widget { .mr-state-widget {
font-size: 13px; font-size: 13px;
background: #F9F9F9; background: #FAFAFA;
margin-bottom: 20px; margin-bottom: 20px;
color: #666; color: #666;
border: 1px solid #EEE; border: 1px solid #e5e5e5;
@include box-shadow(0 1px 1px rgba(0, 0, 0, 0.09)); @include box-shadow(0 1px 1px rgba(0, 0, 0, 0.05));
@include border-radius(3px);
.ci_widget { .ci_widget {
padding: 10px 15px; padding: 10px 15px;
font-size: 15px; font-size: 15px;
border-bottom: 1px solid #BBB; border-bottom: 1px solid #EEE;
color: #777;
background-color: $background-color;
&.ci-success { &.ci-success {
color: $gl-success; color: $gl-success;
border-color: $gl-success;
background-color: #F1FAF1;
} }
&.ci-pending, &.ci-pending,
&.ci-running { &.ci-running {
color: $gl-warning; color: $gl-warning;
border-color: $gl-warning;
background-color: #FAF5F1;
} }
&.ci-failed, &.ci-failed,
&.ci-canceled, &.ci-canceled,
&.ci-error { &.ci-error {
color: $gl-danger; color: $gl-danger;
border-color: $gl-danger;
background-color: #FAF1F1;
} }
} }
...@@ -162,7 +155,8 @@ ...@@ -162,7 +155,8 @@
padding: 10px 15px; padding: 10px 15px;
h4 { h4 {
font-weight: normal; font-weight: bold;
margin: 5px 0;
} }
p:last-child { p:last-child {
......
...@@ -79,11 +79,11 @@ ul.notes { ...@@ -79,11 +79,11 @@ ul.notes {
word-wrap: break-word; word-wrap: break-word;
@include md-typography; @include md-typography;
// Reduce left padding of first ul element // Reduce left padding of first task list ul element
ul.task-list:first-child { ul.task-list:first-child {
padding-left: 10px; padding-left: 10px;
// sub-lists should be padded normally // sub-tasks should be padded normally
ul { ul {
padding-left: 20px; padding-left: 20px;
} }
......
...@@ -84,8 +84,9 @@ ...@@ -84,8 +84,9 @@
} }
.btn { .btn {
line-height: 36px; line-height: 40px;
height: 56px; height: 42px;
padding: 0px 12px;
img { img {
width: 32px; width: 32px;
...@@ -93,3 +94,17 @@ ...@@ -93,3 +94,17 @@
} }
} }
} }
// Profile > Account > Two Factor Authentication
.two-factor-new {
.manual-instructions {
h3 {
margin-top: 0;
}
// Slightly increase the size of the details so they're easier to read
dl {
font-size: 1.1em;
}
}
}
...@@ -48,14 +48,16 @@ ...@@ -48,14 +48,16 @@
} }
.project-home-desc { .project-home-desc {
color: $gray;
float: left;
font-size: 16px; font-size: 16px;
line-height: 1.3; line-height: 1.3;
margin-right: 250px; margin-right: 250px;
}
.project-home-desc { // Render Markdown-generated HTML inline for this block
float: left; p {
color: $gray; display: inline;
}
} }
} }
...@@ -129,7 +131,7 @@ ...@@ -129,7 +131,7 @@
} }
.option-descr { .option-descr {
margin-left: 24px; margin-left: 36px;
color: $gray; color: $gray;
} }
} }
...@@ -209,13 +211,14 @@ ul.nav.nav-projects-tabs { ...@@ -209,13 +211,14 @@ ul.nav.nav-projects-tabs {
line-height: 1.5; line-height: 1.5;
} }
.well { .panel {
padding: 14px; @include border-radius(3px);
h4 { .panel-heading, .panel-footer {
font-weight: normal; font-weight: normal;
margin: 0; background-color: transparent;
color: #555; color: #666;
border-color: #EEE;
} }
.actions { .actions {
...@@ -224,12 +227,18 @@ ul.nav.nav-projects-tabs { ...@@ -224,12 +227,18 @@ ul.nav.nav-projects-tabs {
.nav-pills a { .nav-pills a {
padding: 10px; padding: 10px;
font-weight: bold;
color: $gl-link-color;
} }
.nav { .nav {
margin: 10px 0; margin-bottom: 15px;
} }
} }
.ci-status-image {
max-height: 22px;
}
} }
.transfer-project .select2-container { .transfer-project .select2-container {
......
@mixin gitlab-theme($color-light, $color, $color-darker, $color-dark) { @mixin gitlab-theme($color-light, $color, $color-darker, $color-dark) {
header { header {
&.navbar-gitlab { &.navbar-gitlab {
.app_logo { .header-logo {
background-color: $color-darker; background-color: $color-darker;
border-color: $color-darker;
a { a {
color: $color-light; color: $color-light;
...@@ -19,8 +20,6 @@ ...@@ -19,8 +20,6 @@
} }
.page-with-sidebar { .page-with-sidebar {
background: $color-darker;
.collapse-nav a { .collapse-nav a {
color: #FFF; color: #FFF;
background: $color; background: $color;
...@@ -29,6 +28,12 @@ ...@@ -29,6 +28,12 @@
.sidebar-wrapper { .sidebar-wrapper {
background: $color-darker; background: $color-darker;
border-right: 1px solid $color-darker; border-right: 1px solid $color-darker;
.sidebar-user {
a {
color: $color-light;
}
}
} }
.nav-sidebar li { .nav-sidebar li {
......
...@@ -39,11 +39,13 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController ...@@ -39,11 +39,13 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:sign_in_text, :sign_in_text,
:home_page_url, :home_page_url,
:help_text, :help_text,
:after_sign_out_path,
:max_attachment_size, :max_attachment_size,
:default_project_visibility, :default_project_visibility,
:default_snippet_visibility, :default_snippet_visibility,
:restricted_signup_domains_raw, :restricted_signup_domains_raw,
:version_check_enabled, :version_check_enabled,
:user_oauth_applications,
restricted_visibility_levels: [], restricted_visibility_levels: [],
) )
end end
......
class Admin::DeployKeysController < Admin::ApplicationController class Admin::DeployKeysController < Admin::ApplicationController
before_action :deploy_keys, only: [:index] before_action :deploy_keys, only: [:index]
before_action :deploy_key, only: [:show, :destroy] before_action :deploy_key, only: [:destroy]
def index def index
end
def show
end end
def new def new
......
...@@ -47,7 +47,7 @@ class Admin::GroupsController < Admin::ApplicationController ...@@ -47,7 +47,7 @@ class Admin::GroupsController < Admin::ApplicationController
end end
def destroy def destroy
@group.destroy DestroyGroupService.new(@group, current_user).execute
redirect_to admin_groups_path, notice: 'Group was successfully deleted.' redirect_to admin_groups_path, notice: 'Group was successfully deleted.'
end end
......
...@@ -86,11 +86,7 @@ class Admin::UsersController < Admin::ApplicationController ...@@ -86,11 +86,7 @@ class Admin::UsersController < Admin::ApplicationController
end end
def destroy def destroy
# 1. Remove groups where user is the only owner DeleteUserService.new.execute(user)
user.solo_owned_groups.map(&:destroy)
# 2. Remove user with all authored content including personal projects
user.destroy
respond_to do |format| respond_to do |format|
format.html { redirect_to admin_users_path } format.html { redirect_to admin_users_path }
......
...@@ -89,7 +89,7 @@ class ApplicationController < ActionController::Base ...@@ -89,7 +89,7 @@ class ApplicationController < ActionController::Base
end end
def after_sign_out_path_for(resource) def after_sign_out_path_for(resource)
new_user_session_path current_application_settings.after_sign_out_path || new_user_session_path
end end
def abilities def abilities
...@@ -295,14 +295,14 @@ class ApplicationController < ActionController::Base ...@@ -295,14 +295,14 @@ class ApplicationController < ActionController::Base
def get_issues_collection def get_issues_collection
set_filters_params set_filters_params
issues = IssuesFinder.new.execute(current_user, @filter_params) @issuable_finder = IssuesFinder.new(current_user, @filter_params)
issues @issuable_finder.execute
end end
def get_merge_requests_collection def get_merge_requests_collection
set_filters_params set_filters_params
merge_requests = MergeRequestsFinder.new.execute(current_user, @filter_params) @issuable_finder = MergeRequestsFinder.new(current_user, @filter_params)
merge_requests @issuable_finder.execute
end end
def github_import_enabled? def github_import_enabled?
......
...@@ -82,7 +82,11 @@ class Groups::GroupMembersController < Groups::ApplicationController ...@@ -82,7 +82,11 @@ class Groups::GroupMembersController < Groups::ApplicationController
redirect_to(dashboard_groups_path, notice: "You left #{group.name} group.") redirect_to(dashboard_groups_path, notice: "You left #{group.name} group.")
else else
return render_403 if @group.last_owner?(current_user)
redirect_to(dashboard_groups_path, alert: "You can not leave #{group.name} group because you're the last owner. Transfer or delete the group.")
else
return render_403
end
end end
end end
......
...@@ -84,7 +84,7 @@ class GroupsController < Groups::ApplicationController ...@@ -84,7 +84,7 @@ class GroupsController < Groups::ApplicationController
end end
def destroy def destroy
@group.destroy DestroyGroupService.new(@group, current_user).execute
redirect_to root_path, notice: 'Group was removed.' redirect_to root_path, notice: 'Group was removed.'
end end
......
class Oauth::ApplicationsController < Doorkeeper::ApplicationsController class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
include Gitlab::CurrentSettings
include PageLayoutHelper include PageLayoutHelper
before_action :verify_user_oauth_applications_enabled
before_action :authenticate_user! before_action :authenticate_user!
layout 'profile' layout 'profile'
...@@ -32,6 +34,12 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController ...@@ -32,6 +34,12 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
private private
def verify_user_oauth_applications_enabled
return if current_application_settings.user_oauth_applications?
redirect_to applications_profile_url
end
def set_application def set_application
@application = current_user.oauth_applications.find(params[:id]) @application = current_user.oauth_applications.find(params[:id])
end end
......
class OmniauthCallbacksController < Devise::OmniauthCallbacksController class OmniauthCallbacksController < Devise::OmniauthCallbacksController
include AuthenticatesWithTwoFactor include AuthenticatesWithTwoFactor
protect_from_forgery except: :kerberos protect_from_forgery except: [:kerberos, :saml]
Gitlab.config.omniauth.providers.each do |provider| Gitlab.config.omniauth.providers.each do |provider|
define_method provider['name'] do define_method provider['name'] do
...@@ -81,7 +81,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController ...@@ -81,7 +81,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
end end
flash[:notice] = message flash[:notice] = message
redirect_to new_user_session_path redirect_to new_user_session_path
end end
......
...@@ -36,4 +36,24 @@ class PasswordsController < Devise::PasswordsController ...@@ -36,4 +36,24 @@ class PasswordsController < Devise::PasswordsController
end end
end end
end end
def edit
super
reset_password_token = Devise.token_generator.digest(
User,
:reset_password_token,
resource.reset_password_token
)
unless reset_password_token.nil?
user = User.where(
reset_password_token: reset_password_token
).first_or_initialize
unless user.reset_password_period_valid?
flash[:alert] = 'Your password reset token has expired.'
redirect_to(new_user_password_url(user_email: user['email']))
end
end
end
end end
class Profiles::TwoFactorAuthsController < Profiles::ApplicationController class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
def new def new
unless current_user.otp_secret unless current_user.otp_secret
current_user.otp_secret = User.generate_otp_secret current_user.otp_secret = User.generate_otp_secret(32)
current_user.save! current_user.save!
end end
...@@ -18,6 +18,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController ...@@ -18,6 +18,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
else else
@error = 'Invalid pin code' @error = 'Invalid pin code'
@qr_code = build_qr_code @qr_code = build_qr_code
render 'new' render 'new'
end end
end end
......
...@@ -18,10 +18,6 @@ class Projects::DeployKeysController < Projects::ApplicationController ...@@ -18,10 +18,6 @@ class Projects::DeployKeysController < Projects::ApplicationController
@available_public_keys -= @available_project_keys @available_public_keys -= @available_project_keys
end end
def show
@key = @project.deploy_keys.find(params[:id])
end
def new def new
@key = @project.deploy_keys.new @key = @project.deploy_keys.new
......
...@@ -53,6 +53,6 @@ class Projects::HooksController < Projects::ApplicationController ...@@ -53,6 +53,6 @@ class Projects::HooksController < Projects::ApplicationController
end end
def hook_params def hook_params
params.require(:hook).permit(:url, :push_events, :issues_events, :merge_requests_events, :tag_push_events) params.require(:hook).permit(:url, :push_events, :issues_events, :merge_requests_events, :tag_push_events, :note_events)
end end
end end
...@@ -19,7 +19,15 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -19,7 +19,15 @@ class Projects::IssuesController < Projects::ApplicationController
def index def index
terms = params['issue_search'] terms = params['issue_search']
@issues = get_issues_collection @issues = get_issues_collection
@issues = @issues.full_search(terms) if terms.present?
if terms.present?
if terms =~ /\A#(\d+)\z/
@issues = @issues.where(iid: $1)
else
@issues = @issues.full_search(terms)
end
end
@issues = @issues.page(params[:page]).per(PER_PAGE) @issues = @issues.page(params[:page]).per(PER_PAGE)
respond_to do |format| respond_to do |format|
......
...@@ -2,10 +2,13 @@ require 'gitlab/satellite/satellite' ...@@ -2,10 +2,13 @@ require 'gitlab/satellite/satellite'
class Projects::MergeRequestsController < Projects::ApplicationController class Projects::MergeRequestsController < Projects::ApplicationController
before_action :module_enabled before_action :module_enabled
before_action :merge_request, only: [:edit, :update, :show, :diffs, :automerge, :automerge_check, :ci_status, :toggle_subscription] before_action :merge_request, only: [
before_action :closes_issues, only: [:edit, :update, :show, :diffs] :edit, :update, :show, :diffs, :commits, :automerge, :automerge_check,
before_action :validates_merge_request, only: [:show, :diffs] :ci_status, :toggle_subscription
before_action :define_show_vars, only: [:show, :diffs] ]
before_action :closes_issues, only: [:edit, :update, :show, :diffs, :commits]
before_action :validates_merge_request, only: [:show, :diffs, :commits]
before_action :define_show_vars, only: [:show, :diffs, :commits]
# Allow read any merge_request # Allow read any merge_request
before_action :authorize_read_merge_request! before_action :authorize_read_merge_request!
...@@ -19,7 +22,15 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -19,7 +22,15 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def index def index
terms = params['issue_search'] terms = params['issue_search']
@merge_requests = get_merge_requests_collection @merge_requests = get_merge_requests_collection
@merge_requests = @merge_requests.full_search(terms) if terms.present?
if terms.present?
if terms =~ /\A[#!](\d+)\z/
@merge_requests = @merge_requests.where(iid: $1)
else
@merge_requests = @merge_requests.full_search(terms)
end
end
@merge_requests = @merge_requests.page(params[:page]).per(PER_PAGE) @merge_requests = @merge_requests.page(params[:page]).per(PER_PAGE)
respond_to do |format| respond_to do |format|
...@@ -59,6 +70,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -59,6 +70,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end end
end end
def commits
render 'show'
end
def new def new
params[:merge_request] ||= ActionController::Parameters.new(source_project: @project) params[:merge_request] ||= ActionController::Parameters.new(source_project: @project)
@merge_request = MergeRequests::BuildService.new(project, current_user, merge_request_params).execute @merge_request = MergeRequests::BuildService.new(project, current_user, merge_request_params).execute
......
...@@ -84,12 +84,16 @@ class Projects::ProjectMembersController < Projects::ApplicationController ...@@ -84,12 +84,16 @@ class Projects::ProjectMembersController < Projects::ApplicationController
end end
def leave def leave
if @project.namespace == current_user.namespace
return redirect_to(:back, alert: 'You can not leave your own project. Transfer or delete the project.')
end
@project_member = @project.project_members.find_by(user_id: current_user) @project_member = @project.project_members.find_by(user_id: current_user)
@project_member.destroy @project_member.destroy
log_audit_event(@project_member, action: :destroy) log_audit_event(@project_member, action: :destroy)
respond_to do |format| respond_to do |format|
format.html { redirect_to :back } format.html { redirect_to dashboard_path }
format.js { render nothing: true } format.js { render nothing: true }
end end
end end
......
...@@ -97,18 +97,15 @@ class ProjectsController < ApplicationController ...@@ -97,18 +97,15 @@ class ProjectsController < ApplicationController
return access_denied! unless can?(current_user, :remove_project, @project) return access_denied! unless can?(current_user, :remove_project, @project)
::Projects::DestroyService.new(@project, current_user, {}).execute ::Projects::DestroyService.new(@project, current_user, {}).execute
flash[:alert] = 'Project deleted.'
respond_to do |format| if request.referer.include?('/admin')
format.html do redirect_to admin_namespaces_projects_path
flash[:alert] = 'Project deleted.' else
redirect_to dashboard_path
if request.referer.include?('/admin')
redirect_to admin_namespaces_projects_path
else
redirect_to dashboard_path
end
end
end end
rescue Projects::DestroyService::DestroyError => ex
redirect_to edit_project_path(@project), alert: ex.message
end end
def autocomplete_sources def autocomplete_sources
......
...@@ -6,7 +6,7 @@ class RegistrationsController < Devise::RegistrationsController ...@@ -6,7 +6,7 @@ class RegistrationsController < Devise::RegistrationsController
end end
def destroy def destroy
current_user.destroy DeleteUserService.new.execute(current_user)
respond_to do |format| respond_to do |format|
format.html { redirect_to new_user_session_path, notice: "Account successfully removed." } format.html { redirect_to new_user_session_path, notice: "Account successfully removed." }
......
...@@ -2,6 +2,7 @@ class SessionsController < Devise::SessionsController ...@@ -2,6 +2,7 @@ class SessionsController < Devise::SessionsController
include AuthenticatesWithTwoFactor include AuthenticatesWithTwoFactor
prepend_before_action :authenticate_with_two_factor, only: [:create] prepend_before_action :authenticate_with_two_factor, only: [:create]
before_action :auto_sign_in_with_provider, only: [:new]
def new def new
redirect_path = redirect_path =
...@@ -75,6 +76,21 @@ class SessionsController < Devise::SessionsController ...@@ -75,6 +76,21 @@ class SessionsController < Devise::SessionsController
end end
end end
def auto_sign_in_with_provider
provider = Gitlab.config.omniauth.auto_sign_in_with_provider
return unless provider.present?
# Auto sign in with an Omniauth provider only if the standard "you need to sign-in" alert is
# registered or no alert at all. In case of another alert (such as a blocked user), it is safer
# to do nothing to prevent redirection loops with certain Omniauth providers.
return unless flash[:alert].blank? || flash[:alert] == I18n.t('devise.failure.unauthenticated')
# Prevent alert from popping up on the first page shown after authentication.
flash[:alert] = nil
redirect_to omniauth_authorize_path(:user, provider.to_sym)
end
def valid_otp_attempt?(user) def valid_otp_attempt?(user)
user.valid_otp?(user_params[:otp_attempt]) || user.valid_otp?(user_params[:otp_attempt]) ||
user.invalidate_otp_backup_code!(user_params[:otp_attempt]) user.invalidate_otp_backup_code!(user_params[:otp_attempt])
......
...@@ -16,7 +16,7 @@ issues = project.issues_for_user_filtered_by(user, params) ...@@ -16,7 +16,7 @@ issues = project.issues_for_user_filtered_by(user, params)
Better use this: Better use this:
```ruby ```ruby
issues = IssuesFinder.new.execute(project, user, filter) issues = IssuesFinder.new(project, user, filter).execute
``` ```
It will help keep models thiner. It will help keep models thiner.
...@@ -23,10 +23,12 @@ class IssuableFinder ...@@ -23,10 +23,12 @@ class IssuableFinder
attr_accessor :current_user, :params attr_accessor :current_user, :params
def execute(current_user, params) def initialize(current_user, params)
@current_user = current_user @current_user = current_user
@params = params @params = params
end
def execute
items = init_collection items = init_collection
items = by_scope(items) items = by_scope(items)
items = by_state(items) items = by_state(items)
...@@ -40,6 +42,77 @@ class IssuableFinder ...@@ -40,6 +42,77 @@ class IssuableFinder
items = sort(items) items = sort(items)
end end
def group
return @group if defined?(@group)
@group =
if params[:group_id].present?
Group.find(params[:group_id])
else
nil
end
end
def project
return @project if defined?(@project)
@project =
if params[:project_id].present?
Project.find(params[:project_id])
else
nil
end
end
def search
params[:search].presence
end
def milestones?
params[:milestone_title].present?
end
def milestones
return @milestones if defined?(@milestones)
@milestones =
if milestones? && params[:milestone_title] != NONE
Milestone.where(title: params[:milestone_title])
else
nil
end
end
def assignee?
params[:assignee_id].present?
end
def assignee
return @assignee if defined?(@assignee)
@assignee =
if assignee? && params[:assignee_id] != NONE
User.find(params[:assignee_id])
else
nil
end
end
def author?
params[:author_id].present?
end
def author
return @author if defined?(@author)
@author =
if author? && params[:author_id] != NONE
User.find(params[:author_id])
else
nil
end
end
private private
def init_collection def init_collection
...@@ -75,6 +148,10 @@ class IssuableFinder ...@@ -75,6 +148,10 @@ class IssuableFinder
case params[:state] case params[:state]
when 'closed' when 'closed'
items.closed items.closed
when 'rejected'
items.respond_to?(:rejected) ? items.rejected : items.closed
when 'merged'
items.respond_to?(:merged) ? items.merged : items.closed
when 'all' when 'all'
items items
when 'opened' when 'opened'
...@@ -85,25 +162,19 @@ class IssuableFinder ...@@ -85,25 +162,19 @@ class IssuableFinder
end end
def by_group(items) def by_group(items)
if params[:group_id].present? items = items.of_group(group) if group
items = items.of_group(Group.find(params[:group_id]))
end
items items
end end
def by_project(items) def by_project(items)
if params[:project_id].present? items = items.of_projects(project.id) if project
items = items.of_projects(params[:project_id])
end
items items
end end
def by_search(items) def by_search(items)
if params[:search].present? items = items.search(search) if search
items = items.search(params[:search])
end
items items
end end
...@@ -113,25 +184,24 @@ class IssuableFinder ...@@ -113,25 +184,24 @@ class IssuableFinder
end end
def by_milestone(items) def by_milestone(items)
if params[:milestone_title].present? if milestones?
milestone_ids = (params[:milestone_title] == NONE ? nil : Milestone.where(title: params[:milestone_title]).pluck(:id)) items = items.where(milestone_id: milestones.try(:pluck, :id))
items = items.where(milestone_id: milestone_ids)
end end
items items
end end
def by_assignee(items) def by_assignee(items)
if params[:assignee_id].present? if assignee?
items = items.where(assignee_id: (params[:assignee_id] == NONE ? nil : params[:assignee_id])) items = items.where(assignee_id: assignee.try(:id))
end end
items items
end end
def by_author(items) def by_author(items)
if params[:author_id].present? if author?
items = items.where(author_id: (params[:author_id] == NONE ? nil : params[:author_id])) items = items.where(author_id: author.try(:id))
end end
items items
...@@ -151,10 +221,6 @@ class IssuableFinder ...@@ -151,10 +221,6 @@ class IssuableFinder
items items
end end
def project
Project.where(id: params[:project_id]).first if params[:project_id].present?
end
def current_user_related? def current_user_related?
params[:scope] == 'created-by-me' || params[:scope] == 'authored' || params[:scope] == 'assigned-to-me' params[:scope] == 'created-by-me' || params[:scope] == 'authored' || params[:scope] == 'assigned-to-me'
end end
......
...@@ -228,18 +228,28 @@ module ApplicationHelper ...@@ -228,18 +228,28 @@ module ApplicationHelper
end end
def render_markup(file_name, file_content) def render_markup(file_name, file_content)
GitHub::Markup.render(file_name, file_content). if gitlab_markdown?(file_name)
force_encoding(file_content.encoding).html_safe Haml::Helpers.preserve(markdown(file_content))
elsif asciidoc?(file_name)
asciidoc(file_content)
else
GitHub::Markup.render(file_name, file_content).
force_encoding(file_content.encoding).html_safe
end
rescue RuntimeError rescue RuntimeError
simple_format(file_content) simple_format(file_content)
end end
def markup?(filename) def markup?(filename)
Gitlab::MarkdownHelper.markup?(filename) Gitlab::MarkupHelper.markup?(filename)
end end
def gitlab_markdown?(filename) def gitlab_markdown?(filename)
Gitlab::MarkdownHelper.gitlab_markdown?(filename) Gitlab::MarkupHelper.gitlab_markdown?(filename)
end
def asciidoc?(filename)
Gitlab::MarkupHelper.asciidoc?(filename)
end end
# Overrides ActionView::Helpers::UrlHelper#link_to to add `rel="nofollow"` to # Overrides ActionView::Helpers::UrlHelper#link_to to add `rel="nofollow"` to
...@@ -275,10 +285,6 @@ module ApplicationHelper ...@@ -275,10 +285,6 @@ module ApplicationHelper
html_options html_options
end end
def escaped_autolink(text)
auto_link ERB::Util.html_escape(text), link: :urls
end
def promo_host def promo_host
'about.gitlab.com' 'about.gitlab.com'
end end
...@@ -326,7 +332,12 @@ module ApplicationHelper ...@@ -326,7 +332,12 @@ module ApplicationHelper
end end
def state_filters_text_for(entity, project) def state_filters_text_for(entity, project)
entity_title = entity.to_s.humanize titles = {
opened: "Open",
merged: "Accepted"
}
entity_title = titles[entity] || entity.to_s.humanize
count = count =
if project.nil? if project.nil?
......
...@@ -23,6 +23,10 @@ module ApplicationSettingsHelper ...@@ -23,6 +23,10 @@ module ApplicationSettingsHelper
current_application_settings.help_text current_application_settings.help_text
end end
def user_oauth_applications?
current_application_settings.user_oauth_applications
end
# Return a group of checkboxes that use Bootstrap's button plugin for a # Return a group of checkboxes that use Bootstrap's button plugin for a
# toggle button effect. # toggle button effect.
def restricted_level_checkboxes(help_block_id) def restricted_level_checkboxes(help_block_id)
......
module BlobHelper module BlobHelper
def highlight(blob_name, blob_content, nowrap = false) def highlight(blob_name, blob_content, nowrap: false, continue: false)
formatter = Rugments::Formatters::HTML.new( @formatter ||= Rugments::Formatters::HTML.new(
nowrap: nowrap, nowrap: nowrap,
cssclass: 'code highlight', cssclass: 'code highlight',
lineanchors: true, lineanchors: true,
...@@ -8,12 +8,14 @@ module BlobHelper ...@@ -8,12 +8,14 @@ module BlobHelper
) )
begin begin
lexer = Rugments::Lexer.guess(filename: blob_name, source: blob_content) @lexer ||= Rugments::Lexer.guess(filename: blob_name, source: blob_content).new
rescue Rugments::Lexer::AmbiguousGuess result = @formatter.format(@lexer.lex(blob_content, continue: continue)).html_safe
rescue
lexer = Rugments::Lexers::PlainText lexer = Rugments::Lexers::PlainText
result = @formatter.format(lexer.lex(blob_content)).html_safe
end end
formatter.format(lexer.lex(blob_content)).html_safe result
end end
def no_highlight_files def no_highlight_files
...@@ -55,7 +57,7 @@ module BlobHelper ...@@ -55,7 +57,7 @@ module BlobHelper
end end
def editing_preview_title(filename) def editing_preview_title(filename)
if Gitlab::MarkdownHelper.previewable?(filename) if Gitlab::MarkupHelper.previewable?(filename)
'Preview' 'Preview'
else else
'Preview changes' 'Preview changes'
......
...@@ -101,6 +101,10 @@ module DiffHelper ...@@ -101,6 +101,10 @@ module DiffHelper
(bottom) ? 'js-unfold-bottom' : '' (bottom) ? 'js-unfold-bottom' : ''
end end
def unfold_class(unfold)
(unfold) ? 'unfold js-unfold' : ''
end
def diff_line_content(line) def diff_line_content(line)
if line.blank? if line.blank?
" &nbsp;" " &nbsp;"
......
...@@ -35,4 +35,23 @@ module EmailsHelper ...@@ -35,4 +35,23 @@ module EmailsHelper
lexer = Rugments::Lexers::Diff.new lexer = Rugments::Lexers::Diff.new
raw formatter.format(lexer.lex(diffcontent)) raw formatter.format(lexer.lex(diffcontent))
end end
def password_reset_token_valid_time
valid_hours = Devise.reset_password_within / 60 / 60
if valid_hours >= 24
unit = 'day'
valid_length = (valid_hours / 24).floor
else
unit = 'hour'
valid_length = valid_hours.floor
end
pluralize(valid_length, unit)
end
def reset_token_expire_message
link_tag = link_to('request a new one', new_user_password_url(user_email: @user.email))
msg = "This link is valid for #{password_reset_token_valid_time}. "
msg << "After it expires, you can #{link_tag}."
end
end end
require 'nokogiri'
module GitlabMarkdownHelper module GitlabMarkdownHelper
include Gitlab::Markdown include Gitlab::Markdown
...@@ -21,41 +23,59 @@ module GitlabMarkdownHelper ...@@ -21,41 +23,59 @@ module GitlabMarkdownHelper
gfm_body = gfm(escaped_body, {}, html_options) gfm_body = gfm(escaped_body, {}, html_options)
gfm_body.gsub!(%r{<a.*?>.*?</a>}m) do |match| fragment = Nokogiri::XML::DocumentFragment.parse(gfm_body)
"</a>#{match}#{link_to("", url, html_options)[0..-5]}" # "</a>".length +1 if fragment.children.size == 1 && fragment.children[0].name == 'a'
# Fragment has only one node, and it's a link generated by `gfm`.
# Replace it with our requested link.
text = fragment.children[0].text
fragment.children[0].replace(link_to(text, url, html_options))
else
# Traverse the fragment's first generation of children looking for pure
# text, wrapping anything found in the requested link
fragment.children.each do |node|
next unless node.text?
node.replace(link_to(node.text, url, html_options))
end
end end
link_to(gfm_body.html_safe, url, html_options) fragment.to_html.html_safe
end end
MARKDOWN_OPTIONS = {
no_intra_emphasis: true,
tables: true,
fenced_code_blocks: true,
strikethrough: true,
lax_spacing: true,
space_after_headers: true,
superscript: true,
footnotes: true
}.freeze
def markdown(text, options={}) def markdown(text, options={})
unless @markdown && options == @options unless @markdown && options == @options
@options = options @options = options
options.merge!(
# Handled further down the line by Gitlab::Markdown::SanitizationFilter
escape_html: false
)
# see https://github.com/vmg/redcarpet#darling-i-packed-you-a-couple-renderers-for-lunch # see https://github.com/vmg/redcarpet#darling-i-packed-you-a-couple-renderers-for-lunch
rend = Redcarpet::Render::GitlabHTML.new(self, user_color_scheme_class, options) rend = Redcarpet::Render::GitlabHTML.new(self, user_color_scheme_class, options)
# see https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use # see https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use
@markdown = Redcarpet::Markdown.new(rend, @markdown = Redcarpet::Markdown.new(rend, MARKDOWN_OPTIONS)
no_intra_emphasis: true,
tables: true,
fenced_code_blocks: true,
strikethrough: true,
lax_spacing: true,
space_after_headers: true,
superscript: true,
footnotes: true
)
end end
@markdown.render(text).html_safe @markdown.render(text).html_safe
end end
def asciidoc(text)
Gitlab::Asciidoc.render(text, {
commit: @commit,
project: @project,
project_wiki: @project_wiki,
requested_path: @path,
ref: @ref
})
end
# Return the first line of +text+, up to +max_chars+, after parsing the line # Return the first line of +text+, up to +max_chars+, after parsing the line
# as Markdown. HTML tags in the parsed output are not counted toward the # as Markdown. HTML tags in the parsed output are not counted toward the
# +max_chars+ limit. If the length limit falls within a tag's contents, then # +max_chars+ limit. If the length limit falls within a tag's contents, then
...@@ -67,8 +87,11 @@ module GitlabMarkdownHelper ...@@ -67,8 +87,11 @@ module GitlabMarkdownHelper
end end
def render_wiki_content(wiki_page) def render_wiki_content(wiki_page)
if wiki_page.format == :markdown case wiki_page.format
when :markdown
markdown(wiki_page.content) markdown(wiki_page.content)
when :asciidoc
asciidoc(wiki_page.content)
else else
wiki_page.formatted_content.html_safe wiki_page.formatted_content.html_safe
end end
...@@ -122,15 +145,25 @@ module GitlabMarkdownHelper ...@@ -122,15 +145,25 @@ module GitlabMarkdownHelper
end end
end end
# Returns the text necessary to reference `entity` across projects
#
# project - Project to reference
# entity - Object that responds to `to_reference`
#
# Examples:
#
# cross_project_reference(project, project.issues.first)
# # => 'namespace1/project1#123'
#
# cross_project_reference(project, project.merge_requests.first)
# # => 'namespace1/project1!345'
#
# Returns a String
def cross_project_reference(project, entity) def cross_project_reference(project, entity)
path = project.path_with_namespace if entity.respond_to?(:to_reference)
"#{project.to_reference}#{entity.to_reference}"
if entity.kind_of?(Issue)
[path, entity.iid].join('#')
elsif entity.kind_of?(MergeRequest)
[path, entity.iid].join('!')
else else
raise 'Not supported type' ''
end end
end end
end end
module LabelsHelper module LabelsHelper
include ActionView::Helpers::TagHelper include ActionView::Helpers::TagHelper
# Link to a Label
#
# label - Label object to link to
# project - Project object which will be used as the context for the label's
# link. If omitted, defaults to `@project`, or the label's own
# project.
# block - An optional block that will be passed to `link_to`, forming the
# body of the link element. If omitted, defaults to
# `render_colored_label`.
#
# Examples:
#
# # Allow the generated link to use the label's own project
# link_to_label(label)
#
# # Force the generated link to use @project
# @project = Project.first
# link_to_label(label)
#
# # Force the generated link to use a provided project
# link_to_label(label, project: Project.last)
#
# # Customize link body with a block
# link_to_label(label) { "My Custom Label Text" }
#
# Returns a String
def link_to_label(label, project: nil, &block)
project ||= @project || label.project
link = namespace_project_issues_path(project.namespace, project,
label_name: label.name)
if block_given?
link_to link, &block
else
link_to render_colored_label(label), link
end
end
def project_label_names def project_label_names
@project.labels.pluck(:title) @project.labels.pluck(:title)
end end
......
...@@ -148,7 +148,7 @@ module ProjectsHelper ...@@ -148,7 +148,7 @@ module ProjectsHelper
nav_tabs << [:files, :commits, :network, :graphs] nav_tabs << [:files, :commits, :network, :graphs]
end end
if project.repo_exists? && project.merge_requests_enabled if project.repo_exists? && can?(current_user, :read_merge_request, project)
nav_tabs << :merge_requests nav_tabs << :merge_requests
end end
...@@ -156,11 +156,19 @@ module ProjectsHelper ...@@ -156,11 +156,19 @@ module ProjectsHelper
nav_tabs << :settings nav_tabs << :settings
end end
[:issues, :wiki, :snippets].each do |feature| if can?(current_user, :read_issue, project)
nav_tabs << feature if project.send :"#{feature}_enabled" nav_tabs << :issues
end end
if project.issues_enabled || project.merge_requests_enabled if can?(current_user, :read_wiki, project)
nav_tabs << :wiki
end
if can?(current_user, :read_project_snippet, project)
nav_tabs << :snippets
end
if can?(current_user, :read_milestone, project)
nav_tabs << [:milestones, :labels] nav_tabs << [:milestones, :labels]
end end
...@@ -302,4 +310,16 @@ module ProjectsHelper ...@@ -302,4 +310,16 @@ module ProjectsHelper
nil nil
end end
end end
def user_max_access_in_project(user, project)
level = project.team.max_member_access(user)
if level
Gitlab::Access.options_with_owner.key(level)
end
end
def leave_project_message(project)
"Are you sure you want to leave \"#{project.name}\" project?"
end
end end
...@@ -11,6 +11,7 @@ module SelectsHelper ...@@ -11,6 +11,7 @@ module SelectsHelper
any_user = opts[:any_user] || false any_user = opts[:any_user] || false
email_user = opts[:email_user] || false email_user = opts[:email_user] || false
first_user = opts[:first_user] && current_user ? current_user.username : false first_user = opts[:first_user] && current_user ? current_user.username : false
project = opts[:project] || @project
html = { html = {
class: css_class, class: css_class,
...@@ -22,8 +23,8 @@ module SelectsHelper ...@@ -22,8 +23,8 @@ module SelectsHelper
} }
unless opts[:scope] == :all unless opts[:scope] == :all
if @project if project
html['data-project-id'] = @project.id html['data-project-id'] = project.id
elsif @group elsif @group
html['data-group-id'] = @group.id html['data-group-id'] = @group.id
end end
......
...@@ -25,13 +25,7 @@ module TreeHelper ...@@ -25,13 +25,7 @@ module TreeHelper
end end
def render_readme(readme) def render_readme(readme)
if gitlab_markdown?(readme.name) render_markup(readme.name, readme.data)
preserve(markdown(readme.data))
elsif markup?(readme.name)
render_markup(readme.name, readme.data)
else
simple_format(readme.data)
end
end end
# Return an image icon depending on the file type and mode # Return an image icon depending on the file type and mode
......
...@@ -93,7 +93,8 @@ module Emails ...@@ -93,7 +93,8 @@ module Emails
"pushed to" "pushed to"
end end
@subject = "[#{@project.path_with_namespace}]" @subject = "[Git]"
@subject << "[#{@project.path_with_namespace}]"
@subject << "[#{@ref_name}]" if action == :push @subject << "[#{@ref_name}]" if action == :push
@subject << " " @subject << " "
......
...@@ -117,6 +117,27 @@ class Ability ...@@ -117,6 +117,27 @@ class Ability
rules -= project_archived_rules rules -= project_archived_rules
end end
unless project.issues_enabled
rules -= named_abilities('issue')
end
unless project.merge_requests_enabled
rules -= named_abilities('merge_request')
end
unless project.issues_enabled or project.merge_requests_enabled
rules -= named_abilities('label')
rules -= named_abilities('milestone')
end
unless project.snippets_enabled
rules -= named_abilities('project_snippet')
end
unless project.wiki_enabled
rules -= named_abilities('wiki')
end
rules rules
end end
end end
...@@ -288,5 +309,16 @@ class Ability ...@@ -288,5 +309,16 @@ class Ability
abilities abilities
end end
end end
private
def named_abilities(name)
[
:"read_#{name}",
:"write_#{name}",
:"modify_#{name}",
:"admin_#{name}"
]
end
end end
end end
...@@ -19,6 +19,8 @@ ...@@ -19,6 +19,8 @@
# default_project_visibility :integer # default_project_visibility :integer
# default_snippet_visibility :integer # default_snippet_visibility :integer
# restricted_signup_domains :text # restricted_signup_domains :text
# user_oauth_applications :bool default(TRUE)
# after_sign_out_path :string(255)
# #
class ApplicationSetting < ActiveRecord::Base class ApplicationSetting < ActiveRecord::Base
...@@ -31,6 +33,10 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -31,6 +33,10 @@ class ApplicationSetting < ActiveRecord::Base
format: { with: /\A#{URI.regexp(%w(http https))}\z/, message: "should be a valid url" }, format: { with: /\A#{URI.regexp(%w(http https))}\z/, message: "should be a valid url" },
if: :home_page_url_column_exist if: :home_page_url_column_exist
validates :after_sign_out_path,
allow_blank: true,
format: { with: /\A#{URI.regexp(%w(http https))}\z/, message: "should be a valid url" }
validates_each :restricted_visibility_levels do |record, attr, value| validates_each :restricted_visibility_levels do |record, attr, value|
unless value.nil? unless value.nil?
value.each do |level| value.each do |level|
......
class Commit class Commit
include ActiveModel::Conversion
include StaticModel
extend ActiveModel::Naming extend ActiveModel::Naming
include ActiveModel::Conversion
include Mentionable include Mentionable
include Participable include Participable
include Referable
include StaticModel
attr_mentionable :safe_message attr_mentionable :safe_message
participant :author, :committer, :notes, :mentioned_users participant :author, :committer, :notes, :mentioned_users
...@@ -56,6 +58,34 @@ class Commit ...@@ -56,6 +58,34 @@ class Commit
@raw.id @raw.id
end end
def ==(other)
(self.class === other) && (raw == other.raw)
end
def self.reference_prefix
'@'
end
# Pattern used to extract commit references from text
#
# The SHA can be between 6 and 40 hex characters.
#
# This pattern supports cross-project references.
def self.reference_pattern
%r{
(?:#{Project.reference_pattern}#{reference_prefix})?
(?<commit>\h{6,40})
}x
end
def to_reference(from_project = nil)
if cross_project_reference?(from_project)
"#{project.to_reference}@#{id}"
else
id
end
end
def diff_line_count def diff_line_count
@diff_line_count ||= Commit::diff_line_count(self.diffs) @diff_line_count ||= Commit::diff_line_count(self.diffs)
@diff_line_count @diff_line_count
...@@ -126,11 +156,6 @@ class Commit ...@@ -126,11 +156,6 @@ class Commit
Gitlab::ClosingIssueExtractor.new(project, current_user).closed_by_message(safe_message) Gitlab::ClosingIssueExtractor.new(project, current_user).closed_by_message(safe_message)
end end
# Mentionable override.
def gfm_reference
"commit #{id}"
end
def author def author
User.find_for_commit(author_email, author_name) User.find_for_commit(author_email, author_name)
end end
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
# #
class CommitRange class CommitRange
include ActiveModel::Conversion include ActiveModel::Conversion
include Referable
attr_reader :sha_from, :notation, :sha_to attr_reader :sha_from, :notation, :sha_to
...@@ -28,10 +29,24 @@ class CommitRange ...@@ -28,10 +29,24 @@ class CommitRange
# See `exclude_start?` # See `exclude_start?`
attr_reader :exclude_start attr_reader :exclude_start
# The beginning and ending SHA sums can be between 6 and 40 hex characters, # The beginning and ending SHAs can be between 6 and 40 hex characters, and
# and the range selection can be double- or triple-dot. # the range notation can be double- or triple-dot.
PATTERN = /\h{6,40}\.{2,3}\h{6,40}/ PATTERN = /\h{6,40}\.{2,3}\h{6,40}/
def self.reference_prefix
'@'
end
# Pattern used to extract commit range references from text
#
# This pattern supports cross-project references.
def self.reference_pattern
%r{
(?:#{Project.reference_pattern}#{reference_prefix})?
(?<commit_range>#{PATTERN})
}x
end
# Initialize a CommitRange # Initialize a CommitRange
# #
# range_string - The String commit range. # range_string - The String commit range.
...@@ -59,6 +74,17 @@ class CommitRange ...@@ -59,6 +74,17 @@ class CommitRange
"#{sha_from[0..7]}#{notation}#{sha_to[0..7]}" "#{sha_from[0..7]}#{notation}#{sha_to[0..7]}"
end end
def to_reference(from_project = nil)
# Not using to_s because we want the full SHAs
reference = sha_from + notation + sha_to
if cross_project_reference?(from_project)
reference = project.to_reference + '@' + reference
end
reference
end
# Returns a String for use in a link's title attribute # Returns a String for use in a link's title attribute
def reference_title def reference_title
"Commits #{suffixed_sha_from} through #{sha_to}" "Commits #{suffixed_sha_from} through #{sha_to}"
......
...@@ -20,10 +20,15 @@ module Mentionable ...@@ -20,10 +20,15 @@ module Mentionable
end end
end end
# Generate a GFM back-reference that will construct a link back to this Mentionable when rendered. Must # Returns the text used as the body of a Note when this object is referenced
# be overridden if this model object can be referenced directly by GFM notation. #
def gfm_reference # By default this will be the class name and the result of calling
raise NotImplementedError.new("#{self.class} does not implement #gfm_reference") # `to_reference` on the object.
def gfm_reference(from_project = nil)
# "MergeRequest" > "merge_request" > "Merge request" > "merge request"
friendly_name = self.class.to_s.underscore.humanize.downcase
"#{friendly_name} #{to_reference(from_project)}"
end end
# Construct a String that contains possible GFM references. # Construct a String that contains possible GFM references.
......
...@@ -35,8 +35,8 @@ module Participable ...@@ -35,8 +35,8 @@ module Participable
end end
end end
def participants(current_user = self.author) def participants(current_user = self.author, project = self.project)
self.class.participant_attrs.flat_map do |attr| participants = self.class.participant_attrs.flat_map do |attr|
meth = method(attr) meth = method(attr)
value = value =
...@@ -46,20 +46,28 @@ module Participable ...@@ -46,20 +46,28 @@ module Participable
meth.call meth.call
end end
participants_for(value, current_user) participants_for(value, current_user, project)
end.compact.uniq end.compact.uniq
if project
participants.select! do |user|
user.can?(:read_project, project)
end
end
participants
end end
private private
def participants_for(value, current_user = nil) def participants_for(value, current_user = nil, project = nil)
case value case value
when User when User
[value] [value]
when Enumerable, ActiveRecord::Relation when Enumerable, ActiveRecord::Relation
value.flat_map { |v| participants_for(v, current_user) } value.flat_map { |v| participants_for(v, current_user, project) }
when Participable when Participable
value.participants(current_user) value.participants(current_user, project)
end end
end end
end end
# == Referable concern
#
# Contains functionality related to making a model referable in Markdown, such
# as "#1", "!2", "~3", etc.
module Referable
extend ActiveSupport::Concern
# Returns the String necessary to reference this object in Markdown
#
# from_project - Refering Project object
#
# This should be overridden by the including class.
#
# Examples:
#
# Issue.first.to_reference # => "#1"
# Issue.last.to_reference(other_project) # => "cross-project#1"
#
# Returns a String
def to_reference(_from_project = nil)
''
end
module ClassMethods
# The character that prefixes the actual reference identifier
#
# This should be overridden by the including class.
#
# Examples:
#
# Issue.reference_prefix # => '#'
# MergeRequest.reference_prefix # => '!'
#
# Returns a String
def reference_prefix
''
end
# Regexp pattern used to match references to this object
#
# This must be overridden by the including class.
#
# Returns a Regexp
def reference_pattern
raise NotImplementedError, "#{self} does not implement #{__method__}"
end
end
private
# Check if a reference is being done cross-project
#
# from_project - Refering Project object
def cross_project_reference?(from_project)
if self.is_a?(Project)
self != from_project
else
from_project && self.project && self.project != from_project
end
end
end
class ExternalIssue class ExternalIssue
include Referable
def initialize(issue_identifier, project) def initialize(issue_identifier, project)
@issue_identifier, @project = issue_identifier, project @issue_identifier, @project = issue_identifier, project
end end
...@@ -26,4 +28,13 @@ class ExternalIssue ...@@ -26,4 +28,13 @@ class ExternalIssue
def project def project
@project @project
end end
# Pattern used to extract `JIRA-123` issue references from text
def self.reference_pattern
%r{(?<issue>([A-Z\-]+-)\d+)}
end
def to_reference(_from_project = nil)
id
end
end end
...@@ -17,6 +17,8 @@ require 'carrierwave/orm/activerecord' ...@@ -17,6 +17,8 @@ require 'carrierwave/orm/activerecord'
require 'file_size_validator' require 'file_size_validator'
class Group < Namespace class Group < Namespace
include Referable
has_many :group_members, dependent: :destroy, as: :source, class_name: 'GroupMember' has_many :group_members, dependent: :destroy, as: :source, class_name: 'GroupMember'
has_many :users, through: :group_members has_many :users, through: :group_members
has_many :project_group_links, dependent: :destroy has_many :project_group_links, dependent: :destroy
...@@ -40,6 +42,18 @@ class Group < Namespace ...@@ -40,6 +42,18 @@ class Group < Namespace
def sort(method) def sort(method)
order_by(method) order_by(method)
end end
def reference_prefix
User.reference_prefix
end
def reference_pattern
User.reference_pattern
end
end
def to_reference(_from_project = nil)
"#{self.class.reference_prefix}#{name}"
end end
def human_name def human_name
...@@ -108,10 +122,14 @@ class Group < Namespace ...@@ -108,10 +122,14 @@ class Group < Namespace
end end
def post_create_hook def post_create_hook
Gitlab::AppLogger.info("Group \"#{name}\" was created")
system_hook_service.execute_hooks_for(self, :create) system_hook_service.execute_hooks_for(self, :create)
end end
def post_destroy_hook def post_destroy_hook
Gitlab::AppLogger.info("Group \"#{name}\" was removed")
system_hook_service.execute_hooks_for(self, :destroy) system_hook_service.execute_hooks_for(self, :destroy)
end end
......
...@@ -44,7 +44,7 @@ class GroupMilestone ...@@ -44,7 +44,7 @@ class GroupMilestone
def percent_complete def percent_complete
((closed_items_count * 100) / total_items_count).abs ((closed_items_count * 100) / total_items_count).abs
rescue ZeroDivisionError rescue ZeroDivisionError
100 0
end end
def state def state
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
# issues_events :boolean default(FALSE), not null # issues_events :boolean default(FALSE), not null
# merge_requests_events :boolean default(FALSE), not null # merge_requests_events :boolean default(FALSE), not null
# tag_push_events :boolean default(FALSE) # tag_push_events :boolean default(FALSE)
# note_events :boolean default(FALSE), not null
# #
class ProjectHook < WebHook class ProjectHook < WebHook
...@@ -21,5 +22,6 @@ class ProjectHook < WebHook ...@@ -21,5 +22,6 @@ class ProjectHook < WebHook
scope :push_hooks, -> { where(push_events: true) } scope :push_hooks, -> { where(push_events: true) }
scope :tag_push_hooks, -> { where(tag_push_events: true) } scope :tag_push_hooks, -> { where(tag_push_events: true) }
scope :issue_hooks, -> { where(issues_events: true) } scope :issue_hooks, -> { where(issues_events: true) }
scope :note_hooks, -> { where(note_events: true) }
scope :merge_request_hooks, -> { where(merge_requests_events: true) } scope :merge_request_hooks, -> { where(merge_requests_events: true) }
end end
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
# issues_events :boolean default(FALSE), not null # issues_events :boolean default(FALSE), not null
# merge_requests_events :boolean default(FALSE), not null # merge_requests_events :boolean default(FALSE), not null
# tag_push_events :boolean default(FALSE) # tag_push_events :boolean default(FALSE)
# note_events :boolean default(FALSE), not null
# #
class ServiceHook < WebHook class ServiceHook < WebHook
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
# issues_events :boolean default(FALSE), not null # issues_events :boolean default(FALSE), not null
# merge_requests_events :boolean default(FALSE), not null # merge_requests_events :boolean default(FALSE), not null
# tag_push_events :boolean default(FALSE) # tag_push_events :boolean default(FALSE)
# note_events :boolean default(FALSE), not null
# #
class SystemHook < WebHook class SystemHook < WebHook
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
# issues_events :boolean default(FALSE), not null # issues_events :boolean default(FALSE), not null
# merge_requests_events :boolean default(FALSE), not null # merge_requests_events :boolean default(FALSE), not null
# tag_push_events :boolean default(FALSE) # tag_push_events :boolean default(FALSE)
# note_events :boolean default(FALSE), not null
# #
class WebHook < ActiveRecord::Base class WebHook < ActiveRecord::Base
...@@ -21,6 +22,7 @@ class WebHook < ActiveRecord::Base ...@@ -21,6 +22,7 @@ class WebHook < ActiveRecord::Base
default_value_for :push_events, true default_value_for :push_events, true
default_value_for :issues_events, false default_value_for :issues_events, false
default_value_for :note_events, false
default_value_for :merge_requests_events, false default_value_for :merge_requests_events, false
default_value_for :tag_push_events, false default_value_for :tag_push_events, false
......
...@@ -21,10 +21,11 @@ require 'carrierwave/orm/activerecord' ...@@ -21,10 +21,11 @@ require 'carrierwave/orm/activerecord'
require 'file_size_validator' require 'file_size_validator'
class Issue < ActiveRecord::Base class Issue < ActiveRecord::Base
include Issuable
include InternalId include InternalId
include Taskable include Issuable
include Referable
include Sortable include Sortable
include Taskable
ActsAsTaggableOn.strict_case_match = true ActsAsTaggableOn.strict_case_match = true
...@@ -53,10 +54,28 @@ class Issue < ActiveRecord::Base ...@@ -53,10 +54,28 @@ class Issue < ActiveRecord::Base
attributes attributes
end end
# Mentionable overrides. def self.reference_prefix
'#'
end
# Pattern used to extract `#123` issue references from text
#
# This pattern supports cross-project references.
def self.reference_pattern
%r{
(#{Project.reference_pattern})?
#{Regexp.escape(reference_prefix)}(?<issue>\d+)
}x
end
def to_reference(from_project = nil)
reference = "#{self.class.reference_prefix}#{iid}"
if cross_project_reference?(from_project)
reference = project.to_reference + reference
end
def gfm_reference reference
"issue ##{iid}"
end end
# Reset issue events cache # Reset issue events cache
......
...@@ -11,6 +11,8 @@ ...@@ -11,6 +11,8 @@
# #
class Label < ActiveRecord::Base class Label < ActiveRecord::Base
include Referable
DEFAULT_COLOR = '#428BCA' DEFAULT_COLOR = '#428BCA'
default_value_for :color, DEFAULT_COLOR default_value_for :color, DEFAULT_COLOR
...@@ -34,6 +36,45 @@ class Label < ActiveRecord::Base ...@@ -34,6 +36,45 @@ class Label < ActiveRecord::Base
alias_attribute :name, :title alias_attribute :name, :title
def self.reference_prefix
'~'
end
# Pattern used to extract label references from text
def self.reference_pattern
%r{
#{reference_prefix}
(?:
(?<label_id>\d+) | # Integer-based label ID, or
(?<label_name>
[A-Za-z0-9_-]+ | # String-based single-word label title, or
"[^&\?,]+" # String-based multi-word label surrounded in quotes
)
)
}x
end
# Returns the String necessary to reference this Label in Markdown
#
# format - Symbol format to use (default: :id, optional: :name)
#
# Note that its argument differs from other objects implementing Referable. If
# a non-Symbol argument is given (such as a Project), it will default to :id.
#
# Examples:
#
# Label.first.to_reference # => "~1"
# Label.first.to_reference(:name) # => "~\"bug\""
#
# Returns a String
def to_reference(format = :id)
if format == :name && !name.include?('"')
%(#{self.class.reference_prefix}"#{name}")
else
"#{self.class.reference_prefix}#{id}"
end
end
def open_issues_count def open_issues_count
issues.opened.count issues.opened.count
end end
......
...@@ -25,10 +25,11 @@ require Rails.root.join("app/models/commit") ...@@ -25,10 +25,11 @@ require Rails.root.join("app/models/commit")
require Rails.root.join("lib/static_model") require Rails.root.join("lib/static_model")
class MergeRequest < ActiveRecord::Base class MergeRequest < ActiveRecord::Base
include Issuable
include Taskable
include InternalId include InternalId
include Issuable
include Referable
include Sortable include Sortable
include Taskable
belongs_to :target_project, foreign_key: :target_project_id, class_name: "Project" belongs_to :target_project, foreign_key: :target_project_id, class_name: "Project"
belongs_to :source_project, foreign_key: :source_project_id, class_name: "Project" belongs_to :source_project, foreign_key: :source_project_id, class_name: "Project"
...@@ -136,7 +137,31 @@ class MergeRequest < ActiveRecord::Base ...@@ -136,7 +137,31 @@ class MergeRequest < ActiveRecord::Base
# Closed scope for merge request should return # Closed scope for merge request should return
# both merged and closed mr's # both merged and closed mr's
scope :closed, -> { with_states(:closed, :merged) } scope :closed, -> { with_states(:closed, :merged) }
scope :declined, -> { with_states(:closed) } scope :rejected, -> { with_states(:closed) }
def self.reference_prefix
'!'
end
# Pattern used to extract `!123` merge request references from text
#
# This pattern supports cross-project references.
def self.reference_pattern
%r{
(#{Project.reference_pattern})?
#{Regexp.escape(reference_prefix)}(?<merge_request>\d+)
}x
end
def to_reference(from_project = nil)
reference = "#{self.class.reference_prefix}#{iid}"
if cross_project_reference?(from_project)
reference = project.to_reference + reference
end
reference
end
def validate_branches def validate_branches
if target_project == source_project && target_branch == source_branch if target_project == source_project && target_branch == source_branch
...@@ -175,7 +200,6 @@ class MergeRequest < ActiveRecord::Base ...@@ -175,7 +200,6 @@ class MergeRequest < ActiveRecord::Base
def update_merge_request_diff def update_merge_request_diff
if source_branch_changed? || target_branch_changed? if source_branch_changed? || target_branch_changed?
reload_code reload_code
mark_as_unchecked
end end
end end
...@@ -292,11 +316,6 @@ class MergeRequest < ActiveRecord::Base ...@@ -292,11 +316,6 @@ class MergeRequest < ActiveRecord::Base
end end
end end
# Mentionable override.
def gfm_reference
"merge request !#{iid}"
end
def target_project_path def target_project_path
if target_project if target_project
target_project.path_with_namespace target_project.path_with_namespace
......
...@@ -66,7 +66,7 @@ class Milestone < ActiveRecord::Base ...@@ -66,7 +66,7 @@ class Milestone < ActiveRecord::Base
def percent_complete def percent_complete
((closed_items_count * 100) / total_items_count).abs ((closed_items_count * 100) / total_items_count).abs
rescue ZeroDivisionError rescue ZeroDivisionError
100 0
end end
def expires_at def expires_at
......
...@@ -72,7 +72,7 @@ class Namespace < ActiveRecord::Base ...@@ -72,7 +72,7 @@ class Namespace < ActiveRecord::Base
path.gsub!(/[^a-zA-Z0-9_\-\.]/, "") path.gsub!(/[^a-zA-Z0-9_\-\.]/, "")
# Users with the great usernames of "." or ".." would end up with a blank username. # Users with the great usernames of "." or ".." would end up with a blank username.
# Work around that by setting their username to "blank", followed by a counter. # Work around that by setting their username to "blank", followed by a counter.
path = "blank" if path.blank? path = "blank" if path.blank?
counter = 0 counter = 0
...@@ -99,7 +99,18 @@ class Namespace < ActiveRecord::Base ...@@ -99,7 +99,18 @@ class Namespace < ActiveRecord::Base
end end
def rm_dir def rm_dir
gitlab_shell.rm_namespace(path) # Move namespace directory into trash.
# We will remove it later async
new_path = "#{path}+#{id}+deleted"
if gitlab_shell.mv_namespace(path, new_path)
message = "Namespace directory \"#{path}\" moved to \"#{new_path}\""
Gitlab::AppLogger.info message
# Remove namespace directroy async with delay so
# GitLab has time to remove all projects first
GitlabShellWorker.perform_in(5.minutes, :rm_namespace, new_path)
end
end end
def move_dir def move_dir
......
...@@ -326,8 +326,8 @@ class Note < ActiveRecord::Base ...@@ -326,8 +326,8 @@ class Note < ActiveRecord::Base
end end
# Mentionable override. # Mentionable override.
def gfm_reference def gfm_reference(from_project = nil)
noteable.gfm_reference noteable.gfm_reference(from_project)
end end
# Mentionable override. # Mentionable override.
......
...@@ -33,11 +33,12 @@ require 'carrierwave/orm/activerecord' ...@@ -33,11 +33,12 @@ require 'carrierwave/orm/activerecord'
require 'file_size_validator' require 'file_size_validator'
class Project < ActiveRecord::Base class Project < ActiveRecord::Base
include Sortable include Gitlab::ConfigHelper
include Gitlab::ShellAdapter include Gitlab::ShellAdapter
include Gitlab::VisibilityLevel include Gitlab::VisibilityLevel
include Gitlab::ConfigHelper
include Rails.application.routes.url_helpers include Rails.application.routes.url_helpers
include Referable
include Sortable
extend Gitlab::ConfigHelper extend Gitlab::ConfigHelper
extend Enumerize extend Enumerize
...@@ -252,6 +253,11 @@ class Project < ActiveRecord::Base ...@@ -252,6 +253,11 @@ class Project < ActiveRecord::Base
order_by(method) order_by(method)
end end
end end
def reference_pattern
name_pattern = Gitlab::Regex::NAMESPACE_REGEX_STR
%r{(?<project>#{name_pattern}/#{name_pattern})}
end
end end
def team def team
...@@ -310,6 +316,10 @@ class Project < ActiveRecord::Base ...@@ -310,6 +316,10 @@ class Project < ActiveRecord::Base
path path
end end
def to_reference(_from_project = nil)
path_with_namespace
end
def web_url def web_url
[gitlab_config.url, path_with_namespace].join('/') [gitlab_config.url, path_with_namespace].join('/')
end end
......
...@@ -40,6 +40,12 @@ class GitlabCiService < CiService ...@@ -40,6 +40,12 @@ class GitlabCiService < CiService
def execute(data) def execute(data)
return unless supported_events.include?(data[:object_kind]) return unless supported_events.include?(data[:object_kind])
ci_yaml_file = ci_yaml_file(data)
if ci_yaml_file
data.merge!(ci_yaml_file: ci_yaml_file)
end
service_hook.execute(data) service_hook.execute(data)
end end
...@@ -123,6 +129,14 @@ class GitlabCiService < CiService ...@@ -123,6 +129,14 @@ class GitlabCiService < CiService
private private
def ci_yaml_file(data)
ref = data[:checkout_sha]
repo = project.repository
commit = repo.commit(ref)
blob = Gitlab::Git::Blob.find(repo, commit.id, ".gitlab-ci.yml")
blob && blob.data
end
def fork_registration_path def fork_registration_path
project_url.sub(/projects\/\d*/, "#{API_PREFIX}/forks") project_url.sub(/projects\/\d*/, "#{API_PREFIX}/forks")
end end
......
...@@ -63,7 +63,7 @@ class HipchatService < Service ...@@ -63,7 +63,7 @@ class HipchatService < Service
private private
def gate def gate
options = { api_version: api_version || 'v2' } options = { api_version: api_version.present? ? api_version : 'v2' }
options[:server_url] = server unless server.blank? options[:server_url] = server unless server.blank?
@gate ||= HipChat::Client.new(token, options) @gate ||= HipChat::Client.new(token, options)
end end
......
...@@ -2,7 +2,7 @@ class ProjectWiki ...@@ -2,7 +2,7 @@ class ProjectWiki
include Gitlab::ShellAdapter include Gitlab::ShellAdapter
MARKUPS = { MARKUPS = {
'Markdown' => :markdown, 'Markdown' => :md,
'RDoc' => :rdoc, 'RDoc' => :rdoc,
'AsciiDoc' => :asciidoc 'AsciiDoc' => :asciidoc
} unless defined?(MARKUPS) } unless defined?(MARKUPS)
......
...@@ -370,8 +370,55 @@ class Repository ...@@ -370,8 +370,55 @@ class Repository
@root_ref ||= raw_repository.root_ref @root_ref ||= raw_repository.root_ref
end end
def commit_file(user, path, content, message, ref)
path[0] = '' if path[0] == '/'
committer = user_to_comitter(user)
options = {}
options[:committer] = committer
options[:author] = committer
options[:commit] = {
message: message,
branch: ref
}
options[:file] = {
content: content,
path: path
}
Gitlab::Git::Blob.commit(raw_repository, options)
end
def remove_file(user, path, message, ref)
path[0] = '' if path[0] == '/'
committer = user_to_comitter(user)
options = {}
options[:committer] = committer
options[:author] = committer
options[:commit] = {
message: message,
branch: ref
}
options[:file] = {
path: path
}
Gitlab::Git::Blob.remove(raw_repository, options)
end
private private
def user_to_comitter(user)
{
email: user.email,
name: user.name,
time: Time.now
}
end
def cache def cache
@cache ||= RepositoryCache.new(path_with_namespace) @cache ||= RepositoryCache.new(path_with_namespace)
end end
......
...@@ -16,14 +16,16 @@ ...@@ -16,14 +16,16 @@
# #
class Snippet < ActiveRecord::Base class Snippet < ActiveRecord::Base
include Sortable
include Linguist::BlobHelper
include Gitlab::VisibilityLevel include Gitlab::VisibilityLevel
include Linguist::BlobHelper
include Participable include Participable
include Referable
include Sortable
default_value_for :visibility_level, Snippet::PRIVATE default_value_for :visibility_level, Snippet::PRIVATE
belongs_to :author, class_name: "User" belongs_to :author, class_name: 'User'
belongs_to :project
has_many :notes, as: :noteable, dependent: :destroy has_many :notes, as: :noteable, dependent: :destroy
...@@ -50,6 +52,30 @@ class Snippet < ActiveRecord::Base ...@@ -50,6 +52,30 @@ class Snippet < ActiveRecord::Base
participant :author, :notes participant :author, :notes
def self.reference_prefix
'$'
end
# Pattern used to extract `$123` snippet references from text
#
# This pattern supports cross-project references.
def self.reference_pattern
%r{
(#{Project.reference_pattern})?
#{Regexp.escape(reference_prefix)}(?<snippet>\d+)
}x
end
def to_reference(from_project = nil)
reference = "#{self.class.reference_prefix}#{id}"
if cross_project_reference?(from_project)
reference = project.to_reference + reference
end
reference
end
def self.content_types def self.content_types
[ [
".rb", ".py", ".pl", ".scala", ".c", ".cpp", ".java", ".rb", ".py", ".pl", ".scala", ".c", ".cpp", ".java",
......
class Tree class Tree
include Gitlab::MarkdownHelper include Gitlab::MarkupHelper
attr_accessor :repository, :sha, :path, :entries attr_accessor :repository, :sha, :path, :entries
def initialize(repository, sha, path = '/') def initialize(repository, sha, path = '/')
path = '/' if path.blank? path = '/' if path.blank?
@repository = repository @repository = repository
@sha = sha @sha = sha
@path = path @path = path
...@@ -20,7 +20,7 @@ class Tree ...@@ -20,7 +20,7 @@ class Tree
available_readmes = blobs.select(&:readme?) available_readmes = blobs.select(&:readme?)
if available_readmes.count == 0 if available_readmes.count == 0
return @readme = nil return @readme = nil
end end
# Take the first previewable readme, or the first available readme, if we # Take the first previewable readme, or the first available readme, if we
......
...@@ -62,11 +62,13 @@ require 'carrierwave/orm/activerecord' ...@@ -62,11 +62,13 @@ require 'carrierwave/orm/activerecord'
require 'file_size_validator' require 'file_size_validator'
class User < ActiveRecord::Base class User < ActiveRecord::Base
include Sortable
include Gitlab::ConfigHelper
include TokenAuthenticatable
extend Gitlab::ConfigHelper extend Gitlab::ConfigHelper
include Gitlab::ConfigHelper
include Gitlab::CurrentSettings include Gitlab::CurrentSettings
include Referable
include Sortable
include TokenAuthenticatable
default_value_for :admin, false default_value_for :admin, false
default_value_for :can_create_group, gitlab_config.default_can_create_group default_value_for :can_create_group, gitlab_config.default_can_create_group
...@@ -274,6 +276,18 @@ class User < ActiveRecord::Base ...@@ -274,6 +276,18 @@ class User < ActiveRecord::Base
username username
end end
def reference_prefix
'@'
end
# Pattern used to extract `@user` user references from text
def reference_pattern
%r{
#{Regexp.escape(reference_prefix)}
(?<user>#{Gitlab::Regex::NAMESPACE_REGEX_STR})
}x
end
end end
# #
...@@ -284,6 +298,10 @@ class User < ActiveRecord::Base ...@@ -284,6 +298,10 @@ class User < ActiveRecord::Base
username username
end end
def to_reference(_from_project = nil)
"#{self.class.reference_prefix}#{username}"
end
def notification def notification
@notification ||= Notification.new(self) @notification ||= Notification.new(self)
end end
...@@ -493,7 +511,7 @@ class User < ActiveRecord::Base ...@@ -493,7 +511,7 @@ class User < ActiveRecord::Base
end end
def sanitize_attrs def sanitize_attrs
%w(name username skype linkedin twitter bio).each do |attr| %w(name username skype linkedin twitter).each do |attr|
value = self.send(attr) value = self.send(attr)
self.send("#{attr}=", Sanitize.clean(value)) if value.present? self.send("#{attr}=", Sanitize.clean(value)) if value.present?
end end
...@@ -669,6 +687,12 @@ class User < ActiveRecord::Base ...@@ -669,6 +687,12 @@ class User < ActiveRecord::Base
end end
end end
def namespaces
namespace_ids = groups.pluck(:id)
namespace_ids.push(namespace.id)
Namespace.where(id: namespace_ids)
end
def oauth_authorized_tokens def oauth_authorized_tokens
Doorkeeper::AccessToken.where(resource_owner_id: self.id, revoked_at: nil) Doorkeeper::AccessToken.where(resource_owner_id: self.id, revoked_at: nil)
end end
...@@ -703,4 +727,8 @@ class User < ActiveRecord::Base ...@@ -703,4 +727,8 @@ class User < ActiveRecord::Base
true true
end end
def can_be_removed?
!solo_owned_groups.present?
end
end end
class DeleteUserService
def execute(user)
if user.solo_owned_groups.present?
user.errors[:base] << 'You must transfer ownership or delete groups before you can remove user'
user
else
user.personal_projects.each do |project|
# Skip repository removal because we remove directory with namespace
# that contain all this repositories
::Projects::DestroyService.new(project, current_user, skip_repo: true).execute
end
user.destroy
end
end
end
class DestroyGroupService
attr_accessor :group, :current_user
def initialize(group, user)
@group, @current_user = group, user
end
def execute
@group.projects.each do |project|
# Skip repository removal because we remove directory with namespace
# that contain all this repositories
::Projects::DestroyService.new(project, current_user, skip_repo: true).execute
end
@group.destroy
end
end
...@@ -17,5 +17,12 @@ module Files ...@@ -17,5 +17,12 @@ module Files
def git_hook def git_hook
project.git_hook project.git_hook
end end
def after_commit(sha)
commit = repository.commit(sha)
full_ref = 'refs/heads/' + (params[:new_branch] || ref)
old_sha = commit.parent_id || Gitlab::Git::BLANK_SHA
GitPushService.new.execute(project, current_user, old_sha, sha, full_ref)
end
end end
end end
require_relative "base_service" require_relative "base_service"
module Files module Files
class CreateService < BaseService class CreateService < Files::BaseService
def execute def execute
allowed = Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(ref) allowed = Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(ref)
...@@ -37,16 +37,24 @@ module Files ...@@ -37,16 +37,24 @@ module Files
end end
end end
content =
if params[:encoding] == 'base64'
Base64.decode64(params[:content])
else
params[:content]
end
new_file_action = Gitlab::Satellite::NewFileAction.new(current_user, project, ref, file_path) sha = repository.commit_file(
created_successfully = new_file_action.commit!( current_user,
params[:content], file_path,
content,
params[:commit_message], params[:commit_message],
params[:encoding], params[:new_branch] || ref
params[:new_branch]
) )
if created_successfully
if sha
after_commit(sha)
success success
else else
error("Your changes could not be committed, because the file has been changed") error("Your changes could not be committed, because the file has been changed")
......
require_relative "base_service" require_relative "base_service"
module Files module Files
class DeleteService < BaseService class DeleteService < Files::BaseService
def execute def execute
allowed = ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(ref) allowed = ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(ref)
...@@ -23,14 +23,15 @@ module Files ...@@ -23,14 +23,15 @@ module Files
return error("You can only edit text files") return error("You can only edit text files")
end end
delete_file_action = Gitlab::Satellite::DeleteFileAction.new(current_user, project, ref, path) sha = repository.remove_file(
current_user,
deleted_successfully = delete_file_action.commit!( path,
nil, params[:commit_message],
params[:commit_message] ref
) )
if deleted_successfully if sha
after_commit(sha)
success success
else else
error("Your changes could not be committed, because the file has been changed") error("Your changes could not be committed, because the file has been changed")
......
require_relative "base_service" require_relative "base_service"
module Files module Files
class UpdateService < BaseService class UpdateService < Files::BaseService
def execute def execute
allowed = ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(ref) allowed = ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(ref)
...@@ -23,14 +23,22 @@ module Files ...@@ -23,14 +23,22 @@ module Files
return error("You can only edit text files") return error("You can only edit text files")
end end
edit_file_action = Gitlab::Satellite::EditFileAction.new(current_user, project, ref, path) content =
edit_file_action.commit!( if params[:encoding] == 'base64'
params[:content], Base64.decode64(params[:content])
else
params[:content]
end
sha = repository.commit_file(
current_user,
path,
content,
params[:commit_message], params[:commit_message],
params[:encoding], params[:new_branch] || ref
params[:new_branch]
) )
after_commit(sha)
success success
rescue Gitlab::Satellite::CheckoutFailed => ex rescue Gitlab::Satellite::CheckoutFailed => ex
error("Your changes could not be committed because ref '#{ref}' could not be checked out", 400) error("Your changes could not be committed because ref '#{ref}' could not be checked out", 400)
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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