Commit b48b0704 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'master' into drop-satellites

Signed-off-by: default avatarDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
parents c17f5d06 326b827c
Please view this file on the master branch, on stable branches it's out of date. Please view this file on the master branch, on stable branches it's out of date.
v 7.13.0 (unreleased) v 7.14.0 (unreleased)
- Fix multi-line syntax highlighting (Stan Hu)
- Fix network graph when branch name has single quotes (Stan Hu)
- Upgrade gitlab_git to version 7.2.6 to fix Error 500 when creating network graphs (Stan Hu)
- Add support for Unicode filenames in relative links (Hiroyuki Sato)
- Fix URL used for refreshing notes if relative_url is present (Bartłomiej Święcki)
- Fix commit data retrieval when branch name has single quotes (Stan Hu)
- Check that project was actually created rather than just validated in import:repos task (Stan Hu)
- Fix full screen mode for snippet comments (Daniel Gerhardt)
- Fix 404 error in files view after deleting the last file in a repository (Stan Hu)
- Fix the "Reload with full diff" URL button (Stan Hu)
- Fix label read access for unauthenticated users (Daniel Gerhardt)
- Fix access to disabled features for unauthenticated users (Daniel Gerhardt)
- Fix OAuth provider bug where GitLab would not go return to the redirect_uri after sign-in (Stan Hu)
- Fix file upload dialog for comment editing (Daniel Gerhardt)
- Set OmniAuth full_host parameter to ensure redirect URIs are correct (Stan Hu)
- Expire Rails cache entries after two weeks to prevent endless Redis growth
- Add support for destroying project milestones (Stan Hu)
- Add fetch command to the MR page.
- Allow custom backup archive permissions
- Add fetch command to the MR page
- Add project star and fork count, group avatar URL and user/group web URL attributes to API
- Fix bug causing Bitbucket importer to crash when OAuth application had been removed.
- Add fetch command to the MR page.
- Add ability to manage user email addresses via the API.
- Show buttons to add license, changelog and contribution guide if they're missing.
- Disabled autocapitalize and autocorrect on login field (Daryl Chan)
- Mention group and project name in creation, update and deletion notices (Achilleas Pipinellis)
- Remove redis-store TTL monkey patch
v 7.13.2
- Fix randomly failed spec
- Create project services on Project creation
- Add admin_merge_request ability to Developer level and up
- Fix Error 500 when browsing projects with no HEAD (Stan Hu)
- Fix labels / assignee / milestone for the merge requests when issues are disabled
- Show the first tab automatically on MergeRequests#new
- Add rake task 'gitlab:update_commit_count' (Daniel Gerhardt)
- Fix Gmail Actions
v 7.13.1
- Fix: Label modifications are not reflected in existing notes and in the issue list
- Fix: Label not shown in the Issue list, although it's set through web interface
- Fix: Group/project references are linked incorrectly
- Improve documentation
- Fix of migration: Check if session_expire_delay column exists before adding the column
- Fix: ActionView::Template::Error
- Fix: "Create Merge Request" isn't always shown in event for newly pushed branch
- Fix bug causing "Remove source-branch" option not to work for merge requests from the same project.
- Render Note field hints consistently for "new" and "edit" forms
v 7.13.0
- Remove repository graph log to fix slow cache updates after push event (Stan Hu)
- Return comments in created order in merge request API (Stan Hu)
- Only enable HSTS header for HTTPS and port 443 (Stan Hu) - Only enable HSTS header for HTTPS and port 443 (Stan Hu)
- Fix user autocomplete for unauthenticated users accessing public projects (Stan Hu) - Fix user autocomplete for unauthenticated users accessing public projects (Stan Hu)
- Fix redirection to home page URL for unauthorized users (Daniel Gerhardt) - Fix redirection to home page URL for unauthorized users (Daniel Gerhardt)
...@@ -46,12 +99,15 @@ v 7.13.0 (unreleased) ...@@ -46,12 +99,15 @@ v 7.13.0 (unreleased)
- Faster code search in repository and wiki. Fixes search page timeout for big repositories - Faster code search in repository and wiki. Fixes search page timeout for big repositories
- Allow administrators to disable 2FA for a specific user - Allow administrators to disable 2FA for a specific user
- Add error message for SSH key linebreaks - Add error message for SSH key linebreaks
- Store commits count in database (will populate with valid values only after first push)
- Rebuild cache after push to repository in background job
v 7.12.2 v 7.12.2
- Correctly show anonymous authorized applications under Profile > Applications. - Correctly show anonymous authorized applications under Profile > Applications.
- Faster automerge check and merge itself when source and target branches are in same repository - Faster automerge check and merge itself when source and target branches are in same repository
- Audit log for user authentication - Audit log for user authentication
- Fix transferring of project to another group using the API. - Fix transferring of project to another group using the API.
- Allow custom label to be set for authentication providers.
v 7.12.1 v 7.12.1
- Fix error when deleting a user who has projects (Stan Hu) - Fix error when deleting a user who has projects (Stan Hu)
...@@ -121,6 +177,7 @@ v 7.12.0 ...@@ -121,6 +177,7 @@ v 7.12.0
- Improve group removing logic - Improve group removing logic
- Trigger create-hooks on backup restore task - Trigger create-hooks on backup restore task
- Add option to automatically link omniauth and LDAP identities - Add option to automatically link omniauth and LDAP identities
- Allow special character in users bio. I.e.: I <3 GitLab
v 7.11.4 v 7.11.4
- Fix missing bullets when creating lists - Fix missing bullets when creating lists
...@@ -139,9 +196,6 @@ v 7.11.1 ...@@ -139,9 +196,6 @@ v 7.11.1
v 7.11.0 v 7.11.0
- Fall back to Plaintext when Syntaxhighlighting doesn't work. Fixes some buggy lexers (Hannes Rosenögger) - 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. - Get editing comments to work in Chrome 43 again.
- Allow special character in users bio. I.e.: I <3 GitLab
v 7.11.0
- Fix broken view when viewing history of a file that includes a path that used to be another file (Stan Hu) - Fix broken view when viewing history of a file that includes a path that used to be another file (Stan Hu)
- Don't show duplicate deploy keys - Don't show duplicate deploy keys
- Fix commit time being displayed in the wrong timezone in some cases (Hannes Rosenögger) - Fix commit time being displayed in the wrong timezone in some cases (Hannes Rosenögger)
......
...@@ -38,7 +38,7 @@ gem "browser", '~> 0.8.0' ...@@ -38,7 +38,7 @@ gem "browser", '~> 0.8.0'
# Extracting information from a git repository # Extracting information from a git repository
# Provide access to Gitlab::Git library # Provide access to Gitlab::Git library
gem "gitlab_git", '~> 7.2.5' gem "gitlab_git", '~> 7.2.6'
# Ruby/Rack Git Smart-HTTP Server Handler # Ruby/Rack Git Smart-HTTP Server Handler
# GitLab fork with a lot of changes (improved thread-safety, better memory usage etc) # GitLab fork with a lot of changes (improved thread-safety, better memory usage etc)
...@@ -227,7 +227,7 @@ end ...@@ -227,7 +227,7 @@ end
group :development, :test do group :development, :test do
gem 'awesome_print' gem 'awesome_print'
gem 'byebug' gem 'byebug', platform: :mri
gem 'fuubar', '~> 2.0.0' gem 'fuubar', '~> 2.0.0'
gem 'pry-rails' gem 'pry-rails'
...@@ -272,4 +272,3 @@ end ...@@ -272,4 +272,3 @@ end
gem "newrelic_rpm" gem "newrelic_rpm"
gem 'octokit', '3.7.0' gem 'octokit', '3.7.0'
gem "rugments", "~> 1.0.0.beta8"
...@@ -271,7 +271,7 @@ GEM ...@@ -271,7 +271,7 @@ GEM
mime-types (~> 1.19) mime-types (~> 1.19)
gitlab_emoji (0.1.0) gitlab_emoji (0.1.0)
gemojione (~> 2.0) gemojione (~> 2.0)
gitlab_git (7.2.5) gitlab_git (7.2.6)
activesupport (~> 4.0) activesupport (~> 4.0)
charlock_holmes (~> 0.6) charlock_holmes (~> 0.6)
gitlab-linguist (~> 3.0) gitlab-linguist (~> 3.0)
...@@ -288,7 +288,7 @@ GEM ...@@ -288,7 +288,7 @@ GEM
github-markup (~> 1.3.1) github-markup (~> 1.3.1)
gollum-grit_adapter (~> 0.1, >= 0.1.1) gollum-grit_adapter (~> 0.1, >= 0.1.1)
nokogiri (~> 1.6.4) nokogiri (~> 1.6.4)
rouge (~> 1.7.4) rouge (~> 1.9)
sanitize (~> 2.1.0) sanitize (~> 2.1.0)
stringex (~> 2.5.1) stringex (~> 2.5.1)
gon (5.0.1) gon (5.0.1)
...@@ -508,7 +508,7 @@ GEM ...@@ -508,7 +508,7 @@ GEM
rdoc (3.12.2) rdoc (3.12.2)
json (~> 1.4) json (~> 1.4)
redcarpet (3.3.2) redcarpet (3.3.2)
redis (3.1.0) redis (3.2.1)
redis-actionpack (4.0.0) redis-actionpack (4.0.0)
actionpack (~> 4) actionpack (~> 4)
redis-rack (~> 1.5.0) redis-rack (~> 1.5.0)
...@@ -525,7 +525,7 @@ GEM ...@@ -525,7 +525,7 @@ GEM
redis-actionpack (~> 4) redis-actionpack (~> 4)
redis-activesupport (~> 4) redis-activesupport (~> 4)
redis-store (~> 1.1.0) redis-store (~> 1.1.0)
redis-store (1.1.4) redis-store (1.1.6)
redis (>= 2.2) redis (>= 2.2)
request_store (1.0.5) request_store (1.0.5)
rerun (0.10.0) rerun (0.10.0)
...@@ -536,7 +536,7 @@ GEM ...@@ -536,7 +536,7 @@ GEM
netrc (~> 0.7) netrc (~> 0.7)
rinku (1.7.3) rinku (1.7.3)
rotp (1.6.1) rotp (1.6.1)
rouge (1.7.7) rouge (1.9.1)
rqrcode (0.4.2) rqrcode (0.4.2)
rqrcode-rails3 (0.1.7) rqrcode-rails3 (0.1.7)
rqrcode (>= 0.4.2) rqrcode (>= 0.4.2)
...@@ -579,7 +579,6 @@ GEM ...@@ -579,7 +579,6 @@ GEM
rubyntlm (0.5.0) rubyntlm (0.5.0)
rubypants (0.2.0) rubypants (0.2.0)
rugged (0.22.2) rugged (0.22.2)
rugments (1.0.0.beta8)
safe_yaml (1.0.4) safe_yaml (1.0.4)
sanitize (2.1.0) sanitize (2.1.0)
nokogiri (>= 1.4.4) nokogiri (>= 1.4.4)
...@@ -784,7 +783,7 @@ DEPENDENCIES ...@@ -784,7 +783,7 @@ DEPENDENCIES
gitlab-grack (~> 2.0.2) gitlab-grack (~> 2.0.2)
gitlab-linguist (~> 3.0.1) gitlab-linguist (~> 3.0.1)
gitlab_emoji (~> 0.1) gitlab_emoji (~> 0.1)
gitlab_git (~> 7.2.5) gitlab_git (~> 7.2.6)
gitlab_meta (= 7.0) gitlab_meta (= 7.0)
gitlab_omniauth-ldap (= 1.2.1) gitlab_omniauth-ldap (= 1.2.1)
gollum-lib (~> 4.0.2) gollum-lib (~> 4.0.2)
...@@ -836,7 +835,6 @@ DEPENDENCIES ...@@ -836,7 +835,6 @@ DEPENDENCIES
rqrcode-rails3 rqrcode-rails3
rspec-rails (~> 3.3.0) rspec-rails (~> 3.3.0)
rubocop (= 0.28.0) rubocop (= 0.28.0)
rugments (~> 1.0.0.beta8)
sanitize (~> 2.0) sanitize (~> 2.0)
sass-rails (~> 4.0.5) sass-rails (~> 4.0.5)
sdoc sdoc
......
# GitLab
[![build status](https://ci.gitlab.com/projects/1/status.png?ref=master)](https://ci.gitlab.com/projects/1?ref=master)
[![Build Status](https://semaphoreapp.com/api/v1/projects/2f1a5809-418b-4cc2-a1f4-819607579fe7/243338/badge.png)](https://semaphoreapp.com/gitlabhq/gitlabhq)
[![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.svg)](https://codeclimate.com/github/gitlabhq/gitlabhq)
[![Coverage Status](https://coveralls.io/repos/gitlabhq/gitlabhq/badge.png?branch=master)](https://coveralls.io/r/gitlabhq/gitlabhq?branch=master)
## Canonical source ## Canonical source
The source of GitLab Community Edition is [hosted on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/) and there are mirrors to make [contributing](CONTRIBUTING.md) as easy as possible. The source of GitLab Community Edition is [hosted on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/) and there are mirrors to make [contributing](CONTRIBUTING.md) as easy as possible.
# ![logo](https://about.gitlab.com/images/gitlab_logo.png) GitLab
## Open source software to collaborate on code ## Open source software to collaborate on code
To see how GitLab looks please see the [features page on our website](https://about.gitlab.com/features/). To see how GitLab looks please see the [features page on our website](https://about.gitlab.com/features/).
...@@ -17,21 +22,12 @@ To see how GitLab looks please see the [features page on our website](https://ab ...@@ -17,21 +22,12 @@ To see how GitLab looks please see the [features page on our website](https://ab
## Editions ## Editions
There are two editions of GitLab. 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 use EE and get official support please [become a subscriber](https://about.gitlab.com/pricing/).
## Code status
- [![build status](https://ci.gitlab.com/projects/1/status.png?ref=master)](https://ci.gitlab.com/projects/1?ref=master) on ci.gitlab.com (master branch)
- [![Build Status](https://semaphoreapp.com/api/v1/projects/2f1a5809-418b-4cc2-a1f4-819607579fe7/243338/badge.png)](https://semaphoreapp.com/gitlabhq/gitlabhq) - GitLab Community Edition (CE) is available freely under the MIT Expat license.
- GitLab Enterprise Edition (EE) includes [extra features](https://about.gitlab.com/features/#compare) that are more useful for organizations with more than 100 users. To use EE and get official support please [become a subscriber](https://about.gitlab.com/pricing/).
- [![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.svg)](https://codeclimate.com/github/gitlabhq/gitlabhq) Included with the GitLab Omnibus Packages is [GitLab CI](https://about.gitlab.com/gitlab-ci/) that can easily build, test and deploy code.
- [![Coverage Status](https://coveralls.io/repos/gitlabhq/gitlabhq/badge.png?branch=master)](https://coveralls.io/r/gitlabhq/gitlabhq?branch=master)
## Website ## Website
...@@ -46,24 +42,40 @@ On [about.gitlab.com](https://about.gitlab.com/) you can find more information a ...@@ -46,24 +42,40 @@ On [about.gitlab.com](https://about.gitlab.com/) you can find more information a
## Requirements ## Requirements
GitLab requires the following software:
- Ubuntu/Debian/CentOS/RHEL
- Ruby (MRI) 2.0 or 2.1
- Git 1.7.10+
- Redis 2.0+
- MySQL or PostgreSQL
Please see the [requirements documentation](doc/install/requirements.md) for system requirements and more information about the supported operating systems. Please see the [requirements documentation](doc/install/requirements.md) for system requirements and more information about the supported operating systems.
## Installation ## Installation
The recommended way to install GitLab is using the provided [Omnibus packages](https://about.gitlab.com/downloads/). Compared to an installation from source, this is faster and less error prone. Just select your operating system, download the respective package (Debian or RPM) and install it using the system's package manager. The recommended way to install GitLab is with the [Omnibus packages](https://about.gitlab.com/downloads/) on our package server.
Compared to an installation from source, this is faster and less error prone.
Just select your operating system, download the respective package (Debian or RPM) and install it using the system's package manager.
There are various other options to install GitLab, please refer to the [installation page on the GitLab website](https://about.gitlab.com/installation/) for more information. There are various other options to install GitLab, please refer to the [installation page on the GitLab website](https://about.gitlab.com/installation/) for more information.
You can access a new installation with the login **`root`** and password **`5iveL!fe`**, after login you are required to set a unique password. You can access a new installation with the login **`root`** and password **`5iveL!fe`**, after login you are required to set a unique password.
## Install a development environment
To work on GitLab itself, we recommend setting up your development environment with [the GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit).
If you do not use the GitLab Development Kit you need to install and setup all the dependencies yourself, this is a lot of work and error prone.
One small thing you also have to do when installing it yourself is to copy the example development unicorn configuration file:
cp config/unicorn.rb.example.development config/unicorn.rb
Instructions on how to start GitLab and how to run the tests can be found in the [development section of the GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit#development).
## Software stack
GitLab is a Ruby on Rails application that runs on the following software:
- Ubuntu/Debian/CentOS/RHEL
- Ruby (MRI) 2.0 or 2.1
- Git 1.7.10+
- Redis 2.0+
- MySQL or PostgreSQL
For more information please see the [architecture documentation](http://doc.gitlab.com/ce/development/architecture.html).
## Third-party applications ## Third-party applications
There are a lot of [third-party applications integrating with GitLab](https://about.gitlab.com/applications/). These include GUI Git clients, mobile applications and API wrappers for various languages. There are a lot of [third-party applications integrating with GitLab](https://about.gitlab.com/applications/). These include GUI Git clients, mobile applications and API wrappers for various languages.
...@@ -76,16 +88,6 @@ Since 2011 a minor or major version of GitLab is released on the 22nd of every m ...@@ -76,16 +88,6 @@ Since 2011 a minor or major version of GitLab is released on the 22nd of every m
For upgrading information please see our [update page](https://about.gitlab.com/update/). For upgrading information please see our [update page](https://about.gitlab.com/update/).
## Install a development environment
To work on GitLab itself, we recommend setting up your development environment with [the GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit).
If you do not use the GitLab Development Kit you need to install and setup all the dependencies yourself, this is a lot of work and error prone.
One small thing you also have to do when installing it yourself is to copy the example development unicorn configuration file:
cp config/unicorn.rb.example.development config/unicorn.rb
Instructions on how to start GitLab and how to run the tests can be found in the [development section of the GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit#development).
## Documentation ## Documentation
All documentation can be found on [doc.gitlab.com/ce/](http://doc.gitlab.com/ce/). All documentation can be found on [doc.gitlab.com/ce/](http://doc.gitlab.com/ce/).
......
...@@ -164,9 +164,10 @@ $ -> ...@@ -164,9 +164,10 @@ $ ->
$('.account-box').hover -> $(@).toggleClass('hover') $('.account-box').hover -> $(@).toggleClass('hover')
# Commit show suppressed diff # Commit show suppressed diff
$(".diff-content").on "click", ".supp_diff_link", -> $(document).on 'click', '.diff-content .js-show-suppressed-diff', ->
$(@).next('table').show() $container = $(@).parent()
$(@).remove() $container.next('table').show()
$container.remove()
$('.navbar-toggle').on 'click', -> $('.navbar-toggle').on 'click', ->
$('.header-content .title').toggle() $('.header-content .title').toggle()
......
...@@ -31,6 +31,10 @@ class @Diff ...@@ -31,6 +31,10 @@ class @Diff
bottom: unfoldBottom bottom: unfoldBottom
offset: offset offset: offset
unfold: unfold unfold: unfold
# indent is used to compensate for single space indent to fit
# '+' and '-' prepended to diff lines,
# see https://gitlab.com/gitlab-org/gitlab-ce/issues/707
indent: 1
$.get(link, params, (response) => $.get(link, params, (response) =>
target.parent().replaceWith(response) target.parent().replaceWith(response)
......
...@@ -128,7 +128,10 @@ class Dispatcher ...@@ -128,7 +128,10 @@ class Dispatcher
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
new ZenMode() new ZenMode()
new DropzoneInput($('.wiki-form')) new DropzoneInput($('.wiki-form'))
when 'snippets', 'labels', 'graphs' when 'snippets'
shortcut_handler = new ShortcutsNavigation()
new ZenMode() if path[2] == 'show'
when 'labels', 'graphs'
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
when 'project_members', 'deploy_keys', 'hooks', 'services', 'protected_branches' when 'project_members', 'deploy_keys', 'hooks', 'services', 'protected_branches'
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
......
...@@ -70,7 +70,7 @@ class @LineHighlighter ...@@ -70,7 +70,7 @@ class @LineHighlighter
@clearHighlight() @clearHighlight()
lineNumber = $(event.target).data('line-number') lineNumber = $(event.target).closest('a').data('line-number')
current = @hashToRange(@_hash) current = @hashToRange(@_hash)
unless current[0] && event.shiftKey unless current[0] && event.shiftKey
......
...@@ -15,9 +15,7 @@ class @MergeRequest ...@@ -15,9 +15,7 @@ class @MergeRequest
this.$('.show-all-commits').on 'click', => this.$('.show-all-commits').on 'click', =>
this.showAllCommits() this.showAllCommits()
# `MergeRequests#new` has no tab-persisting or lazy-loading behavior @initTabs()
unless @opts.action == 'new'
new MergeRequestTabs(@opts)
# Prevent duplicate event bindings # Prevent duplicate event bindings
@disableTaskList() @disableTaskList()
...@@ -29,6 +27,14 @@ class @MergeRequest ...@@ -29,6 +27,14 @@ class @MergeRequest
$: (selector) -> $: (selector) ->
this.$el.find(selector) this.$el.find(selector)
initTabs: ->
if @opts.action != 'new'
# `MergeRequests#new` has no tab-persisting or lazy-loading behavior
new MergeRequestTabs(@opts)
else
# Show the first tab (Commits)
$('.merge-request-tabs a[data-toggle="tab"]:first').tab('show')
showAllCommits: -> showAllCommits: ->
this.$('.first-commits').remove() this.$('.first-commits').remove()
this.$('.all-commits').removeClass 'hide' this.$('.all-commits').removeClass 'hide'
......
...@@ -49,12 +49,6 @@ class @MergeRequestTabs ...@@ -49,12 +49,6 @@ class @MergeRequestTabs
# Store the `location` object, allowing for easier stubbing in tests # Store the `location` object, allowing for easier stubbing in tests
@_location = location @_location = location
switch @opts.action
when 'commits'
@commitsLoaded = true
when 'diffs'
@diffsLoaded = true
@bindEvents() @bindEvents()
@activateTab(@opts.action) @activateTab(@opts.action)
...@@ -102,7 +96,7 @@ class @MergeRequestTabs ...@@ -102,7 +96,7 @@ class @MergeRequestTabs
action = 'notes' if action == 'show' action = 'notes' if action == 'show'
# Remove a trailing '/commits' or '/diffs' # Remove a trailing '/commits' or '/diffs'
new_state = @_location.pathname.replace(/\/(commits|diffs)\/?$/, '') new_state = @_location.pathname.replace(/\/(commits|diffs)(\.html)?\/?$/, '')
# Append the new action if we're on a tab other than 'notes' # Append the new action if we're on a tab other than 'notes'
unless action == 'notes' unless action == 'notes'
...@@ -133,7 +127,7 @@ class @MergeRequestTabs ...@@ -133,7 +127,7 @@ class @MergeRequestTabs
return if @diffsLoaded return if @diffsLoaded
@_get @_get
url: "#{source}.json" url: "#{source}.json" + @_location.search
success: (data) => success: (data) =>
document.getElementById('diffs').innerHTML = data.html document.getElementById('diffs').innerHTML = data.html
@diffsLoaded = true @diffsLoaded = true
......
...@@ -10,7 +10,6 @@ class @Notes ...@@ -10,7 +10,6 @@ class @Notes
constructor: (notes_url, note_ids, last_fetched_at, view) -> constructor: (notes_url, note_ids, last_fetched_at, view) ->
@notes_url = notes_url @notes_url = notes_url
@notes_url = gon.relative_url_root + @notes_url if gon.relative_url_root?
@note_ids = note_ids @note_ids = note_ids
@last_fetched_at = last_fetched_at @last_fetched_at = last_fetched_at
@view = view @view = view
...@@ -298,7 +297,7 @@ class @Notes ...@@ -298,7 +297,7 @@ class @Notes
note.find(".note-header").hide() note.find(".note-header").hide()
base_form = note.find(".note-edit-form") base_form = note.find(".note-edit-form")
form = base_form.clone().insertAfter(base_form) form = base_form.clone().insertAfter(base_form)
form.addClass('current-note-edit-form') form.addClass('current-note-edit-form gfm-form')
form.find('.div-dropzone').remove() form.find('.div-dropzone').remove()
# Show the attachment delete link # Show the attachment delete link
......
...@@ -70,7 +70,7 @@ ...@@ -70,7 +70,7 @@
font-family: $monospace_font; font-family: $monospace_font;
white-space: pre; white-space: pre;
word-wrap: normal; word-wrap: normal;
padding: 0; padding: 1px 2px;
} }
kbd { kbd {
......
...@@ -184,7 +184,7 @@ li.note { ...@@ -184,7 +184,7 @@ li.note {
} }
} }
.supp_diff_link, .show-suppressed-diff,
.show-all-commits { .show-all-commits {
cursor: pointer; cursor: pointer;
} }
......
...@@ -38,6 +38,10 @@ code { ...@@ -38,6 +38,10 @@ code {
} }
} }
a > code {
color: $link-color;
}
/** /**
* Wiki typography * Wiki typography
* *
......
...@@ -65,6 +65,17 @@ ...@@ -65,6 +65,17 @@
color: #777; color: #777;
} }
.suppressed-container {
padding: ($padding-base-vertical + 5px) $padding-base-horizontal;
text-align: center;
// "Changes suppressed. Click to show." link
.show-suppressed-diff {
font-size: 110%;
font-weight: bold;
}
}
table { table {
width: 100%; width: 100%;
font-family: $monospace_font; font-family: $monospace_font;
......
...@@ -105,6 +105,8 @@ ul.notes { ...@@ -105,6 +105,8 @@ ul.notes {
} }
hr { hr {
// Darken 'whitesmoke' a bit to make it more visible in note bodies
border-color: darken(#F5F5F5, 8%);
margin: 10px 0; margin: 10px 0;
} }
} }
......
...@@ -297,6 +297,15 @@ table.table.protected-branches-list tr.no-border { ...@@ -297,6 +297,15 @@ table.table.protected-branches-list tr.no-border {
ul.nav-pills { display:inline-block; } ul.nav-pills { display:inline-block; }
li { display:inline; } li { display:inline; }
a { float:left; } a { float:left; }
li.missing a {
color: #bbb;
border: 1px dashed #ccc;
&:hover {
background-color: #FAFAFA;
}
}
} }
pre.light-well { pre.light-well {
......
...@@ -299,14 +299,14 @@ class ApplicationController < ActionController::Base ...@@ -299,14 +299,14 @@ class ApplicationController < ActionController::Base
end end
def github_import_enabled? def github_import_enabled?
OauthHelper.enabled_oauth_providers.include?(:github) Gitlab::OAuth::Provider.enabled?(:github)
end end
def gitlab_import_enabled? def gitlab_import_enabled?
OauthHelper.enabled_oauth_providers.include?(:gitlab) Gitlab::OAuth::Provider.enabled?(:gitlab)
end end
def bitbucket_import_enabled? def bitbucket_import_enabled?
OauthHelper.enabled_oauth_providers.include?(:bitbucket) && Gitlab::BitbucketImport.public_key.present? Gitlab::OAuth::Provider.enabled?(:bitbucket) && Gitlab::BitbucketImport.public_key.present?
end end
end end
...@@ -18,4 +18,10 @@ class Groups::ApplicationController < ApplicationController ...@@ -18,4 +18,10 @@ class Groups::ApplicationController < ApplicationController
return render_404 return render_404
end end
end end
def authorize_admin_group_member!
unless can?(current_user, :admin_group_member, group)
return render_403
end
end
end end
...@@ -5,6 +5,7 @@ class Groups::GroupMembersController < Groups::ApplicationController ...@@ -5,6 +5,7 @@ class Groups::GroupMembersController < Groups::ApplicationController
# Authorize # Authorize
before_action :authorize_read_group! before_action :authorize_read_group!
before_action :authorize_admin_group!, except: [:index, :leave] before_action :authorize_admin_group!, except: [:index, :leave]
before_action :authorize_admin_group_member!, only: [:create, :resend_invite]
def index def index
@project = @group.projects.find(params[:project_id]) if params[:project_id] @project = @group.projects.find(params[:project_id]) if params[:project_id]
...@@ -28,6 +29,9 @@ class Groups::GroupMembersController < Groups::ApplicationController ...@@ -28,6 +29,9 @@ class Groups::GroupMembersController < Groups::ApplicationController
def update def update
@member = @group.group_members.find(params[:id]) @member = @group.group_members.find(params[:id])
return render_403 unless can?(current_user, :update_group_member, @member)
@member.update_attributes(member_params) @member.update_attributes(member_params)
end end
......
...@@ -24,7 +24,7 @@ class GroupsController < Groups::ApplicationController ...@@ -24,7 +24,7 @@ class GroupsController < Groups::ApplicationController
if @group.save if @group.save
@group.add_owner(current_user) @group.add_owner(current_user)
redirect_to @group, notice: 'Group was successfully created.' redirect_to @group, notice: "Group '#{@group.name}' was successfully created."
else else
render action: "new" render action: "new"
end end
...@@ -75,7 +75,7 @@ class GroupsController < Groups::ApplicationController ...@@ -75,7 +75,7 @@ class GroupsController < Groups::ApplicationController
def update def update
if @group.update_attributes(group_params) if @group.update_attributes(group_params)
redirect_to edit_group_path(@group), notice: 'Group was successfully updated.' redirect_to edit_group_path(@group), notice: "Group '#{@group.name}' was successfully updated."
else else
render action: "edit" render action: "edit"
end end
...@@ -84,7 +84,7 @@ class GroupsController < Groups::ApplicationController ...@@ -84,7 +84,7 @@ class GroupsController < Groups::ApplicationController
def destroy def destroy
DestroyGroupService.new(@group, current_user).execute DestroyGroupService.new(@group, current_user).execute
redirect_to root_path, notice: 'Group was removed.' redirect_to root_path, alert: "Group '#{@group.name} was deleted."
end end
protected protected
......
...@@ -3,6 +3,7 @@ class Import::BitbucketController < Import::BaseController ...@@ -3,6 +3,7 @@ class Import::BitbucketController < Import::BaseController
before_action :bitbucket_auth, except: :callback before_action :bitbucket_auth, except: :callback
rescue_from OAuth::Error, with: :bitbucket_unauthorized rescue_from OAuth::Error, with: :bitbucket_unauthorized
rescue_from Gitlab::BitbucketImport::Client::Unauthorized, with: :bitbucket_unauthorized
def callback def callback
request_token = session.delete(:oauth_request_token) request_token = session.delete(:oauth_request_token)
......
...@@ -72,10 +72,11 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController ...@@ -72,10 +72,11 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
end end
end end
rescue Gitlab::OAuth::SignupDisabledError => e rescue Gitlab::OAuth::SignupDisabledError => e
message = "Signing in using your #{oauth['provider']} account without a pre-existing GitLab account is not allowed." label = Gitlab::OAuth::Provider.label_for(oauth['provider'])
message = "Signing in using your #{label} account without a pre-existing GitLab account is not allowed."
if current_application_settings.signup_enabled? if current_application_settings.signup_enabled?
message << " Create a GitLab account first, and then connect it to your #{oauth['provider']} account." message << " Create a GitLab account first, and then connect it to your #{label} account."
end end
flash[:notice] = message flash[:notice] = message
......
...@@ -64,7 +64,12 @@ class Projects::MilestonesController < Projects::ApplicationController ...@@ -64,7 +64,12 @@ class Projects::MilestonesController < Projects::ApplicationController
end end
def destroy def destroy
return access_denied! unless can?(current_user, :admin_milestone, @milestone) return access_denied! unless can?(current_user, :admin_milestone, @project)
update_params = { milestone: nil }
@milestone.issues.each do |issue|
Issues::UpdateService.new(@project, current_user, update_params).execute(issue)
end
@milestone.destroy @milestone.destroy
......
...@@ -7,6 +7,10 @@ class Projects::NetworkController < Projects::ApplicationController ...@@ -7,6 +7,10 @@ class Projects::NetworkController < Projects::ApplicationController
before_action :authorize_download_code! before_action :authorize_download_code!
def show def show
@url = namespace_project_network_path(@project.namespace, @project, @ref, @options.merge(format: :json))
@commit_url = namespace_project_commit_path(@project.namespace, @project, 'ae45ca32').gsub("ae45ca32", "%s")
respond_to do |format| respond_to do |format|
format.html format.html
......
class Projects::RefsController < Projects::ApplicationController class Projects::RefsController < Projects::ApplicationController
include ExtractsPath include ExtractsPath
include TreeHelper
before_action :require_non_empty_project before_action :require_non_empty_project
before_action :assign_ref_vars before_action :assign_ref_vars
...@@ -60,6 +61,11 @@ class Projects::RefsController < Projects::ApplicationController ...@@ -60,6 +61,11 @@ class Projects::RefsController < Projects::ApplicationController
} }
end end
if @logs.present?
@log_url = namespace_project_tree_url(@project.namespace, @project, tree_join(@ref, @path || '/'))
@more_log_url = logs_file_namespace_project_ref_path(@project.namespace, @project, @ref, @path || '', offset: (@offset + @limit))
end
respond_to do |format| respond_to do |format|
format.html { render_404 } format.html { render_404 }
format.js format.js
......
...@@ -7,13 +7,15 @@ class Projects::TreeController < Projects::ApplicationController ...@@ -7,13 +7,15 @@ class Projects::TreeController < Projects::ApplicationController
before_action :authorize_download_code! before_action :authorize_download_code!
def show def show
return not_found! unless @repository.commit(@ref)
if tree.entries.empty? if tree.entries.empty?
if @repository.blob_at(@commit.id, @path) if @repository.blob_at(@commit.id, @path)
redirect_to( redirect_to(
namespace_project_blob_path(@project.namespace, @project, namespace_project_blob_path(@project.namespace, @project,
File.join(@ref, @path)) File.join(@ref, @path))
) and return ) and return
else elsif @path.present?
return not_found! return not_found!
end end
end end
......
class ProjectsController < ApplicationController class ProjectsController < ApplicationController
prepend_before_filter :render_go_import, only: [:show] prepend_before_filter :render_go_import, only: [:show]
skip_before_action :authenticate_user!, only: [:show] skip_before_action :authenticate_user!, only: [:show, :activity]
before_action :project, except: [:new, :create] before_action :project, except: [:new, :create]
before_action :repository, except: [:new, :create] before_action :repository, except: [:new, :create]
...@@ -24,7 +24,7 @@ class ProjectsController < ApplicationController ...@@ -24,7 +24,7 @@ class ProjectsController < ApplicationController
if @project.saved? if @project.saved?
redirect_to( redirect_to(
project_path(@project), project_path(@project),
notice: 'Project was successfully created.' notice: "Project '#{@project.name}' was successfully created."
) )
else else
render 'new' render 'new'
...@@ -36,11 +36,11 @@ class ProjectsController < ApplicationController ...@@ -36,11 +36,11 @@ class ProjectsController < ApplicationController
respond_to do |format| respond_to do |format|
if status if status
flash[:notice] = 'Project was successfully updated.' flash[:notice] = "Project '#{@project.name}' was successfully updated."
format.html do format.html do
redirect_to( redirect_to(
edit_project_path(@project), edit_project_path(@project),
notice: 'Project was successfully updated.' notice: "Project '#{@project.name}' was successfully updated."
) )
end end
format.js format.js
...@@ -100,7 +100,7 @@ class ProjectsController < ApplicationController ...@@ -100,7 +100,7 @@ class ProjectsController < ApplicationController
return access_denied! unless can?(current_user, :remove_project, @project) return access_denied! unless can?(current_user, :remove_project, @project)
::Projects::DestroyService.new(@project, current_user, {}).execute ::Projects::DestroyService.new(@project, current_user, {}).execute
flash[:alert] = 'Project deleted.' flash[:alert] = "Project '#{@project.name}' was deleted."
if request.referer.include?('/admin') if request.referer.include?('/admin')
redirect_to admin_namespaces_projects_path redirect_to admin_namespaces_projects_path
......
...@@ -90,7 +90,7 @@ class SessionsController < Devise::SessionsController ...@@ -90,7 +90,7 @@ class SessionsController < Devise::SessionsController
# Prevent alert from popping up on the first page shown after authentication. # Prevent alert from popping up on the first page shown after authentication.
flash[:alert] = nil flash[:alert] = nil
redirect_to omniauth_authorize_path(:user, provider.to_sym) redirect_to user_omniauth_authorize_path(provider.to_sym)
end end
def valid_otp_attempt?(user) def valid_otp_attempt?(user)
......
module AuthHelper
PROVIDERS_WITH_ICONS = %w(twitter github gitlab bitbucket google_oauth2).freeze
FORM_BASED_PROVIDERS = [/\Aldap/, 'kerberos'].freeze
def ldap_enabled?
Gitlab.config.ldap.enabled
end
def provider_has_icon?(name)
PROVIDERS_WITH_ICONS.include?(name.to_s)
end
def auth_providers
Gitlab::OAuth::Provider.providers
end
def label_for_provider(name)
Gitlab::OAuth::Provider.label_for(name)
end
def form_based_provider?(name)
FORM_BASED_PROVIDERS.any? { |pattern| pattern === name.to_s }
end
def form_based_providers
auth_providers.select { |provider| form_based_provider?(provider) }
end
def button_based_providers
auth_providers.reject { |provider| form_based_provider?(provider) }
end
def provider_image_tag(provider, size = 64)
label = label_for_provider(provider)
if provider_has_icon?(provider)
file_name = "#{provider.to_s.split('_').first}_#{size}.png"
image_tag(image_path("auth_buttons/#{file_name}"), alt: label, title: "Sign in with #{label}")
else
label
end
end
def auth_active?(provider)
current_user.identities.exists?(provider: provider.to_s)
end
extend self
end
module BlobHelper module BlobHelper
def highlight(blob_name, blob_content, nowrap: false, continue: false) def highlight(blob_name, blob_content, nowrap: false, continue: false)
@formatter ||= Rugments::Formatters::HTML.new( @formatter ||= Rouge::Formatters::HTMLGitlab.new(
nowrap: nowrap, nowrap: nowrap,
cssclass: 'code highlight', cssclass: 'code highlight',
lineanchors: true, lineanchors: true,
...@@ -8,11 +8,11 @@ module BlobHelper ...@@ -8,11 +8,11 @@ module BlobHelper
) )
begin begin
@lexer ||= Rugments::Lexer.guess(filename: blob_name, source: blob_content).new @lexer ||= Rouge::Lexer.guess(filename: blob_name, source: blob_content).new
result = @formatter.format(@lexer.lex(blob_content, continue: continue)).html_safe result = @formatter.format(@lexer.lex(blob_content, continue: continue)).html_safe
rescue rescue
lexer = Rugments::Lexers::PlainText @lexer = Rouge::Lexers::PlainText
result = @formatter.format(lexer.lex(blob_content)).html_safe result = @formatter.format(@lexer.lex(blob_content)).html_safe
end end
result result
......
...@@ -31,8 +31,8 @@ module EmailsHelper ...@@ -31,8 +31,8 @@ module EmailsHelper
end end
def color_email_diff(diffcontent) def color_email_diff(diffcontent)
formatter = Rugments::Formatters::HTML.new(cssclass: "highlight", inline_theme: :github) formatter = Rouge::Formatters::HTML.new(css_class: 'highlight', inline_theme: 'github')
lexer = Rugments::Lexers::Diff.new lexer = Rouge::Lexers::Diff
raw formatter.format(lexer.lex(diffcontent)) raw formatter.format(lexer.lex(diffcontent))
end end
......
module OauthHelper
def ldap_enabled?
Gitlab.config.ldap.enabled
end
def default_providers
[:twitter, :github, :gitlab, :bitbucket, :google_oauth2, :ldap]
end
def enabled_oauth_providers
Devise.omniauth_providers
end
def enabled_social_providers
enabled_oauth_providers.select do |name|
[:saml, :twitter, :gitlab, :github, :bitbucket, :google_oauth2].include?(name.to_sym)
end
end
def additional_providers
enabled_oauth_providers.reject{|provider| provider.to_s.starts_with?('ldap')}
end
def oauth_image_tag(provider, size = 64)
file_name = "#{provider.to_s.split('_').first}_#{size}.png"
image_tag(image_path("authbuttons/#{file_name}"), alt: "Sign in with #{provider.to_s.titleize}")
end
def oauth_active?(provider)
current_user.identities.exists?(provider: provider.to_s)
end
extend self
end
module ProfileHelper
def show_profile_username_tab?
current_user.can_change_username?
end
def show_profile_social_tab?
enabled_social_providers.any?
end
def show_profile_remove_tab?
signup_enabled?
end
end
...@@ -131,8 +131,12 @@ module ProjectsHelper ...@@ -131,8 +131,12 @@ module ProjectsHelper
nav_tabs << :snippets nav_tabs << :snippets
end end
if can?(current_user, :read_label, project)
nav_tabs << :labels
end
if can?(current_user, :read_milestone, project) if can?(current_user, :read_milestone, project)
nav_tabs << [:milestones, :labels] nav_tabs << :milestones
end end
nav_tabs.flatten nav_tabs.flatten
...@@ -180,7 +184,43 @@ module ProjectsHelper ...@@ -180,7 +184,43 @@ module ProjectsHelper
end end
end end
def contribution_guide_url(project) def add_contribution_guide_path(project)
if project && !project.repository.contribution_guide
namespace_project_new_blob_path(
project.namespace,
project,
project.default_branch,
file_name: "CONTRIBUTING.md",
commit_message: "Add contribution guide"
)
end
end
def add_changelog_path(project)
if project && !project.repository.changelog
namespace_project_new_blob_path(
project.namespace,
project,
project.default_branch,
file_name: "CHANGELOG",
commit_message: "Add changelog"
)
end
end
def add_license_path(project)
if project && !project.repository.license
namespace_project_new_blob_path(
project.namespace,
project,
project.default_branch,
file_name: "LICENSE",
commit_message: "Add license"
)
end
end
def contribution_guide_path(project)
if project && contribution_guide = project.repository.contribution_guide if project && contribution_guide = project.repository.contribution_guide
namespace_project_blob_path( namespace_project_blob_path(
project.namespace, project.namespace,
...@@ -191,7 +231,7 @@ module ProjectsHelper ...@@ -191,7 +231,7 @@ module ProjectsHelper
end end
end end
def changelog_url(project) def changelog_path(project)
if project && changelog = project.repository.changelog if project && changelog = project.repository.changelog
namespace_project_blob_path( namespace_project_blob_path(
project.namespace, project.namespace,
...@@ -202,7 +242,7 @@ module ProjectsHelper ...@@ -202,7 +242,7 @@ module ProjectsHelper
end end
end end
def license_url(project) def license_path(project)
if project && license = project.repository.license if project && license = project.repository.license
namespace_project_blob_path( namespace_project_blob_path(
project.namespace, project.namespace,
...@@ -213,7 +253,7 @@ module ProjectsHelper ...@@ -213,7 +253,7 @@ module ProjectsHelper
end end
end end
def version_url(project) def version_path(project)
if project && version = project.repository.version if project && version = project.repository.version
namespace_project_blob_path( namespace_project_blob_path(
project.namespace, project.namespace,
...@@ -274,6 +314,21 @@ module ProjectsHelper ...@@ -274,6 +314,21 @@ module ProjectsHelper
end end
def readme_cache_key def readme_cache_key
[@project.id, @project.commit.sha, "readme"].join('-') sha = @project.commit.try(:sha) || 'nil'
[@project.id, sha, "readme"].join('-')
end
def round_commit_count(project)
count = project.commit_count
if count > 10000
'10000+'
elsif count > 5000
'5000+'
elsif count > 1000
'1000+'
else
count
end
end end
end end
...@@ -31,10 +31,11 @@ class Ability ...@@ -31,10 +31,11 @@ class Ability
end end
if project && project.public? if project && project.public?
[ rules = [
:read_project, :read_project,
:read_wiki, :read_wiki,
:read_issue, :read_issue,
:read_label,
:read_milestone, :read_milestone,
:read_project_snippet, :read_project_snippet,
:read_project_member, :read_project_member,
...@@ -42,6 +43,8 @@ class Ability ...@@ -42,6 +43,8 @@ class Ability
:read_note, :read_note,
:download_code :download_code
] ]
rules - project_disabled_features_rules(project)
else else
group = if subject.kind_of?(Group) group = if subject.kind_of?(Group)
subject subject
...@@ -102,28 +105,7 @@ class Ability ...@@ -102,28 +105,7 @@ class Ability
rules -= project_archived_rules rules -= project_archived_rules
end end
unless project.issues_enabled rules - project_disabled_features_rules(project)
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
end end
...@@ -158,12 +140,13 @@ class Ability ...@@ -158,12 +140,13 @@ class Ability
:create_project_snippet, :create_project_snippet,
:update_issue, :update_issue,
:admin_issue, :admin_issue,
:admin_label, :admin_label
] ]
end end
def project_dev_rules def project_dev_rules
project_report_rules + [ project_report_rules + [
:admin_merge_request,
:create_merge_request, :create_merge_request,
:create_wiki, :create_wiki,
:push_code :push_code
...@@ -205,6 +188,33 @@ class Ability ...@@ -205,6 +188,33 @@ class Ability
] ]
end end
def project_disabled_features_rules(project)
rules = []
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
def group_abilities(user, group) def group_abilities(user, group)
rules = [] rules = []
...@@ -223,7 +233,8 @@ class Ability ...@@ -223,7 +233,8 @@ class Ability
if group.has_owner?(user) || user.admin? if group.has_owner?(user) || user.admin?
rules.push(*[ rules.push(*[
:admin_group, :admin_group,
:admin_namespace :admin_namespace,
:admin_group_member
]) ])
end end
...@@ -285,7 +296,7 @@ class Ability ...@@ -285,7 +296,7 @@ class Ability
rules = [] rules = []
target_user = subject.user target_user = subject.user
group = subject.group group = subject.group
can_manage = group_abilities(user, group).include?(:admin_group) can_manage = group_abilities(user, group).include?(:admin_group_member)
if can_manage && (user != target_user) if can_manage && (user != target_user)
rules << :update_group_member rules << :update_group_member
......
...@@ -14,13 +14,14 @@ ...@@ -14,13 +14,14 @@
# default_branch_protection :integer default(2) # default_branch_protection :integer default(2)
# twitter_sharing_enabled :boolean default(TRUE) # twitter_sharing_enabled :boolean default(TRUE)
# restricted_visibility_levels :text # restricted_visibility_levels :text
# version_check_enabled :boolean default(TRUE)
# max_attachment_size :integer default(10), not null # max_attachment_size :integer default(10), not null
# session_expire_delay :integer default(10080), not null
# default_project_visibility :integer # default_project_visibility :integer
# default_snippet_visibility :integer # default_snippet_visibility :integer
# restricted_signup_domains :text # restricted_signup_domains :text
# user_oauth_applications :bool default(TRUE) # user_oauth_applications :boolean default(TRUE)
# after_sign_out_path :string(255) # after_sign_out_path :string(255)
# session_expire_delay :integer default(10080), not null
# #
class ApplicationSetting < ActiveRecord::Base class ApplicationSetting < ActiveRecord::Base
......
# == Schema Information
#
# Table name: audit_events
#
# id :integer not null, primary key
# author_id :integer not null
# type :string(255) not null
# entity_id :integer not null
# entity_type :string(255) not null
# details :text
# created_at :datetime
# updated_at :datetime
#
class AuditEvent < ActiveRecord::Base class AuditEvent < ActiveRecord::Base
serialize :details, Hash serialize :details, Hash
......
...@@ -159,6 +159,16 @@ module Issuable ...@@ -159,6 +159,16 @@ module Issuable
end end
end end
# Convert this Issuable class name to a format usable by Ability definitions
#
# Examples:
#
# issuable.class # => MergeRequest
# issuable.to_ability_name # => "merge_request"
def to_ability_name
self.class.to_s.underscore
end
private private
def filter_superceded_votes(votes, notes) def filter_superceded_votes(votes, notes)
......
...@@ -56,6 +56,12 @@ class Group < Namespace ...@@ -56,6 +56,12 @@ class Group < Namespace
name name
end end
def avatar_url(size = nil)
if avatar.present?
[gitlab_config.url, avatar.url].join
end
end
def owners def owners
@owners ||= group_members.owners.map(&:user) @owners ||= group_members.owners.map(&:user)
end end
......
...@@ -21,12 +21,13 @@ ...@@ -21,12 +21,13 @@
# import_url :string(255) # import_url :string(255)
# visibility_level :integer default(0), not null # visibility_level :integer default(0), not null
# archived :boolean default(FALSE), not null # archived :boolean default(FALSE), not null
# avatar :string(255)
# import_status :string(255) # import_status :string(255)
# repository_size :float default(0.0) # repository_size :float default(0.0)
# star_count :integer default(0), not null # star_count :integer default(0), not null
# import_type :string(255) # import_type :string(255)
# import_source :string(255) # import_source :string(255)
# avatar :string(255) # commit_count :integer default(0)
# #
require 'carrierwave/orm/activerecord' require 'carrierwave/orm/activerecord'
...@@ -36,7 +37,6 @@ class Project < ActiveRecord::Base ...@@ -36,7 +37,6 @@ class Project < ActiveRecord::Base
include Gitlab::ConfigHelper include Gitlab::ConfigHelper
include Gitlab::ShellAdapter include Gitlab::ShellAdapter
include Gitlab::VisibilityLevel include Gitlab::VisibilityLevel
include Rails.application.routes.url_helpers
include Referable include Referable
include Sortable include Sortable
...@@ -316,7 +316,7 @@ class Project < ActiveRecord::Base ...@@ -316,7 +316,7 @@ class Project < ActiveRecord::Base
end end
def web_url def web_url
[gitlab_config.url, path_with_namespace].join('/') Rails.application.routes.url_helpers.namespace_project_url(self.namespace, self)
end end
def web_url_without_protocol def web_url_without_protocol
...@@ -433,7 +433,7 @@ class Project < ActiveRecord::Base ...@@ -433,7 +433,7 @@ class Project < ActiveRecord::Base
if avatar.present? if avatar.present?
[gitlab_config.url, avatar.url].join [gitlab_config.url, avatar.url].join
elsif avatar_in_git elsif avatar_in_git
[gitlab_config.url, namespace_project_avatar_path(namespace, self)].join Rails.application.routes.url_helpers.namespace_project_avatar_url(namespace, self)
end end
end end
...@@ -563,7 +563,7 @@ class Project < ActiveRecord::Base ...@@ -563,7 +563,7 @@ class Project < ActiveRecord::Base
end end
def http_url_to_repo def http_url_to_repo
[gitlab_config.url, '/', path_with_namespace, '.git'].join('') "#{web_url}.git"
end end
# Check if current branch name is marked as protected in the system # Check if current branch name is marked as protected in the system
...@@ -672,6 +672,10 @@ class Project < ActiveRecord::Base ...@@ -672,6 +672,10 @@ class Project < ActiveRecord::Base
update_attribute(:repository_size, repository.size) update_attribute(:repository_size, repository.size)
end end
def update_commit_count
update_attribute(:commit_count, repository.commit_count)
end
def forks_count def forks_count
ForkedProjectLink.where(forked_from_project_id: self.id).count ForkedProjectLink.where(forked_from_project_id: self.id).count
end end
...@@ -689,14 +693,14 @@ class Project < ActiveRecord::Base ...@@ -689,14 +693,14 @@ class Project < ActiveRecord::Base
if gitlab_shell.fork_repository(forked_from_project.path_with_namespace, self.namespace.path) if gitlab_shell.fork_repository(forked_from_project.path_with_namespace, self.namespace.path)
true true
else else
errors.add(:base, 'Failed to fork repository') errors.add(:base, 'Failed to fork repository via gitlab-shell')
false false
end end
else else
if gitlab_shell.add_repository(path_with_namespace) if gitlab_shell.add_repository(path_with_namespace)
true true
else else
errors.add(:base, 'Failed to create repository') errors.add(:base, 'Failed to create repository via gitlab-shell')
false false
end end
end end
......
...@@ -22,8 +22,12 @@ class GitlabCiService < CiService ...@@ -22,8 +22,12 @@ class GitlabCiService < CiService
API_PREFIX = "api/v1" API_PREFIX = "api/v1"
prop_accessor :project_url, :token prop_accessor :project_url, :token
validates :project_url, presence: true, if: :activated? validates :project_url,
validates :token, presence: true, if: :activated? presence: true,
format: { with: /\A#{URI.regexp(%w(http https))}\z/, message: "should be a valid url" }, if: :activated?
validates :token,
presence: true,
format: { with: /\A([A-Za-z0-9]+)\z/ }, if: :activated?
after_save :compose_service_hook, if: :activated? after_save :compose_service_hook, if: :activated?
......
...@@ -94,18 +94,6 @@ class Repository ...@@ -94,18 +94,6 @@ class Repository
gitlab_shell.rm_tag(path_with_namespace, tag_name) gitlab_shell.rm_tag(path_with_namespace, tag_name)
end end
def round_commit_count
if commit_count > 10000
'10000+'
elsif commit_count > 5000
'5000+'
elsif commit_count > 1000
'1000+'
else
commit_count
end
end
def branch_names def branch_names
cache.fetch(:branch_names) { raw_repository.branch_names } cache.fetch(:branch_names) { raw_repository.branch_names }
end end
...@@ -130,28 +118,29 @@ class Repository ...@@ -130,28 +118,29 @@ class Repository
cache.fetch(:size) { raw_repository.size } cache.fetch(:size) { raw_repository.size }
end end
def cache_keys
%i(size branch_names tag_names commit_count
readme version contribution_guide changelog license)
end
def build_cache
cache_keys.each do |key|
unless cache.exist?(key)
send(key)
end
end
end
def expire_cache def expire_cache
%i(size branch_names tag_names commit_count graph_log cache_keys.each do |key|
readme version contribution_guide changelog license).each do |key|
cache.expire(key) cache.expire(key)
end end
end end
def graph_log def rebuild_cache
cache.fetch(:graph_log) do cache_keys.each do |key|
commits = raw_repository.log(limit: 6000, skip_merges: true, cache.expire(key)
ref: root_ref) send(key)
commits.map do |rugged_commit|
commit = Gitlab::Git::Commit.new(rugged_commit)
{
author_name: commit.author_name,
author_email: commit.author_email,
additions: commit.stats.additions,
deletions: commit.stats.deletions,
}
end
end end
end end
...@@ -463,8 +452,7 @@ class Repository ...@@ -463,8 +452,7 @@ class Repository
filename = nil filename = nil
startline = 0 startline = 0
lines = result.lines result.each_line.each_with_index do |line, index|
lines.each_with_index do |line, index|
if line =~ /^.*:.*:\d+:/ if line =~ /^.*:.*:\d+:/
ref, filename, startline = line.split(':') ref, filename, startline = line.split(':')
startline = startline.to_i - index startline = startline.to_i - index
...@@ -472,11 +460,11 @@ class Repository ...@@ -472,11 +460,11 @@ class Repository
end end
end end
data = lines.map do |line| data = ""
line.sub(ref, '').sub(filename, '').sub(/^:-\d+-/, '').sub(/^::\d+:/, '')
end
data = data.join("") result.each_line do |line|
data << line.sub(ref, '').sub(filename, '').sub(/^:-\d+-/, '').sub(/^::\d+:/, '')
end
OpenStruct.new( OpenStruct.new(
filename: filename, filename: filename,
......
# == Schema Information
#
# Table name: audit_events
#
# id :integer not null, primary key
# author_id :integer not null
# type :string(255) not null
# entity_id :integer not null
# entity_type :string(255) not null
# details :text
# created_at :datetime
# updated_at :datetime
#
class SecurityEvent < AuditEvent class SecurityEvent < AuditEvent
end end
...@@ -57,6 +57,7 @@ ...@@ -57,6 +57,7 @@
# otp_backup_codes :text # otp_backup_codes :text
# public_email :string(255) default(""), not null # public_email :string(255) default(""), not null
# dashboard :integer default(0) # dashboard :integer default(0)
# project_view :integer default(0)
# #
require 'carrierwave/orm/activerecord' require 'carrierwave/orm/activerecord'
...@@ -274,6 +275,10 @@ class User < ActiveRecord::Base ...@@ -274,6 +275,10 @@ class User < ActiveRecord::Base
value: login.to_s.downcase).first value: login.to_s.downcase).first
end end
def find_by_username!(username)
find_by!('lower(username) = ?', username.downcase)
end
def by_username_or_id(name_or_id) def by_username_or_id(name_or_id)
where('users.username = ? OR users.id = ?', name_or_id.to_s, name_or_id.to_i).first where('users.username = ? OR users.id = ?', name_or_id.to_s, name_or_id.to_i).first
end end
......
...@@ -19,7 +19,6 @@ class GitPushService ...@@ -19,7 +19,6 @@ class GitPushService
@project, @user = project, user @project, @user = project, user
project.repository.expire_cache project.repository.expire_cache
project.update_repository_size
if push_remove_branch?(ref, newrev) if push_remove_branch?(ref, newrev)
@push_commits = [] @push_commits = []
...@@ -59,6 +58,7 @@ class GitPushService ...@@ -59,6 +58,7 @@ class GitPushService
EventCreateService.new.push(project, user, @push_data) EventCreateService.new.push(project, user, @push_data)
project.execute_hooks(@push_data.dup, :push_hooks) project.execute_hooks(@push_data.dup, :push_hooks)
project.execute_services(@push_data.dup, :push_hooks) project.execute_services(@push_data.dup, :push_hooks)
ProjectCacheWorker.perform_async(project.id)
end end
protected protected
......
...@@ -2,15 +2,15 @@ class GitTagPushService ...@@ -2,15 +2,15 @@ class GitTagPushService
attr_accessor :project, :user, :push_data attr_accessor :project, :user, :push_data
def execute(project, user, oldrev, newrev, ref) def execute(project, user, oldrev, newrev, ref)
@project, @user = project, user project.repository.expire_cache
@project, @user = project, user
@push_data = build_push_data(oldrev, newrev, ref) @push_data = build_push_data(oldrev, newrev, ref)
EventCreateService.new.push(project, user, @push_data) EventCreateService.new.push(project, user, @push_data)
project.execute_hooks(@push_data.dup, :tag_push_hooks) project.execute_hooks(@push_data.dup, :tag_push_hooks)
project.execute_services(@push_data.dup, :tag_push_hooks) project.execute_services(@push_data.dup, :tag_push_hooks)
ProjectCacheWorker.perform_async(project.id)
project.repository.expire_cache
true true
end end
......
...@@ -27,8 +27,10 @@ class IssuableBaseService < BaseService ...@@ -27,8 +27,10 @@ class IssuableBaseService < BaseService
old_branch, new_branch) old_branch, new_branch)
end end
def filter_params def filter_params(issuable_ability_name = :issue)
unless can?(current_user, :admin_issue, project) ability = :"admin_#{issuable_ability_name}"
unless can?(current_user, ability, project)
params.delete(:milestone_id) params.delete(:milestone_id)
params.delete(:label_ids) params.delete(:label_ids)
params.delete(:assignee_id) params.delete(:assignee_id)
......
...@@ -10,6 +10,10 @@ module Issues ...@@ -10,6 +10,10 @@ module Issues
private private
def filter_params
super(:issue)
end
def execute_hooks(issue, action = 'open') def execute_hooks(issue, action = 'open')
issue_data = hook_data(issue, action) issue_data = hook_data(issue, action)
issue.project.execute_hooks(issue_data, :issue_hooks) issue.project.execute_hooks(issue_data, :issue_hooks)
......
...@@ -20,5 +20,11 @@ module MergeRequests ...@@ -20,5 +20,11 @@ module MergeRequests
merge_request.project.execute_services(merge_data, :merge_request_hooks) merge_request.project.execute_services(merge_data, :merge_request_hooks)
end end
end end
private
def filter_params
super(:merge_request)
end
end end
end end
...@@ -85,6 +85,8 @@ module Projects ...@@ -85,6 +85,8 @@ module Projects
@project.create_wiki if @project.wiki_enabled? @project.create_wiki if @project.wiki_enabled?
@project.build_missing_services
event_service.create_project(@project, current_user) event_service.create_project(@project, current_user)
system_hook_service.execute_hooks_for(@project, :create) system_hook_service.execute_hooks_for(@project, :create)
......
...@@ -51,6 +51,7 @@ ...@@ -51,6 +51,7 @@
= paginate @projects, param_name: 'projects_page', theme: 'gitlab' = paginate @projects, param_name: 'projects_page', theme: 'gitlab'
.col-md-6 .col-md-6
- if can?(current_user, :admin_group_member, @group)
.panel.panel-default .panel.panel-default
.panel-heading .panel-heading
Add user(s) to the group: Add user(s) to the group:
...@@ -86,6 +87,7 @@ ...@@ -86,6 +87,7 @@
(invited) (invited)
%span.pull-right.light %span.pull-right.light
= member.human_access = member.human_access
- if can?(current_user, :destroy_group_member, member)
= link_to group_group_member_path(@group, member), data: { confirm: remove_user_from_group_message(@group, member) }, method: :delete, remote: true, class: "btn-xs btn btn-remove", title: 'Remove user from group' do = link_to group_group_member_path(@group, member), data: { confirm: remove_user_from_group_message(@group, member) }, method: :delete, remote: true, class: "btn-xs btn btn-remove", title: 'Remove user from group' do
%i.fa.fa-minus.fa-inverse %i.fa.fa-minus.fa-inverse
.panel-footer .panel-footer
......
...@@ -8,7 +8,8 @@ ...@@ -8,7 +8,8 @@
.form-group .form-group
= f.label :provider, class: 'control-label' = f.label :provider, class: 'control-label'
.col-sm-10 .col-sm-10
= f.select :provider, Gitlab::OAuth::Provider.names, { allow_blank: false }, class: 'form-control' - values = Gitlab::OAuth::Provider.providers.map { |name| ["#{Gitlab::OAuth::Provider.label_for(name)} (#{name})", name] }
= f.select :provider, values, { allow_blank: false }, class: 'form-control'
.form-group .form-group
= f.label :extern_uid, "Identifier", class: 'control-label' = f.label :extern_uid, "Identifier", class: 'control-label'
.col-sm-10 .col-sm-10
......
%tr %tr
%td %td
= identity.provider = "#{Gitlab::OAuth::Provider.label_for(identity.provider)} (#{identity.provider})"
%td %td
= identity.extern_uid = identity.extern_uid
%td %td
......
= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| = form_for(resource, as: resource_name, url: session_path(resource_name)) do |f|
= f.text_field :login, class: "form-control top", placeholder: "Username or Email", autofocus: "autofocus" = f.text_field :login, class: "form-control top", placeholder: "Username or Email", autofocus: "autofocus", autocapitalize: "off", autocorrect: "off"
= f.password_field :password, class: "form-control bottom", placeholder: "Password" = f.password_field :password, class: "form-control bottom", placeholder: "Password"
- if devise_mapping.rememberable? - if devise_mapping.rememberable?
.remember-me.checkbox .remember-me.checkbox
......
...@@ -6,4 +6,4 @@ ...@@ -6,4 +6,4 @@
%label{for: "remember_me"} %label{for: "remember_me"}
= check_box_tag :remember_me, '1', false, id: 'remember_me' = check_box_tag :remember_me, '1', false, id: 'remember_me'
%span Remember me %span Remember me
= button_tag "#{server['label']} Sign in", class: "btn-save btn" = button_tag "Sign in", class: "btn-save btn"
%p %p
%span.light %span.light
Sign in with &nbsp; Sign in with &nbsp;
- providers = additional_providers - providers = button_based_providers
- providers.each do |provider| - providers.each do |provider|
%span.light %span.light
- if default_providers.include?(provider) - has_icon = provider_has_icon?(provider)
= link_to oauth_image_tag(provider), omniauth_authorize_path(resource_name, provider), method: :post, class: 'oauth-image-link' = link_to provider_image_tag(provider), user_omniauth_authorize_path(provider), method: :post, class: (has_icon ? 'oauth-image-link' : 'btn'), "data-no-turbolink" => "true"
- else
= link_to provider.to_s.titleize, omniauth_authorize_path(resource_name, provider), method: :post, class: "btn", "data-no-turbolink" => "true"
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
.login-heading .login-heading
%h3 Sign in %h3 Sign in
.login-body .login-body
- if ldap_enabled? - if form_based_providers.any?
%ul.nav.nav-tabs %ul.nav.nav-tabs
- @ldap_servers.each_with_index do |server, i| - @ldap_servers.each_with_index do |server, i|
%li{class: (:active if i.zero?)} %li{class: (:active if i.zero?)}
......
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
= image_tag avatar_icon(event.author_email, 24), class: "avatar s24", alt:'' = image_tag avatar_icon(event.author_email, 24), class: "avatar s24", alt:''
= render "events/event/created_project", event: event = render "events/event/created_project", event: event
- else - else
= cache event do
= image_tag avatar_icon(event.author_email, 24), class: "avatar s24", alt:'' = image_tag avatar_icon(event.author_email, 24), class: "avatar s24", alt:''
- if event.push? - if event.push?
= render "events/event/push", event: event = render "events/event/push", event: event
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
.repo-info .repo-info
- unless project.empty_repo? - unless project.empty_repo?
= link_to pluralize(project.repository.round_commit_count, 'commit'), namespace_project_commits_path(project.namespace, project, project.default_branch) = link_to pluralize(round_commit_count(project), 'commit'), namespace_project_commits_path(project.namespace, project, project.default_branch)
&middot; &middot;
= link_to pluralize(project.repository.branch_names.count, 'branch'), namespace_project_branches_path(project.namespace, project) = link_to pluralize(project.repository.branch_names.count, 'branch'), namespace_project_branches_path(project.namespace, project)
&middot; &middot;
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
= link_to member.created_by.name, user_path(member.created_by) = link_to member.created_by.name, user_path(member.created_by)
= time_ago_with_tooltip(member.created_at) = time_ago_with_tooltip(member.created_at)
- if show_controls && can?(current_user, :admin_group, @group) - if show_controls && can?(current_user, :admin_group_member, member)
= link_to resend_invite_group_group_member_path(@group, member), method: :post, class: "btn-xs btn", title: 'Resend invite' do = link_to resend_invite_group_group_member_path(@group, member), method: :post, class: "btn-xs btn", title: 'Resend invite' do
Resend invite Resend invite
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
= search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control search-text-input' } = search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control search-text-input' }
= button_tag 'Search', class: 'btn' = button_tag 'Search', class: 'btn'
- if current_user && current_user.can?(:admin_group, @group) - if current_user && current_user.can?(:admin_group_member, @group)
.pull-right .pull-right
= button_tag class: 'btn btn-new js-toggle-button', type: 'button' do = button_tag class: 'btn btn-new js-toggle-button', type: 'button' do
Add members Add members
......
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
= yield = yield
%div.footer{style: "margin-top: 10px;"} %div.footer{style: "margin-top: 10px;"}
%p %p
\— &mdash;
%br %br
- if @target_url - if @target_url
#{link_to "View it on GitLab", @target_url} #{link_to "View it on GitLab", @target_url}
......
...@@ -59,22 +59,22 @@ ...@@ -59,22 +59,22 @@
%div %div
= link_to 'Enable Two-factor Authentication', new_profile_two_factor_auth_path, class: 'btn btn-success' = link_to 'Enable Two-factor Authentication', new_profile_two_factor_auth_path, class: 'btn btn-success'
- if show_profile_social_tab? - if button_based_providers.any?
.panel.panel-default .panel.panel-default
.panel-heading .panel-heading
Connected Accounts Connected Accounts
.panel-body .panel-body
.oauth-buttons.append-bottom-10 .oauth-buttons.append-bottom-10
%p Click on icon to activate signin with one of the following services %p Click on icon to activate signin with one of the following services
- enabled_social_providers.each do |provider| - button_based_providers.each do |provider|
.btn-group .btn-group
= link_to oauth_image_tag(provider), omniauth_authorize_path(User, provider), = link_to provider_image_tag(provider), user_omniauth_authorize_path(provider), method: :post, class: "btn btn-lg #{'active' if auth_active?(provider)}", "data-no-turbolink" => "true"
method: :post, class: "btn btn-lg #{'active' if oauth_active?(provider)}"
- if oauth_active?(provider) - if auth_active?(provider)
= link_to unlink_profile_account_path(provider: provider), method: :delete, class: 'btn btn-lg' do = link_to unlink_profile_account_path(provider: provider), method: :delete, class: 'btn btn-lg' do
= icon('close') = icon('close')
- if show_profile_username_tab? - if current_user.can_change_username?
.panel.panel-warning.update-username .panel.panel-warning.update-username
.panel-heading .panel-heading
Change Username Change Username
...@@ -94,7 +94,7 @@ ...@@ -94,7 +94,7 @@
%div %div
= f.submit 'Save username', class: "btn btn-warning" = f.submit 'Save username', class: "btn btn-warning"
- if show_profile_remove_tab? - if signup_enabled?
.panel.panel-danger.remove-account .panel.panel-danger.remove-account
.panel-heading .panel-heading
Remove account Remove account
......
...@@ -6,14 +6,13 @@ ...@@ -6,14 +6,13 @@
- @key.errors.full_messages.each do |msg| - @key.errors.full_messages.each do |msg|
%li= msg %li= msg
.form-group
= f.label :title, class: 'control-label'
.col-sm-10= f.text_field :title, class: "form-control"
.form-group .form-group
= f.label :key, class: 'control-label' = f.label :key, class: 'control-label'
.col-sm-10 .col-sm-10
= f.text_area :key, class: "form-control", rows: 8 = f.text_area :key, class: "form-control", rows: 8
.form-group
= f.label :title, class: 'control-label'
.col-sm-10= f.text_field :title, class: "form-control"
.form-actions .form-actions
= f.submit 'Add key', class: "btn btn-create" = f.submit 'Add key', class: "btn btn-create"
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
Download the Google Authenticator application from App Store for iOS or Google Download the Google Authenticator application from App Store for iOS or Google
Play for Android and scan this code. Play for Android and scan this code.
More information is available in the #{link_to('documentation', help_page_path('workflow', 'two_factor_authentication'))}. More information is available in the #{link_to('documentation', help_page_path('profile', 'two_factor_authentication'))}.
%hr %hr
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
%td.old_line.diff-line-num{data: {linenumber: line_old}} %td.old_line.diff-line-num{data: {linenumber: line_old}}
= link_to raw(line_old), "#" = link_to raw(line_old), "#"
%td.new_line= link_to raw(line_new) , "#" %td.new_line= link_to raw(line_new) , "#"
%td.line_content.noteable_line= line %td.line_content.noteable_line= ' ' * @form.indent + line
- if @form.unfold? && @form.bottom? && @form.to < @blob.loc - if @form.unfold? && @form.bottom? && @form.to < @blob.loc
%tr.line_holder{ id: @form.to } %tr.line_holder{ id: @form.to }
......
- too_big = diff_file.diff_lines.count > Commit::DIFF_SAFE_LINES - too_big = diff_file.diff_lines.count > Commit::DIFF_SAFE_LINES
- if too_big - if too_big
%a.supp_diff_link Changes suppressed. Click to show .suppressed-container
%a.show-suppressed-diff.js-show-suppressed-diff Changes suppressed. Click to show.
%table.text-file{class: "#{'hide' if too_big}"} %table.text-file{class: "#{'hide' if too_big}"}
- last_line = 0 - last_line = 0
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
Too many changes to show. Too many changes to show.
.pull-right .pull-right
- unless diff_hard_limit_enabled? - unless diff_hard_limit_enabled?
= link_to "Reload with full diff", url_for(params.merge(force_show_diff: true, format: nil)), class: "btn btn-sm btn-warning" = link_to "Reload with full diff", url_for(params.merge(force_show_diff: true, format: :html)), class: "btn btn-sm btn-warning"
- if current_controller?(:commit) or current_controller?(:merge_requests) - if current_controller?(:commit) or current_controller?(:merge_requests)
- if current_controller?(:commit) - if current_controller?(:commit)
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
.issue-check .issue-check
= check_box_tag dom_id(issue,"selected"), nil, false, 'data-id' => issue.id, class: "selected_issue" = check_box_tag dom_id(issue,"selected"), nil, false, 'data-id' => issue.id, class: "selected_issue"
= cache issue do
.issue-title .issue-title
%span.issue-title-text %span.issue-title-text
= link_to_gfm issue.title, issue_path(issue), class: "row_title" = link_to_gfm issue.title, issue_path(issue), class: "row_title"
......
...@@ -31,6 +31,16 @@ ...@@ -31,6 +31,16 @@
%li= link_to "Email Patches", merge_request_path(@merge_request, format: :patch) %li= link_to "Email Patches", merge_request_path(@merge_request, format: :patch)
%li= link_to "Plain Diff", merge_request_path(@merge_request, format: :diff) %li= link_to "Plain Diff", merge_request_path(@merge_request, format: :diff)
- if @merge_request.open? and @merge_request.source_branch_exists?
.append-bottom-20
.slead
%span
Fetch the branch with
%strong.label-branch<
git fetch
\ #{@merge_request.source_project.http_url_to_repo}
\ #{@merge_request.source_branch}
= render "projects/merge_requests/show/how_to_merge" = render "projects/merge_requests/show/how_to_merge"
= render "projects/merge_requests/widget/show.html.haml" = render "projects/merge_requests/widget/show.html.haml"
...@@ -56,11 +66,9 @@ ...@@ -56,11 +66,9 @@
#notes.notes.tab-pane.voting_notes #notes.notes.tab-pane.voting_notes
= render "projects/merge_requests/discussion" = render "projects/merge_requests/discussion"
#commits.commits.tab-pane #commits.commits.tab-pane
- if current_page?(action: 'commits') - # This tab is always loaded via AJAX
= render "projects/merge_requests/show/commits"
#diffs.diffs.tab-pane #diffs.diffs.tab-pane
- if current_page?(action: 'diffs') - # This tab is always loaded via AJAX
= render "projects/merge_requests/show/diffs"
.mr-loading-status .mr-loading-status
= spinner = spinner
......
:plain :plain
$(".mr_source_commit").html("#{commit_to_html(@commit, @source_project, false)}"); $(".mr_source_commit").html("#{commit_to_html(@commit, @source_project, false)}");
$('.js-timeago').timeago()
:plain :plain
$(".mr_target_commit").html("#{commit_to_html(@commit, @target_project, false)}"); $(".mr_target_commit").html("#{commit_to_html(@commit, @target_project, false)}");
$('.js-timeago').timeago()
...@@ -6,9 +6,11 @@ ...@@ -6,9 +6,11 @@
%span.label.label-inverse= @merge_request.source_branch %span.label.label-inverse= @merge_request.source_branch
does not exist in does not exist in
%span.label.label-info= @merge_request.source_project_path %span.label.label-info= @merge_request.source_project_path
%br
%strong Please close this merge request and open a new merge request to change source branches.
- else - else
%span.label.label-inverse= @merge_request.target_branch %span.label.label-inverse= @merge_request.target_branch
does not exist in does not exist in
%span.label.label-info= @merge_request.target_project_path %span.label.label-info= @merge_request.target_project_path
%br %br
%strong Please close this merge request or change branches with existing one %strong Please close this merge request or change to another target branch.
...@@ -5,6 +5,10 @@ ...@@ -5,6 +5,10 @@
%i.fa.fa-pencil-square-o %i.fa.fa-pencil-square-o
Edit Edit
= link_to 'Close Milestone', namespace_project_milestone_path(@project.namespace, @project, milestone, milestone: {state_event: :close }), method: :put, remote: true, class: "btn btn-sm btn-close" = link_to 'Close Milestone', namespace_project_milestone_path(@project.namespace, @project, milestone, milestone: {state_event: :close }), method: :put, remote: true, class: "btn btn-sm btn-close"
= link_to namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-sm btn-remove" do
%i.fa.fa-trash-o
Remove
%h4 %h4
= link_to_gfm truncate(milestone.title, length: 100), namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone) = link_to_gfm truncate(milestone.title, length: 100), namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone)
- if milestone.expired? and not milestone.closed? - if milestone.expired? and not milestone.closed?
......
...@@ -19,6 +19,9 @@ ...@@ -19,6 +19,9 @@
= link_to 'Close Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :close }), method: :put, class: "btn btn-close btn-grouped" = link_to 'Close Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :close }), method: :put, class: "btn btn-close btn-grouped"
- else - else
= link_to 'Reopen Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-reopen btn-grouped" = link_to 'Reopen Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-reopen btn-grouped"
= link_to namespace_project_milestone_path(@project.namespace, @project, @milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-grouped btn-remove" do
%i.fa.fa-trash-o
Remove
%hr %hr
- if @milestone.issues.any? && @milestone.can_be_closed? - if @milestone.issues.any? && @milestone.can_be_closed?
......
...@@ -17,9 +17,9 @@ ...@@ -17,9 +17,9 @@
:javascript :javascript
network_graph = new Network({ network_graph = new Network({
url: '#{namespace_project_network_path(@project.namespace, @project, @ref, @options.merge(format: :json))}', url: "#{escape_javascript(@url)}",
commit_url: '#{namespace_project_commit_path(@project.namespace, @project, 'ae45ca32').gsub("ae45ca32", "%s")}', commit_url: "#{escape_javascript(@commit_url)}",
ref: '#{@ref}', ref: "#{escape_javascript(@ref)}",
commit_id: '#{@commit.id}' commit_id: '#{@commit.id}'
}) })
new ShortcutsNetwork(network_graph.branch_graph) new ShortcutsNetwork(network_graph.branch_graph)
...@@ -3,10 +3,7 @@ ...@@ -3,10 +3,7 @@
= note_target_fields(note) = note_target_fields(note)
= render layout: 'projects/md_preview', locals: { preview_class: 'note-text' } do = render layout: 'projects/md_preview', locals: { preview_class: 'note-text' } do
= render 'projects/zen', f: f, attr: :note, classes: 'note_text js-note-text js-task-list-field' = render 'projects/zen', f: f, attr: :note, classes: 'note_text js-note-text js-task-list-field'
= render 'projects/notes/hints'
.comment-hints.clearfix
.pull-left #{link_to 'Markdown ', help_page_path('markdown', 'markdown'),{ target: '_blank', tabindex: -1 }}
.pull-right #{link_to 'Attach a file', '#', class: 'markdown-selector', tabindex: -1 }
.note-form-actions .note-form-actions
.buttons .buttons
......
...@@ -8,18 +8,8 @@ ...@@ -8,18 +8,8 @@
= f.hidden_field :noteable_type = f.hidden_field :noteable_type
= render layout: 'projects/md_preview', locals: { preview_class: "note-text", referenced_users: true } do = render layout: 'projects/md_preview', locals: { preview_class: "note-text", referenced_users: true } do
= render 'projects/zen', f: f, attr: :note, = render 'projects/zen', f: f, attr: :note, classes: 'note_text js-note-text'
classes: 'note_text js-note-text' = render 'projects/notes/hints'
.comment-hints.clearfix
.pull-left
= link_to "Markdown ", help_page_path("markdown", "markdown"),{ target: '_blank', tabindex: -1 }
tip:
= random_markdown_tip
.pull-right
= link_to '#', class: 'markdown-selector', tabindex: -1 do
Attach a file
= icon('paperclip')
.error-alert .error-alert
.note-form-actions .note-form-actions
......
.comment-hints.clearfix
.pull-left
= link_to 'Markdown', help_page_path('markdown', 'markdown'), target: '_blank', tabindex: -1
tip:
= random_markdown_tip
.pull-right
= link_to '#', class: 'markdown-selector', tabindex: -1 do
= icon('paperclip')
Attach a file
...@@ -56,7 +56,6 @@ ...@@ -56,7 +56,6 @@
.note-body{class: note_editable?(note) ? 'js-task-list-container' : ''} .note-body{class: note_editable?(note) ? 'js-task-list-container' : ''}
= cache [note, 'markdown'] do
.note-text .note-text
= preserve do = preserve do
= markdown(note.note, {no_header_anchors: true}) = markdown(note.note, {no_header_anchors: true})
......
...@@ -11,9 +11,11 @@ ...@@ -11,9 +11,11 @@
- if @logs.present? - if @logs.present?
:plain :plain
var current_url = location.href.replace(/\/?$/, '/'); var current_url = location.href.replace(/\/?$/, '/');
var log_url = '#{namespace_project_tree_url(@project.namespace, @project, tree_join(@ref, @path || '/'))}'.replace(/\/?$/, '/'); var log_url = "#{escape_javascript(@log_url)}".replace(/\/?$/, '/');
if(current_url == log_url) { if(current_url == log_url) {
// Load 10 more commit log for each file in tree // Load more commit logs for each file in tree
// if we still on the same page // if we still on the same page
ajaxGet('#{logs_file_namespace_project_ref_path(@project.namespace, @project, @ref, @path || '', offset: (@offset + @limit))}'); var url = "#{escape_javascript(@more_log_url)}";
ajaxGet(url);
} }
...@@ -6,33 +6,50 @@ ...@@ -6,33 +6,50 @@
= render 'shared/no_ssh' = render 'shared/no_ssh'
= render 'shared/no_password' = render 'shared/no_password'
= render 'projects/last_push' - if prefer_readme?
= render 'projects/last_push'
= render "home_panel" = render "home_panel"
.project-stats .project-stats
%ul.nav.nav-pills %ul.nav.nav-pills
%li %li
= link_to namespace_project_commits_path(@project.namespace, @project, @ref || @repository.root_ref) do = link_to namespace_project_commits_path(@project.namespace, @project, @ref || @repository.root_ref) do
= pluralize(number_with_delimiter(@repository.commit_count), 'commit') = pluralize(number_with_delimiter(@project.commit_count), 'commit')
%li %li
= link_to namespace_project_branches_path(@project.namespace, @project) do = link_to namespace_project_branches_path(@project.namespace, @project) do
= pluralize(number_with_delimiter(@repository.branch_names.count), 'branch') = pluralize(number_with_delimiter(@repository.branch_names.count), 'branch')
%li %li
= link_to namespace_project_tags_path(@project.namespace, @project) do = link_to namespace_project_tags_path(@project.namespace, @project) do
= pluralize(number_with_delimiter(@repository.tag_names.count), 'tag') = pluralize(number_with_delimiter(@repository.tag_names.count), 'tag')
- if @repository.changelog - if @repository.changelog
%li %li
= link_to changelog_url(@project) do = link_to changelog_path(@project) do
Changelog Changelog
- if @repository.license - if @repository.license
%li %li
= link_to license_url(@project) do = link_to license_path(@project) do
License License
- if @repository.contribution_guide - if @repository.contribution_guide
%li %li
= link_to contribution_guide_url(@project) do = link_to contribution_guide_path(@project) do
Contribution guide Contribution guide
- if current_user && can_push_branch?(@project, @project.default_branch)
- unless @repository.changelog
%li.missing
= link_to add_changelog_path(@project) do
Add Changelog
- unless @repository.license
%li.missing
= link_to add_license_path(@project) do
Add License
- unless @repository.contribution_guide
%li.missing
= link_to add_contribution_guide_path(@project) do
Add Contribution guide
- if @project.archived? - if @project.archived?
.text-warning.center.prepend-top-20 .text-warning.center.prepend-top-20
%p %p
......
...@@ -49,5 +49,5 @@ ...@@ -49,5 +49,5 @@
:javascript :javascript
// Load last commit log for each file in tree // Load last commit log for each file in tree
$('#tree-slider').waitForImages(function() { $('#tree-slider').waitForImages(function() {
ajaxGet('#{@logs_path}'); ajaxGet("#{escape_javascript(@logs_path)}");
}); });
...@@ -3,6 +3,10 @@ ...@@ -3,6 +3,10 @@
%h3.page-title %h3.page-title
= @page.title = @page.title
= render 'main_links' = render 'main_links'
.wiki-last-edit-by
Last edited by #{@page.commit.author.name} #{time_ago_with_tooltip(@page.commit.authored_date)}
- if @page.historical? - if @page.historical?
.warning_message .warning_message
This is an old version of this page. This is an old version of this page.
...@@ -16,6 +20,6 @@ ...@@ -16,6 +20,6 @@
= render_wiki_content(@page) = render_wiki_content(@page)
%hr %hr
.wiki-last-edit-by .wiki-last-edit-by
Last edited by #{@page.commit.author.name} #{time_ago_with_tooltip(@page.commit.authored_date)} Last edited by #{@page.commit.author.name} #{time_ago_with_tooltip(@page.commit.authored_date)}
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
- else - else
none none
.issuable-context-selectbox .issuable-context-selectbox
- if can?(current_user, :admin_issue, @project) - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
= users_select_tag("#{issuable.class.table_name.singularize}[assignee_id]", placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: issuable.assignee_id, project: @target_project, null_user: true) = users_select_tag("#{issuable.class.table_name.singularize}[assignee_id]", placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: issuable.assignee_id, project: @target_project, null_user: true)
%div.prepend-top-20.clearfix %div.prepend-top-20.clearfix
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
- else - else
none none
.issuable-context-selectbox .issuable-context-selectbox
- if can?(current_user, :admin_issue, @project) - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
= f.select(:milestone_id, milestone_options(issuable), { include_blank: 'Select milestone' }, {class: 'select2 select2-compact js-select2 js-milestone'}) = f.select(:milestone_id, milestone_options(issuable), { include_blank: 'Select milestone' }, {class: 'select2 select2-compact js-select2 js-milestone'})
= hidden_field_tag :issuable_context = hidden_field_tag :issuable_context
= f.submit class: 'btn hide' = f.submit class: 'btn hide'
......
...@@ -38,7 +38,7 @@ ...@@ -38,7 +38,7 @@
.clearfix .clearfix
.error-alert .error-alert
%hr %hr
- if can?(current_user, :admin_issue, @project) - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
.form-group .form-group
.issue-assignee .issue-assignee
= f.label :assignee_id, class: 'control-label' do = f.label :assignee_id, class: 'control-label' do
...@@ -100,7 +100,7 @@ ...@@ -100,7 +100,7 @@
= link_to 'Change branches', mr_change_branches_path(@merge_request) = link_to 'Change branches', mr_change_branches_path(@merge_request)
.form-actions .form-actions
- if !issuable.project.empty_repo? && (guide_url = contribution_guide_url(issuable.project)) && !issuable.persisted? - if !issuable.project.empty_repo? && (guide_url = contribution_guide_path(issuable.project)) && !issuable.persisted?
%p %p
Please review the Please review the
%strong #{link_to 'guidelines for contribution', guide_url} %strong #{link_to 'guidelines for contribution', guide_url}
......
class ProjectCacheWorker
include Sidekiq::Worker
sidekiq_options queue: :default
def perform(project_id)
project = Project.find(project_id)
project.update_repository_size
project.update_commit_count
if project.repository.root_ref
project.repository.build_cache
end
end
end
...@@ -27,7 +27,7 @@ class RepositoryImportWorker ...@@ -27,7 +27,7 @@ class RepositoryImportWorker
project.import_finish project.import_finish
project.save project.save
project.update_repository_size ProjectCacheWorker.perform_async(project.id)
Gitlab::BitbucketImport::KeyDeleter.new(project).execute if project.import_type == 'bitbucket' Gitlab::BitbucketImport::KeyDeleter.new(project).execute if project.import_type == 'bitbucket'
end end
end end
...@@ -96,6 +96,7 @@ module Gitlab ...@@ -96,6 +96,7 @@ module Gitlab
end end
redis_config_hash[:namespace] = 'cache:gitlab' redis_config_hash[:namespace] = 'cache:gitlab'
redis_config_hash[:expires_in] = 2.weeks # Cache should not grow forever
config.cache_store = :redis_store, redis_config_hash config.cache_store = :redis_store, redis_config_hash
# This is needed for gitlab-shell # This is needed for gitlab-shell
......
...@@ -209,20 +209,29 @@ production: &base ...@@ -209,20 +209,29 @@ production: &base
# arguments, followed by optional 'args' which can be either a hash or an array. # arguments, followed by optional 'args' which can be either a hash or an array.
# Documentation for this is available at http://doc.gitlab.com/ce/integration/omniauth.html # Documentation for this is available at http://doc.gitlab.com/ce/integration/omniauth.html
providers: providers:
# - { name: 'google_oauth2', app_id: 'YOUR_APP_ID', # - { name: 'google_oauth2',
# label: 'Google',
# app_id: 'YOUR_APP_ID',
# app_secret: 'YOUR_APP_SECRET', # app_secret: 'YOUR_APP_SECRET',
# args: { access_type: 'offline', approval_prompt: '' } } # args: { access_type: 'offline', approval_prompt: '' } }
# - { name: 'twitter', app_id: 'YOUR_APP_ID', # - { name: 'twitter',
# app_secret: 'YOUR_APP_SECRET'} # app_id: 'YOUR_APP_ID',
# - { name: 'github', app_id: 'YOUR_APP_ID', # app_secret: 'YOUR_APP_SECRET' }
# - { name: 'github',
# label: 'GitHub',
# app_id: 'YOUR_APP_ID',
# app_secret: 'YOUR_APP_SECRET', # app_secret: 'YOUR_APP_SECRET',
# args: { scope: 'user:email' } } # args: { scope: 'user:email' } }
# - { name: 'gitlab', app_id: 'YOUR_APP_ID', # - { name: 'gitlab',
# label: 'GitLab.com',
# app_id: 'YOUR_APP_ID',
# app_secret: 'YOUR_APP_SECRET', # app_secret: 'YOUR_APP_SECRET',
# args: { scope: 'api' } } # args: { scope: 'api' } }
# - { name: 'bitbucket', app_id: 'YOUR_APP_ID', # - { name: 'bitbucket',
# app_secret: 'YOUR_APP_SECRET'} # app_id: 'YOUR_APP_ID',
# app_secret: 'YOUR_APP_SECRET' }
# - { name: 'saml', # - { name: 'saml',
# label: 'Our SAML Provider',
# args: { # args: {
# assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback', # assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback',
# idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8', # idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8',
...@@ -247,6 +256,7 @@ production: &base ...@@ -247,6 +256,7 @@ production: &base
## Backup settings ## Backup settings
backup: backup:
path: "tmp/backups" # Relative paths are relative to Rails.root (default: tmp/backups/) path: "tmp/backups" # Relative paths are relative to Rails.root (default: tmp/backups/)
# archive_permissions: 0640 # Permissions for the resulting backup.tar file (default: 0600)
# keep_time: 604800 # default: 0 (forever) (in seconds) # keep_time: 604800 # default: 0 (forever) (in seconds)
# upload: # upload:
# # Fog storage connection settings, see http://fog.io/storage/ . # # Fog storage connection settings, see http://fog.io/storage/ .
...@@ -338,6 +348,8 @@ test: ...@@ -338,6 +348,8 @@ test:
# user: YOUR_USERNAME # user: YOUR_USERNAME
satellites: satellites:
path: tmp/tests/gitlab-satellites/ path: tmp/tests/gitlab-satellites/
backup:
path: tmp/tests/backups
gitlab_shell: gitlab_shell:
path: tmp/tests/gitlab-shell/ path: tmp/tests/gitlab-shell/
repos_path: tmp/tests/repositories/ repos_path: tmp/tests/repositories/
......
...@@ -170,6 +170,7 @@ Settings.gitlab_shell['ssh_path_prefix'] ||= Settings.send(:build_gitlab_shell_s ...@@ -170,6 +170,7 @@ Settings.gitlab_shell['ssh_path_prefix'] ||= Settings.send(:build_gitlab_shell_s
Settings['backup'] ||= Settingslogic.new({}) Settings['backup'] ||= Settingslogic.new({})
Settings.backup['keep_time'] ||= 0 Settings.backup['keep_time'] ||= 0
Settings.backup['path'] = File.expand_path(Settings.backup['path'] || "tmp/backups/", Rails.root) Settings.backup['path'] = File.expand_path(Settings.backup['path'] || "tmp/backups/", Rails.root)
Settings.backup['archive_permissions'] ||= 0600
Settings.backup['upload'] ||= Settingslogic.new({ 'remote_directory' => nil, 'connection' => nil }) Settings.backup['upload'] ||= Settingslogic.new({ 'remote_directory' => nil, 'connection' => nil })
# Convert upload connection settings to use symbol keys, to make Fog happy # Convert upload connection settings to use symbol keys, to make Fog happy
if Settings.backup['upload']['connection'] if Settings.backup['upload']['connection']
......
...@@ -11,6 +11,7 @@ if Gitlab::LDAP::Config.enabled? ...@@ -11,6 +11,7 @@ if Gitlab::LDAP::Config.enabled?
end end
end end
OmniAuth.config.full_host = Settings.gitlab['url']
OmniAuth.config.allowed_request_methods = [:post] OmniAuth.config.allowed_request_methods = [:post]
#In case of auto sign-in, the GET method is used (users don't get to click on a button) #In case of auto sign-in, the GET method is used (users don't get to click on a button)
OmniAuth.config.allowed_request_methods << :get if Gitlab.config.omniauth.auto_sign_in_with_provider.present? OmniAuth.config.allowed_request_methods << :get if Gitlab.config.omniauth.auto_sign_in_with_provider.present?
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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