Commit 73e00301 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'master' into admin-edit-identities

Signed-off-by: default avatarDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>

Conflicts:
	app/views/admin/users/show.html.haml
parents 3fe3cbf2 cc9b5c49
Please view this file on the master branch, on stable branches it's out of date.
v 7.13.0 (unreleased)
- Update maintenance documentation to explain no need to recompile asssets for omnibus installations (Stan Hu)
- Support commenting on diffs in side-by-side mode (Stan Hu)
- Fix JavaScript error when clicking on the comment button on a diff line that has a comment already (Stan Hu)
- Remove project visibility icons from dashboard projects list
......@@ -8,8 +9,12 @@ v 7.13.0 (unreleased)
- Allow users to customize their default Dashboard page.
- Update ssl_ciphers in Nginx example to remove DHE settings. This will deny forward secrecy for Android 2.3.7, Java 6 and OpenSSL 0.9.8
- Admin can edit and remove user identities
- Convert CRLF newlines to LF when committing using the web editor.
- API request /projects/:project_id/merge_requests?state=closed will return only closed merge requests without merged one. If you need ones that were merged - use state=merged.
v 7.12.0 (unreleased)
- Fix Error 500 when one user attempts to access a personal, internal snippet (Stan Hu)
- Disable changing of target branch in new merge request page when a branch has already been specified (Stan Hu)
- Fix post-receive errors on a push when an external issue tracker is configured (Stan Hu)
- Update oauth button logos for Twitter and Google to recommended assets
- Fix hooks for web based events with external issue references (Daniel Gerhardt)
......@@ -48,8 +53,8 @@ v 7.12.0 (unreleased)
- Add validation to wiki page creation (only [a-zA-Z0-9/_-] are allowed) (Jeroen van Baarsen)
- Fix new/empty milestones showing 100% completion value (Jonah Bishop)
- Add a note when an Issue or Merge Request's title changes
- Consistently refer to MRs as either Accepted or Rejected.
- Add Accepted and Rejected tabs to MR lists.
- Consistently refer to MRs as either Merged or Closed.
- Add Merged tab to MR lists.
- Prefix EmailsOnPush email subject with `[Git]`.
- Group project contributions by both name and email.
- Clarify navigation labels for Project Settings and Group Settings.
......@@ -61,7 +66,7 @@ v 7.12.0 (unreleased)
- Allow to configure a URL to show after sign out
- Add an option to automatically sign-in with an Omniauth provider
- Better performance for web editor (switched from satellites to rugged)
- GitLab CI service sends .gitlab-ci.yaml in each push call
- GitLab CI service sends .gitlab-ci.yml in each push call
- When remove project - move repository and schedule it removal
- Improve group removing logic
- Trigger create-hooks on backup restore task
......
......@@ -167,15 +167,17 @@ If you add a dependency in GitLab (such as an operating system package) please c
This is also the style used by linting tools such as [RuboCop](https://github.com/bbatsov/rubocop), [PullReview](https://www.pullreview.com/) and [Hound CI](https://houndci.com).
## Code of conduct
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion.
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
Instances of abusive, harassing, or otherwise unacceptable behavior can be
reported by emailing contact@gitlab.com
This code of conduct applies both within project spaces and in public spaces when an individual is representing the project or its community.
Instances of abusive, harassing, or otherwise unacceptable behavior can be reported by emailing contact@gitlab.com
This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
\ No newline at end of file
This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.1.0, available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/)
......@@ -34,7 +34,7 @@ gem "browser", '~> 0.8.0'
# Extracting information from a git repository
# Provide access to Gitlab::Git library
gem "gitlab_git", '~> 7.2.3'
gem "gitlab_git", '~> 7.2.5'
# Ruby/Rack Git Smart-HTTP Server Handler
# GitLab fork with a lot of changes (improved thread-safety, better memory usage etc)
......@@ -222,16 +222,16 @@ group :development do
end
group :development, :test do
gem 'awesome_print'
gem 'byebug'
gem 'pry-rails'
gem 'coveralls', require: false
gem 'database_cleaner', '~> 1.4.0'
gem 'factory_girl_rails'
gem 'rspec-rails', '~> 3.3.0'
gem 'rubocop', '0.28.0', require: false
gem 'spinach-rails'
gem "rspec-rails", '2.99'
gem 'capybara', '~> 2.2.1'
gem 'capybara-screenshot', '~> 1.0.0'
gem "pry-rails"
gem "awesome_print"
gem "database_cleaner"
gem 'factory_girl_rails'
# Prevent occasions where minitest is not bundled in packaged versions of ruby (see #3826)
gem 'minitest', '~> 5.3.0'
......@@ -239,8 +239,9 @@ group :development, :test do
# Generate Fake data
gem 'ffaker', '~> 2.0.0'
# PhantomJS driver for Capybara
gem 'poltergeist', '~> 1.5.1'
gem 'capybara', '~> 2.3.0'
gem 'capybara-screenshot', '~> 1.0.0'
gem 'poltergeist', '~> 1.6.0'
gem 'teaspoon', '~> 1.0.0'
gem 'teaspoon-jasmine'
......@@ -249,14 +250,12 @@ group :development, :test do
gem 'spring-commands-rspec', '~> 1.0.0'
gem 'spring-commands-spinach', '~> 1.0.0'
gem 'spring-commands-teaspoon', '~> 0.0.2'
gem "byebug"
end
group :test do
gem 'simplecov', require: false
gem 'shoulda-matchers', '~> 2.8.0', require: false
gem 'email_spec'
gem 'email_spec', '~> 1.6.0'
gem 'webmock', '~> 1.21.0'
gem 'test_after_commit'
end
......
......@@ -82,7 +82,7 @@ GEM
columnize (~> 0.8)
debugger-linecache (~> 1.2)
cal-heatmap-rails (0.0.1)
capybara (2.2.1)
capybara (2.3.0)
mime-types (>= 1.16)
nokogiri (>= 1.3.3)
rack (>= 1.0.0)
......@@ -125,7 +125,7 @@ GEM
d3_rails (3.5.5)
railties (>= 3.1.0)
daemons (1.1.9)
database_cleaner (1.3.0)
database_cleaner (1.4.1)
debug_inspector (0.0.2)
debugger-linecache (1.2.0)
default_value_for (3.0.0)
......@@ -154,7 +154,7 @@ GEM
dotenv (0.9.0)
dropzonejs-rails (0.4.14)
rails (> 3.1)
email_spec (1.5.0)
email_spec (1.6.0)
launchy (~> 2.1)
mail (~> 2.2)
encryptor (1.3.0)
......@@ -266,7 +266,7 @@ GEM
mime-types (~> 1.19)
gitlab_emoji (0.1.0)
gemojione (~> 2.0)
gitlab_git (7.2.3)
gitlab_git (7.2.5)
activesupport (~> 4.0)
charlock_holmes (~> 0.6)
gitlab-linguist (~> 3.0)
......@@ -348,7 +348,7 @@ GEM
actionpack (>= 3.0.0)
activesupport (>= 3.0.0)
kgio (2.9.2)
launchy (2.4.2)
launchy (2.4.3)
addressable (~> 2.3)
letter_opener (1.1.2)
launchy (~> 2.2)
......@@ -431,8 +431,8 @@ GEM
orm_adapter (0.5.0)
parser (2.2.0.2)
ast (>= 1.1, < 3.0)
pg (0.15.1)
poltergeist (1.5.1)
pg (0.18.2)
poltergeist (1.6.0)
capybara (~> 2.1)
cliver (~> 0.3.1)
multi_json (~> 1.0)
......@@ -449,7 +449,7 @@ GEM
quiet_assets (1.0.2)
railties (>= 3.1, < 5.0)
racc (1.4.10)
rack (1.5.4)
rack (1.5.5)
rack-accept (0.4.5)
rack (>= 0.4)
rack-attack (4.3.0)
......@@ -530,21 +530,23 @@ GEM
rqrcode (0.4.2)
rqrcode-rails3 (0.1.7)
rqrcode (>= 0.4.2)
rspec-collection_matchers (1.1.2)
rspec-expectations (>= 2.99.0.beta1)
rspec-core (2.99.2)
rspec-expectations (2.99.2)
diff-lcs (>= 1.1.3, < 2.0)
rspec-mocks (2.99.3)
rspec-rails (2.99.0)
actionpack (>= 3.0)
activemodel (>= 3.0)
activesupport (>= 3.0)
railties (>= 3.0)
rspec-collection_matchers
rspec-core (~> 2.99.0)
rspec-expectations (~> 2.99.0)
rspec-mocks (~> 2.99.0)
rspec-core (3.3.1)
rspec-support (~> 3.3.0)
rspec-expectations (3.3.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.3.0)
rspec-mocks (3.3.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.3.0)
rspec-rails (3.3.2)
actionpack (>= 3.0, < 4.3)
activesupport (>= 3.0, < 4.3)
railties (>= 3.0, < 4.3)
rspec-core (~> 3.3.0)
rspec-expectations (~> 3.3.0)
rspec-mocks (~> 3.3.0)
rspec-support (~> 3.3.0)
rspec-support (3.3.0)
rubocop (0.28.0)
astrolabe (~> 1.3)
parser (>= 2.2.0.pre.7, < 3.0)
......@@ -707,7 +709,9 @@ GEM
webmock (1.21.0)
addressable (>= 2.3.6)
crack (>= 0.3.2)
websocket-driver (0.3.3)
websocket-driver (0.5.4)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.2)
wikicloth (0.8.1)
builder
expression_parser
......@@ -735,7 +739,7 @@ DEPENDENCIES
browser (~> 0.8.0)
byebug
cal-heatmap-rails (~> 0.0.1)
capybara (~> 2.2.1)
capybara (~> 2.3.0)
capybara-screenshot (~> 1.0.0)
carrierwave
charlock_holmes
......@@ -744,7 +748,7 @@ DEPENDENCIES
coveralls
creole (~> 0.3.6)
d3_rails (~> 3.5.5)
database_cleaner
database_cleaner (~> 1.4.0)
default_value_for (~> 3.0.0)
devise (= 3.2.4)
devise-async (= 0.9.0)
......@@ -752,7 +756,7 @@ DEPENDENCIES
diffy (~> 3.0.3)
doorkeeper (= 2.1.3)
dropzonejs-rails
email_spec
email_spec (~> 1.6.0)
enumerize
factory_girl_rails
ffaker (~> 2.0.0)
......@@ -765,7 +769,7 @@ DEPENDENCIES
gitlab-grack (~> 2.0.2)
gitlab-linguist (~> 3.0.1)
gitlab_emoji (~> 0.1)
gitlab_git (~> 7.2.3)
gitlab_git (~> 7.2.5)
gitlab_meta (= 7.0)
gitlab_omniauth-ldap (= 1.2.1)
gollum-lib (~> 4.0.2)
......@@ -800,7 +804,7 @@ DEPENDENCIES
omniauth-twitter
org-ruby (= 0.9.12)
pg
poltergeist (~> 1.5.1)
poltergeist (~> 1.6.0)
pry-rails
quiet_assets (~> 1.0.1)
rack-attack (~> 4.3.0)
......@@ -815,7 +819,7 @@ DEPENDENCIES
request_store
rerun (~> 0.10.0)
rqrcode-rails3
rspec-rails (= 2.99)
rspec-rails (~> 3.3.0)
rubocop (= 0.28.0)
rugments (~> 1.0.0.beta7)
sanitize (~> 2.0)
......
app/assets/images/favicon.ico

