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.
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)
- Don't show duplicate deploy keys
- Fix commit time being displayed in the wrong timezone in some cases (Hannes Rosenögger)
......@@ -11,8 +78,6 @@ v 7.11.0 (unreleased)
- Don't allow a merge request to be merged when its title starts with "WIP".
- Add a page title to every page.
- 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)
- Ignore invalid lines in .gitmodules
- Fix "Cannot move project" error message from popping up after a successful transfer (Stan Hu)
......@@ -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)
- Improve project page UI
- 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`)
- 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.
......@@ -32,20 +96,19 @@ v 7.11.0 (unreleased)
- Show Atom feed buttons everywhere where applicable.
- 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.
- Explain how to get a new password reset token in welcome emails
- 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.
- 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.
- Show incompatible projects in Google Code import status (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.
- 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)
- 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
- Improve UI for sidebar. Increase separation between navigation and content
- Improve new project command options (Ben Bodenmiller)
......@@ -66,6 +129,17 @@ v 7.11.0 (unreleased)
- Add style for <kbd> element in markdown
- Spin spinner icon next to "Checking for CI status..." on MR page.
- 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
- Fix CI links on MR page
......@@ -150,12 +224,12 @@ v 7.9.0 (unreleased)
- Ability to skip some items from backup (database, respositories or uploads)
- Archive repositories in background worker.
- 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)
- Fix and improve help rendering (Sullivan Sénéchal)
- Fix final line in EmailsOnPush email diff being rendered as error.
- 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
- 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
......@@ -164,7 +238,7 @@ v 7.9.0 (unreleased)
- Fix stuck Merge Request merging events from old installations (Ben Bodenmiller)
- Fix merge request comments on files with multiple commits
- Fix Resource Owner Password Authentication Flow
v 7.9.4
- 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
......@@ -470,6 +544,12 @@ v 7.5.0
- Use secret token with GitLab internal API.
- 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
- Fix raw snippets view
- Fix security issue for member api
......
......@@ -29,11 +29,9 @@ You can also sign up on [CodeTriage](http://www.codetriage.com/gitlabhq/gitlabhq
## 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.
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).
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.
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
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.
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.
......@@ -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. [Shell commands](doc/development/shell_commands.md) created by GitLab contributors to enhance security
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. [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).
......@@ -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
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"
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"
# Make links from text
gem 'rails_autolink', '~> 1.1'
# Default values for AR models
gem "default_value_for", "~> 3.0.0"
......@@ -31,6 +20,7 @@ gem 'omniauth-shibboleth'
gem 'omniauth-kerberos', group: :kerberos
gem 'omniauth-gitlab'
gem 'omniauth-bitbucket'
gem 'omniauth-saml'
gem 'doorkeeper', '2.1.3'
gem "rack-oauth2", "~> 1.0.5"
......@@ -44,12 +34,16 @@ gem "browser"
# Extracting information from a git repository
# 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
# 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'
# 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 'net-ldap'
......@@ -57,6 +51,10 @@ gem 'net-ldap'
gem 'gollum-lib', '~> 4.0.2'
# 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"
# API
......@@ -95,7 +93,7 @@ gem "seed-fu"
# Markdown and HTML processing
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 'redcarpet', '~> 3.2.3'
gem 'RedCloth'
......@@ -103,7 +101,7 @@ gem 'rdoc', '~>3.6'
gem 'org-ruby', '= 0.9.12'
gem 'creole', '~>0.3.6'
gem 'wikicloth', '=0.8.1'
gem 'asciidoctor', '= 0.1.4'
gem 'asciidoctor', '~> 1.5.2'
# Diffs
gem 'diffy', '~> 3.0.3'
......@@ -173,7 +171,7 @@ gem "underscore-rails", "~> 1.4.4"
gem "sanitize", '~> 2.0'
# Protect against bruteforcing
gem "rack-attack"
gem "rack-attack", '~> 4.3.0'
# Ace editor
gem 'ace-rails-ap'
......@@ -187,23 +185,23 @@ gem 'charlock_holmes'
gem "sass-rails", '~> 4.0.2'
gem "coffee-rails"
gem "uglifier"
gem 'turbolinks'
gem 'turbolinks', '~> 2.5.0'
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-rails"
gem "jquery-ui-rails"
gem "jquery-scrollto-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 'jquery-rails', '3.1.2'
gem 'jquery-scrollto-rails'
gem 'jquery-ui-rails'
gem 'nprogress-rails'
gem 'raphael-rails', '~> 2.1.2'
gem 'request_store'
gem "virtus"
gem 'addressable'
gem 'select2-rails'
gem 'virtus'
gem "gitlab-license", "~> 0.0.2"
......@@ -242,25 +240,18 @@ group :development, :test do
gem 'minitest', '~> 5.3.0'
# Generate Fake data
gem "ffaker"
# 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')
gem 'ffaker', '~> 2.0.0'
# PhantomJS driver for Capybara
gem 'poltergeist', '~> 1.5.1'
gem 'jasmine-rails'
gem 'teaspoon', '~> 1.0.0'
gem 'teaspoon-jasmine'
gem "spring", '~> 1.3.1'
gem "spring-commands-rspec", '1.0.4'
gem "spring-commands-spinach", '1.0.0'
gem 'spring', '~> 1.3.1'
gem 'spring-commands-rspec', '~> 1.0.0'
gem 'spring-commands-spinach', '~> 1.0.0'
gem 'spring-commands-teaspoon', '~> 0.0.2'
gem "byebug"
end
......@@ -280,4 +271,4 @@ end
gem "newrelic_rpm"
gem 'octokit', '3.7.0'
gem "rugments"
gem "rugments", "~> 1.0.0.beta7"
......@@ -42,7 +42,7 @@ GEM
arel (5.0.1.20140414130214)
asana (0.0.6)
activeresource (>= 3.2.3)
asciidoctor (0.1.4)
asciidoctor (1.5.2)
ast (2.0.0)
astrolabe (1.3.0)
parser (>= 2.2.0.pre.3, < 3.0)
......@@ -101,13 +101,13 @@ GEM
coderay (1.1.0)
coercible (1.0.0)
descendants_tracker (~> 0.0.1)
coffee-rails (4.0.1)
coffee-rails (4.1.0)
coffee-script (>= 2.2.0)
railties (>= 4.0.0, < 5.0)
coffee-script (2.2.0)
coffee-script (2.4.1)
coffee-script-source
execjs
coffee-script-source (1.6.3)
coffee-script-source (1.9.1.1)
colored (1.2)
colorize (0.5.8)
columnize (0.9.0)
......@@ -176,7 +176,7 @@ GEM
faraday_middleware (0.9.0)
faraday (>= 0.7.4, < 0.9)
fastercsv (1.5.5)
ffaker (1.22.1)
ffaker (2.0.0)
ffi (1.9.8)
fog (1.21.0)
fog-brightbox
......@@ -226,11 +226,11 @@ GEM
mime-types (~> 1.19)
gitlab_emoji (0.1.0)
gemojione (~> 2.0)
gitlab_git (7.1.12)
gitlab_git (7.2.2)
activesupport (~> 4.0)
charlock_holmes (~> 0.6)
gitlab-linguist (~> 3.0)
rugged (~> 0.21.2)
rugged (~> 0.22.2)
gitlab_meta (7.0)
gitlab_omniauth-ldap (1.2.1)
net-ldap (~> 0.9)
......@@ -262,19 +262,6 @@ GEM
grape-entity (0.4.2)
activesupport
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)
tilt
haml-rails (0.5.3)
......@@ -301,14 +288,8 @@ GEM
i18n (0.7.0)
ice_cube (0.11.1)
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-rails (3.1.0)
jquery-rails (3.1.2)
railties (>= 3.0, < 5.0)
thor (>= 0.14, < 2.0)
jquery-scrollto-rails (1.4.3)
......@@ -333,13 +314,14 @@ GEM
celluloid (~> 0.16.0)
rb-fsevent (>= 0.9.3)
rb-inotify (>= 0.9)
lumberjack (1.0.4)
macaddr (1.7.1)
systemu (~> 2.6.2)
mail (2.6.3)
mime-types (>= 1.16, < 3)
method_source (0.8.2)
mime-types (1.25.1)
mimemagic (0.3.0)
mini_portile (0.6.1)
mini_portile (0.6.2)
minitest (5.3.5)
mousetrap-rails (1.4.6)
multi_json (1.10.1)
......@@ -351,7 +333,7 @@ GEM
net-ssh (>= 2.6.5)
net-ssh (2.8.0)
newrelic_rpm (3.9.4.245)
nokogiri (1.6.5)
nokogiri (1.6.6.2)
mini_portile (~> 0.6.0)
nprogress-rails (0.1.2.3)
oauth (0.4.7)
......@@ -390,6 +372,9 @@ GEM
omniauth-oauth2 (1.1.1)
oauth2 (~> 0.8.0)
omniauth (~> 1.0)
omniauth-saml (1.3.1)
omniauth (~> 1.1)
ruby-saml (~> 0.8.1)
omniauth-shibboleth (1.1.1)
omniauth (>= 1.0.0)
omniauth-twitter (1.0.1)
......@@ -401,7 +386,6 @@ GEM
parser (2.2.0.2)
ast (>= 1.1, < 3.0)
pg (0.15.1)
phantomjs (1.9.8.0)
poltergeist (1.5.1)
capybara (~> 2.1)
cliver (~> 0.3.1)
......@@ -419,10 +403,10 @@ GEM
quiet_assets (1.0.2)
railties (>= 3.1, < 5.0)
racc (1.4.10)
rack (1.5.2)
rack (1.5.3)
rack-accept (0.4.5)
rack (>= 0.4)
rack-attack (4.2.0)
rack-attack (4.3.0)
rack
rack-cors (0.2.9)
rack-mini-profiler (0.9.0)
......@@ -451,8 +435,6 @@ GEM
sprockets-rails (~> 2.0)
rails-observers (0.1.2)
activemodel (~> 4.0)
rails_autolink (1.1.6)
rails (> 3.1)
railties (4.1.9)
actionpack (= 4.1.9)
activesupport (= 4.1.9)
......@@ -498,10 +480,6 @@ GEM
rqrcode (0.4.2)
rqrcode-rails3 (0.1.7)
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-expectations (>= 2.99.0.beta1)
rspec-core (2.99.2)
......@@ -524,6 +502,9 @@ GEM
rainbow (>= 1.99.1, < 3.0)
ruby-progressbar (~> 1.4)
ruby-progressbar (1.7.1)
ruby-saml (0.8.2)
nokogiri (>= 1.5.0)
uuid (~> 2.3)
ruby2ruby (2.1.3)
ruby_parser (~> 3.1)
sexp_processor (~> 4.0)
......@@ -531,8 +512,8 @@ GEM
sexp_processor (~> 4.1)
rubyntlm (0.5.0)
rubypants (0.2.0)
rugged (0.21.4)
rugments (1.0.0.beta6)
rugged (0.22.2)
rugments (1.0.0.beta7)
safe_yaml (0.9.7)
sanitize (2.1.0)
nokogiri (>= 1.4.4)
......@@ -548,9 +529,9 @@ GEM
sdoc (0.3.20)
json (>= 1.1.3)
rdoc (~> 3.10)
seed-fu (2.3.1)
activerecord (>= 3.1, < 4.2)
activesupport (>= 3.1, < 4.2)
seed-fu (2.3.5)
activerecord (>= 3.1, < 4.3)
activesupport (>= 3.1, < 4.3)
select2-rails (3.5.2)
thor (~> 0.14)
settingslogic (2.0.9)
......@@ -590,11 +571,13 @@ GEM
capybara (>= 2.0.0)
railties (>= 3)
spinach (>= 0.4)
spring (1.3.3)
spring (1.3.6)
spring-commands-rspec (1.0.4)
spring (>= 0.9.1)
spring-commands-spinach (1.0.0)
spring (>= 0.9.1)
spring-commands-teaspoon (0.0.2)
spring (>= 0.9.1)
sprockets (2.11.0)
hike (~> 1.2)
multi_json (~> 1.0)
......@@ -607,8 +590,13 @@ GEM
stamp (0.5.0)
state_machine (1.2.0)
stringex (2.5.2)
systemu (2.6.5)
task_list (1.0.2)
html-pipeline
teaspoon (1.0.2)
railties (>= 3.2.5, < 5)
teaspoon-jasmine (2.2.0)
teaspoon (>= 1.0.0)
temple (0.6.7)
term-ansicolor (1.2.2)
tins (~> 0.8)
......@@ -634,7 +622,7 @@ GEM
multi_json (~> 1.7)
twitter-stream (~> 0.1)
tins (0.13.1)
turbolinks (2.0.0)
turbolinks (2.5.3)
coffee-rails
twitter-stream (0.1.16)
eventmachine (>= 0.12.8)
......@@ -655,6 +643,8 @@ GEM
raindrops (~> 0.7)
unicorn-worker-killer (0.4.2)
unicorn (~> 4)
uuid (2.3.7)
macaddr (~> 1.0)
version_sorter (2.0.0)
virtus (1.0.1)
axiom-types (~> 0.0.5)
......@@ -684,7 +674,7 @@ DEPENDENCIES
addressable
annotate (~> 2.6.0.beta2)
asana (~> 0.0.6)
asciidoctor (= 0.1.4)
asciidoctor (~> 1.5.2)
attr_encrypted (= 1.3.4)
awesome_print
better_errors
......@@ -714,7 +704,7 @@ DEPENDENCIES
email_spec
enumerize
factory_girl_rails
ffaker
ffaker (~> 2.0.0)
fog (~> 1.14)
font-awesome-rails (~> 4.2)
foreman
......@@ -725,23 +715,19 @@ DEPENDENCIES
gitlab-license (~> 0.0.2)
gitlab-linguist (~> 3.0.1)
gitlab_emoji (~> 0.1)
gitlab_git (~> 7.1.12)
gitlab_git (~> 7.2.2)
gitlab_meta (= 7.0)
gitlab_omniauth-ldap (= 1.2.1)
gollum-lib (~> 4.0.2)
gon (~> 5.0.0)
grape (~> 0.6.1)
grape-entity (~> 0.4.2)
growl
guard-rspec
guard-spinach
haml-rails
hipchat (~> 1.5.0)
html-pipeline (~> 1.11.0)
httparty
jasmine-rails
jquery-atwho-rails (~> 1.0.0)
jquery-rails
jquery-rails (= 3.1.2)
jquery-scrollto-rails
jquery-turbolinks
jquery-ui-rails
......@@ -760,6 +746,7 @@ DEPENDENCIES
omniauth-gitlab
omniauth-google-oauth2
omniauth-kerberos
omniauth-saml
omniauth-shibboleth
omniauth-twitter
org-ruby (= 0.9.12)
......@@ -767,15 +754,12 @@ DEPENDENCIES
poltergeist (~> 1.5.1)
pry-rails
quiet_assets (~> 1.0.1)
rack-attack
rack-attack (~> 4.3.0)
rack-cors
rack-mini-profiler
rack-oauth2 (~> 1.0.5)
rails (~> 4.1.0)
rails_autolink (~> 1.1)
raphael-rails (~> 2.1.2)
rb-fsevent
rb-inotify
rdoc (~> 3.6)
redcarpet (~> 3.2.3)
redis-rails
......@@ -784,7 +768,7 @@ DEPENDENCIES
rqrcode-rails3
rspec-rails (= 2.99)
rubocop (= 0.28.0)
rugments
rugments (~> 1.0.0.beta7)
sanitize (~> 2.0)
sass-rails (~> 4.0.2)
sdoc
......@@ -801,15 +785,18 @@ DEPENDENCIES
slim
spinach-rails
spring (~> 1.3.1)
spring-commands-rspec (= 1.0.4)
spring-commands-spinach (= 1.0.0)
spring-commands-rspec (~> 1.0.0)
spring-commands-spinach (~> 1.0.0)
spring-commands-teaspoon (~> 0.0.2)
stamp
state_machine
task_list (~> 1.0.0)
task_list (= 1.0.2)
teaspoon (~> 1.0.0)
teaspoon-jasmine
test_after_commit
thin
tinder (~> 1.9.2)
turbolinks
turbolinks (~> 2.5.0)
uglifier
underscore-rails (~> 1.4.4)
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.
*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.
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
......
7.11.0.pre-ee
7.12.0.pre-ee
......@@ -49,8 +49,6 @@ window.slugify = (text) ->
window.ajaxGet = (url) ->
$.ajax({type: "GET", url: url, dataType: "script"})
window.showAndHide = (selector) ->
window.split = (val) ->
return val.split( /,\s*/ )
......@@ -92,15 +90,7 @@ window.disableButtonIfAnyEmptyField = (form, form_selector, button_selector) ->
window.sanitize = (str) ->
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 = ->
$(document).unbind('scroll')
$(document).off('scroll')
window.shiftWindow = ->
......@@ -118,7 +108,10 @@ $.timeago.settings.allowFuture = true
$ ->
# 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', ->
$(this).closest('li').fadeOut()
......@@ -142,8 +135,8 @@ $ ->
# Place the logo tooltip on the right when collapsed, bottom when expanded
$el.parents('header').hasClass('header-collapsed') and 'right' or 'bottom'
else
# Otherwise use the data-placement attribute like normal
$el.data('placement')
# Otherwise use the data-placement attribute, or 'bottom' if undefined
$el.data('placement') or 'bottom'
})
# Form submitter
......@@ -176,6 +169,10 @@ $ ->
$(@).next('table').show()
$(@).remove()
$('.navbar-toggle').on 'click', ->
$('.header-content .title').toggle()
$('.header-content .navbar-collapse').toggle()
# Show/hide comments on diff
$("body").on "click", ".js-toggle-diff-comments", (e) ->
$(@).toggleClass('active')
......@@ -191,14 +188,3 @@ $ ->
new ConfirmDangerModal(form, text)
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
new Milestone()
when 'projects:milestones:new', 'projects:milestones:edit'
new ZenMode()
new DropzoneInput($('.milestone-form'))
when 'projects:compare:show'
new Diff()
when 'projects:issues:new','projects:issues:edit'
......
$.fn.showAndHide = ->
$(@).show().
delay(3000).
fadeOut()
$.fn.enableButton = ->
$(@).removeAttr('disabled').
removeClass('disabled')
$.fn.disableButton = ->
$(@).attr('disabled', 'disabled').
addClass('disabled')
# 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')
#= require jquery
#= require jquery.waitforimages
#= require task_list
......
#= require jquery
#= require bootstrap
#= require jquery.waitforimages
#= require task_list
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) ->
@initContextWidget()
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.activateTabFromPath()
this.initMergeWidget()
this.$('.show-all-commits').on 'click', =>
......@@ -65,8 +79,18 @@ class @MergeRequest
, 'json'
bindEvents: ->
this.$('.merge-request-tabs').on 'click', 'li', (event) =>
this.activateTab($(event.currentTarget).data('action'))
this.$('.merge-request-tabs a[data-toggle="tab"]').on 'shown.bs.tab', (e) =>
$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', ->
$('.automerge_widget.can_be_merged').hide()
......@@ -84,21 +108,54 @@ class @MergeRequest
this.$('.remove_source_branch_in_progress').hide()
this.$('.remove_source_branch_widget.failed').show()
activateTab: (action) ->
this.$('.merge-request-tabs li').removeClass 'active'
this.$('.tab-content').hide()
switch action
when 'diffs'
this.$('.merge-request-tabs .diffs-tab').addClass 'active'
this.loadDiff() unless @diffs_loaded
this.$('.diffs').show()
$(".diff-header").trigger("sticky_kit:recalc")
when 'commits'
this.$('.merge-request-tabs .commits-tab').addClass 'active'
this.$('.commits').show()
else
this.$('.merge-request-tabs .notes-tab').addClass 'active'
this.$('.notes').show()
# Activate a tab based on the current URL path
#
# If the current action is 'show' or 'new' (i.e., initial page load),
# activates the first tab, otherwise activates the tab corresponding to the
# current action (diffs, commits).
activateTabFromPath: ->
if @opts.action == 'show' || @opts.action == 'new'
this.$('.merge-request-tabs a[data-toggle="tab"]:first').tab('show')
else
this.$(".merge-request-tabs a[data-action='#{@opts.action}']").tab('show')
# Replaces the current Merge Request-specific action in the URL with a new one
#
# If the action is "notes", the URL is reset to the standard
# `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) ->
$('.automerge_widget').hide()
......
#= require jquery
#= require autosave
#= require bootstrap
#= require dropzone
#= require dropzone_input
#= require gfm_auto_complete
......@@ -312,6 +310,14 @@ class @Notes
form.show()
textarea = form.find("textarea")
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")
###
......
......@@ -12,11 +12,11 @@ class @Profile
$(this).find('.update-failed').hide()
$('.update-username form').on 'ajax:complete', ->
$(this).find('.btn-save').enableButton()
$(this).find('.btn-save').enable()
$(this).find('.loading-gif').hide()
$('.update-notifications').on 'ajax:complete', ->
$(this).find('.btn-save').enableButton()
$(this).find('.btn-save').enable()
$('.js-choose-user-avatar-button').bind "click", ->
......
#= require jquery
#= require mousetrap
#= require shortcuts_navigation
class @ShortcutsIssuable extends ShortcutsNavigation
......
#= require d3
#= require jquery
#= require stat_graph_contributors_util
class @ContributorsStatGraph
......
......@@ -2,11 +2,15 @@ window.ContributorsStatGraphUtil =
parse_log: (log) ->
total = {}
by_author = {}
by_email = {}
for entry in log
@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]
@store_data(entry, total[entry.date], by_author[entry.author_name][entry.date])
data = by_author[entry.author_name] #|| by_email[entry.author_email]
data ?= @add_author(entry, by_author, by_email)
@add_date(entry.date, data) unless data[entry.date]
@store_data(entry, total[entry.date], data[entry.date])
total = _.toArray(total)
by_author = _.toArray(by_author)
total: total, by_author: by_author
......@@ -15,10 +19,12 @@ window.ContributorsStatGraphUtil =
collection[date] = {}
collection[date].date = date
add_author: (author, by_author) ->
by_author[author.author_name] = {}
by_author[author.author_name].author_name = author.author_name
by_author[author.author_name].author_email = author.author_email
add_author: (author, by_author, by_email) ->
data = {}
data.author_name = author.author_name
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_commits(total, by_author)
......
class @Wikis
constructor: ->
$('.build-new-wiki').bind "click", ->
$('.build-new-wiki').bind "click", (e) ->
$('[data-error~=slug]').addClass("hidden")
$('p.hint').show()
field = $('#new_wiki_path')
slug = field.val()
path = field.attr('data-wikis-path')
valid_slug_pattern = /^[\w\/-]+$/
if(slug.length > 0)
location.href = path + "/" + slug
slug = field.val()
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
@fullscreen_prefix = 'fullscreen_'
#= require dropzone
#= require mousetrap
#= require mousetrap/pause
class @ZenMode
constructor: ->
@active_zen_area = null
@active_checkbox = null
......@@ -12,34 +14,31 @@ class @ZenMode
$('body').on 'click', '.zen-enter-link', (e) =>
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) =>
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) =>
checkbox = e.currentTarget
if checkbox.checked
# Disable other keyboard shortcuts in ZEN mode
Mousetrap.pause()
@udpateActiveZenArea(checkbox)
@updateActiveZenArea(checkbox)
else
@exitZenMode()
$(document).on 'keydown', (e) =>
if e.keyCode is $.ui.keyCode.ESCAPE
if e.keyCode is 27 # Esc
@exitZenMode()
e.preventDefault()
$(window).on 'hashchange', @updateZenModeFromLocationHash
udpateActiveZenArea: (checkbox) =>
updateActiveZenArea: (checkbox) =>
@active_checkbox = $(checkbox)
@active_checkbox.prop('checked', true)
@active_zen_area = @active_checkbox.parent().find('textarea')
@active_zen_area.focus()
window.location.hash = ZenMode.fullscreen_prefix + @active_checkbox.prop('id')
exitZenMode: =>
if @active_zen_area isnt null
......@@ -47,21 +46,9 @@ class @ZenMode
@active_checkbox.prop('checked', false)
@active_zen_area = null
@active_checkbox = null
window.location.hash = ''
window.scrollTo(window.pageXOffset, @scroll_position)
@restoreScroll(@scroll_position)
# Enable dropzone when leaving ZEN mode
Dropzone.forElement('.div-dropzone').enable()
checkboxFromLocationHash: (e) ->
id = $.trim(window.location.hash.replace('#' + ZenMode.fullscreen_prefix, ''))
if id
return $('.zennable input[type=checkbox]#' + id)[0]
else
return null
updateZenModeFromLocationHash: (e) =>
checkbox = @checkboxFromLocationHash()
if checkbox
@udpateActiveZenArea(checkbox)
else
@exitZenMode()
restoreScroll: (y) ->
window.scrollTo(window.pageXOffset, y)
$style_color: #474D57;
$hover: #FFF3EB;
$hover: #FFFAF1;
$gl-text-color: #222222;
$gl-link-color: #446e9b;
$nprogress-color: #c0392b;
$gl-font-size: 14px;
$list-font-size: 15px;
$sidebar_collapsed_width: 52px;
$sidebar_width: 230px;
$avatar_radius: 50%;
$code_font_size: 13px;
......
......@@ -316,7 +316,7 @@ table {
}
.btn-sign-in {
margin-top: 5px;
margin-top: 7px;
text-shadow: none;
}
......@@ -351,9 +351,8 @@ table {
}
#nprogress .spinner {
top: auto !important;
bottom: 20px !important;
left: 20px !important;
top: 15px !important;
right: 10px !important;
}
.header-with-avatar {
......
......@@ -89,7 +89,6 @@ label {
@include box-shadow(none);
}
.issuable-description,
.wiki-content {
margin-top: 35px;
}
......@@ -2,7 +2,14 @@
* Application Header
*
*/
$header-height: 46px;
header {
&.navbar-empty {
background: #FFF;
border-bottom: 1px solid #EEE;
}
&.navbar-gitlab {
z-index: 100;
margin-bottom: 0;
......@@ -11,161 +18,104 @@ header {
width: 100%;
.container {
background: #FFF;
width: 100% !important;
padding: 0;
background: #FFF;
border-bottom: 1px solid #EEE;
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 {
color: #666;
color: #888;
font-size: 14px;
line-height: 32px;
padding: 6px 10px;
line-height: 19px;
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 {
background: none;
background-color: #EEE;
}
}
/** NAV block with links and profile **/
.nav {
float: right;
margin-right: 0;
}
.navbar-toggle {
color: #666;
margin: 0;
border-radius: 0;
position: absolute;
right: 2px;
&:hover {
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;
}
}
}
}
/**
*
* Logo holder
*
*/
.app_logo {
.header-logo {
border-bottom: 1px solid transparent;
float: left;
margin-right: 9px;
height: $header-height;
width: $sidebar_width;
a {
float: left;
height: 46px;
height: $header-height;
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 {
width: 36px;
height: 36px;
float: left;
}
}
&:hover {
background-color: #EEE;
}
}
.profile-pic {
padding: 0px !important;
width: 46px;
height: 46px;
margin-left: 5px;
img {
width: 46px;
height: 46px;
.header-content {
border-bottom: 1px solid #EEE;
padding-right: 35px;
height: $header-height;
.title {
position: relative;
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 {
margin-right: 10px;
margin-left: 10px;
......@@ -177,6 +127,7 @@ header {
}
.search-input {
width: 220px;
background-image: image-url("icon-search.png");
background-repeat: no-repeat;
background-position: 10px;
......@@ -184,56 +135,74 @@ header {
padding: 4px 6px;
padding-left: 25px;
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;
border-color: #f5f5f5;
&:focus {
@include box-shadow(none);
outline: none;
border-color: #DDD;
background-color: #FFF;
}
}
}
}
.search .search-input {
width: 300px;
&:focus {
width: 330px;
}
}
@mixin collapsed-header {
.header-logo {
width: $sidebar_collapsed_width;
@media (max-width: 1200px) {
.search .search-input {
width: 200px;
&:focus {
width: 230px;
h3 {
display: none;
}
}
}
@media (max-width: $screen-xs-max) {
#nprogress .spinner {
right: 35px !important;
.header-content {
.title {
margin-left: 30px;
}
}
}
@media (max-width: $screen-md-max) {
.header-collapsed, .header-expanded {
width: 52px;
header .container .title {
max-width: 43%;
}
h3 {
display: none;
}
.header-collapsed, .header-expanded {
@include collapsed-header;
}
}
@media(min-width: $screen-md-max) {
.header-collapsed {
width: 52px;
h3 {
display: none;
}
@include collapsed-header;
}
.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 @@
&:hover {
background: $hover;
border-bottom: 1px solid darken($hover, 10%);
}
&:last-child {
......
......@@ -57,7 +57,7 @@
}
.container .title {
margin-left: 6px !important;
margin-left: 15px !important;
max-width: 70% !important;
}
}
......
.page-with-sidebar {
background: $background-color;
.sidebar-wrapper {
position: fixed;
top: 0;
......@@ -102,13 +100,13 @@
padding-left: 50px;
.sidebar-wrapper {
width: 52px;
width: $sidebar_collapsed_width;
.nav-sidebar {
margin-top: 29px;
position: fixed;
top: 45px;
width: 52px;
width: $sidebar_collapsed_width;
li a {
padding-left: 18px;
......@@ -125,7 +123,21 @@
.collapse-nav a {
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 @@
@include expanded-sidebar;
}
}
.sidebar-user {
position: absolute;
bottom: 0;
width: 100%;
padding: 10px;
color: #fff;
.avatar {
margin-top: 5px;
}
}
......@@ -23,6 +23,13 @@ pre {
font-family: $monospace_font;
}
code {
&.key-fingerprint {
background: $body-bg;
color: $text-color;
}
}
/**
* Wiki typography
*
......
.zennable {
position: relative;
input {
.zen-toggle-comment {
display: none;
}
......@@ -26,10 +26,12 @@
}
}
// Hide the Enter link when we're in Zen mode
input:checked ~ .zen-backdrop .zen-enter-link {
display: none;
}
// Show the Leave link when we're in Zen mode
input:checked ~ .zen-backdrop .zen-leave-link {
display: block;
position: absolute;
......@@ -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 {
color: white;
}
......@@ -78,6 +83,9 @@
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 {
color: #999;
}
......
......@@ -123,38 +123,31 @@
.mr-state-widget {
font-size: 13px;
background: #F9F9F9;
background: #FAFAFA;
margin-bottom: 20px;
color: #666;
border: 1px solid #EEE;
@include box-shadow(0 1px 1px rgba(0, 0, 0, 0.09));
border: 1px solid #e5e5e5;
@include box-shadow(0 1px 1px rgba(0, 0, 0, 0.05));
@include border-radius(3px);
.ci_widget {
padding: 10px 15px;
font-size: 15px;
border-bottom: 1px solid #BBB;
color: #777;
background-color: $background-color;
border-bottom: 1px solid #EEE;
&.ci-success {
color: $gl-success;
border-color: $gl-success;
background-color: #F1FAF1;
}
&.ci-pending,
&.ci-running {
color: $gl-warning;
border-color: $gl-warning;
background-color: #FAF5F1;
}
&.ci-failed,
&.ci-canceled,
&.ci-error {
color: $gl-danger;
border-color: $gl-danger;
background-color: #FAF1F1;
}
}
......@@ -162,7 +155,8 @@
padding: 10px 15px;
h4 {
font-weight: normal;
font-weight: bold;
margin: 5px 0;
}
p:last-child {
......
......@@ -79,11 +79,11 @@ ul.notes {
word-wrap: break-word;
@include md-typography;
// Reduce left padding of first ul element
// Reduce left padding of first task list ul element
ul.task-list:first-child {
padding-left: 10px;
// sub-lists should be padded normally
// sub-tasks should be padded normally
ul {
padding-left: 20px;
}
......
......@@ -84,8 +84,9 @@
}
.btn {
line-height: 36px;
height: 56px;
line-height: 40px;
height: 42px;
padding: 0px 12px;
img {
width: 32px;
......@@ -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 @@
}
.project-home-desc {
color: $gray;
float: left;
font-size: 16px;
line-height: 1.3;
margin-right: 250px;
}
.project-home-desc {
float: left;
color: $gray;
// Render Markdown-generated HTML inline for this block
p {
display: inline;
}
}
}
......@@ -129,7 +131,7 @@
}
.option-descr {
margin-left: 24px;
margin-left: 36px;
color: $gray;
}
}
......@@ -209,13 +211,14 @@ ul.nav.nav-projects-tabs {
line-height: 1.5;
}
.well {
padding: 14px;
.panel {
@include border-radius(3px);
h4 {
.panel-heading, .panel-footer {
font-weight: normal;
margin: 0;
color: #555;
background-color: transparent;
color: #666;
border-color: #EEE;
}
.actions {
......@@ -224,12 +227,18 @@ ul.nav.nav-projects-tabs {
.nav-pills a {
padding: 10px;
font-weight: bold;
color: $gl-link-color;
}
.nav {
margin: 10px 0;
margin-bottom: 15px;
}
}
.ci-status-image {
max-height: 22px;
}
}
.transfer-project .select2-container {
......
@mixin gitlab-theme($color-light, $color, $color-darker, $color-dark) {
header {
&.navbar-gitlab {
.app_logo {
.header-logo {
background-color: $color-darker;
border-color: $color-darker;
a {
color: $color-light;
......@@ -19,8 +20,6 @@
}
.page-with-sidebar {
background: $color-darker;
.collapse-nav a {
color: #FFF;
background: $color;
......@@ -29,6 +28,12 @@
.sidebar-wrapper {
background: $color-darker;
border-right: 1px solid $color-darker;
.sidebar-user {
a {
color: $color-light;
}
}
}
.nav-sidebar li {
......
......@@ -39,11 +39,13 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:sign_in_text,
:home_page_url,
:help_text,
:after_sign_out_path,
:max_attachment_size,
:default_project_visibility,
:default_snippet_visibility,
:restricted_signup_domains_raw,
:version_check_enabled,
:user_oauth_applications,
restricted_visibility_levels: [],
)
end
......
class Admin::DeployKeysController < Admin::ApplicationController
before_action :deploy_keys, only: [:index]
before_action :deploy_key, only: [:show, :destroy]
before_action :deploy_key, only: [:destroy]
def index
end
def show
end
def new
......
......@@ -47,7 +47,7 @@ class Admin::GroupsController < Admin::ApplicationController
end
def destroy
@group.destroy
DestroyGroupService.new(@group, current_user).execute
redirect_to admin_groups_path, notice: 'Group was successfully deleted.'
end
......
......@@ -86,11 +86,7 @@ class Admin::UsersController < Admin::ApplicationController
end
def destroy
# 1. Remove groups where user is the only owner
user.solo_owned_groups.map(&:destroy)
# 2. Remove user with all authored content including personal projects
user.destroy
DeleteUserService.new.execute(user)
respond_to do |format|
format.html { redirect_to admin_users_path }
......
......@@ -89,7 +89,7 @@ class ApplicationController < ActionController::Base
end
def after_sign_out_path_for(resource)
new_user_session_path
current_application_settings.after_sign_out_path || new_user_session_path
end
def abilities
......@@ -295,14 +295,14 @@ class ApplicationController < ActionController::Base
def get_issues_collection
set_filters_params
issues = IssuesFinder.new.execute(current_user, @filter_params)
issues
@issuable_finder = IssuesFinder.new(current_user, @filter_params)
@issuable_finder.execute
end
def get_merge_requests_collection
set_filters_params
merge_requests = MergeRequestsFinder.new.execute(current_user, @filter_params)
merge_requests
@issuable_finder = MergeRequestsFinder.new(current_user, @filter_params)
@issuable_finder.execute
end
def github_import_enabled?
......
......@@ -82,7 +82,11 @@ class Groups::GroupMembersController < Groups::ApplicationController
redirect_to(dashboard_groups_path, notice: "You left #{group.name} group.")
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
......
......@@ -84,7 +84,7 @@ class GroupsController < Groups::ApplicationController
end
def destroy
@group.destroy
DestroyGroupService.new(@group, current_user).execute
redirect_to root_path, notice: 'Group was removed.'
end
......
class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
include Gitlab::CurrentSettings
include PageLayoutHelper
before_action :verify_user_oauth_applications_enabled
before_action :authenticate_user!
layout 'profile'
......@@ -32,6 +34,12 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
private
def verify_user_oauth_applications_enabled
return if current_application_settings.user_oauth_applications?
redirect_to applications_profile_url
end
def set_application
@application = current_user.oauth_applications.find(params[:id])
end
......
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
include AuthenticatesWithTwoFactor
protect_from_forgery except: :kerberos
protect_from_forgery except: [:kerberos, :saml]
Gitlab.config.omniauth.providers.each do |provider|
define_method provider['name'] do
......@@ -81,7 +81,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
end
flash[:notice] = message
redirect_to new_user_session_path
end
......
......@@ -36,4 +36,24 @@ class PasswordsController < Devise::PasswordsController
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
class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
def new
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!
end
......@@ -18,6 +18,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
else
@error = 'Invalid pin code'
@qr_code = build_qr_code
render 'new'
end
end
......
......@@ -18,10 +18,6 @@ class Projects::DeployKeysController < Projects::ApplicationController
@available_public_keys -= @available_project_keys
end
def show
@key = @project.deploy_keys.find(params[:id])
end
def new
@key = @project.deploy_keys.new
......
......@@ -53,6 +53,6 @@ class Projects::HooksController < Projects::ApplicationController
end
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
......@@ -19,7 +19,15 @@ class Projects::IssuesController < Projects::ApplicationController
def index
terms = params['issue_search']
@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)
respond_to do |format|
......
......@@ -2,10 +2,13 @@ require 'gitlab/satellite/satellite'
class Projects::MergeRequestsController < Projects::ApplicationController
before_action :module_enabled
before_action :merge_request, only: [:edit, :update, :show, :diffs, :automerge, :automerge_check, :ci_status, :toggle_subscription]
before_action :closes_issues, only: [:edit, :update, :show, :diffs]
before_action :validates_merge_request, only: [:show, :diffs]
before_action :define_show_vars, only: [:show, :diffs]
before_action :merge_request, only: [
:edit, :update, :show, :diffs, :commits, :automerge, :automerge_check,
:ci_status, :toggle_subscription
]
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
before_action :authorize_read_merge_request!
......@@ -19,7 +22,15 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def index
terms = params['issue_search']
@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)
respond_to do |format|
......@@ -59,6 +70,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
end
def commits
render 'show'
end
def new
params[:merge_request] ||= ActionController::Parameters.new(source_project: @project)
@merge_request = MergeRequests::BuildService.new(project, current_user, merge_request_params).execute
......
......@@ -84,12 +84,16 @@ class Projects::ProjectMembersController < Projects::ApplicationController
end
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.destroy
log_audit_event(@project_member, action: :destroy)
respond_to do |format|
format.html { redirect_to :back }
format.html { redirect_to dashboard_path }
format.js { render nothing: true }
end
end
......
......@@ -97,18 +97,15 @@ class ProjectsController < ApplicationController
return access_denied! unless can?(current_user, :remove_project, @project)
::Projects::DestroyService.new(@project, current_user, {}).execute
flash[:alert] = 'Project deleted.'
respond_to do |format|
format.html do
flash[:alert] = 'Project deleted.'
if request.referer.include?('/admin')
redirect_to admin_namespaces_projects_path
else
redirect_to dashboard_path
end
end
if request.referer.include?('/admin')
redirect_to admin_namespaces_projects_path
else
redirect_to dashboard_path
end
rescue Projects::DestroyService::DestroyError => ex
redirect_to edit_project_path(@project), alert: ex.message
end
def autocomplete_sources
......
......@@ -6,7 +6,7 @@ class RegistrationsController < Devise::RegistrationsController
end
def destroy
current_user.destroy
DeleteUserService.new.execute(current_user)
respond_to do |format|
format.html { redirect_to new_user_session_path, notice: "Account successfully removed." }
......
......@@ -2,6 +2,7 @@ class SessionsController < Devise::SessionsController
include AuthenticatesWithTwoFactor
prepend_before_action :authenticate_with_two_factor, only: [:create]
before_action :auto_sign_in_with_provider, only: [:new]
def new
redirect_path =
......@@ -75,6 +76,21 @@ class SessionsController < Devise::SessionsController
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)
user.valid_otp?(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)
Better use this:
```ruby
issues = IssuesFinder.new.execute(project, user, filter)
issues = IssuesFinder.new(project, user, filter).execute
```
It will help keep models thiner.
......@@ -23,10 +23,12 @@ class IssuableFinder
attr_accessor :current_user, :params
def execute(current_user, params)
def initialize(current_user, params)
@current_user = current_user
@params = params
end
def execute
items = init_collection
items = by_scope(items)
items = by_state(items)
......@@ -40,6 +42,77 @@ class IssuableFinder
items = sort(items)
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
def init_collection
......@@ -75,6 +148,10 @@ class IssuableFinder
case params[:state]
when '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'
items
when 'opened'
......@@ -85,25 +162,19 @@ class IssuableFinder
end
def by_group(items)
if params[:group_id].present?
items = items.of_group(Group.find(params[:group_id]))
end
items = items.of_group(group) if group
items
end
def by_project(items)
if params[:project_id].present?
items = items.of_projects(params[:project_id])
end
items = items.of_projects(project.id) if project
items
end
def by_search(items)
if params[:search].present?
items = items.search(params[:search])
end
items = items.search(search) if search
items
end
......@@ -113,25 +184,24 @@ class IssuableFinder
end
def by_milestone(items)
if params[:milestone_title].present?
milestone_ids = (params[:milestone_title] == NONE ? nil : Milestone.where(title: params[:milestone_title]).pluck(:id))
items = items.where(milestone_id: milestone_ids)
if milestones?
items = items.where(milestone_id: milestones.try(:pluck, :id))
end
items
end
def by_assignee(items)
if params[:assignee_id].present?
items = items.where(assignee_id: (params[:assignee_id] == NONE ? nil : params[:assignee_id]))
if assignee?
items = items.where(assignee_id: assignee.try(:id))
end
items
end
def by_author(items)
if params[:author_id].present?
items = items.where(author_id: (params[:author_id] == NONE ? nil : params[:author_id]))
if author?
items = items.where(author_id: author.try(:id))
end
items
......@@ -151,10 +221,6 @@ class IssuableFinder
items
end
def project
Project.where(id: params[:project_id]).first if params[:project_id].present?
end
def current_user_related?
params[:scope] == 'created-by-me' || params[:scope] == 'authored' || params[:scope] == 'assigned-to-me'
end
......
......@@ -228,18 +228,28 @@ module ApplicationHelper
end
def render_markup(file_name, file_content)
GitHub::Markup.render(file_name, file_content).
force_encoding(file_content.encoding).html_safe
if gitlab_markdown?(file_name)
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
simple_format(file_content)
end
def markup?(filename)
Gitlab::MarkdownHelper.markup?(filename)
Gitlab::MarkupHelper.markup?(filename)
end
def gitlab_markdown?(filename)
Gitlab::MarkdownHelper.gitlab_markdown?(filename)
Gitlab::MarkupHelper.gitlab_markdown?(filename)
end
def asciidoc?(filename)
Gitlab::MarkupHelper.asciidoc?(filename)
end
# Overrides ActionView::Helpers::UrlHelper#link_to to add `rel="nofollow"` to
......@@ -275,10 +285,6 @@ module ApplicationHelper
html_options
end
def escaped_autolink(text)
auto_link ERB::Util.html_escape(text), link: :urls
end
def promo_host
'about.gitlab.com'
end
......@@ -326,7 +332,12 @@ module ApplicationHelper
end
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 =
if project.nil?
......
......@@ -23,6 +23,10 @@ module ApplicationSettingsHelper
current_application_settings.help_text
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
# toggle button effect.
def restricted_level_checkboxes(help_block_id)
......
module BlobHelper
def highlight(blob_name, blob_content, nowrap = false)
formatter = Rugments::Formatters::HTML.new(
def highlight(blob_name, blob_content, nowrap: false, continue: false)
@formatter ||= Rugments::Formatters::HTML.new(
nowrap: nowrap,
cssclass: 'code highlight',
lineanchors: true,
......@@ -8,12 +8,14 @@ module BlobHelper
)
begin
lexer = Rugments::Lexer.guess(filename: blob_name, source: blob_content)
rescue Rugments::Lexer::AmbiguousGuess
@lexer ||= Rugments::Lexer.guess(filename: blob_name, source: blob_content).new
result = @formatter.format(@lexer.lex(blob_content, continue: continue)).html_safe
rescue
lexer = Rugments::Lexers::PlainText
result = @formatter.format(lexer.lex(blob_content)).html_safe
end
formatter.format(lexer.lex(blob_content)).html_safe
result
end
def no_highlight_files
......@@ -55,7 +57,7 @@ module BlobHelper
end
def editing_preview_title(filename)
if Gitlab::MarkdownHelper.previewable?(filename)
if Gitlab::MarkupHelper.previewable?(filename)
'Preview'
else
'Preview changes'
......
......@@ -101,6 +101,10 @@ module DiffHelper
(bottom) ? 'js-unfold-bottom' : ''
end
def unfold_class(unfold)
(unfold) ? 'unfold js-unfold' : ''
end
def diff_line_content(line)
if line.blank?
" &nbsp;"
......
......@@ -35,4 +35,23 @@ module EmailsHelper
lexer = Rugments::Lexers::Diff.new
raw formatter.format(lexer.lex(diffcontent))
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
require 'nokogiri'
module GitlabMarkdownHelper
include Gitlab::Markdown
......@@ -21,41 +23,59 @@ module GitlabMarkdownHelper
gfm_body = gfm(escaped_body, {}, html_options)
gfm_body.gsub!(%r{<a.*?>.*?</a>}m) do |match|
"</a>#{match}#{link_to("", url, html_options)[0..-5]}" # "</a>".length +1
fragment = Nokogiri::XML::DocumentFragment.parse(gfm_body)
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
link_to(gfm_body.html_safe, url, html_options)
fragment.to_html.html_safe
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={})
unless @markdown && 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
rend = Redcarpet::Render::GitlabHTML.new(self, user_color_scheme_class, options)
# see https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use
@markdown = Redcarpet::Markdown.new(rend,
no_intra_emphasis: true,
tables: true,
fenced_code_blocks: true,
strikethrough: true,
lax_spacing: true,
space_after_headers: true,
superscript: true,
footnotes: true
)
@markdown = Redcarpet::Markdown.new(rend, MARKDOWN_OPTIONS)
end
@markdown.render(text).html_safe
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
# 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
......@@ -67,8 +87,11 @@ module GitlabMarkdownHelper
end
def render_wiki_content(wiki_page)
if wiki_page.format == :markdown
case wiki_page.format
when :markdown
markdown(wiki_page.content)
when :asciidoc
asciidoc(wiki_page.content)
else
wiki_page.formatted_content.html_safe
end
......@@ -122,15 +145,25 @@ module GitlabMarkdownHelper
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)
path = project.path_with_namespace
if entity.kind_of?(Issue)
[path, entity.iid].join('#')
elsif entity.kind_of?(MergeRequest)
[path, entity.iid].join('!')
if entity.respond_to?(:to_reference)
"#{project.to_reference}#{entity.to_reference}"
else
raise 'Not supported type'
''
end
end
end
module LabelsHelper
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
@project.labels.pluck(:title)
end
......
......@@ -148,7 +148,7 @@ module ProjectsHelper
nav_tabs << [:files, :commits, :network, :graphs]
end
if project.repo_exists? && project.merge_requests_enabled
if project.repo_exists? && can?(current_user, :read_merge_request, project)
nav_tabs << :merge_requests
end
......@@ -156,11 +156,19 @@ module ProjectsHelper
nav_tabs << :settings
end
[:issues, :wiki, :snippets].each do |feature|
nav_tabs << feature if project.send :"#{feature}_enabled"
if can?(current_user, :read_issue, project)
nav_tabs << :issues
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]
end
......@@ -302,4 +310,16 @@ module ProjectsHelper
nil
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
......@@ -11,6 +11,7 @@ module SelectsHelper
any_user = opts[:any_user] || false
email_user = opts[:email_user] || false
first_user = opts[:first_user] && current_user ? current_user.username : false
project = opts[:project] || @project
html = {
class: css_class,
......@@ -22,8 +23,8 @@ module SelectsHelper
}
unless opts[:scope] == :all
if @project
html['data-project-id'] = @project.id
if project
html['data-project-id'] = project.id
elsif @group
html['data-group-id'] = @group.id
end
......
......@@ -25,13 +25,7 @@ module TreeHelper
end
def render_readme(readme)
if gitlab_markdown?(readme.name)
preserve(markdown(readme.data))
elsif markup?(readme.name)
render_markup(readme.name, readme.data)
else
simple_format(readme.data)
end
render_markup(readme.name, readme.data)
end
# Return an image icon depending on the file type and mode
......
......@@ -93,7 +93,8 @@ module Emails
"pushed to"
end
@subject = "[#{@project.path_with_namespace}]"
@subject = "[Git]"
@subject << "[#{@project.path_with_namespace}]"
@subject << "[#{@ref_name}]" if action == :push
@subject << " "
......
......@@ -117,6 +117,27 @@ class Ability
rules -= project_archived_rules
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
end
end
......@@ -288,5 +309,16 @@ class Ability
abilities
end
end
private
def named_abilities(name)
[
:"read_#{name}",
:"write_#{name}",
:"modify_#{name}",
:"admin_#{name}"
]
end
end
end
......@@ -19,6 +19,8 @@
# default_project_visibility :integer
# default_snippet_visibility :integer
# restricted_signup_domains :text
# user_oauth_applications :bool default(TRUE)
# after_sign_out_path :string(255)
#
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" },
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|
unless value.nil?
value.each do |level|
......
class Commit
include ActiveModel::Conversion
include StaticModel
extend ActiveModel::Naming
include ActiveModel::Conversion
include Mentionable
include Participable
include Referable
include StaticModel
attr_mentionable :safe_message
participant :author, :committer, :notes, :mentioned_users
......@@ -56,6 +58,34 @@ class Commit
@raw.id
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
@diff_line_count ||= Commit::diff_line_count(self.diffs)
@diff_line_count
......@@ -126,11 +156,6 @@ class Commit
Gitlab::ClosingIssueExtractor.new(project, current_user).closed_by_message(safe_message)
end
# Mentionable override.
def gfm_reference
"commit #{id}"
end
def author
User.find_for_commit(author_email, author_name)
end
......
......@@ -19,6 +19,7 @@
#
class CommitRange
include ActiveModel::Conversion
include Referable
attr_reader :sha_from, :notation, :sha_to
......@@ -28,10 +29,24 @@ class CommitRange
# See `exclude_start?`
attr_reader :exclude_start
# The beginning and ending SHA sums can be between 6 and 40 hex characters,
# and the range selection can be double- or triple-dot.
# The beginning and ending SHAs can be between 6 and 40 hex characters, and
# the range notation can be double- or triple-dot.
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
#
# range_string - The String commit range.
......@@ -59,6 +74,17 @@ class CommitRange
"#{sha_from[0..7]}#{notation}#{sha_to[0..7]}"
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
def reference_title
"Commits #{suffixed_sha_from} through #{sha_to}"
......
......@@ -20,10 +20,15 @@ module Mentionable
end
end
# Generate a GFM back-reference that will construct a link back to this Mentionable when rendered. Must
# be overridden if this model object can be referenced directly by GFM notation.
def gfm_reference
raise NotImplementedError.new("#{self.class} does not implement #gfm_reference")
# Returns the text used as the body of a Note when this object is referenced
#
# By default this will be the class name and the result of calling
# `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
# Construct a String that contains possible GFM references.
......
......@@ -35,8 +35,8 @@ module Participable
end
end
def participants(current_user = self.author)
self.class.participant_attrs.flat_map do |attr|
def participants(current_user = self.author, project = self.project)
participants = self.class.participant_attrs.flat_map do |attr|
meth = method(attr)
value =
......@@ -46,20 +46,28 @@ module Participable
meth.call
end
participants_for(value, current_user)
participants_for(value, current_user, project)
end.compact.uniq
if project
participants.select! do |user|
user.can?(:read_project, project)
end
end
participants
end
private
def participants_for(value, current_user = nil)
def participants_for(value, current_user = nil, project = nil)
case value
when User
[value]
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
value.participants(current_user)
value.participants(current_user, project)
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
include Referable
def initialize(issue_identifier, project)
@issue_identifier, @project = issue_identifier, project
end
......@@ -26,4 +28,13 @@ class ExternalIssue
def project
@project
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
......@@ -17,6 +17,8 @@ require 'carrierwave/orm/activerecord'
require 'file_size_validator'
class Group < Namespace
include Referable
has_many :group_members, dependent: :destroy, as: :source, class_name: 'GroupMember'
has_many :users, through: :group_members
has_many :project_group_links, dependent: :destroy
......@@ -40,6 +42,18 @@ class Group < Namespace
def sort(method)
order_by(method)
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
def human_name
......@@ -108,10 +122,14 @@ class Group < Namespace
end
def post_create_hook
Gitlab::AppLogger.info("Group \"#{name}\" was created")
system_hook_service.execute_hooks_for(self, :create)
end
def post_destroy_hook
Gitlab::AppLogger.info("Group \"#{name}\" was removed")
system_hook_service.execute_hooks_for(self, :destroy)
end
......
......@@ -44,7 +44,7 @@ class GroupMilestone
def percent_complete
((closed_items_count * 100) / total_items_count).abs
rescue ZeroDivisionError
100
0
end
def state
......
......@@ -13,6 +13,7 @@
# issues_events :boolean default(FALSE), not null
# merge_requests_events :boolean default(FALSE), not null
# tag_push_events :boolean default(FALSE)
# note_events :boolean default(FALSE), not null
#
class ProjectHook < WebHook
......@@ -21,5 +22,6 @@ class ProjectHook < WebHook
scope :push_hooks, -> { where(push_events: true) }
scope :tag_push_hooks, -> { where(tag_push_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) }
end
......@@ -13,6 +13,7 @@
# issues_events :boolean default(FALSE), not null
# merge_requests_events :boolean default(FALSE), not null
# tag_push_events :boolean default(FALSE)
# note_events :boolean default(FALSE), not null
#
class ServiceHook < WebHook
......
......@@ -13,6 +13,7 @@
# issues_events :boolean default(FALSE), not null
# merge_requests_events :boolean default(FALSE), not null
# tag_push_events :boolean default(FALSE)
# note_events :boolean default(FALSE), not null
#
class SystemHook < WebHook
......
......@@ -13,6 +13,7 @@
# issues_events :boolean default(FALSE), not null
# merge_requests_events :boolean default(FALSE), not null
# tag_push_events :boolean default(FALSE)
# note_events :boolean default(FALSE), not null
#
class WebHook < ActiveRecord::Base
......@@ -21,6 +22,7 @@ class WebHook < ActiveRecord::Base
default_value_for :push_events, true
default_value_for :issues_events, false
default_value_for :note_events, false
default_value_for :merge_requests_events, false
default_value_for :tag_push_events, false
......
......@@ -21,10 +21,11 @@ require 'carrierwave/orm/activerecord'
require 'file_size_validator'
class Issue < ActiveRecord::Base
include Issuable
include InternalId
include Taskable
include Issuable
include Referable
include Sortable
include Taskable
ActsAsTaggableOn.strict_case_match = true
......@@ -53,10 +54,28 @@ class Issue < ActiveRecord::Base
attributes
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
"issue ##{iid}"
reference
end
# Reset issue events cache
......
......@@ -11,6 +11,8 @@
#
class Label < ActiveRecord::Base
include Referable
DEFAULT_COLOR = '#428BCA'
default_value_for :color, DEFAULT_COLOR
......@@ -34,6 +36,45 @@ class Label < ActiveRecord::Base
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
issues.opened.count
end
......
......@@ -25,10 +25,11 @@ require Rails.root.join("app/models/commit")
require Rails.root.join("lib/static_model")
class MergeRequest < ActiveRecord::Base
include Issuable
include Taskable
include InternalId
include Issuable
include Referable
include Sortable
include Taskable
belongs_to :target_project, foreign_key: :target_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
# Closed scope for merge request should return
# both merged and closed mr's
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
if target_project == source_project && target_branch == source_branch
......@@ -175,7 +200,6 @@ class MergeRequest < ActiveRecord::Base
def update_merge_request_diff
if source_branch_changed? || target_branch_changed?
reload_code
mark_as_unchecked
end
end
......@@ -292,11 +316,6 @@ class MergeRequest < ActiveRecord::Base
end
end
# Mentionable override.
def gfm_reference
"merge request !#{iid}"
end
def target_project_path
if target_project
target_project.path_with_namespace
......
......@@ -66,7 +66,7 @@ class Milestone < ActiveRecord::Base
def percent_complete
((closed_items_count * 100) / total_items_count).abs
rescue ZeroDivisionError
100
0
end
def expires_at
......
......@@ -72,7 +72,7 @@ class Namespace < ActiveRecord::Base
path.gsub!(/[^a-zA-Z0-9_\-\.]/, "")
# 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?
counter = 0
......@@ -99,7 +99,18 @@ class Namespace < ActiveRecord::Base
end
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
def move_dir
......
......@@ -326,8 +326,8 @@ class Note < ActiveRecord::Base
end
# Mentionable override.
def gfm_reference
noteable.gfm_reference
def gfm_reference(from_project = nil)
noteable.gfm_reference(from_project)
end
# Mentionable override.
......
......@@ -33,11 +33,12 @@ require 'carrierwave/orm/activerecord'
require 'file_size_validator'
class Project < ActiveRecord::Base
include Sortable
include Gitlab::ConfigHelper
include Gitlab::ShellAdapter
include Gitlab::VisibilityLevel
include Gitlab::ConfigHelper
include Rails.application.routes.url_helpers
include Referable
include Sortable
extend Gitlab::ConfigHelper
extend Enumerize
......@@ -252,6 +253,11 @@ class Project < ActiveRecord::Base
order_by(method)
end
end
def reference_pattern
name_pattern = Gitlab::Regex::NAMESPACE_REGEX_STR
%r{(?<project>#{name_pattern}/#{name_pattern})}
end
end
def team
......@@ -310,6 +316,10 @@ class Project < ActiveRecord::Base
path
end
def to_reference(_from_project = nil)
path_with_namespace
end
def web_url
[gitlab_config.url, path_with_namespace].join('/')
end
......
......@@ -40,6 +40,12 @@ class GitlabCiService < CiService
def execute(data)
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)
end
......@@ -123,6 +129,14 @@ class GitlabCiService < CiService
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
project_url.sub(/projects\/\d*/, "#{API_PREFIX}/forks")
end
......
......@@ -63,7 +63,7 @@ class HipchatService < Service
private
def gate
options = { api_version: api_version || 'v2' }
options = { api_version: api_version.present? ? api_version : 'v2' }
options[:server_url] = server unless server.blank?
@gate ||= HipChat::Client.new(token, options)
end
......
......@@ -2,7 +2,7 @@ class ProjectWiki
include Gitlab::ShellAdapter
MARKUPS = {
'Markdown' => :markdown,
'Markdown' => :md,
'RDoc' => :rdoc,
'AsciiDoc' => :asciidoc
} unless defined?(MARKUPS)
......
......@@ -370,8 +370,55 @@ class Repository
@root_ref ||= raw_repository.root_ref
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
def user_to_comitter(user)
{
email: user.email,
name: user.name,
time: Time.now
}
end
def cache
@cache ||= RepositoryCache.new(path_with_namespace)
end
......
......@@ -16,14 +16,16 @@
#
class Snippet < ActiveRecord::Base
include Sortable
include Linguist::BlobHelper
include Gitlab::VisibilityLevel
include Linguist::BlobHelper
include Participable
include Referable
include Sortable
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
......@@ -50,6 +52,30 @@ class Snippet < ActiveRecord::Base
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
[
".rb", ".py", ".pl", ".scala", ".c", ".cpp", ".java",
......
class Tree
include Gitlab::MarkdownHelper
include Gitlab::MarkupHelper
attr_accessor :repository, :sha, :path, :entries
def initialize(repository, sha, path = '/')
path = '/' if path.blank?
@repository = repository
@sha = sha
@path = path
......@@ -20,7 +20,7 @@ class Tree
available_readmes = blobs.select(&:readme?)
if available_readmes.count == 0
return @readme = nil
return @readme = nil
end
# Take the first previewable readme, or the first available readme, if we
......
......@@ -62,11 +62,13 @@ require 'carrierwave/orm/activerecord'
require 'file_size_validator'
class User < ActiveRecord::Base
include Sortable
include Gitlab::ConfigHelper
include TokenAuthenticatable
extend Gitlab::ConfigHelper
include Gitlab::ConfigHelper
include Gitlab::CurrentSettings
include Referable
include Sortable
include TokenAuthenticatable
default_value_for :admin, false
default_value_for :can_create_group, gitlab_config.default_can_create_group
......@@ -274,6 +276,18 @@ class User < ActiveRecord::Base
username
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
#
......@@ -284,6 +298,10 @@ class User < ActiveRecord::Base
username
end
def to_reference(_from_project = nil)
"#{self.class.reference_prefix}#{username}"
end
def notification
@notification ||= Notification.new(self)
end
......@@ -493,7 +511,7 @@ class User < ActiveRecord::Base
end
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)
self.send("#{attr}=", Sanitize.clean(value)) if value.present?
end
......@@ -669,6 +687,12 @@ class User < ActiveRecord::Base
end
end
def namespaces
namespace_ids = groups.pluck(:id)
namespace_ids.push(namespace.id)
Namespace.where(id: namespace_ids)
end
def oauth_authorized_tokens
Doorkeeper::AccessToken.where(resource_owner_id: self.id, revoked_at: nil)
end
......@@ -703,4 +727,8 @@ class User < ActiveRecord::Base
true
end
def can_be_removed?
!solo_owned_groups.present?
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
def git_hook
project.git_hook
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
require_relative "base_service"
module Files
class CreateService < BaseService
class CreateService < Files::BaseService
def execute
allowed = Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(ref)
......@@ -37,16 +37,24 @@ module Files
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)
created_successfully = new_file_action.commit!(
params[:content],
sha = repository.commit_file(
current_user,
file_path,
content,
params[:commit_message],
params[:encoding],
params[:new_branch]
params[:new_branch] || ref
)
if created_successfully
if sha
after_commit(sha)
success
else
error("Your changes could not be committed, because the file has been changed")
......
require_relative "base_service"
module Files
class DeleteService < BaseService
class DeleteService < Files::BaseService
def execute
allowed = ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(ref)
......@@ -23,14 +23,15 @@ module Files
return error("You can only edit text files")
end
delete_file_action = Gitlab::Satellite::DeleteFileAction.new(current_user, project, ref, path)
deleted_successfully = delete_file_action.commit!(
nil,
params[:commit_message]
sha = repository.remove_file(
current_user,
path,
params[:commit_message],
ref
)
if deleted_successfully
if sha
after_commit(sha)
success
else
error("Your changes could not be committed, because the file has been changed")
......
require_relative "base_service"
module Files
class UpdateService < BaseService
class UpdateService < Files::BaseService
def execute
allowed = ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(ref)
......@@ -23,14 +23,22 @@ module Files
return error("You can only edit text files")
end
edit_file_action = Gitlab::Satellite::EditFileAction.new(current_user, project, ref, path)
edit_file_action.commit!(
params[:content],
content =
if params[:encoding] == 'base64'
Base64.decode64(params[:content])
else
params[:content]
end
sha = repository.commit_file(
current_user,
path,
content,
params[:commit_message],
params[:encoding],
params[:new_branch]
params[:new_branch] || ref
)
after_commit(sha)
success
rescue Gitlab::Satellite::CheckoutFailed => ex
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