32.2 KB | W: | H:

app/assets/images/favicon.ico

5.3 KB | W: | H:

app/assets/images/favicon.ico
app/assets/images/favicon.ico
app/assets/images/favicon.ico
app/assets/images/favicon.ico
  • 2-up
  • Swipe
  • Onion skin
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="210px" height="210px" viewBox="0 0 210 210" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
<!-- Generator: Sketch 3.3.2 (12043) - http://www.bohemiancoding.com/sketch -->
<title>Slice 1</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
<g id="logo" sketch:type="MSLayerGroup" transform="translate(0.000000, 10.000000)">
<g id="Page-1" sketch:type="MSShapeGroup">
<g id="Fill-1-+-Group-24">
<g id="Group-24">
<g id="Group">
<path d="M105.0614,193.655 L105.0614,193.655 L143.7014,74.734 L66.4214,74.734 L105.0614,193.655 L105.0614,193.655 Z" id="Fill-4" fill="#E24329"></path>
<path d="M105.0614,193.6548 L66.4214,74.7338 L12.2684,74.7338 L105.0614,193.6548 L105.0614,193.6548 Z" id="Fill-8" fill="#FC6D26"></path>
<path d="M12.2685,74.7341 L12.2685,74.7341 L0.5265,110.8731 C-0.5445,114.1691 0.6285,117.7801 3.4325,119.8171 L105.0615,193.6551 L12.2685,74.7341 L12.2685,74.7341 Z" id="Fill-12" fill="#FCA326"></path>
<path d="M12.2685,74.7342 L66.4215,74.7342 L43.1485,3.1092 C41.9515,-0.5768 36.7375,-0.5758 35.5405,3.1092 L12.2685,74.7342 L12.2685,74.7342 Z" id="Fill-16" fill="#E24329"></path>
<path d="M105.0614,193.6548 L143.7014,74.7338 L197.8544,74.7338 L105.0614,193.6548 L105.0614,193.6548 Z" id="Fill-18" fill="#FC6D26"></path>
<path d="M197.8544,74.7341 L197.8544,74.7341 L209.5964,110.8731 C210.6674,114.1691 209.4944,117.7801 206.6904,119.8171 L105.0614,193.6551 L197.8544,74.7341 L197.8544,74.7341 Z" id="Fill-20" fill="#FCA326"></path>
<path d="M197.8544,74.7342 L143.7014,74.7342 L166.9744,3.1092 C168.1714,-0.5768 173.3854,-0.5758 174.5824,3.1092 L197.8544,74.7342 L197.8544,74.7342 Z" id="Fill-22" fill="#E24329"></path>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="546px" height="194px" viewBox="0 0 546 194" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
<!-- Generator: Sketch 3.3.2 (12043) - http://www.bohemiancoding.com/sketch -->
<title>Fill 1 + Group 24</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
<g id="Fill-1-+-Group-24" sketch:type="MSLayerGroup">
<g id="Group-24" sketch:type="MSShapeGroup">
<path d="M316.7906,65.3001 C301.5016,65.3001 292.0046,77.4461 292.0046,97.0001 C292.0046,116.5541 301.5016,128.7001 316.7906,128.7001 C322.5346,128.7001 327.8716,127.0711 332.2226,123.9881 L332.4336,123.8391 L332.4336,101.8711 L310.4336,101.8711 L310.4336,94.0711 L341.4336,94.0711 L341.4336,126.8061 C334.8706,133.1501 326.3546,136.5001 316.7906,136.5001 C296.2666,136.5001 283.0046,120.9951 283.0046,97.0001 C283.0046,73.0051 296.2666,57.5001 316.7906,57.5001 C326.7826,57.5001 335.2176,61.1481 341.2206,68.0561 L335.2246,73.0381 C330.6986,67.9041 324.4986,65.3001 316.7906,65.3001 L316.7906,65.3001 Z M489.8836,135.2501 L482.9356,135.2501 L480.6016,128.8021 L480.0486,129.2991 C479.9716,129.3681 472.2196,136.2501 462.4606,136.2501 C452.6096,136.2501 445.4606,129.6961 445.4606,120.6671 C445.4606,107.5951 456.7446,104.8511 466.2096,104.8511 C473.5836,104.8511 480.1886,106.5111 480.2546,106.5281 L480.8776,106.6871 L480.8776,105.1011 C480.8776,97.9861 476.4356,94.3781 467.6726,94.3781 C462.3646,94.3781 456.7556,95.6891 451.4236,98.1701 L447.8206,91.9581 C452.5266,88.8961 459.6726,85.3781 467.6726,85.3781 C481.5806,85.3781 489.8836,92.9341 489.8836,105.5891 L489.8836,135.2501 Z M470.6886,111.7771 C460.0716,111.7771 454.4606,114.8511 454.4606,120.6671 C454.4606,124.7281 457.5256,127.2501 462.4606,127.2501 C470.5906,127.2501 477.7276,123.9181 480.6626,121.9481 L480.8836,121.8001 L480.8836,112.6201 L480.4676,112.5491 C480.4226,112.5411 475.8766,111.7771 470.6886,111.7771 L470.6886,111.7771 Z M440.4576,127.4501 L440.4576,135.2501 L410.4606,135.2501 L410.4606,61.2501 L419.4606,61.2501 L419.4606,127.4501 L440.4576,127.4501 Z M520.9416,136.5001 C515.0966,136.5001 508.6886,135.6961 501.8926,134.1091 L501.8926,61.2501 L510.8926,61.2501 L510.8926,89.3131 L511.6656,88.8111 C511.7146,88.7791 516.7346,85.5711 523.6536,85.5711 C525.0336,85.5711 526.4146,85.7001 527.7486,85.9521 C539.0936,88.2761 545.8666,97.4301 545.8666,110.4391 C545.8666,125.7831 535.6176,136.5001 520.9416,136.5001 L520.9416,136.5001 Z M521.9426,94.3781 C518.3636,94.3781 514.6196,95.6031 511.1166,97.9191 L510.8926,98.0681 L510.8926,127.9021 L511.3196,127.9651 C514.6986,128.4601 517.9356,128.7121 520.9416,128.7121 C530.3176,128.7121 536.8666,121.1971 536.8666,110.4391 C536.8666,100.2321 531.4266,94.3781 521.9426,94.3781 L521.9426,94.3781 Z M398.4516,86.2501 L398.4516,94.0501 L383.4516,94.0501 L383.4516,116.9501 C383.4516,119.7551 384.5436,122.3921 386.5276,124.3741 C388.5096,126.3581 391.1466,127.4501 393.9516,127.4501 L398.4516,127.4501 L398.4516,135.2501 L393.9516,135.2501 C383.1996,135.2501 374.4516,126.5021 374.4516,115.7501 L374.4516,61.2501 L383.4516,61.2501 L383.4516,86.2501 L398.4516,86.2501 Z M353.4426,66.2501 L362.4426,66.2501 L362.4426,75.2501 L353.4426,75.2501 L353.4426,66.2501 Z M353.4426,86.2501 L362.4426,86.2501 L362.4426,135.2501 L353.4426,135.2501 L353.4426,86.2501 Z" id="Fill-2" fill="#8C929D"></path>
<g id="Group">
<path d="M105.0614,193.655 L105.0614,193.655 L143.7014,74.734 L66.4214,74.734 L105.0614,193.655 L105.0614,193.655 Z" id="Fill-4" fill="#E24329"></path>
<path id="Fill-6" fill="#FC6D26"></path>
<path d="M105.0614,193.6548 L66.4214,74.7338 L12.2684,74.7338 L105.0614,193.6548 Z" id="Fill-8" fill="#FC6D26"></path>
<path id="Fill-10" fill="#FC6D26"></path>
<path d="M12.2685,74.7341 L12.2685,74.7341 L0.5265,110.8731 C-0.5445,114.1691 0.6285,117.7801 3.4325,119.8171 L105.0615,193.6551 L12.2685,74.7341 Z" id="Fill-12" fill="#FCA326"></path>
<path id="Fill-14" fill="#FC6D26"></path>
<path d="M12.2685,74.7342 L66.4215,74.7342 L43.1485,3.1092 C41.9515,-0.5768 36.7375,-0.5758 35.5405,3.1092 L12.2685,74.7342 Z" id="Fill-16" fill="#E24329"></path>
<path d="M105.0614,193.6548 L143.7014,74.7338 L197.8544,74.7338 L105.0614,193.6548 Z" id="Fill-18" fill="#FC6D26"></path>
<path d="M197.8544,74.7341 L197.8544,74.7341 L209.5964,110.8731 C210.6674,114.1691 209.4944,117.7801 206.6904,119.8171 L105.0614,193.6551 L197.8544,74.7341 Z" id="Fill-20" fill="#FCA326"></path>
<path d="M197.8544,74.7342 L143.7014,74.7342 L166.9744,3.1092 C168.1714,-0.5768 173.3854,-0.5758 174.5824,3.1092 L197.8544,74.7342 Z" id="Fill-22" fill="#E24329"></path>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
......@@ -141,8 +141,7 @@ $ ->
$('.trigger-submit').on 'change', ->
$(@).parents('form').submit()
$("abbr.timeago").timeago()
$('.js-timeago').timeago()
$('abbr.timeago, .js-timeago').timeago()
# Flash
if (flash = $(".flash-container")).length > 0
......
class @BlobView
constructor: ->
# handle multi-line select
handleMultiSelect = (e) ->
[ first_line, last_line ] = parseSelectedLines()
[ line_number ] = parseSelectedLines($(this).attr("id"))
hash = "L#{line_number}"
if e.shiftKey and not isNaN(first_line) and not isNaN(line_number)
if line_number < first_line
last_line = first_line
first_line = line_number
else
last_line = line_number
hash = if first_line == last_line then "L#{first_line}" else "L#{first_line}-#{last_line}"
setHash(hash)
e.preventDefault()
# See if there are lines selected
# "#L12" and "#L34-56" supported
highlightBlobLines = (e) ->
[ first_line, last_line ] = parseSelectedLines()
unless isNaN first_line
$("#tree-content-holder .highlight .line").removeClass("hll")
$("#LC#{line}").addClass("hll") for line in [first_line..last_line]
$.scrollTo("#L#{first_line}", offset: -50) unless e?
# parse selected lines from hash
# always return first and last line (initialized to NaN)
parseSelectedLines = (str) ->
first_line = NaN
last_line = NaN
hash = str || window.location.hash
if hash isnt ""
matches = hash.match(/\#?L(\d+)(\-(\d+))?/)
first_line = parseInt(matches?[1])
last_line = parseInt(matches?[3])
last_line = first_line if isNaN(last_line)
[ first_line, last_line ]
setHash = (hash) ->
hash = hash.replace(/^\#/, "")
nodes = $("#" + hash)
# if any nodes are using this id, they must be temporarily changed
# also, add a temporary div at the top of the screen to prevent scrolling
if nodes.length > 0
scroll_top = $(document).scrollTop()
nodes.attr("id", "")
tmp = $("<div></div>")
.css({ position: "absolute", visibility: "hidden", top: scroll_top + "px" })
.attr("id", hash)
.appendTo(document.body)
window.location.hash = hash
# restore the nodes
if nodes.length > 0
tmp.remove()
nodes.attr("id", hash)
# initialize multi-line select
$("#tree-content-holder .line-numbers a[id^=L]").on("click", handleMultiSelect)
# Highlight the correct lines on load
highlightBlobLines()
# Highlight the correct lines when the hash part of the URL changes
$(window).on("hashchange", highlightBlobLines)
......@@ -87,7 +87,7 @@ class Dispatcher
new TreeView()
shortcut_handler = new ShortcutsNavigation()
when 'projects:blob:show'
new BlobView()
new LineHighlighter()
shortcut_handler = new ShortcutsNavigation()
when 'projects:labels:new', 'projects:labels:edit'
new Labels()
......
# LineHighlighter
#
# Handles single- and multi-line selection and highlight for blob views.
#
#= require jquery.scrollTo
#
# ### Example Markup
#
# <div id="tree-content-holder">
# <div class="file-content">
# <div class="line-numbers">
# <a href="#L1" id="L1" data-line-number="1">1</a>
# <a href="#L2" id="L2" data-line-number="2">2</a>
# <a href="#L3" id="L3" data-line-number="3">3</a>
# <a href="#L4" id="L4" data-line-number="4">4</a>
# <a href="#L5" id="L5" data-line-number="5">5</a>
# </div>
# <pre class="code highlight">
# <code>
# <span id="LC1" class="line">...</span>
# <span id="LC2" class="line">...</span>
# <span id="LC3" class="line">...</span>
# <span id="LC4" class="line">...</span>
# <span id="LC5" class="line">...</span>
# </code>
# </pre>
# </div>
# </div>
#
class @LineHighlighter
# CSS class applied to highlighted lines
highlightClass: 'hll'
# Internal copy of location.hash so we're not dependent on `location` in tests
_hash: ''
# Initialize a LineHighlighter object
#
# hash - String URL hash for dependency injection in tests
constructor: (hash = location.hash) ->
@_hash = hash
@bindEvents()
unless hash == ''
range = @hashToRange(hash)
if range[0]
@highlightRange(range)
# Scroll to the first highlighted line on initial load
# Offset -50 for the sticky top bar, and another -100 for some context
$.scrollTo("#L#{range[0]}", offset: -150)
bindEvents: ->
$('#tree-content-holder').on 'mousedown', 'a[data-line-number]', @clickHandler
# While it may seem odd to bind to the mousedown event and then throw away
# the click event, there is a method to our madness.
#
# If not done this way, the line number anchor will sometimes keep its
# active state even when the event is cancelled, resulting in an ugly border
# around the link and/or a persisted underline text decoration.
$('#tree-content-holder').on 'click', 'a[data-line-number]', (event) ->
event.preventDefault()
clickHandler: (event) =>
event.preventDefault()
@clearHighlight()
lineNumber = $(event.target).data('line-number')
current = @hashToRange(@_hash)
unless current[0] && event.shiftKey
# If there's no current selection, or there is but Shift wasn't held,
# treat this like a single-line selection.
@setHash(lineNumber)
@highlightLine(lineNumber)
else if event.shiftKey
if lineNumber < current[0]
range = [lineNumber, current[0]]
else
range = [current[0], lineNumber]
@setHash(range[0], range[1])
@highlightRange(range)
# Unhighlight previously highlighted lines
clearHighlight: ->
$(".#{@highlightClass}").removeClass(@highlightClass)
# Convert a URL hash String into line numbers
#
# hash - Hash String
#
# Examples:
#
# hashToRange('#L5') # => [5, null]
# hashToRange('#L5-15') # => [5, 15]
# hashToRange('#foo') # => [null, null]
#
# Returns an Array
hashToRange: (hash) ->
matches = hash.match(/^#?L(\d+)(?:-(\d+))?$/)
if matches && matches.length
first = parseInt(matches[1])
last = if matches[2] then parseInt(matches[2]) else null
[first, last]
else
[null, null]
# Highlight a single line
#
# lineNumber - Line number to highlight
highlightLine: (lineNumber) =>
$("#LC#{lineNumber}").addClass(@highlightClass)
# Highlight all lines within a range
#
# range - Array containing the starting and ending line numbers
highlightRange: (range) ->
if range[1]
for lineNumber in [range[0]..range[1]]
@highlightLine(lineNumber)
else
@highlightLine(range[0])
# Set the URL hash string
setHash: (firstLineNumber, lastLineNumber) =>
if lastLineNumber
hash = "#L#{firstLineNumber}-#{lastLineNumber}"
else
hash = "#L#{firstLineNumber}"
@_hash = hash
@__setLocationHash__(hash)
# Make the actual hash change in the browser
#
# This method is stubbed in tests.
__setLocationHash__: (value) ->
# We're using pushState instead of assigning location.hash directly to
# prevent the page from scrolling on the hashchange event
history.pushState({turbolinks: false, url: value}, document.title, value)
#= require jquery.waitforimages
#= require task_list
#= require merge_request_tabs
class @MergeRequest
# Initialize MergeRequest behavior
#
# Options:
# action - String, current controller action
# diffs_loaded - Boolean, have diffs been pre-rendered server-side?
# (default: true if `action` is 'diffs', otherwise false)
# commits_loaded - Boolean, have commits been pre-rendered server-side?
# (default: false)
# action - String, current controller action
#
constructor: (@opts) ->
@initContextWidget()
this.$el = $('.merge-request')
@diffs_loaded = @opts.diffs_loaded or @opts.action == 'diffs'
@commits_loaded = @opts.commits_loaded or false
this.bindEvents()
this.activateTabFromPath()
this.$('.show-all-commits').on 'click', =>
this.showAllCommits()
# `MergeRequests#new` has no tab-persisting or lazy-loading behavior
unless @opts.action == 'new'
new MergeRequestTabs(@opts)
# Prevent duplicate event bindings
@disableTaskList()
......@@ -52,83 +48,6 @@ class @MergeRequest
$(".context .inline-update").on "change", "#merge_request_assignee_id", ->
$(this).submit()
bindEvents: ->
this.$('.merge-request-tabs a[data-toggle="tab"]').on 'shown.bs.tab', (e) =>
$target = $(e.target)
tab_action = $target.data('action')
# Lazy-load diffs
if tab_action == 'diffs'
this.loadDiff() unless @diffs_loaded
$('.diff-header').trigger('sticky_kit:recalc')
# Skip tab-persisting behavior on MergeRequests#new
unless @opts.action == 'new'
@setCurrentAction(tab_action)
# Activate a tab based on the current URL path
#
# If the current action is 'show' or 'new' (i.e., initial page load),
# activates the first tab, otherwise activates the tab corresponding to the
# current action (diffs, commits).
activateTabFromPath: ->
if @opts.action == 'show' || @opts.action == 'new'
this.$('.merge-request-tabs a[data-toggle="tab"]:first').tab('show')
else
this.$(".merge-request-tabs a[data-action='#{@opts.action}']").tab('show')
# Replaces the current Merge Request-specific action in the URL with a new one
#
# If the action is "notes", the URL is reset to the standard
# `MergeRequests#show` route.
#
# Examples:
#
# location.pathname # => "/namespace/project/merge_requests/1"
# setCurrentAction('diffs')
# location.pathname # => "/namespace/project/merge_requests/1/diffs"
#
# location.pathname # => "/namespace/project/merge_requests/1/diffs"
# setCurrentAction('notes')
# location.pathname # => "/namespace/project/merge_requests/1"
#
# location.pathname # => "/namespace/project/merge_requests/1/diffs"
# setCurrentAction('commits')
# location.pathname # => "/namespace/project/merge_requests/1/commits"
setCurrentAction: (action) ->
# Normalize action, just to be safe
action = 'notes' if action == 'show'
# Remove a trailing '/commits' or '/diffs'
new_state = location.pathname.replace(/\/(commits|diffs)\/?$/, '')
# Append the new action if we're on a tab other than 'notes'
unless action == 'notes'
new_state += "/#{action}"
# Ensure parameters and hash come along for the ride
new_state += location.search + location.hash
# Replace the current history state with the new one without breaking
# Turbolinks' history.
#
# See https://github.com/rails/turbolinks/issues/363
history.replaceState {turbolinks: true, url: new_state}, '', new_state
loadDiff: (event) ->
$.ajax
type: 'GET'
url: this.$('.merge-request-tabs .diffs-tab a').attr('href') + ".json"
beforeSend: =>
this.$('.mr-loading-status .loading').show()
complete: =>
@diffs_loaded = true
this.$('.mr-loading-status .loading').hide()
success: (data) =>
this.$(".diffs").html(data.html)
dataType: 'json'
showAllCommits: ->
this.$('.first-commits').remove()
this.$('.all-commits').removeClass 'hide'
......
# MergeRequestTabs
#
# Handles persisting and restoring the current tab selection and lazily-loading
# content on the MergeRequests#show page.
#
# ### Example Markup
#
# <ul class="nav nav-tabs merge-request-tabs">
# <li class="notes-tab active">
# <a data-action="notes" data-target="#notes" data-toggle="tab" href="/foo/bar/merge_requests/1">
# Discussion
# </a>
# </li>
# <li class="commits-tab">
# <a data-action="commits" data-target="#commits" data-toggle="tab" href="/foo/bar/merge_requests/1/commits">
# Commits
# </a>
# </li>
# <li class="diffs-tab">
# <a data-action="diffs" data-target="#diffs" data-toggle="tab" href="/foo/bar/merge_requests/1/diffs">
# Diffs
# </a>
# </li>
# </ul>
#
# <div class="tab-content">
# <div class="notes tab-pane active" id="notes">
# Notes Content
# </div>
# <div class="commits tab-pane" id="commits">
# Commits Content
# </div>
# <div class="diffs tab-pane" id="diffs">
# Diffs Content
# </div>
# </div>
#
# <div class="mr-loading-status">
# <div class="loading">
# Loading Animation
# </div>
# </div>
#
class @MergeRequestTabs
diffsLoaded: false
commitsLoaded: false
constructor: (@opts = {}) ->
# Store the `location` object, allowing for easier stubbing in tests
@_location = location
@bindEvents()
@activateTab(@opts.action)
switch @opts.action
when 'commits' then @commitsLoaded = true
when 'diffs' then @diffsLoaded = true
bindEvents: ->
$(document).on 'shown.bs.tab', '.merge-request-tabs a[data-toggle="tab"]', @tabShown
tabShown: (event) =>
$target = $(event.target)
action = $target.data('action')
if action == 'commits'
@loadCommits($target.attr('href'))
else if action == 'diffs'
@loadDiff($target.attr('href'))
@setCurrentAction(action)
# Activate a tab based on the current action
activateTab: (action) ->
action = 'notes' if action == 'show'
$(".merge-request-tabs a[data-action='#{action}']").tab('show')
# Replaces the current Merge Request-specific action in the URL with a new one
#
# If the action is "notes", the URL is reset to the standard
# `MergeRequests#show` route.
#
# Examples:
#
# location.pathname # => "/namespace/project/merge_requests/1"
# setCurrentAction('diffs')
# location.pathname # => "/namespace/project/merge_requests/1/diffs"
#
# location.pathname # => "/namespace/project/merge_requests/1/diffs"
# setCurrentAction('notes')
# location.pathname # => "/namespace/project/merge_requests/1"
#
# location.pathname # => "/namespace/project/merge_requests/1/diffs"
# setCurrentAction('commits')
# location.pathname # => "/namespace/project/merge_requests/1/commits"
#
# Returns the new URL String
setCurrentAction: (action) =>
# Normalize action, just to be safe
action = 'notes' if action == 'show'
# Remove a trailing '/commits' or '/diffs'
new_state = @_location.pathname.replace(/\/(commits|diffs)\/?$/, '')
# Append the new action if we're on a tab other than 'notes'
unless action == 'notes'
new_state += "/#{action}"
# Ensure parameters and hash come along for the ride
new_state += @_location.search + @_location.hash
# Replace the current history state with the new one without breaking
# Turbolinks' history.
#
# See https://github.com/rails/turbolinks/issues/363
history.replaceState {turbolinks: true, url: new_state}, document.title, new_state
new_state
loadCommits: (source) ->
return if @commitsLoaded
@_get
url: "#{source}.json"
success: (data) =>
document.getElementById('commits').innerHTML = data.html
$('.js-timeago').timeago()
@commitsLoaded = true
loadDiff: (source) ->
return if @diffsLoaded
@_get
url: "#{source}.json"
success: (data) =>
document.getElementById('diffs').innerHTML = data.html
$('.diff-header').trigger('sticky_kit:recalc')
@diffsLoaded = true
toggleLoading: ->
$('.mr-loading-status .loading').toggle()
_get: (options) ->
defaults = {
beforeSend: @toggleLoading
complete: @toggleLoading
dataType: 'json'
type: 'GET'
}
options = $.extend({}, defaults, options)
$.ajax(options)
......@@ -10,6 +10,10 @@ header {
.center-logo {
margin: 8px 0;
text-align: center;
img {
height: 32px;
}
}
}
......
class DashboardController < Dashboard::ApplicationController
before_action :load_projects, except: [:projects]
before_action :load_projects
before_action :event_filter, only: :show
respond_to :html
def show
......
......@@ -24,7 +24,7 @@ class PasswordsController < Devise::PasswordsController
super do |resource|
# TODO (rspeicher): In Devise master (> 3.4.1), we can set
# `Devise.sign_in_after_reset_password = false` and avoid this mess.
if resource.errors.empty? && resource.try(:otp_required_for_login?)
if resource.errors.empty? && resource.try(:two_factor_enabled?)
resource.unlock_access! if unlockable?(resource)
# Since we are not signing this user in, we use the :updated_not_active
......
......@@ -10,7 +10,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
def create
if current_user.valid_otp?(params[:pin_code])
current_user.otp_required_for_login = true
current_user.two_factor_enabled = true
@codes = current_user.generate_otp_backup_codes!
current_user.save!
......@@ -30,7 +30,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
def destroy
current_user.update_attributes({
otp_required_for_login: false,
two_factor_enabled: false,
encrypted_otp_secret: nil,
encrypted_otp_secret_iv: nil,
encrypted_otp_secret_salt: nil,
......
......@@ -71,7 +71,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
def commits
render 'show'
respond_to do |format|
format.html { render 'show' }
format.json { render json: { html: view_to_html_string('projects/merge_requests/show/_commits') } }
end
end
def new
......
......@@ -57,7 +57,7 @@ class SessionsController < Devise::SessionsController
def authenticate_with_two_factor
user = self.resource = find_user
return unless user && user.otp_required_for_login
return unless user && user.two_factor_enabled?
if user_params[:otp_attempt].present? && session[:otp_user_id]
if valid_otp_attempt?(user)
......
......@@ -45,10 +45,10 @@ class IssuableFinder
def group
return @group if defined?(@group)
@group =
@group =
if params[:group_id].present?
Group.find(params[:group_id])
else
else
nil
end
end
......@@ -56,10 +56,10 @@ class IssuableFinder
def project
return @project if defined?(@project)
@project =
@project =
if params[:project_id].present?
Project.find(params[:project_id])
else
else
nil
end
end
......@@ -76,7 +76,7 @@ class IssuableFinder
return @milestones if defined?(@milestones)
@milestones =
if milestones? && params[:milestone_title] != NONE
if milestones? && params[:milestone_title] != NONE
Milestone.where(title: params[:milestone_title])
else
nil
......@@ -90,7 +90,7 @@ class IssuableFinder
def assignee
return @assignee if defined?(@assignee)
@assignee =
@assignee =
if assignee? && params[:assignee_id] != NONE
User.find(params[:assignee_id])
else
......@@ -105,7 +105,7 @@ class IssuableFinder
def author
return @author if defined?(@author)
@author =
@author =
if author? && params[:author_id] != NONE
User.find(params[:author_id])
else
......@@ -148,8 +148,6 @@ class IssuableFinder
case params[:state]
when 'closed'
items.closed
when 'rejected'
items.respond_to?(:rejected) ? items.rejected : items.closed
when 'merged'
items.respond_to?(:merged) ? items.merged : items.closed
when 'all'
......
......@@ -16,6 +16,6 @@ module AppearancesHelper
end
def brand_header_logo
image_tag 'logo-white.png'
image_tag 'logo.svg'
end
end
......@@ -179,14 +179,33 @@ module ApplicationHelper
BroadcastMessage.current
end
def time_ago_with_tooltip(date, placement = 'top', html_class = 'time_ago')
capture_haml do
haml_tag :time, date.to_s,
class: html_class, datetime: date.getutc.iso8601, title: date.in_time_zone.stamp('Aug 21, 2011 9:23pm'),
data: { toggle: 'tooltip', placement: placement }
haml_tag :script, "$('." + html_class + "').timeago().tooltip()"
end.html_safe
# Render a `time` element with Javascript-based relative date and tooltip
#
# time - Time object
# placement - Tooltip placement String (default: "top")
# html_class - Custom class for `time` element (default: "time_ago")
# skip_js - When true, exclude the `script` tag (default: false)
#
# By default also includes a `script` element with Javascript necessary to
# initialize the `timeago` jQuery extension. If this method is called many
# times, for example rendering hundreds of commits, it's advisable to disable
# this behavior using the `skip_js` argument and re-initializing `timeago`
# manually once all of the elements have been rendered.
#
# A `js-timeago` class is always added to the element, even when a custom
# `html_class` argument is provided.
#
# Returns an HTML-safe String
def time_ago_with_tooltip(time, placement: 'top', html_class: 'time_ago', skip_js: false)
element = content_tag :time, time.to_s,
class: "#{html_class} js-timeago",
datetime: time.getutc.iso8601,
title: time.in_time_zone.stamp('Aug 21, 2011 9:23pm'),
data: { toggle: 'tooltip', placement: placement }
element += javascript_tag "$('.js-timeago').timeago()" unless skip_js
element
end
def render_markup(file_name, file_content)
......@@ -214,39 +233,6 @@ module ApplicationHelper
Gitlab::MarkupHelper.asciidoc?(filename)
end
# Overrides ActionView::Helpers::UrlHelper#link_to to add `rel="nofollow"` to
# external links
def link_to(name = nil, options = nil, html_options = {})
if options.kind_of?(String)
if !options.start_with?('#', '/')
html_options = add_nofollow(options, html_options)
end
end
super
end
# Add `"rel=nofollow"` to external links
#
# link - String link to check
# html_options - Hash of `html_options` passed to `link_to`
#
# Returns `html_options`, adding `rel: nofollow` for external links
def add_nofollow(link, html_options = {})
begin
uri = URI(link)
if uri && uri.absolute? && uri.host != Gitlab.config.gitlab.host
rel = html_options.fetch(:rel, '')
html_options[:rel] = (rel + ' nofollow').strip
end
rescue URI::Error
# noop
end
html_options
end
def promo_host
'about.gitlab.com'
end
......@@ -295,10 +281,9 @@ module ApplicationHelper
def state_filters_text_for(entity, project)
titles = {
opened: "Open",
merged: "Accepted"
opened: "Open"
}
entity_title = titles[entity] || entity.to_s.humanize
count =
......
module BroadcastMessagesHelper
def broadcast_styling(broadcast_message)
if(broadcast_message.color || broadcast_message.font)
"background-color:#{broadcast_message.color};color:#{broadcast_message.font}"
else
""
styling = ''
if broadcast_message.color.present?
styling << "background-color: #{broadcast_message.color}"
styling << '; ' if broadcast_message.font.present?
end
if broadcast_message.font.present?
styling << "color: #{broadcast_message.font}"
end
styling
end
end
module IconsHelper
include FontAwesome::Rails::IconHelper
# Creates an icon tag given icon name(s) and possible icon modifiers.
#
# Right now this method simply delegates directly to `fa_icon` from the
......
......@@ -45,13 +45,13 @@ module IssuesHelper
def issue_timestamp(issue)
# Shows the created at time and the updated at time if different
ts = "#{time_ago_with_tooltip(issue.created_at, 'bottom', 'note_created_ago')}"
ts = time_ago_with_tooltip(issue.created_at, placement: 'bottom', html_class: 'note_created_ago')
if issue.updated_at != issue.created_at
ts << capture_haml do
haml_tag :span do
haml_concat '&middot;'
haml_concat icon('edit', title: 'edited')
haml_concat time_ago_with_tooltip(issue.updated_at, 'bottom', 'issue_edited_ago')
haml_concat time_ago_with_tooltip(issue.updated_at, placement: 'bottom', html_class: 'issue_edited_ago')
end
end
end
......
......@@ -25,13 +25,13 @@ module NotesHelper
def note_timestamp(note)
# Shows the created at time and the updated at time if different
ts = "#{time_ago_with_tooltip(note.created_at, 'bottom', 'note_created_ago')}"
ts = time_ago_with_tooltip(note.created_at, placement: 'bottom', html_class: 'note_created_ago')
if note.updated_at != note.created_at
ts << capture_haml do
haml_tag :span do
haml_concat '&middot;'
haml_concat icon('edit', title: 'edited')
haml_concat time_ago_with_tooltip(note.updated_at, 'bottom', 'note_edited_ago')
haml_concat time_ago_with_tooltip(note.updated_at, placement: 'bottom', html_class: 'note_edited_ago')
end
end
end
......
module NotificationsHelper
include IconsHelper
def notification_icon(notification)
if notification.disabled?
icon('volume-off', class: 'ns-mute')
......
......@@ -211,7 +211,7 @@ module ProjectsHelper
def project_last_activity(project)
if project.last_activity_at
time_ago_with_tooltip(project.last_activity_at, 'bottom', 'last_activity_time_ago')
time_ago_with_tooltip(project.last_activity_at, placement: 'bottom', html_class: 'last_activity_time_ago')
else
"Never"
end
......
......@@ -263,7 +263,7 @@ class Ability
:"modify_#{name}",
]
else
if subject.respond_to?(:project)
if subject.respond_to?(:project) && subject.project
project_abilities(user, subject.project)
else
[]
......
......@@ -75,7 +75,7 @@ module Mentionable
refs.reject! { |ref| without.include?(ref) }
refs.each do |ref|
Note.create_cross_reference_note(ref, local_reference, a)
SystemNoteService.cross_reference(ref, local_reference, a)
end
end
......
......@@ -125,16 +125,14 @@ class MergeRequest < ActiveRecord::Base
validate :validate_fork
scope :of_group, ->(group) { where("source_project_id in (:group_project_ids) OR target_project_id in (:group_project_ids)", group_project_ids: group.project_ids) }
scope :merged, -> { with_state(:merged) }
scope :by_branch, ->(branch_name) { where("(source_branch LIKE :branch) OR (target_branch LIKE :branch)", branch: branch_name) }
scope :cared, ->(user) { where('assignee_id = :user OR author_id = :user', user: user.id) }
scope :by_milestone, ->(milestone) { where(milestone_id: milestone) }
scope :in_projects, ->(project_ids) { where("source_project_id in (:project_ids) OR target_project_id in (:project_ids)", project_ids: project_ids) }
scope :of_projects, ->(ids) { where(target_project_id: ids) }
# Closed scope for merge request should return
# both merged and closed mr's
scope :closed, -> { with_states(:closed, :merged) }
scope :rejected, -> { with_states(:closed) }
scope :merged, -> { with_state(:merged) }
scope :closed, -> { with_state(:closed) }
scope :closed_and_merged, -> { with_states(:closed, :merged) }
def self.reference_prefix
'!'
......@@ -417,4 +415,14 @@ class MergeRequest < ActiveRecord::Base
def can_be_merged_by?(user)
::Gitlab::GitAccess.new(user, project).can_push_to_branch?(target_branch)
end
def state_human_name
if merged?
"Merged"
elsif closed?
"Closed"
else
"Open"
end
end
end
......@@ -56,7 +56,7 @@ class Milestone < ActiveRecord::Base
end
def closed_items_count
self.issues.closed.count + self.merge_requests.closed.count
self.issues.closed.count + self.merge_requests.closed_and_merged.count
end
def total_items_count
......
......@@ -63,11 +63,6 @@ class Note < ActiveRecord::Base
after_update :set_references
class << self
# TODO (rspeicher): Update usages
def create_cross_reference_note(*args)
SystemNoteService.cross_reference(*args)
end
def discussions_from_notes(notes)
discussion_ids = []
discussions = []
......
......@@ -5,8 +5,13 @@ class Repository
def initialize(path_with_namespace, default_branch = nil, project = nil)
@path_with_namespace = path_with_namespace
@raw_repository = Gitlab::Git::Repository.new(path_to_repo) if path_with_namespace
@project = project
if path_with_namespace
@raw_repository = Gitlab::Git::Repository.new(path_to_repo)
@raw_repository.autocrlf = :input
end
rescue Gitlab::Git::Repository::NoRepository
nil
end
......
......@@ -34,7 +34,6 @@ class Snippet < ActiveRecord::Base
validates :author, presence: true
validates :title, presence: true, length: { within: 0..255 }
validates :file_name,
presence: true,
length: { within: 0..255 },
format: { with: Gitlab::Regex.file_name_regex,
message: Gitlab::Regex.file_name_regex_message }
......
......@@ -172,6 +172,9 @@ class User < ActiveRecord::Base
after_create :post_create_hook
after_destroy :post_destroy_hook
# User's Dashboard preference
# Note: When adding an option, it MUST go on the end of the array.
enum dashboard: [:projects, :stars]
alias_attribute :private_token, :authentication_token
......@@ -220,10 +223,26 @@ class User < ActiveRecord::Base
end
def find_for_commit(email, name)
# Prefer email match over name match
User.where(email: email).first ||
User.joins(:emails).where(emails: { email: email }).first ||
User.where(name: name).first
user_table = arel_table
email_table = Email.arel_table
# Use ARel to build a query:
query = user_table.
# SELECT "users".* FROM "users"
project(user_table[Arel.star]).
# LEFT OUTER JOIN "emails"
join(email_table, Arel::Nodes::OuterJoin).
# ON "users"."id" = "emails"."user_id"
on(user_table[:id].eq(email_table[:user_id])).
# WHERE ("user"."email" = '<email>' OR "user"."name" = '<name>')
# OR "emails"."email" = '<email>'
where(
user_table[:email].eq(email).
or(user_table[:name].eq(name)).
or(email_table[:email].eq(email))
)
find_by_sql(query.to_sql).first
end
def filter(filter_name)
......@@ -297,6 +316,18 @@ class User < ActiveRecord::Base
@reset_token
end
# Check if the user has enabled Two-factor Authentication
def two_factor_enabled?
otp_required_for_login
end
# Set whether or not Two-factor Authentication is enabled for the current user
#
# setting - Boolean
def two_factor_enabled=(setting)
self.otp_required_for_login = setting
end
def namespace_uniq
namespace_name = self.username
existing_namespace = Namespace.by_path(namespace_name)
......@@ -704,8 +735,4 @@ class User < ActiveRecord::Base
def can_be_removed?
!solo_owned_groups.present?
end
# User's Dashboard preference
# Note: When adding an option, it MUST go on the end of the array.
enum dashboard: [:projects, :stars]
end
......@@ -105,7 +105,7 @@ class GitPushService
author ||= commit_user(commit)
refs.each do |r|
Note.create_cross_reference_note(r, commit, author)
SystemNoteService.cross_reference(r, commit, author)
end
end
end
......
......@@ -15,7 +15,7 @@ module Notes
# Create a cross-reference note if this Note contains GFM that names an
# issue, merge request, or commit.
note.references.each do |mentioned|
Note.create_cross_reference_note(mentioned, note.noteable, note.author)
SystemNoteService.cross_reference(mentioned, note.noteable, note.author)
end
execute_hooks(note)
......
......@@ -13,7 +13,7 @@ module Notes
# Create a cross-reference note if this Note contains GFM that
# names an issue, merge request, or commit.
note.references.each do |mentioned|
Note.create_cross_reference_note(mentioned, note.noteable, note.author)
SystemNoteService.cross_reference(mentioned, note.noteable, note.author)
end
end
end
......
......@@ -38,6 +38,14 @@
= link_to remove_email_admin_user_path(@user, email), data: { confirm: "Are you sure you want to remove #{email.email}?" }, method: :delete, class: "btn-xs btn btn-remove pull-right", title: 'Remove secondary email', id: "remove_email_#{email.id}" do
%i.fa.fa-times
%li.two-factor-status
%span.light Two-factor Authentication:
%strong{class: @user.two_factor_enabled? ? 'cgreen' : 'cred'}
- if @user.two_factor_enabled?
Enabled
- else
Disabled
%li
%span.light Can create groups:
%strong
......
%header.navbar.navbar-fixed-top.navbar-empty
.container
.center-logo
= image_tag 'logo-white.png', width: 32, height: 32
= brand_header_logo
......@@ -36,7 +36,7 @@
.panel-heading
Two-factor Authentication
.panel-body
- if current_user.otp_required_for_login
- if current_user.two_factor_enabled?
.pull-right
= link_to 'Disable Two-factor Authentication', profile_two_factor_auth_path, method: :delete, class: 'btn btn-close btn-sm',
data: { confirm: 'Are you sure?' }
......
......@@ -15,10 +15,10 @@
- if issuable.is_a?(MergeRequest)
%p.help-block
- if issuable.work_in_progress?
Remove the <code>WIP</code> prefix from the title to allow this
Remove the <code>WIP</code> prefix from the title to allow this
<strong>Work In Progress</strong> merge request to be accepted when it's ready.
- else
Start the title with <code>[WIP]</code> or <code>WIP:</code> to prevent a
Start the title with <code>[WIP]</code> or <code>WIP:</code> to prevent a
<strong>Work In Progress</strong> merge request from being accepted before it's ready.
.form-group.issuable-description
= f.label :description, 'Description', class: 'control-label'
......@@ -81,21 +81,22 @@
- if issuable.is_a?(MergeRequest)
%hr
- unless @merge_request.persisted?
- if @merge_request.new_record?
.form-group
= f.label :source_branch, class: 'control-label' do
%i.fa.fa-code-fork
Source Branch
.col-sm-10
= f.select(:source_branch, [@merge_request.source_branch], { }, { class: 'source_branch select2 span2', disabled: true })
%p.help-block
= link_to 'Change source branch', mr_change_branches_path(@merge_request)
.form-group
= f.label :target_branch, class: 'control-label' do
%i.fa.fa-code-fork
Target Branch
.col-sm-10
= f.select(:target_branch, @merge_request.target_branches, { include_blank: "Select branch" }, { class: 'target_branch select2 span2' })
= f.select(:target_branch, @merge_request.target_branches, { include_blank: "Select branch" }, { class: 'target_branch select2 span2', disabled: @merge_request.new_record? })
- if @merge_request.new_record?
%p.help-block
= link_to 'Change branches', mr_change_branches_path(@merge_request)
.form-actions
- if !issuable.project.empty_repo? && (guide_url = contribution_guide_url(issuable.project)) && !issuable.persisted?
......
......@@ -29,5 +29,5 @@
= commit_author_link(commit, avatar: true, size: 24)
authored
.committed_ago
#{time_ago_with_tooltip(commit.committed_date)} &nbsp;
#{time_ago_with_tooltip(commit.committed_date, skip_js: true)} &nbsp;
= link_to_browse_code(project, commit)
......@@ -28,7 +28,7 @@
= 0
.issue-info
= "##{issue.iid} opened #{time_ago_with_tooltip(issue.created_at, 'bottom')} by #{link_to_member(@project, issue.author, avatar: false)}".html_safe
= "##{issue.iid} opened #{time_ago_with_tooltip(issue.created_at, placement: 'bottom')} by #{link_to_member(@project, issue.author, avatar: false)}".html_safe
- if issue.votes_count > 0
= render 'votes/votes_inline', votable: issue
- if issue.milestone
......@@ -41,4 +41,4 @@
= issue.task_status
.pull-right.issue-updated-at
%small updated #{time_ago_with_tooltip(issue.updated_at, 'bottom', 'issue_update_ago')}
%small updated #{time_ago_with_tooltip(issue.updated_at, placement: 'bottom', html_class: 'issue_update_ago')}
......@@ -9,11 +9,11 @@
- if merge_request.merged?
%span
%i.fa.fa-check
ACCEPTED
MERGED
- elsif merge_request.closed?
%span
%i.fa.fa-ban
REJECTED
CLOSED
- else
%span.hidden-xs.hidden-sm
%span.label-branch<
......@@ -35,7 +35,7 @@
= 0
.merge-request-info
= "##{merge_request.iid} opened #{time_ago_with_tooltip(merge_request.created_at, 'bottom')} by #{link_to_member(@project, merge_request.author, avatar: false)}".html_safe
= "##{merge_request.iid} opened #{time_ago_with_tooltip(merge_request.created_at, placement: 'bottom')} by #{link_to_member(@project, merge_request.author, avatar: false)}".html_safe
- if merge_request.votes_count > 0
= render 'votes/votes_inline', votable: merge_request
- if merge_request.milestone_id?
......@@ -48,4 +48,4 @@
= merge_request.task_status
.pull-right.hidden-xs
%small updated #{time_ago_with_tooltip(merge_request.updated_at, 'bottom', 'merge_request_updated_ago')}
%small updated #{time_ago_with_tooltip(merge_request.updated_at, placement: 'bottom', html_class: 'merge_request_updated_ago')}
......@@ -56,7 +56,8 @@
#notes.notes.tab-pane.voting_notes
= render "projects/merge_requests/discussion"
#commits.commits.tab-pane
= render "projects/merge_requests/show/commits"
- if current_page?(action: 'commits')
= render "projects/merge_requests/show/commits"
#diffs.diffs.tab-pane
- if current_page?(action: 'diffs')
= render "projects/merge_requests/show/diffs"
......@@ -64,7 +65,6 @@
.mr-loading-status
= spinner
:javascript
var merge_request;
......
%h4.page-title
.issue-box{ class: issue_box_class(@merge_request) }
- if @merge_request.merged?
Accepted
- elsif @merge_request.closed?
Rejected
- else
Open
= @merge_request.state_human_name
= "Merge Request ##{@merge_request.iid}"
%small.creator
&middot;
......
......@@ -2,7 +2,7 @@
= render 'projects/merge_requests/widget/heading'
.mr-widget-body
%h4
Rejected
Closed
- if @merge_request.closed_event
by #{link_to_member(@project, @merge_request.closed_event.author, avatar: true)}
#{time_ago_with_tooltip(@merge_request.closed_event.created_at)}
......
......@@ -2,7 +2,7 @@
= render 'projects/merge_requests/widget/heading'
.mr-widget-body
%h4
Accepted
Merged
- if @merge_request.merge_event
by #{link_to_member(@project, @merge_request.merge_event.author, avatar: true)}
#{time_ago_with_tooltip(@merge_request.merge_event.created_at)}
......
......@@ -86,10 +86,10 @@
.col-md-3
= render('merge_requests', title: 'Waiting for merge (open and assigned)', merge_requests: @merge_requests.opened.assigned, id: 'ongoing')
.col-md-3
= render('merge_requests', title: 'Rejected (closed)', merge_requests: @merge_requests.rejected, id: 'closed')
= render('merge_requests', title: 'Rejected (closed)', merge_requests: @merge_requests.closed, id: 'closed')
.col-md-3
.panel.panel-primary
.panel-heading Accepted
.panel-heading Merged
%ul.well-list
- @merge_requests.merged.each do |merge_request|
= render 'merge_request', merge_request: merge_request
......
......@@ -16,7 +16,7 @@
= link_to_member(@project, last_note.author, avatar: false)
%span.discussion-last-update
#{time_ago_with_tooltip(last_note.updated_at, 'bottom', 'discussion_updated_ago')}
#{time_ago_with_tooltip(last_note.updated_at, placement: 'bottom', html_class: 'discussion_updated_ago')}
.discussion-body.js-toggle-content
= render "projects/notes/discussions/diff", discussion_notes: discussion_notes, note: note
......@@ -14,7 +14,7 @@
last updated by
= link_to_member(@project, last_note.author, avatar: false)
%span.discussion-last-update
#{time_ago_with_tooltip(last_note.updated_at, 'bottom', 'discussion_updated_ago')}
#{time_ago_with_tooltip(last_note.updated_at, placement: 'bottom', html_class: 'discussion_updated_ago')}
.discussion-body.js-toggle-content
- if note.for_diff_line?
= render "projects/notes/discussions/diff", discussion_notes: discussion_notes, note: note
......
......@@ -14,6 +14,6 @@
last updated by
= link_to_member(@project, last_note.author, avatar: false)
%span.discussion-last-update
#{time_ago_with_tooltip(last_note.updated_at, 'bottom', 'discussion_updated_ago')}
#{time_ago_with_tooltip(last_note.updated_at, placement: 'bottom', html_class: 'discussion_updated_ago')}
.discussion-body.js-toggle-content.hide
= render "projects/notes/discussions/diff", discussion_notes: discussion_notes, note: note
......@@ -11,6 +11,6 @@
#{merge_request.project.name_with_namespace}
.pull-right
- if merge_request.merged?
%span.label.label-primary Accepted
%span.label.label-primary Merged
- elsif merge_request.closed?
%span.label.label-danger Rejected
%span.label.label-danger Closed
.file-content.code{class: user_color_scheme_class}
.line-numbers
- if blob.data.present?
- blob.data.lines.to_a.size.times do |index|
- blob.data.lines.each_index do |index|
- offset = defined?(first_line_number) ? first_line_number : 1
- i = index + offset
/ We're not using `link_to` because it is too slow once we get to thousands of lines.
%a{href: "#L#{i}", id: "L#{i}", rel: "#L#{i}"}
-# We're not using `link_to` because it is too slow once we get to thousands of lines.
%a{href: "#L#{i}", id: "L#{i}", 'data-line-number' => i}
%i.fa.fa-link
= i
:preserve
......
......@@ -12,10 +12,10 @@
= icon('check-circle')
#{state_filters_text_for(:merged, @project)}
%li{class: ("active" if params[:state] == 'rejected')}
= link_to page_filter_path(state: 'rejected') do
%li{class: ("active" if params[:state] == 'closed')}
= link_to page_filter_path(state: 'closed') do
= icon('ban')
#{state_filters_text_for(:rejected, @project)}
#{state_filters_text_for(:closed, @project)}
- else
%li{class: ("active" if params[:state] == 'closed')}
= link_to page_filter_path(state: 'closed') do
......
......@@ -18,7 +18,7 @@
.col-sm-10
.file-holder.snippet
.file-title
= f.text_field :file_name, placeholder: "example.rb", class: 'form-control snippet-file-name', required: true
= f.text_field :file_name, placeholder: "Optionally name this file to add code highlighting, e.g. example.rb for Ruby.", class: 'form-control snippet-file-name'
.file-content.code
%pre#editor= @snippet.content
= f.hidden_field :content, class: 'snippet-file-content'
......
......@@ -9,7 +9,8 @@
.row
%section.col-md-8
.header-with-avatar
= image_tag avatar_icon(@user.email, 90), class: "avatar avatar-tile s90", alt: ''
= link_to avatar_icon(@user.email), target: '_blank' do
= image_tag avatar_icon(@user.email, 90), class: "avatar avatar-tile s90", alt: ''
%h3
= @user.name
- if @user == current_user
......
# GitLab basics
Step-by-step guides on the basics of working with Git and GitLab.
* [Start using Git on the commandline](start_using_git.md)
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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