Commit 0b525170 authored by Lin Jen-Shin's avatar Lin Jen-Shin

Merge remote-tracking branch 'upstream/master' into pipeline-hooks-without-slack

* upstream/master: (233 commits)
  Fix awardable button mutuality loading spinners
  Update CHANGELOG for 8.10.5
  Clean up project destruction
  Small refactor of doc/development/README.md
  Avoid commit lookup on diff_helper
  Removed extra newline from redis_spec.rb
  Used cached value of project count to reduce DB load
  Remove duplicate link_to statements
  Mention add_column_with_default in downtime guide
  Add missing space to generic badge template
  Rename `run` task helper method to prevent conflict with StateMachine
  Add a method in Project to return a cached value of total count of projects
  spellcheck
  Add svg guidelines to ui guide
  Add Changelog entry for Grape upgrade [ci skip]
  Fix Grape tests.
  Retain old behavior
  Update Grape from 0.13.0 to 0.15.0.
  adds second batch of tests changed to active tense
  fixes part1 of files to start using active tense
  ...
parents 5607fdf9 87b07f6a
......@@ -30,6 +30,7 @@
/config/secrets.yml
/config/sidekiq.yml
/coverage/*
/coverage-javascript/
/db/*.sqlite3
/db/*.sqlite3-journal
/db/data.yml
......
......@@ -222,7 +222,22 @@ teaspoon:
stage: test
<<: *use-db
script:
- curl --silent --location https://deb.nodesource.com/setup_6.x | bash -
- apt-get install --assume-yes nodejs
- npm install --global istanbul
- teaspoon
artifacts:
name: coverage-javascript
expire_in: 31d
paths:
- coverage-javascript/default/
lint-doc:
stage: test
image: "phusion/baseimage:latest"
before_script: []
script:
- scripts/lint-doc.sh
bundler:audit:
stage: test
......@@ -252,6 +267,9 @@ coverage:
notify:slack:
stage: post-test
variables:
USE_DB: "false"
USE_BUNDLE_INSTALL: "false"
script:
- ./scripts/notify_slack.sh "#builds" "Build on \`$CI_BUILD_REF_NAME\` failed! Commit \`$(git log -1 --oneline)\` See <https://gitlab.com/gitlab-org/$(basename "$PWD")/commit/"$CI_BUILD_REF"/builds>"
when: on_failure
......@@ -266,10 +284,12 @@ pages:
stage: pages
dependencies:
- coverage
- teaspoon
script:
- mv public/ .public/
- mkdir public/
- mv coverage public/coverage-ruby
- mv coverage-javascript/default/ public/coverage-javascript/
artifacts:
paths:
- public
......
#
# This list is used by git-shortlog to make contributions from the
# same person appearing to be so.
#
Achilleas Pipinellis <axilleas@axilleas.me> <axilleas@archlinux.gr>
Achilleas Pipinellis <axilleas@axilleas.me> <axilleas@users.noreply.github.com>
Dmitriy Zaporozhets <dzaporozhets@gitlab.com> <dmitriy.zaporozhets@gmail.com>
Dmitriy Zaporozhets <dzaporozhets@gitlab.com> <dzaporozhets@sphereconsultinginc.com>
Douwe Maan <douwe@gitlab.com> <douwe@selenight.nl>
Douwe Maan <douwe@gitlab.com> <me@douwe.me>
Grzegorz Bizon <grzegorz@gitlab.com> <grzegorz.bizon@ntsn.pl>
Grzegorz Bizon <grzegorz@gitlab.com> <grzesiek.bizon@gmail.com>
Jacob Vosmaer <jacob@gitlab.com> <contact@jacobvosmaer.nl>
Jacob Vosmaer <jacob@gitlab.com> Jacob Vosmaer (GitLab) <jacob@gitlab.com>
Jacob Schatz <jschatz@gitlab.com> <jacobschatz@Jacobs-MacBook-Pro.local>
Jacob Schatz <jschatz@gitlab.com> <jacobschatz@Jacobs-MBP.fios-router.home>
Jacob Schatz <jschatz@gitlab.com> <jschatz1@gmail.com>
James Lopez <james@jameslopez.es> <james@gitlab.com>
James Lopez <james@jameslopez.es> <james.lopez@vodafone.com>
Kamil Trzciński <kamil@gitlab.com> <ayufan@ayufan.eu>
Marin Jankovski <maxlazio@gmail.com> <marin@gitlab.com>
Phil Hughes <me@iamphill.com> <theephil@gmail.com>
Rémy Coutable <remy@rymai.me> <remy@gitlab.com>
Robert Schilling <rschilling@student.tugraz.at> <Razer6@users.noreply.github.com>
Robert Schilling <rschilling@student.tugraz.at> <schilling.ro@gmail.com>
Robert Speicher <robert@gitlab.com> <rspeicher@gmail.com>
Stan Hu <stanhu@gmail.com> <stanhu@alum.mit.edu>
Stan Hu <stanhu@gmail.com> <stanhu@packetzoom.com>
Stan Hu <stanhu@gmail.com> <stanhu@users.noreply.github.com>
Stan Hu <stanhu@gmail.com> stanhu <stanhu@gmail.com>
Sytse Sijbrandij <sytse@gitlab.com> <sytse+admin@gitlab.com>
Sytse Sijbrandij <sytse@gitlab.com> <sytse@dosire.com>
Sytse Sijbrandij <sytse@gitlab.com> <sytses@gmail.com>
Sytse Sijbrandij <sytse@gitlab.com> dosire <sytse@gitlab.com>
......@@ -149,19 +149,19 @@ Style/EmptyLinesAroundAccessModifier:
# Keeps track of empty lines around block bodies.
Style/EmptyLinesAroundBlockBody:
Enabled: false
Enabled: true
# Keeps track of empty lines around class bodies.
Style/EmptyLinesAroundClassBody:
Enabled: false
Enabled: true
# Keeps track of empty lines around module bodies.
Style/EmptyLinesAroundModuleBody:
Enabled: false
Enabled: true
# Keeps track of empty lines around method bodies.
Style/EmptyLinesAroundMethodBody:
Enabled: false
Enabled: true
# Avoid the use of END blocks.
Style/EndBlock:
......@@ -373,6 +373,10 @@ Style/SpaceAfterNot:
Style/SpaceAfterSemicolon:
Enabled: true
# Use space around equals in parameter default
Style/SpaceAroundEqualsInParameterDefault:
Enabled: true
# Use a space around keywords if appropriate.
Style/SpaceAroundKeyword:
Enabled: true
......
......@@ -339,13 +339,6 @@ Style/SingleLineBlockParams:
Style/SingleLineMethods:
Enabled: false
# Offense count: 14
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: space, no_space
Style/SpaceAroundEqualsInParameterDefault:
Enabled: false
# Offense count: 119
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
......
Please view this file on the master branch, on stable branches it's out of date.
v 8.11.0 (unreleased)
- Fix don't pass a local variable called `i` to a partial. !20510 (herminiotorres)
- Fix rename `add_users_into_project` and `projects_ids`. !20512 (herminiotorres)
- Fix the title of the toggle dropdown button. !5515 (herminiotorres)
- Improve diff performance by eliminating redundant checks for text blobs
- Convert switch icon into icon font (ClemMakesApps)
- API: Endpoints for enabling and disabling deploy keys
- Use long options for curl examples in documentation !5703 (winniehell)
- Remove magic comments (`# encoding: UTF-8`) from Ruby files. !5456 (winniehell)
- Add support for relative links starting with ./ or / to RelativeLinkFilter (winniehell)
- Ignore URLs starting with // in Markdown links !5677 (winniehell)
- Fix CI status icon link underline (ClemMakesApps)
- The Repository class is now instrumented
- Fix filter label tooltip HTML rendering (ClemMakesApps)
- Cache the commit author in RequestStore to avoid extra lookups in PostReceive
- Expand commit message width in repo view (ClemMakesApps)
- Cache highlighted diff lines for merge requests
- Fix of 'Commits being passed to custom hooks are already reachable when using the UI'
- Fix awardable button mutuality loading spinners (ClemMakesApps)
- Add support for using RequestStore within Sidekiq tasks via SIDEKIQ_REQUEST_STORE env variable
- Optimize maximum user access level lookup in loading of notes
- Add "No one can push" as an option for protected branches. !5081
- Improve performance of AutolinkFilter#text_parse by using XPath
- Add experimental Redis Sentinel support !1877
- Environments have an url to link to
- Update `timeago` plugin to use multiple string/locale settings
- Remove unused images (ClemMakesApps)
- Limit git rev-list output count to one in forced push check
- Clean up unused routes (Josef Strzibny)
- Fix issue on empty project to allow developers to only push to protected branches if given permission
- Add green outline to New Branch button. !5447 (winniehell)
- Optimize generating of cache keys for issues and notes
- Improve performance of syntax highlighting Markdown code blocks
- Update to gitlab_git 10.4.1 and take advantage of preserved Ref objects
- Remove delay when hitting "Reply..." button on page with a lot of discussions
- Retrieve rendered HTML from cache in one request
- Fix renaming repository when name contains invalid chararacters under project settings
- Upgrade Grape from 0.13.0 to 0.15.0. !4601
- Fix devise deprecation warnings.
- Update version_sorter and use new interface for faster tag sorting
- Optimize checking if a user has read access to a list of issues !5370
- Store all DB secrets in secrets.yml, under descriptive names !5274
- Nokogiri's various parsing methods are now instrumented
- Add simple identifier to public SSH keys (muteor)
- Admin page now references docs instead of a specific file !5600 (AnAverageHuman)
- Add a way to send an email and create an issue based on private personal token. Find the email address from issues page. !3363
- Fix filter input alignment (ClemMakesApps)
- Include old revision in merge request update hooks (Ben Boeckel)
- Add build event color in HipChat messages (David Eisner)
- Make fork counter always clickable. !5463 (winniehell)
- Document that webhook secret token is sent in X-Gitlab-Token HTTP header !5664 (lycoperdon)
- Gitlab::Highlight is now instrumented
- All created issues, API or WebUI, can be submitted to Akismet for spam check !5333
- Allow users to import cross-repository pull requests from GitHub
- The overhead of instrumented method calls has been reduced
- Remove `search_id` of labels dropdown filter to fix 'Missleading URI for labels in Merge Requests and Issues view'. !5368 (Scott Le)
- Load project invited groups and members eagerly in `ProjectTeam#fetch_members`
- Add pipeline events hook
- Bump gitlab_git to speedup DiffCollection iterations
- Rewrite description of a blocked user in admin settings. (Elias Werberich)
- Make branches sortable without push permission !5462 (winniehell)
- Check for Ci::Build artifacts at database level on pipeline partial
- Convert image diff background image to CSS (ClemMakesApps)
- Remove unnecessary index_projects_on_builds_enabled index from the projects table
- Make "New issue" button in Issue page less obtrusive !5457 (winniehell)
- Gitlab::Metrics.current_transaction needs to be public for RailsQueueDuration
- Fix search for notes which belongs to deleted objects
- Add GitLab Workhorse version to admin dashboard (Katarzyna Kobierska Ula Budziszewska)
- Allow branch names ending with .json for graph and network page !5579 (winniehell)
- Add the `sprockets-es6` gem
- Multiple trigger variables show in separate lines (Katarzyna Kobierska Ula Budziszewska)
- Profile requests when a header is passed
- Avoid calculation of line_code and position for _line partial when showing diff notes on discussion tab.
- Speedup DiffNote#active? on discussions, preloading noteables and avoid touching git repository to return diff_refs when possible
- Add commit stats in commit api. !5517 (dixpac)
- Add CI configuration button on project page
- Make error pages responsive (Takuya Noguchi)
- Fix skip_repo parameter being ignored when destroying a namespace
- Change requests_profiles resource constraint to catch virtually any file
- Bump gitlab_git to lazy load compare commits
- Reduce number of queries made for merge_requests/:id/diffs
- Sensible state specific default sort order for issues and merge requests !5453 (tomb0y)
- Fix RequestProfiler::Middleware error when code is reloaded in development
- Catch what warden might throw when profiling requests to re-throw it
- Avoid commit lookup on diff_helper passing existing local variable to the helper method
- Add description to new_issue email and new_merge_request_email in text/plain content type. !5663 (dixpac)
- Speed up and reduce memory usage of Commit#repo_changes, Repository#expire_avatar_cache and IrkerWorker
- Add unfold links for Side-by-Side view. !5415 (Tim Masliuchenko)
- Adds support for pending invitation project members importing projects
- Update devise initializer to turn on changed password notification emails. !5648 (tombell)
- Avoid to show the original password field when password is automatically set. !5712 (duduribeiro)
- Fix importing GitLab projects with an invalid MR source project
- Sort folders with submodules in Files view !5521
v 8.10.5
- Add a data migration to fix some missing timestamps in the members table. !5670
- Revert the "Defend against 'Host' header injection" change in the source NGINX templates. !5706
- Cache project count for 5 minutes to reduce DB load. !5746 & !5754
v 8.10.4
- Don't close referenced upstream issues from a forked project.
- Fixes issue with dropdowns `enter` key not working correctly. !5544
- Fix Import/Export project import not working in HA mode. !5618
- Fix Import/Export error checking versions. !5638
v 8.10.3
- Fix Import/Export issue importing milestones and labels not associated properly. !5426
......@@ -135,6 +192,9 @@ v 8.10.0
- Fix check for New Branch button on Issue page. !4630 (winniehell)
- Fix GFM autocomplete not working on wiki pages
- Fixed enter key not triggering click on first row when searching in a dropdown
- Updated dropdowns in issuable form to use new GitLab dropdown style
- Make images fit to the size of the viewport !4810
- Fix check for New Branch button on Issue page !4630 (winniehell)
- Fix MR-auto-close text added to description. !4836
- Support U2F devices in Firefox. !5177
- Fix issue, preventing users w/o push access to sort tags. !5105 (redetection)
......
......@@ -41,6 +41,8 @@ abbreviation.
If you have read this guide and want to know how the GitLab [core team]
operates please see [the GitLab contributing process](PROCESS.md).
- [GitLab Inc engineers should refer to the engineering workflow document](https://about.gitlab.com/handbook/engineering/workflow/)
## Contributor license agreement
By submitting code as an individual you agree to the
......@@ -334,6 +336,10 @@ request is as follows:
1. If your code creates new files on disk please read the
[shared files guidelines](doc/development/shared_files.md).
1. When writing commit messages please follow [these](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) [guidelines](http://chris.beams.io/posts/git-commit/).
1. If your merge request adds one or more migrations, make sure to execute all
migrations on a fresh database before the MR is reviewed. If the review leads
to large changes in the MR, do this again once the review is complete.
1. For more complex migrations, write tests.
The **official merge window** is in the beginning of the month from the 1st to
the 7th day of the month. This is the best time to submit an MR and get
......@@ -459,8 +465,10 @@ merge request:
- multi-line method chaining style **Option B**: dot `.` on previous line
- string literal quoting style **Option A**: single quoted by default
1. [Rails](https://github.com/bbatsov/rails-style-guide)
1. [Newlines styleguide][newlines-styleguide]
1. [Testing](doc/development/testing.md)
1. [CoffeeScript](https://github.com/thoughtbot/guides/tree/master/style/coffeescript)
1. [JavaScript (ES6)](https://github.com/airbnb/javascript)
1. [JavaScript (ES5)](https://github.com/airbnb/javascript/tree/master/es5)
1. [SCSS styleguide][scss-styleguide]
1. [Shell commands](doc/development/shell_commands.md) created by GitLab
contributors to enhance security
......@@ -530,6 +538,7 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor
[rss-naming]: https://github.com/bbatsov/ruby-style-guide/blob/master/README.md#naming
[doc-styleguide]: doc/development/doc_styleguide.md "Documentation styleguide"
[scss-styleguide]: doc/development/scss_styleguide.md "SCSS styleguide"
[newlines-styleguide]: doc/development/newlines_styleguide.md "Newlines styleguide"
[gitlab-design]: https://gitlab.com/gitlab-org/gitlab-design
[free Antetype viewer (Mac OSX only)]: https://itunes.apple.com/us/app/antetype-viewer/id824152298?mt=12
[`gitlab8.atype` file]: https://gitlab.com/gitlab-org/gitlab-design/tree/master/current/
......
......@@ -53,7 +53,7 @@ gem 'browser', '~> 2.2'
# Extracting information from a git repository
# Provide access to Gitlab::Git library
gem 'gitlab_git', '~> 10.4.2'
gem 'gitlab_git', '~> 10.4.5'
# LDAP Auth
# GitLab fork with several improvements to original library. For full list of changes
......@@ -69,7 +69,7 @@ gem 'gollum-rugged_adapter', '~> 0.4.2', require: false
gem 'github-linguist', '~> 4.7.0', require: 'linguist'
# API
gem 'grape', '~> 0.13.0'
gem 'grape', '~> 0.15.0'
gem 'grape-entity', '~> 0.4.2'
gem 'rack-cors', '~> 0.4.0', require: 'rack/cors'
......@@ -154,7 +154,7 @@ gem 'settingslogic', '~> 2.0.9'
# Misc
gem 'version_sorter', '~> 2.0.0'
gem 'version_sorter', '~> 2.1.0'
# Cache
gem 'redis-rails', '~> 4.0.0'
......@@ -326,7 +326,7 @@ group :production do
gem 'gitlab_meta', '7.0'
end
gem 'newrelic_rpm', '~> 3.14'
gem 'newrelic_rpm', '~> 3.16'
gem 'octokit', '~> 4.3.0'
......
......@@ -278,7 +278,7 @@ GEM
diff-lcs (~> 1.1)
mime-types (>= 1.16, < 3)
posix-spawn (~> 0.3)
gitlab_git (10.4.2)
gitlab_git (10.4.5)
activesupport (~> 4.0)
charlock_holmes (~> 0.7.3)
github-linguist (~> 4.7.0)
......@@ -308,7 +308,7 @@ GEM
json
multi_json
request_store (>= 1.0)
grape (0.13.0)
grape (0.15.0)
activesupport
builder
hashie (>= 2.1.0)
......@@ -404,7 +404,7 @@ GEM
nested_form (0.3.2)
net-ldap (0.12.1)
net-ssh (3.0.1)
newrelic_rpm (3.14.1.311)
newrelic_rpm (3.16.0.318)
nokogiri (1.6.8)
mini_portile2 (~> 2.1.0)
pkg-config (~> 1.1.7)
......@@ -778,7 +778,7 @@ GEM
uniform_notifier (1.10.0)
uuid (2.3.8)
macaddr (~> 1.0)
version_sorter (2.0.0)
version_sorter (2.1.0)
virtus (1.0.5)
axiom-types (~> 0.1)
coercible (~> 1.0)
......@@ -870,13 +870,13 @@ DEPENDENCIES
github-linguist (~> 4.7.0)
github-markup (~> 1.4)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab_git (~> 10.4.2)
gitlab_git (~> 10.4.5)
gitlab_meta (= 7.0)
gitlab_omniauth-ldap (~> 1.2.1)
gollum-lib (~> 4.2)
gollum-rugged_adapter (~> 0.4.2)
gon (~> 6.1.0)
grape (~> 0.13.0)
grape (~> 0.15.0)
grape-entity (~> 0.4.2)
hamlit (~> 2.5)
health_check (~> 2.1.0)
......@@ -902,7 +902,7 @@ DEPENDENCIES
mysql2 (~> 0.3.16)
nested_form (~> 0.3.2)
net-ssh (~> 3.0.1)
newrelic_rpm (~> 3.14)
newrelic_rpm (~> 3.16)
nokogiri (~> 1.6.7, >= 1.6.7.2)
oauth2 (~> 1.2.0)
octokit (~> 4.3.0)
......@@ -989,7 +989,7 @@ DEPENDENCIES
unf (~> 0.1.4)
unicorn (~> 4.9.0)
unicorn-worker-killer (~> 0.4.2)
version_sorter (~> 2.0.0)
version_sorter (~> 2.1.0)
virtus (~> 1.0.1)
vmstat (~> 2.1.1)
web-console (~> 2.0)
......
......@@ -8,6 +8,8 @@ treatment, etc.). And so that maintainers know what to expect from contributors
(use the latest version, ensure that the issue is addressed, friendly treatment,
etc.).
- [GitLab Inc engineers should refer to the engineering workflow document](https://about.gitlab.com/handbook/engineering/workflow/)
## Common actions
### Issue team
......
......@@ -287,7 +287,7 @@
$('.page-with-sidebar').toggleClass('page-sidebar-collapsed page-sidebar-expanded').removeClass('page-sidebar-pinned');
$('.navbar-fixed-top').removeClass('header-pinned-nav');
}
return $document.off('click', '.js-nav-pin').on('click', '.js-nav-pin', function(e) {
$document.off('click', '.js-nav-pin').on('click', '.js-nav-pin', function(e) {
var $page, $pinBtn, $tooltip, $topNav, doPinNav, tooltipText;
e.preventDefault();
$pinBtn = $(e.currentTarget);
......@@ -315,6 +315,8 @@
$tooltip.find('.tooltip-inner').text(tooltipText);
return $pinBtn.attr('title', tooltipText).tooltip('fixTitle');
});
});
// Custom time ago
gl.utils.shortTimeAgo($('.js-short-timeago'));
});
}).call(this);
......@@ -161,23 +161,11 @@
$emojiButton = votesBlock.find("[data-emoji=" + mutualVote + "]").parent();
isAlreadyVoted = $emojiButton.hasClass('active');
if (isAlreadyVoted) {
this.showEmojiLoader($emojiButton);
return this.addAward(votesBlock, awardUrl, mutualVote, false, function() {
return $emojiButton.removeClass('is-loading');
});
this.addAward(votesBlock, awardUrl, mutualVote, false);
}
}
};
AwardsHandler.prototype.showEmojiLoader = function($emojiButton) {
var $loader;
$loader = $emojiButton.find('.fa-spinner');
if (!$loader.length) {
$emojiButton.append('<i class="fa fa-spinner fa-spin award-control-icon award-control-icon-loading"></i>');
}
return $emojiButton.addClass('is-loading');
};
AwardsHandler.prototype.isActive = function($emojiButton) {
return $emojiButton.hasClass('active');
};
......
......@@ -128,7 +128,7 @@
$date = $('.js-artifacts-remove');
if ($date.length) {
date = $date.text();
return $date.text($.timefor(new Date(date), ' '));
return $date.text($.timefor(new Date(date.replace(/-/g, '/')), ' '));
}
};
......
......@@ -10,7 +10,7 @@
$(document).off('click', '.js-unfold');
$(document).on('click', '.js-unfold', (function(_this) {
return function(event) {
var line_number, link, offset, old_line, params, prev_new_line, prev_old_line, ref, ref1, since, target, to, unfold, unfoldBottom;
var line_number, link, file, offset, old_line, params, prev_new_line, prev_old_line, ref, ref1, since, target, to, unfold, unfoldBottom;
target = $(event.target);
unfoldBottom = target.hasClass('js-unfold-bottom');
unfold = true;
......@@ -31,14 +31,16 @@
unfold = false;
}
}
link = target.parents('.diff-file').attr('data-blob-diff-path');
file = target.parents('.diff-file');
link = file.data('blob-diff-path');
params = {
since: since,
to: to,
bottom: unfoldBottom,
offset: offset,
unfold: unfold,
indent: 1
indent: 1,
view: file.data('view')
};
return $.get(link, params, function(response) {
return target.parent().replaceWith(response);
......@@ -48,26 +50,13 @@
}
Diff.prototype.lineNumbers = function(line) {
var i, l, len, line_number, line_numbers, lines, results;
if (!line.children().length) {
return [0, 0];
}
lines = line.children().slice(0, 2);
line_numbers = (function() {
var i, len, results;
results = [];
for (i = 0, len = lines.length; i < len; i++) {
l = lines[i];
results.push($(l).attr('data-linenumber'));
}
return results;
})();
results = [];
for (i = 0, len = line_numbers.length; i < len; i++) {
line_number = line_numbers[i];
results.push(parseInt(line_number));
}
return results;
return line.find('.diff-line-num').map(function() {
return parseInt($(this).data('linenumber'));
});
};
return Diff;
......
......@@ -173,8 +173,8 @@
new Search();
break;
case 'projects:protected_branches:index':
new ProtectedBranchesAccessSelect($(".new_protected_branch"), false, true);
new ProtectedBranchesAccessSelect($(".protected-branches-list"), true, false);
new gl.ProtectedBranchCreate();
new gl.ProtectedBranchEditList();
break;
}
switch (path.first()) {
......@@ -186,6 +186,12 @@
break;
case 'projects':
new NamespaceSelects();
break;
case 'labels':
switch (path[2]) {
case 'edit':
new Labels();
}
}
break;
case 'dashboard':
......@@ -211,6 +217,7 @@
new ProjectNew();
break;
case 'show':
new Star();
new ProjectNew();
new ProjectShow();
new NotificationsDropdown();
......
......@@ -47,8 +47,8 @@
}
}
},
setup: function(wrap) {
this.input = $('.js-gfm-input');
setup: function(input) {
this.input = input || $('.js-gfm-input');
this.destroyAtWho();
this.setupAtWho();
if (this.dataSource) {
......
......@@ -28,38 +28,43 @@
};
})(this));
timeout = "";
this.input.on("keyup", (function(_this) {
return function(e) {
this.input
.on('keydown', function (e) {
var keyCode = e.which;
if (keyCode === 13) {
e.preventDefault()
}
})
.on('keyup', function(e) {
var keyCode;
keyCode = e.which;
if (ARROW_KEY_CODES.indexOf(keyCode) >= 0) {
return;
}
if (_this.input.val() !== "" && !$inputContainer.hasClass(HAS_VALUE_CLASS)) {
if (this.input.val() !== "" && !$inputContainer.hasClass(HAS_VALUE_CLASS)) {
$inputContainer.addClass(HAS_VALUE_CLASS);
} else if (_this.input.val() === "" && $inputContainer.hasClass(HAS_VALUE_CLASS)) {
} else if (this.input.val() === "" && $inputContainer.hasClass(HAS_VALUE_CLASS)) {
$inputContainer.removeClass(HAS_VALUE_CLASS);
}
if (keyCode === 13) {
return false;
}
if (_this.options.remote) {
if (this.options.remote) {
clearTimeout(timeout);
return timeout = setTimeout(function() {
var blur_field;
blur_field = _this.shouldBlur(keyCode);
if (blur_field && _this.filterInputBlur) {
_this.input.blur();
var blurField = this.shouldBlur(keyCode);
if (blurField && this.filterInputBlur) {
this.input.blur();
}
return _this.options.query(_this.input.val(), function(data) {
return _this.options.callback(data);
});
}, 250);
return this.options.query(this.input.val(), function(data) {
return this.options.callback(data);
}.bind(this));
}.bind(this), 250);
} else {
return _this.filter(_this.input.val());
return this.filter(this.input.val());
}
};
})(this));
}.bind(this));
}
GitLabDropdownFilter.prototype.shouldBlur = function(keyCode) {
......@@ -382,6 +387,7 @@
GitLabDropdown.prototype.opened = function() {
var contentHtml;
currentIndex = -1;
this.addArrowKeyEvent();
if (this.options.setIndeterminateIds) {
this.options.setIndeterminateIds.call(this);
......@@ -601,7 +607,7 @@
return this.dropdown.before($input);
};
GitLabDropdown.prototype.selectRowAtIndex = function(e, index) {
GitLabDropdown.prototype.selectRowAtIndex = function(index) {
var $el, selector;
selector = ".dropdown-content li:not(.divider,.dropdown-header,.separator):eq(" + index + ") a";
if (this.dropdown.find(".dropdown-toggle-page").length) {
......@@ -609,8 +615,6 @@
}
$el = $(selector, this.dropdown);
if ($el.length) {
e.preventDefault();
e.stopImmediatePropagation();
return $el.first().trigger('click');
}
};
......@@ -619,7 +623,7 @@
var $input, ARROW_KEY_CODES, selector;
ARROW_KEY_CODES = [38, 40];
$input = this.dropdown.find(".dropdown-input-field");
selector = '.dropdown-content li:not(.divider,.dropdown-header,.separator)';
selector = '.dropdown-content li:not(.divider,.dropdown-header,.separator):visible';
if (this.dropdown.find(".dropdown-toggle-page").length) {
selector = ".dropdown-page-one " + selector;
}
......@@ -647,7 +651,7 @@
return false;
}
if (currentKeyCode === 13 && currentIndex !== -1) {
return _this.selectRowAtIndex(e, currentIndex);
return _this.selectRowAtIndex($('.is-focused', _this.dropdown).closest('li').index() - 1);
}
};
})(this));
......
......@@ -21,7 +21,7 @@
this.form.find('.div-dropzone').remove();
this.form.addClass('gfm-form');
disableButtonIfEmptyField(this.form.find('.js-note-text'), this.form.find('.js-comment-button'));
GitLab.GfmAutoComplete.setup();
GitLab.GfmAutoComplete.setup(this.form.find('.js-gfm-input'));
new DropzoneInput(this.form);
autosize(this.textarea);
this.addEventListeners();
......
......@@ -8,13 +8,16 @@
base.utils = {};
}
w.gl.utils.days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
w.gl.utils.formatDate = function(datetime) {
return dateFormat(datetime, 'mmm d, yyyy h:MMtt Z');
};
w.gl.utils.getDayName = function(date) {
return this.days[date.getDay()];
};
return w.gl.utils.localTimeAgo = function($timeagoEls, setTimeago) {
w.gl.utils.localTimeAgo = function($timeagoEls, setTimeago) {
if (setTimeago == null) {
setTimeago = true;
}
......@@ -31,6 +34,39 @@
});
}
};
w.gl.utils.shortTimeAgo = function($el) {
var shortLocale, tmpLocale;
shortLocale = {
prefixAgo: null,
prefixFromNow: null,
suffixAgo: 'ago',
suffixFromNow: 'from now',
seconds: '1 min',
minute: '1 min',
minutes: '%d mins',
hour: '1 hr',
hours: '%d hrs',
day: '1 day',
days: '%d days',
month: '1 month',
months: '%d months',
year: '1 year',
years: '%d years',
wordSeparator: ' ',
numbers: []
};
tmpLocale = $.timeago.settings.strings;
$el.each(function(el) {
var $el1;
$el1 = $(this);
return $el1.attr('title', gl.utils.formatDate($el.attr('datetime')));
});
$.timeago.settings.strings = shortLocale;
$el.timeago();
$.timeago.settings.strings = tmpLocale;
};
})(window);
}).call(this);
function md5 (str) {
// http://kevin.vanzonneveld.net
// + original by: Webtoolkit.info (http://www.webtoolkit.info/)
// + namespaced by: Michael White (http://getsprink.com)
// + tweaked by: Jack
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + input by: Brett Zamir (http://brett-zamir.me)
// + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// - depends on: utf8_encode
// * example 1: md5('Kevin van Zonneveld');
// * returns 1: '6e658d4bfcb59cc13f96c14450ac40b9'
var xl;
var rotateLeft = function (lValue, iShiftBits) {
return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits));
};
var addUnsigned = function (lX, lY) {
var lX4, lY4, lX8, lY8, lResult;
lX8 = (lX & 0x80000000);
lY8 = (lY & 0x80000000);
lX4 = (lX & 0x40000000);
lY4 = (lY & 0x40000000);
lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF);
if (lX4 & lY4) {
return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
}
if (lX4 | lY4) {
if (lResult & 0x40000000) {
return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
} else {
return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
}
} else {
return (lResult ^ lX8 ^ lY8);
}
};
var _F = function (x, y, z) {
return (x & y) | ((~x) & z);
};
var _G = function (x, y, z) {
return (x & z) | (y & (~z));
};
var _H = function (x, y, z) {
return (x ^ y ^ z);
};
var _I = function (x, y, z) {
return (y ^ (x | (~z)));
};
var _FF = function (a, b, c, d, x, s, ac) {
a = addUnsigned(a, addUnsigned(addUnsigned(_F(b, c, d), x), ac));
return addUnsigned(rotateLeft(a, s), b);
};
var _GG = function (a, b, c, d, x, s, ac) {
a = addUnsigned(a, addUnsigned(addUnsigned(_G(b, c, d), x), ac));
return addUnsigned(rotateLeft(a, s), b);
};
var _HH = function (a, b, c, d, x, s, ac) {
a = addUnsigned(a, addUnsigned(addUnsigned(_H(b, c, d), x), ac));
return addUnsigned(rotateLeft(a, s), b);
};
var _II = function (a, b, c, d, x, s, ac) {
a = addUnsigned(a, addUnsigned(addUnsigned(_I(b, c, d), x), ac));
return addUnsigned(rotateLeft(a, s), b);
};
var convertToWordArray = function (str) {
var lWordCount;
var lMessageLength = str.length;
var lNumberOfWords_temp1 = lMessageLength + 8;
var lNumberOfWords_temp2 = (lNumberOfWords_temp1 - (lNumberOfWords_temp1 % 64)) / 64;
var lNumberOfWords = (lNumberOfWords_temp2 + 1) * 16;
var lWordArray = new Array(lNumberOfWords - 1);
var lBytePosition = 0;
var lByteCount = 0;
while (lByteCount < lMessageLength) {
lWordCount = (lByteCount - (lByteCount % 4)) / 4;
lBytePosition = (lByteCount % 4) * 8;
lWordArray[lWordCount] = (lWordArray[lWordCount] | (str.charCodeAt(lByteCount) << lBytePosition));
lByteCount++;
}
lWordCount = (lByteCount - (lByteCount % 4)) / 4;
lBytePosition = (lByteCount % 4) * 8;
lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition);
lWordArray[lNumberOfWords - 2] = lMessageLength << 3;
lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29;
return lWordArray;
};
var wordToHex = function (lValue) {
var wordToHexValue = "",
wordToHexValue_temp = "",
lByte, lCount;
for (lCount = 0; lCount <= 3; lCount++) {
lByte = (lValue >>> (lCount * 8)) & 255;
wordToHexValue_temp = "0" + lByte.toString(16);
wordToHexValue = wordToHexValue + wordToHexValue_temp.substr(wordToHexValue_temp.length - 2, 2);
}
return wordToHexValue;
};
var x = [],
k, AA, BB, CC, DD, a, b, c, d, S11 = 7,
S12 = 12,
S13 = 17,
S14 = 22,
S21 = 5,
S22 = 9,
S23 = 14,
S24 = 20,
S31 = 4,
S32 = 11,
S33 = 16,
S34 = 23,
S41 = 6,
S42 = 10,
S43 = 15,
S44 = 21;
str = this.utf8_encode(str);
x = convertToWordArray(str);
a = 0x67452301;
b = 0xEFCDAB89;
c = 0x98BADCFE;
d = 0x10325476;
xl = x.length;
for (k = 0; k < xl; k += 16) {
AA = a;
BB = b;
CC = c;
DD = d;
a = _FF(a, b, c, d, x[k + 0], S11, 0xD76AA478);
d = _FF(d, a, b, c, x[k + 1], S12, 0xE8C7B756);
c = _FF(c, d, a, b, x[k + 2], S13, 0x242070DB);
b = _FF(b, c, d, a, x[k + 3], S14, 0xC1BDCEEE);
a = _FF(a, b, c, d, x[k + 4], S11, 0xF57C0FAF);
d = _FF(d, a, b, c, x[k + 5], S12, 0x4787C62A);
c = _FF(c, d, a, b, x[k + 6], S13, 0xA8304613);
b = _FF(b, c, d, a, x[k + 7], S14, 0xFD469501);
a = _FF(a, b, c, d, x[k + 8], S11, 0x698098D8);
d = _FF(d, a, b, c, x[k + 9], S12, 0x8B44F7AF);
c = _FF(c, d, a, b, x[k + 10], S13, 0xFFFF5BB1);
b = _FF(b, c, d, a, x[k + 11], S14, 0x895CD7BE);
a = _FF(a, b, c, d, x[k + 12], S11, 0x6B901122);
d = _FF(d, a, b, c, x[k + 13], S12, 0xFD987193);
c = _FF(c, d, a, b, x[k + 14], S13, 0xA679438E);
b = _FF(b, c, d, a, x[k + 15], S14, 0x49B40821);
a = _GG(a, b, c, d, x[k + 1], S21, 0xF61E2562);
d = _GG(d, a, b, c, x[k + 6], S22, 0xC040B340);
c = _GG(c, d, a, b, x[k + 11], S23, 0x265E5A51);
b = _GG(b, c, d, a, x[k + 0], S24, 0xE9B6C7AA);
a = _GG(a, b, c, d, x[k + 5], S21, 0xD62F105D);
d = _GG(d, a, b, c, x[k + 10], S22, 0x2441453);
c = _GG(c, d, a, b, x[k + 15], S23, 0xD8A1E681);
b = _GG(b, c, d, a, x[k + 4], S24, 0xE7D3FBC8);
a = _GG(a, b, c, d, x[k + 9], S21, 0x21E1CDE6);
d = _GG(d, a, b, c, x[k + 14], S22, 0xC33707D6);
c = _GG(c, d, a, b, x[k + 3], S23, 0xF4D50D87);
b = _GG(b, c, d, a, x[k + 8], S24, 0x455A14ED);
a = _GG(a, b, c, d, x[k + 13], S21, 0xA9E3E905);
d = _GG(d, a, b, c, x[k + 2], S22, 0xFCEFA3F8);
c = _GG(c, d, a, b, x[k + 7], S23, 0x676F02D9);
b = _GG(b, c, d, a, x[k + 12], S24, 0x8D2A4C8A);
a = _HH(a, b, c, d, x[k + 5], S31, 0xFFFA3942);
d = _HH(d, a, b, c, x[k + 8], S32, 0x8771F681);
c = _HH(c, d, a, b, x[k + 11], S33, 0x6D9D6122);
b = _HH(b, c, d, a, x[k + 14], S34, 0xFDE5380C);
a = _HH(a, b, c, d, x[k + 1], S31, 0xA4BEEA44);
d = _HH(d, a, b, c, x[k + 4], S32, 0x4BDECFA9);
c = _HH(c, d, a, b, x[k + 7], S33, 0xF6BB4B60);
b = _HH(b, c, d, a, x[k + 10], S34, 0xBEBFBC70);
a = _HH(a, b, c, d, x[k + 13], S31, 0x289B7EC6);
d = _HH(d, a, b, c, x[k + 0], S32, 0xEAA127FA);
c = _HH(c, d, a, b, x[k + 3], S33, 0xD4EF3085);
b = _HH(b, c, d, a, x[k + 6], S34, 0x4881D05);
a = _HH(a, b, c, d, x[k + 9], S31, 0xD9D4D039);
d = _HH(d, a, b, c, x[k + 12], S32, 0xE6DB99E5);
c = _HH(c, d, a, b, x[k + 15], S33, 0x1FA27CF8);
b = _HH(b, c, d, a, x[k + 2], S34, 0xC4AC5665);
a = _II(a, b, c, d, x[k + 0], S41, 0xF4292244);
d = _II(d, a, b, c, x[k + 7], S42, 0x432AFF97);
c = _II(c, d, a, b, x[k + 14], S43, 0xAB9423A7);
b = _II(b, c, d, a, x[k + 5], S44, 0xFC93A039);
a = _II(a, b, c, d, x[k + 12], S41, 0x655B59C3);
d = _II(d, a, b, c, x[k + 3], S42, 0x8F0CCC92);
c = _II(c, d, a, b, x[k + 10], S43, 0xFFEFF47D);
b = _II(b, c, d, a, x[k + 1], S44, 0x85845DD1);
a = _II(a, b, c, d, x[k + 8], S41, 0x6FA87E4F);
d = _II(d, a, b, c, x[k + 15], S42, 0xFE2CE6E0);
c = _II(c, d, a, b, x[k + 6], S43, 0xA3014314);
b = _II(b, c, d, a, x[k + 13], S44, 0x4E0811A1);
a = _II(a, b, c, d, x[k + 4], S41, 0xF7537E82);
d = _II(d, a, b, c, x[k + 11], S42, 0xBD3AF235);
c = _II(c, d, a, b, x[k + 2], S43, 0x2AD7D2BB);
b = _II(b, c, d, a, x[k + 9], S44, 0xEB86D391);
a = addUnsigned(a, AA);
b = addUnsigned(b, BB);
c = addUnsigned(c, CC);
d = addUnsigned(d, DD);
}
var temp = wordToHex(a) + wordToHex(b) + wordToHex(c) + wordToHex(d);
return temp.toLowerCase();
}
function utf8_encode (argString) {
// http://kevin.vanzonneveld.net
// + original by: Webtoolkit.info (http://www.webtoolkit.info/)
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + improved by: sowberry
// + tweaked by: Jack
// + bugfixed by: Onno Marsman
// + improved by: Yves Sucaet
// + bugfixed by: Onno Marsman
// + bugfixed by: Ulrich
// + bugfixed by: Rafal Kukawski
// + improved by: kirilloid
// + bugfixed by: kirilloid
// * example 1: utf8_encode('Kevin van Zonneveld');
// * returns 1: 'Kevin van Zonneveld'
if (argString === null || typeof argString === "undefined") {
return "";
}
var string = (argString + ''); // .replace(/\r\n/g, "\n").replace(/\r/g, "\n");
var utftext = '',
start, end, stringl = 0;
start = end = 0;
stringl = string.length;
for (var n = 0; n < stringl; n++) {
var c1 = string.charCodeAt(n);
var enc = null;
if (c1 < 128) {
end++;
} else if (c1 > 127 && c1 < 2048) {
enc = String.fromCharCode(
(c1 >> 6) | 192,
( c1 & 63) | 128
);
} else if (c1 & 0xF800 != 0xD800) {
enc = String.fromCharCode(
(c1 >> 12) | 224,
((c1 >> 6) & 63) | 128,
( c1 & 63) | 128
);
} else { // surrogate pairs
if (c1 & 0xFC00 != 0xD800) { throw new RangeError("Unmatched trail surrogate at " + n); }
var c2 = string.charCodeAt(++n);
if (c2 & 0xFC00 != 0xDC00) { throw new RangeError("Unmatched lead surrogate at " + (n-1)); }
c1 = ((c1 & 0x3FF) << 10) + (c2 & 0x3FF) + 0x10000;
enc = String.fromCharCode(
(c1 >> 18) | 240,
((c1 >> 12) & 63) | 128,
((c1 >> 6) & 63) | 128,
( c1 & 63) | 128
);
}
if (enc !== null) {
if (end > start) {
utftext += string.slice(start, end);
}
utftext += enc;
start = end = n + 1;
}
}
if (end > start) {
utftext += string.slice(start, stringl);
}
return utftext;
}
......@@ -89,8 +89,14 @@
toggleLabel: function(obj, $el) {
return $el.text().trim();
},
clicked: function(e) {
return $dropdown.closest('form').submit();
clicked: function(selected, $el, e) {
e.preventDefault()
if ($('input[name="ref"]').length) {
var $form = $dropdown.closest('form'),
action = $form.attr('action'),
divider = action.indexOf('?') < 0 ? '?' : '&';
Turbolinks.visit(action + '' + divider + '' + $form.serialize());
}
}
});
});
......
(global => {
global.gl = global.gl || {};
gl.ProtectedBranchAccessDropdown = class {
constructor(options) {
const { $dropdown, data, onSelect } = options;
$dropdown.glDropdown({
data: data,
selectable: true,
inputId: $dropdown.data('input-id'),
fieldName: $dropdown.data('field-name'),
toggleLabel(item) {
return item.text;
},
clicked(item, $el, e) {
e.preventDefault();
onSelect();
}
});
}
}
})(window);
(global => {
global.gl = global.gl || {};
gl.ProtectedBranchCreate = class {
constructor() {
this.$wrap = this.$form = $('#new_protected_branch');
this.buildDropdowns();
}
buildDropdowns() {
const $allowedToMergeDropdown = this.$wrap.find('.js-allowed-to-merge');
const $allowedToPushDropdown = this.$wrap.find('.js-allowed-to-push');
// Cache callback
this.onSelectCallback = this.onSelect.bind(this);
// Allowed to Merge dropdown
new gl.ProtectedBranchAccessDropdown({
$dropdown: $allowedToMergeDropdown,
data: gon.merge_access_levels,
onSelect: this.onSelectCallback
});
// Allowed to Push dropdown
new gl.ProtectedBranchAccessDropdown({
$dropdown: $allowedToPushDropdown,
data: gon.push_access_levels,
onSelect: this.onSelectCallback
});
// Select default
$allowedToPushDropdown.data('glDropdown').selectRowAtIndex(0);
$allowedToMergeDropdown.data('glDropdown').selectRowAtIndex(0);
// Protected branch dropdown
new ProtectedBranchDropdown({
$dropdown: this.$wrap.find('.js-protected-branch-select'),
onSelect: this.onSelectCallback
});
}
// This will run after clicked callback
onSelect() {
// Enable submit button
const $branchInput = this.$wrap.find('input[name="protected_branch[name]"]');
const $allowedToMergeInput = this.$wrap.find('input[name="protected_branch[merge_access_level_attributes][access_level]"]');
const $allowedToPushInput = this.$wrap.find('input[name="protected_branch[push_access_level_attributes][access_level]"]');
if ($branchInput.val() && $allowedToMergeInput.val() && $allowedToPushInput.val()){
this.$form.find('input[type="submit"]').removeAttr('disabled');
}
}
}
})(window);
class ProtectedBranchDropdown {
constructor(options) {
this.onSelect = options.onSelect;
this.$dropdown = options.$dropdown;
this.$dropdownContainer = this.$dropdown.parent();
this.$dropdownFooter = this.$dropdownContainer.find('.dropdown-footer');
this.$protectedBranch = this.$dropdownContainer.find('.create-new-protected-branch');
this.buildDropdown();
this.bindEvents();
// Hide footer
this.$dropdownFooter.addClass('hidden');
}
buildDropdown() {
this.$dropdown.glDropdown({
data: this.getProtectedBranches.bind(this),
filterable: true,
remote: false,
search: {
fields: ['title']
},
selectable: true,
toggleLabel(selected) {
return (selected && 'id' in selected) ? selected.title : 'Protected Branch';
},
fieldName: 'protected_branch[name]',
text(protectedBranch) {
return _.escape(protectedBranch.title);
},
id(protectedBranch) {
return _.escape(protectedBranch.id);
},
onFilter: this.toggleCreateNewButton.bind(this),
clicked: (item, $el, e) => {
e.preventDefault();
this.onSelect();
}
});
}
bindEvents() {
this.$protectedBranch.on('click', this.onClickCreateWildcard.bind(this));
}
onClickCreateWildcard() {
this.$dropdown.data('glDropdown').remote.execute();
this.$dropdown.data('glDropdown').selectRowAtIndex(0);
}
getProtectedBranches(term, callback) {
if (this.selectedBranch) {
callback(gon.open_branches.concat(this.selectedBranch));
} else {
callback(gon.open_branches);
}
}
toggleCreateNewButton(branchName) {
this.selectedBranch = {
title: branchName,
id: branchName,
text: branchName
};
if (branchName) {
this.$dropdownContainer
.find('.create-new-protected-branch code')
.text(branchName);
}
this.$dropdownFooter.toggleClass('hidden', !branchName);
}
}
(global => {
global.gl = global.gl || {};
gl.ProtectedBranchEdit = class {
constructor(options) {
this.$wrap = options.$wrap;
this.$allowedToMergeDropdown = this.$wrap.find('.js-allowed-to-merge');
this.$allowedToPushDropdown = this.$wrap.find('.js-allowed-to-push');
this.buildDropdowns();
}
buildDropdowns() {
// Allowed to merge dropdown
new gl.ProtectedBranchAccessDropdown({
$dropdown: this.$allowedToMergeDropdown,
data: gon.merge_access_levels,
onSelect: this.onSelect.bind(this)
});
// Allowed to push dropdown
new gl.ProtectedBranchAccessDropdown({
$dropdown: this.$allowedToPushDropdown,
data: gon.push_access_levels,
onSelect: this.onSelect.bind(this)
});
}
onSelect() {
const $allowedToMergeInput = this.$wrap.find(`input[name="${this.$allowedToMergeDropdown.data('fieldName')}"]`);
const $allowedToPushInput = this.$wrap.find(`input[name="${this.$allowedToPushDropdown.data('fieldName')}"]`);
$.ajax({
type: 'POST',
url: this.$wrap.data('url'),
dataType: 'json',
data: {
_method: 'PATCH',
id: this.$wrap.data('banchId'),
protected_branch: {
merge_access_level_attributes: {
access_level: $allowedToMergeInput.val()
},
push_access_level_attributes: {
access_level: $allowedToPushInput.val()
}
}
},
success: () => {
this.$wrap.effect('highlight');
},
error() {
$.scrollTo(0);
new Flash('Failed to update branch!');
}
});
}
}
})(window);
(global => {
global.gl = global.gl || {};
gl.ProtectedBranchEditList = class {
constructor() {
this.$wrap = $('.protected-branches-list');
// Build edit forms
this.$wrap.find('.js-protected-branch-edit-form').each((i, el) => {
new gl.ProtectedBranchEdit({
$wrap: $(el)
});
});
}
}
})(window);
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
this.ProtectedBranchSelect = (function() {
function ProtectedBranchSelect(currentProject) {
this.toggleCreateNewButton = bind(this.toggleCreateNewButton, this);
this.getProtectedBranches = bind(this.getProtectedBranches, this);
$('.dropdown-footer').hide();
this.dropdown = $('.js-protected-branch-select').glDropdown({
data: this.getProtectedBranches,
filterable: true,
remote: false,
search: {
fields: ['title']
},
selectable: true,
toggleLabel: function(selected) {
if (selected && 'id' in selected) {
return selected.title;
} else {
return 'Protected Branch';
}
},
fieldName: 'protected_branch[name]',
text: function(protected_branch) {
return _.escape(protected_branch.title);
},
id: function(protected_branch) {
return _.escape(protected_branch.id);
},
onFilter: this.toggleCreateNewButton,
clicked: function() {
return $('.protect-branch-btn').attr('disabled', false);
}
});
$('.create-new-protected-branch').on('click', (function(_this) {
return function(event) {
_this.dropdown.data('glDropdown').remote.execute();
return _this.dropdown.data('glDropdown').selectRowAtIndex(event, 0);
};
})(this));
}
ProtectedBranchSelect.prototype.getProtectedBranches = function(term, callback) {
if (this.selectedBranch) {
return callback(gon.open_branches.concat(this.selectedBranch));
} else {
return callback(gon.open_branches);
}
};
ProtectedBranchSelect.prototype.toggleCreateNewButton = function(branchName) {
this.selectedBranch = {
title: branchName,
id: branchName,
text: branchName
};
if (branchName === '') {
$('.protected-branch-select-footer-list').addClass('hidden');
return $('.dropdown-footer').hide();
} else {
$('.create-new-protected-branch').text("Create Protected Branch: " + branchName);
$('.protected-branch-select-footer-list').removeClass('hidden');
return $('.dropdown-footer').show();
}
};
return ProtectedBranchSelect;
})();
}).call(this);
class ProtectedBranchesAccessSelect {
constructor(container, saveOnSelect, selectDefault) {
this.container = container;
this.saveOnSelect = saveOnSelect;
this.container.find(".allowed-to-merge").each((i, element) => {
var fieldName = $(element).data('field-name');
var dropdown = $(element).glDropdown({
data: gon.merge_access_levels,
selectable: true,
fieldName: fieldName,
clicked: _.chain(this.onSelect).partial(element).bind(this).value()
});
if (selectDefault) {
dropdown.data('glDropdown').selectRowAtIndex(document.createEvent("Event"), 0);
}
});
this.container.find(".allowed-to-push").each((i, element) => {
var fieldName = $(element).data('field-name');
var dropdown = $(element).glDropdown({
data: gon.push_access_levels,
selectable: true,
fieldName: fieldName,
clicked: _.chain(this.onSelect).partial(element).bind(this).value()
});
if (selectDefault) {
dropdown.data('glDropdown').selectRowAtIndex(document.createEvent("Event"), 0);
}
});
}
onSelect(dropdown, selected, element, e) {
$(dropdown).find('.dropdown-toggle-text').text(selected.text);
if (this.saveOnSelect) {
return $.ajax({
type: "POST",
url: $(dropdown).data('url'),
dataType: "json",
data: {
_method: 'PATCH',
id: $(dropdown).data('id'),
protected_branch: {
["" + ($(dropdown).data('type')) + "_attributes"]: {
"access_level": selected.id
}
}
},
success: function() {
var row;
row = $(e.target);
return row.closest('tr').effect('highlight');
},
error: function() {
return new Flash("Failed to update branch!", "alert");
}
});
}
}
}
......@@ -13,14 +13,15 @@
}
$('.js-user-search').each((function(_this) {
return function(i, dropdown) {
var options = {};
var $block, $collapsedSidebar, $dropdown, $loading, $selectbox, $value, abilityName, assignTo, assigneeTemplate, collapsedAssigneeTemplate, defaultLabel, firstUser, issueURL, selectedId, showAnyUser, showNullUser;
$dropdown = $(dropdown);
_this.projectId = $dropdown.data('project-id');
_this.showCurrentUser = $dropdown.data('current-user');
options.projectId = $dropdown.data('project-id');
options.showCurrentUser = $dropdown.data('current-user');
showNullUser = $dropdown.data('null-user');
showAnyUser = $dropdown.data('any-user');
firstUser = $dropdown.data('first-user');
_this.authorId = $dropdown.data('author-id');
options.authorId = $dropdown.data('author-id');
selectedId = $dropdown.data('selected');
defaultLabel = $dropdown.data('default-label');
issueURL = $dropdown.data('issueUpdate');
......@@ -75,7 +76,7 @@
data: function(term, callback) {
var isAuthorFilter;
isAuthorFilter = $('.js-author-search');
return _this.users(term, function(users) {
return _this.users(term, options, function(users) {
var anyUser, index, j, len, name, obj, showDivider;
if (term.length === 0) {
showDivider = 0;
......@@ -185,11 +186,14 @@
$('.ajax-users-select').each((function(_this) {
return function(i, select) {
var firstUser, showAnyUser, showEmailUser, showNullUser;
_this.projectId = $(select).data('project-id');
_this.groupId = $(select).data('group-id');
_this.showCurrentUser = $(select).data('current-user');
_this.authorId = $(select).data('author-id');
_this.skipUsers = $(select).data('skip-users');
var options = {};
options.skipLdap = $(select).hasClass('skip_ldap');
options.projectId = $(select).data('project-id');
options.groupId = $(select).data('group-id');
options.showCurrentUser = $(select).data('current-user');
options.pushCodeToProtectedBranches = $(select).data('push-code-to-protected-branches');
options.authorId = $(select).data('author-id');
options.skipUsers = $(select).data('skip-users');
showNullUser = $(select).data('null-user');
showAnyUser = $(select).data('any-user');
showEmailUser = $(select).data('email-user');
......@@ -199,7 +203,7 @@
multiple: $(select).hasClass('multiselect'),
minimumInputLength: 0,
query: function(query) {
return _this.users(query.term, function(users) {
return _this.users(query.term, options, function(users) {
var anyUser, data, emailUser, index, j, len, name, nullUser, obj, ref;
data = {
results: users
......@@ -309,7 +313,7 @@
});
};
UsersSelect.prototype.users = function(query, callback) {
UsersSelect.prototype.users = function(query, options, callback) {
var url;
url = this.buildUrl(this.usersPath);
return $.ajax({
......@@ -318,11 +322,13 @@
search: query,
per_page: 20,
active: true,
project_id: this.projectId,
group_id: this.groupId,
current_user: this.showCurrentUser,
author_id: this.authorId,
skip_users: this.skipUsers
project_id: options.projectId || null,
group_id: options.groupId || null,
skip_ldap: options.skipLdap || null,
current_user: options.showCurrentUser || null,
push_code_to_protected_branches: options.pushCodeToProtectedBranches || null,
author_id: options.authorId || null,
skip_users: options.skipUsers || null
},
dataType: "json"
}).done(function(users) {
......
......@@ -72,6 +72,14 @@
&.large {
width: 200px;
}
&.wide {
width: 100%;
+ .dropdown-select {
width: 100%;
}
}
}
.dropdown-menu,
......
......@@ -114,6 +114,12 @@ ul.content-list {
font-size: $list-font-size;
color: $list-text-color;
&.no-description {
.title {
line-height: $list-text-height;
}
}
.title {
font-weight: 600;
}
......@@ -134,12 +140,11 @@ ul.content-list {
}
.controls {
padding-top: 1px;
float: right;
> .control-text {
margin-right: $gl-padding-top;
line-height: 40px;
line-height: $list-text-height;
&:last-child {
margin-right: 0;
......@@ -150,7 +155,7 @@ ul.content-list {
> .btn-group {
margin-right: $gl-padding-top;
display: inline-block;
margin-top: 4px;
margin-top: 3px;
margin-bottom: 4px;
&:last-child {
......
......@@ -182,7 +182,6 @@
> form {
display: inline-block;
margin-top: -1px;
}
.icon-label {
......@@ -193,7 +192,6 @@
height: 35px;
display: inline-block;
position: relative;
top: 2px;
margin-right: $gl-padding-top;
/* Medium devices (desktops, 992px and up) */
......
......@@ -23,4 +23,9 @@
margin-top: $gl-padding;
}
}
.panel-title {
font-size: inherit;
line-height: inherit;
}
}
......@@ -43,6 +43,7 @@ $gl-header-color: $gl-title-color;
$list-font-size: $gl-font-size;
$list-title-color: $gl-title-color;
$list-text-color: $gl-text-color;
$list-text-height: 42px;
/*
* Markdown
......
.commits-compare-switch {
@include btn-default;
@include btn-white;
background: image-url("switch_icon.png") no-repeat center center;
text-indent: -9999px;
float: left;
margin-right: 9px;
}
......
......@@ -164,7 +164,10 @@
line-height: 0;
img {
border: 1px solid #fff;
background: image-url('trans_bg.gif');
background-image: linear-gradient(45deg, #e5e5e5 25%, transparent 25%, transparent 75%, #e5e5e5 75%, #e5e5e5 100%),
linear-gradient(45deg, #e5e5e5 25%, transparent 25%, transparent 75%, #e5e5e5 75%, #e5e5e5 100%);
background-size: 10px 10px;
background-position: 0 0, 5px 5px;
max-width: 100%;
}
&.deleted {
......
......@@ -23,15 +23,9 @@
}
.group-row {
&.no-description {
.group-name {
line-height: 44px;
}
}
.stats {
float: right;
line-height: 44px;
line-height: $list-text-height;
color: $gl-gray;
span {
......
......@@ -182,6 +182,17 @@
.btn {
color: inherit;
}
a.btn {
padding: 0;
.has-tooltip {
top: 0;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
line-height: 1.1;
}
}
}
.label-options-toggle {
......
......@@ -512,18 +512,12 @@ pre.light-well {
.project-row {
border-color: $table-border-color;
&.no-description {
.project {
line-height: 40px;
}
}
.project-full-name {
@include str-truncated;
}
.controls {
line-height: 40px;
line-height: $list-text-height;
a:hover {
text-decoration: none;
......@@ -662,13 +656,9 @@ pre.light-well {
}
.new_protected_branch {
.dropdown {
display: inline;
margin-left: 15px;
}
label {
min-width: 120px;
margin-top: 6px;
font-weight: normal;
}
}
......@@ -684,6 +674,21 @@ pre.light-well {
font-weight: 600;
}
}
.settings-message {
margin: 0;
border-radius: 0 0 1px 1px;
padding: 20px 0;
border: none;
}
.table-bordered {
border-radius: 1px;
th:not(:last-child), td:not(:last-child) {
border-right: solid 1px transparent;
}
}
}
.custom-notifications-form {
......
......@@ -58,6 +58,10 @@
.tree_commit {
max-width: 320px;
.str-truncated {
max-width: 100%;
}
}
.tree_time_ago {
......
module DiffForPath
extend ActiveSupport::Concern
def render_diff_for_path(diffs, diff_refs, project)
diff_file = safe_diff_files(diffs, diff_refs: diff_refs, repository: project.repository).find do |diff|
def render_diff_for_path(diffs)
diff_file = diffs.diff_files.find do |diff|
diff.old_path == params[:old_path] && diff.new_path == params[:new_path]
end
......@@ -14,7 +14,7 @@ module DiffForPath
locals = {
diff_file: diff_file,
diff_commit: diff_commit,
diff_refs: diff_refs,
diff_refs: diffs.diff_refs,
blob: blob,
project: project
}
......
......@@ -12,13 +12,14 @@ class Import::GitlabProjectsController < Import::BaseController
return redirect_back_or_default(options: { alert: "You need to upload a GitLab project export archive." })
end
imported_file = project_params[:file].path + "-import"
import_upload_path = Gitlab::ImportExport.import_upload_path(filename: project_params[:file].original_filename)
FileUtils.copy_entry(project_params[:file].path, imported_file)
FileUtils.mkdir_p(File.dirname(import_upload_path))
FileUtils.copy_entry(project_params[:file].path, import_upload_path)
@project = Gitlab::ImportExport::ProjectCreator.new(project_params[:namespace_id],
current_user,
File.expand_path(imported_file),
import_upload_path,
project_params[:path]).execute
if @project.saved?
......
......@@ -50,6 +50,7 @@ class Profiles::PasswordsController < Profiles::ApplicationController
flash[:notice] = "Password was successfully updated. Please login with it"
redirect_to new_user_session_path
else
@user.reload
render 'edit'
end
end
......
......@@ -8,8 +8,9 @@ class Projects::BadgesController < Projects::ApplicationController
respond_to do |format|
format.html { render_404 }
format.svg do
send_data(badge.data, type: badge.type, disposition: 'inline')
render 'badge', locals: { badge: badge.template }
end
end
end
......
......@@ -76,6 +76,8 @@ class Projects::BlobController < Projects::ApplicationController
end
def diff
apply_diff_view_cookie!
@form = UnfoldForm.new(params)
@lines = Gitlab::Highlight.highlight_lines(repository, @ref, @path)
@lines = @lines[@form.since - 1..@form.to - 1]
......
......@@ -28,7 +28,7 @@ class Projects::CommitController < Projects::ApplicationController
end
def diff_for_path
render_diff_for_path(@diffs, @commit.diff_refs, @project)
render_diff_for_path(@commit.diffs(diff_options))
end
def builds
......
......@@ -21,7 +21,7 @@ class Projects::CompareController < Projects::ApplicationController
def diff_for_path
return render_404 unless @compare
render_diff_for_path(@diffs, @diff_refs, @project)
render_diff_for_path(@compare.diffs(diff_options))
end
def create
......@@ -40,18 +40,12 @@ class Projects::CompareController < Projects::ApplicationController
@compare = CompareService.new.execute(@project, @head_ref, @project, @start_ref)
if @compare
@commits = Commit.decorate(@compare.commits, @project)
@start_commit = @project.commit(@start_ref)
@commit = @project.commit(@head_ref)
@base_commit = @project.merge_base_commit(@start_ref, @head_ref)
@commits = @compare.commits
@start_commit = @compare.start_commit
@commit = @compare.commit
@base_commit = @compare.base_commit
@diffs = @compare.diffs(diff_options)
@diff_refs = Gitlab::Diff::DiffRefs.new(
base_sha: @base_commit.try(:sha),
start_sha: @start_commit.try(:sha),
head_sha: @commit.try(:sha)
)
@diff_notes_disabled = true
@grouped_diff_discussions = {}
......
......@@ -12,8 +12,7 @@ class Projects::DeployKeysController < Projects::ApplicationController
end
def new
redirect_to namespace_project_deploy_keys_path(@project.namespace,
@project)
redirect_to namespace_project_deploy_keys_path(@project.namespace, @project)
end
def create
......@@ -21,19 +20,16 @@ class Projects::DeployKeysController < Projects::ApplicationController
set_index_vars
if @key.valid? && @project.deploy_keys << @key
redirect_to namespace_project_deploy_keys_path(@project.namespace,
@project)
redirect_to namespace_project_deploy_keys_path(@project.namespace, @project)
else
render "index"
end
end
def enable
@key = accessible_keys.find(params[:id])
@project.deploy_keys << @key
Projects::EnableDeployKeyService.new(@project, current_user, params).execute
redirect_to namespace_project_deploy_keys_path(@project.namespace,
@project)
redirect_to namespace_project_deploy_keys_path(@project.namespace, @project)
end
def disable
......@@ -45,9 +41,9 @@ class Projects::DeployKeysController < Projects::ApplicationController
protected
def set_index_vars
@enabled_keys ||= @project.deploy_keys
@enabled_keys ||= @project.deploy_keys
@available_keys ||= accessible_keys - @enabled_keys
@available_keys ||= current_user.accessible_deploy_keys - @enabled_keys
@available_project_keys ||= current_user.project_deploy_keys - @enabled_keys
@available_public_keys ||= DeployKey.are_public - @enabled_keys
......@@ -56,10 +52,6 @@ class Projects::DeployKeysController < Projects::ApplicationController
@available_public_keys -= @available_project_keys
end
def accessible_keys
@accessible_keys ||= current_user.accessible_deploy_keys
end
def deploy_key_params
params.require(:deploy_key).permit(:key, :title)
end
......
......@@ -20,9 +20,9 @@ class Projects::GitHttpController < Projects::ApplicationController
elsif receive_pack? && receive_pack_allowed?
render_ok
elsif http_blocked?
render_not_allowed
render_http_not_allowed
else
render_not_found
render_denied
end
end
......@@ -31,7 +31,7 @@ class Projects::GitHttpController < Projects::ApplicationController
if upload_pack? && upload_pack_allowed?
render_ok
else
render_not_found
render_denied
end
end
......@@ -40,7 +40,7 @@ class Projects::GitHttpController < Projects::ApplicationController
if receive_pack? && receive_pack_allowed?
render_ok
else
render_not_found
render_denied
end
end
......@@ -156,8 +156,17 @@ class Projects::GitHttpController < Projects::ApplicationController
render plain: 'Not Found', status: :not_found
end
def render_not_allowed
render plain: download_access.message, status: :forbidden
def render_http_not_allowed
render plain: access_check.message, status: :forbidden
end
def render_denied
if user && user.can?(:read_project, project)
render plain: 'Access denied', status: :forbidden
else
# Do not leak information about project existence
render_not_found
end
end
def ci?
......@@ -168,22 +177,20 @@ class Projects::GitHttpController < Projects::ApplicationController
return false unless Gitlab.config.gitlab_shell.upload_pack
if user
download_access.allowed?
access_check.allowed?
else
ci? || project.public?
end
end
def access
return @access if defined?(@access)
@access = Gitlab::GitAccess.new(user, project, 'http')
@access ||= Gitlab::GitAccess.new(user, project, 'http')
end
def download_access
return @download_access if defined?(@download_access)
@download_access = access.check('git-upload-pack')
def access_check
# Use the magic string '_any' to indicate we do not know what the
# changes are. This is also what gitlab-shell does.
@access_check ||= access.check(git_command, '_any')
end
def http_blocked?
......@@ -193,8 +200,6 @@ class Projects::GitHttpController < Projects::ApplicationController
def receive_pack_allowed?
return false unless Gitlab.config.gitlab_shell.receive_pack
# Skip user authorization on upload request.
# It will be done by the pre-receive hook in the repository.
user.present?
access_check.allowed?
end
end
......@@ -5,6 +5,7 @@ class Projects::IssuesController < Projects::ApplicationController
include ToggleAwardEmoji
include IssuableCollections
before_action :redirect_to_external_issue_tracker, only: [:index, :new]
before_action :module_enabled
before_action :issue, only: [:edit, :update, :show, :referenced_merge_requests,
:related_branches, :can_create_branch]
......@@ -201,6 +202,18 @@ class Projects::IssuesController < Projects::ApplicationController
return render_404 unless @project.issues_enabled && @project.default_issues_tracker?
end
def redirect_to_external_issue_tracker
external = @project.external_issue_tracker
return unless external
if action_name == 'new'
redirect_to external.new_issue_path
else
redirect_to external.issues_url
end
end
# Since iids are implemented only in 6.1
# user may navigate to issue page using old global ids.
#
......
......@@ -85,7 +85,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
respond_to do |format|
format.html { define_discussion_vars }
format.json { render json: { html: view_to_html_string("projects/merge_requests/show/_diffs") } }
format.json do
@diffs = @merge_request.diffs(diff_options)
render json: { html: view_to_html_string("projects/merge_requests/show/_diffs") }
end
end
end
......@@ -103,9 +107,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
define_commit_vars
diffs = @merge_request.diffs(diff_options)
render_diff_for_path(diffs, @merge_request.diff_refs, @merge_request.project)
render_diff_for_path(@merge_request.diffs(diff_options))
end
def commits
......@@ -153,7 +156,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@commits = @merge_request.compare_commits.reverse
@commit = @merge_request.diff_head_commit
@base_commit = @merge_request.diff_base_commit
@diffs = @merge_request.compare.diffs(diff_options) if @merge_request.compare
@diffs = @merge_request.diffs(diff_options) if @merge_request.compare
@diff_notes_disabled = true
@pipeline = @merge_request.pipeline
......@@ -378,6 +381,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
fresh.
discussions
preload_noteable_for_regular_notes(@discussions.flat_map(&:notes))
# This is not executed lazily
@notes = Banzai::NoteRenderer.render(
@discussions.flat_map(&:notes),
......
......@@ -3,7 +3,7 @@ class Projects::PipelinesSettingsController < Projects::ApplicationController
def show
@ref = params[:ref] || @project.default_branch || 'master'
@build_badge = Gitlab::Badge::Build.new(@project, @ref)
@build_badge = Gitlab::Badge::Build.new(@project, @ref).metadata
end
def update
......
......@@ -125,7 +125,7 @@ class ProjectsController < Projects::ApplicationController
def destroy
return access_denied! unless can?(current_user, :remove_project, @project)
::Projects::DestroyService.new(@project, current_user, {}).pending_delete!
::Projects::DestroyService.new(@project, current_user, {}).async_execute
flash[:alert] = "Project '#{@project.name}' will be deleted."
redirect_to dashboard_projects_path
......
......@@ -33,7 +33,7 @@ class RegistrationsController < Devise::RegistrationsController
protected
def build_resource(hash=nil)
def build_resource(hash = nil)
super
end
......
......@@ -101,7 +101,7 @@ class SessionsController < Devise::SessionsController
# Prevent alert from popping up on the first page shown after authentication.
flash[:alert] = nil
redirect_to user_omniauth_authorize_path(provider.to_sym)
redirect_to omniauth_authorize_path(:user, provider)
end
def valid_otp_attempt?(user)
......
......@@ -163,9 +163,13 @@ module ApplicationHelper
# `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)
def time_ago_with_tooltip(time, placement: 'top', html_class: '', skip_js: false, short_format: false)
css_classes = short_format ? 'js-short-timeago' : 'js-timeago'
css_classes << " #{html_class}" unless html_class.blank?
css_classes << ' js-timeago-pending' unless skip_js
element = content_tag :time, time.to_s,
class: "#{html_class} js-timeago #{"js-timeago-pending" unless skip_js}",
class: css_classes,
datetime: time.to_time.getutc.iso8601,
title: time.to_time.in_time_zone.to_s(:medium),
data: { toggle: 'tooltip', placement: placement, container: 'body' }
......
module AvatarsHelper
def author_avatar(commit_or_event, options = {})
user_avatar(options.merge({
user: commit_or_event.author,
......@@ -26,5 +25,4 @@ module AvatarsHelper
mail_to(options[:user_email], avatar)
end
end
end
......@@ -206,10 +206,10 @@ module CommitsHelper
end
end
def view_file_btn(commit_sha, diff, project)
def view_file_btn(commit_sha, diff_new_path, project)
link_to(
namespace_project_blob_path(project.namespace, project,
tree_join(commit_sha, diff.new_path)),
tree_join(commit_sha, diff_new_path)),
class: 'btn view-file js-view-file btn-file-option'
) do
raw('View file @') + content_tag(:span, commit_sha[0..6],
......
......@@ -13,12 +13,11 @@ module DiffHelper
end
def diff_view
diff_views = %w(inline parallel)
if diff_views.include?(cookies[:diff_view])
cookies[:diff_view]
else
diff_views.first
@diff_view ||= begin
diff_views = %w(inline parallel)
diff_view = cookies[:diff_view]
diff_view = diff_views.first unless diff_views.include?(diff_view)
diff_view.to_sym
end
end
......@@ -30,19 +29,26 @@ module DiffHelper
options[:paths] = params.values_at(:old_path, :new_path)
end
Commit.max_diff_options.merge(options)
options
end
def safe_diff_files(diffs, diff_refs: nil, repository: nil)
diffs.decorate! { |diff| Gitlab::Diff::File.new(diff, diff_refs: diff_refs, repository: repository) }
end
def diff_match_line(old_pos, new_pos, text: '', view: :inline, bottom: false)
content = content_tag :td, text, class: "line_content match #{view == :inline ? '' : view}"
cls = ['diff-line-num', 'unfold', 'js-unfold']
cls << 'js-unfold-bottom' if bottom
def unfold_bottom_class(bottom)
bottom ? 'js-unfold js-unfold-bottom' : ''
end
html = ''
if old_pos
html << content_tag(:td, '...', class: cls + ['old_line'], data: { linenumber: old_pos })
html << content unless view == :inline
end
if new_pos
html << content_tag(:td, '...', class: cls + ['new_line'], data: { linenumber: new_pos })
html << content
end
def unfold_class(unfold)
unfold ? 'unfold js-unfold' : ''
html.html_safe
end
def diff_line_content(line, line_type = nil)
......@@ -71,11 +77,11 @@ module DiffHelper
end
def inline_diff_btn
diff_btn('Inline', 'inline', diff_view == 'inline')
diff_btn('Inline', 'inline', diff_view == :inline)
end
def parallel_diff_btn
diff_btn('Side-by-side', 'parallel', diff_view == 'parallel')
diff_btn('Side-by-side', 'parallel', diff_view == :parallel)
end
def submodule_link(blob, ref, repository = @repository)
......@@ -103,11 +109,11 @@ module DiffHelper
end
end
def diff_file_html_data(project, diff_file)
commit = commit_for_diff(diff_file)
def diff_file_html_data(project, diff_file_path, diff_commit_id)
{
blob_diff_path: namespace_project_blob_diff_path(project.namespace, project,
tree_join(commit.id, diff_file.file_path))
tree_join(diff_commit_id, diff_file_path)),
view: diff_view
}
end
......
module ExploreHelper
def filter_projects_path(options={})
def filter_projects_path(options = {})
exist_opts = {
sort: params[:sort],
scope: params[:scope],
......
......@@ -13,38 +13,6 @@ module IssuesHelper
OpenStruct.new(id: 0, title: 'None (backlog)', name: 'Unassigned')
end
def url_for_project_issues(project = @project, options = {})
return '' if project.nil?
url =
if options[:only_path]
project.issues_tracker.project_path
else
project.issues_tracker.project_url
end
# Ensure we return a valid URL to prevent possible XSS.
URI.parse(url).to_s
rescue URI::InvalidURIError
''
end
def url_for_new_issue(project = @project, options = {})
return '' if project.nil?
url =
if options[:only_path]
project.issues_tracker.new_issue_path
else
project.issues_tracker.new_issue_url
end
# Ensure we return a valid URL to prevent possible XSS.
URI.parse(url).to_s
rescue URI::InvalidURIError
''
end
def url_for_issue(issue_iid, project = @project, options = {})
return '' if project.nil?
......
......@@ -92,6 +92,10 @@ module NotesHelper
project.team.max_member_access_for_user_ids(user_ids)
end
def preload_noteable_for_regular_notes(notes)
ActiveRecord::Associations::Preloader.new.preload(notes.select { |note| !note.for_commit? }, :noteable)
end
def note_max_access_for_user(note)
note.project.team.human_max_access(note.author_id)
end
......
......@@ -263,6 +263,10 @@ module ProjectsHelper
filename_path(project, :version)
end
def ci_configuration_path(project)
filename_path(project, :gitlab_ci_yml)
end
def project_wiki_path_with_version(proj, page, version, is_newest)
url_params = is_newest ? {} : { version_id: version }
namespace_project_wiki_path(proj.namespace, proj, page, url_params)
......
......@@ -107,7 +107,7 @@ module SearchHelper
Sanitize.clean(str)
end
def search_filter_path(options={})
def search_filter_path(options = {})
exist_opts = {
search: params[:search],
project_id: params[:project_id],
......
......@@ -4,23 +4,11 @@ module TreeHelper
#
# contents - A Grit::Tree object for the current tree
def render_tree(tree)
# Render Folders before Files/Submodules
# Sort submodules and folders together by name ahead of files
folders, files, submodules = tree.trees, tree.blobs, tree.submodules
tree = ""
# Render folders if we have any
tree << render(partial: 'projects/tree/tree_item', collection: folders,
locals: { type: 'folder' }) if folders.present?
# Render files if we have any
tree << render(partial: 'projects/tree/blob_item', collection: files,
locals: { type: 'file' }) if files.present?
# Render submodules if we have any
tree << render(partial: 'projects/tree/submodule_item',
collection: submodules) if submodules.present?
items = (folders + submodules).sort_by(&:name) + files
tree << render(partial: "projects/tree/tree_row", collection: items) if items.present?
tree.html_safe
end
......
......@@ -6,6 +6,10 @@ class Ability
return [] unless user.is_a?(User)
return [] if user.blocked?
abilities_by_subject_class(user: user, subject: subject)
end
def abilities_by_subject_class(user:, subject:)
case subject
when CommitStatus then commit_status_abilities(user, subject)
when Project then project_abilities(user, subject)
......
......@@ -104,7 +104,7 @@ class Commit
end
def diff_line_count
@diff_line_count ||= Commit::diff_line_count(self.diffs)
@diff_line_count ||= Commit::diff_line_count(raw_diffs)
@diff_line_count
end
......@@ -123,15 +123,17 @@ class Commit
# In case this first line is longer than 100 characters, it is cut off
# after 80 characters and ellipses (`&hellp;`) are appended.
def title
title = safe_message
full_title.length > 100 ? full_title[0..79] << "…" : full_title
end
return no_commit_message if title.blank?
# Returns the full commits title
def full_title
return @full_title if @full_title
title_end = title.index("\n")
if (!title_end && title.length > 100) || (title_end && title_end > 100)
title[0..79] << "…"
if safe_message.blank?
@full_title = no_commit_message
else
title.split("\n", 2).first
@full_title = safe_message.split("\n", 2).first
end
end
......@@ -315,6 +317,14 @@ class Commit
nil
end
def raw_diffs(*args)
raw.diffs(*args)
end
def diffs(diff_options = nil)
Gitlab::Diff::FileCollection::Commit.new(self, diff_options: diff_options)
end
private
def find_author_by_any_email
......@@ -324,7 +334,7 @@ class Commit
def repo_changes
changes = { added: [], modified: [], removed: [] }
diffs.each do |diff|
raw_diffs(deltas_only: true).each do |diff|
if diff.deleted_file
changes[:removed] << diff.old_path
elsif diff.renamed_file || diff.new_file
......
class Compare
delegate :same, :head, :base, to: :@compare
attr_reader :project
def self.decorate(compare, project)
if compare.is_a?(Compare)
compare
else
self.new(compare, project)
end
end
def initialize(compare, project)
@compare = compare
@project = project
end
def commits
@commits ||= Commit.decorate(@compare.commits, project)
end
def start_commit
return @start_commit if defined?(@start_commit)
commit = @compare.base
@start_commit = commit ? ::Commit.new(commit, project) : nil
end
def head_commit
return @head_commit if defined?(@head_commit)
commit = @compare.head
@head_commit = commit ? ::Commit.new(commit, project) : nil
end
alias_method :commit, :head_commit
def base_commit
return @base_commit if defined?(@base_commit)
@base_commit = if start_commit && head_commit
project.merge_base_commit(start_commit.id, head_commit.id)
else
nil
end
end
def raw_diffs(*args)
@compare.diffs(*args)
end
def diffs(diff_options = nil)
Gitlab::Diff::FileCollection::Compare.new(self,
project: project,
diff_options: diff_options,
diff_refs: diff_refs)
end
def diff_refs
Gitlab::Diff::DiffRefs.new(
base_sha: base_commit.try(:sha),
start_sha: start_commit.try(:sha),
head_sha: commit.try(:sha)
)
end
end
module FasterCacheKeys
# A faster version of Rails' "cache_key" method.
#
# Rails' default "cache_key" method uses all kind of complex logic to figure
# out the cache key. In many cases this complexity and overhead may not be
# needed.
#
# This method does not do any timestamp parsing as this process is quite
# expensive and not needed when generating cache keys. This method also relies
# on the table name instead of the cache namespace name as the latter uses
# complex logic to generate the exact same value (as when using the table
# name) in 99% of the cases.
def cache_key
"#{self.class.table_name}/#{id}-#{read_attribute_before_type_cast(:updated_at)}"
end
end
......@@ -67,7 +67,7 @@ class DiffNote < Note
return false unless supported?
return true if for_commit?
diff_refs ||= self.noteable.diff_refs
diff_refs ||= noteable_diff_refs
self.position.diff_refs == diff_refs
end
......@@ -78,6 +78,14 @@ class DiffNote < Note
!self.for_merge_request? || self.noteable.support_new_diff_notes?
end
def noteable_diff_refs
if noteable.respond_to?(:diff_sha_refs)
noteable.diff_sha_refs
else
noteable.diff_refs
end
end
def set_original_position
self.original_position = self.position.dup
end
......@@ -96,7 +104,7 @@ class DiffNote < Note
self.project,
nil,
old_diff_refs: self.position.diff_refs,
new_diff_refs: self.noteable.diff_refs,
new_diff_refs: noteable_diff_refs,
paths: self.position.paths
).execute(self)
end
......
......@@ -49,6 +49,12 @@ class Discussion
self.noteable == target && !diff_discussion?
end
def active?
return @active if defined?(@active)
@active = first_note.active?
end
def expanded?
!diff_discussion? || active?
end
......
......@@ -7,6 +7,7 @@ class Issue < ActiveRecord::Base
include Sortable
include Taskable
include Spammable
include FasterCacheKeys
DueDateStruct = Struct.new(:title, :name).freeze
NoDueDate = DueDateStruct.new('No Due Date', '0').freeze
......
......@@ -26,8 +26,9 @@ class Key < ActiveRecord::Base
end
def publishable_key
# Removes anything beyond the keytype and key itself
self.key.split[0..1].join(' ')
# Strip out the keys comment so we don't leak email addresses
# Replace with simple ident of user_name (hostname)
self.key.split[0..1].push("#{self.user_name} (#{Gitlab.config.gitlab.host})").join(' ')
end
# projects that has this key
......
......@@ -85,7 +85,7 @@ class LegacyDiffNote < Note
return nil unless noteable
return @diff if defined?(@diff)
@diff = noteable.diffs(Commit.max_diff_options).find do |d|
@diff = noteable.raw_diffs(Commit.max_diff_options).find do |d|
d.new_path && Digest::SHA1.hexdigest(d.new_path) == diff_file_hash
end
end
......@@ -116,7 +116,7 @@ class LegacyDiffNote < Note
# Find the diff on noteable that matches our own
def find_noteable_diff
diffs = noteable.diffs(Commit.max_diff_options)
diffs = noteable.raw_diffs(Commit.max_diff_options)
diffs.find { |d| d.new_path == self.diff.new_path }
end
end
......@@ -21,19 +21,19 @@ class ProjectMember < Member
# or symbol like :master representing role
#
# Ex.
# add_users_into_projects(
# add_users_to_projects(
# project_ids,
# user_ids,
# ProjectMember::MASTER
# )
#
# add_users_into_projects(
# add_users_to_projects(
# project_ids,
# user_ids,
# :master
# )
#
def add_users_into_projects(project_ids, user_ids, access, current_user = nil)
def add_users_to_projects(project_ids, user_ids, access, current_user = nil)
access_level = if roles_hash.has_key?(access)
roles_hash[access]
elsif roles_hash.values.include?(access.to_i)
......
......@@ -164,8 +164,16 @@ class MergeRequest < ActiveRecord::Base
merge_request_diff ? merge_request_diff.first_commit : compare_commits.first
end
def diffs(*args)
merge_request_diff ? merge_request_diff.diffs(*args) : compare.diffs(*args)
def raw_diffs(*args)
merge_request_diff ? merge_request_diff.raw_diffs(*args) : compare.raw_diffs(*args)
end
def diffs(diff_options = nil)
if self.compare
self.compare.diffs(diff_options)
else
Gitlab::Diff::FileCollection::MergeRequest.new(self, diff_options: diff_options)
end
end
def diff_size
......@@ -255,6 +263,19 @@ class MergeRequest < ActiveRecord::Base
)
end
# Return diff_refs instance trying to not touch the git repository
def diff_sha_refs
if merge_request_diff && merge_request_diff.diff_refs_by_sha?
return Gitlab::Diff::DiffRefs.new(
base_sha: merge_request_diff.base_commit_sha,
start_sha: merge_request_diff.start_commit_sha,
head_sha: merge_request_diff.head_commit_sha
)
else
diff_refs
end
end
def validate_branches
if target_project == source_project && target_branch == source_branch
errors.add :branch_conflict, "You can not use same project/branch for source and target"
......@@ -300,6 +321,8 @@ class MergeRequest < ActiveRecord::Base
merge_request_diff.reload_content
MergeRequests::MergeRequestDiffCacheService.new.execute(self)
new_diff_refs = self.diff_refs
update_diff_notes_positions(
......@@ -659,7 +682,7 @@ class MergeRequest < ActiveRecord::Base
end
def support_new_diff_notes?
diff_refs && diff_refs.complete?
diff_sha_refs && diff_sha_refs.complete?
end
def update_diff_notes_positions(old_diff_refs:, new_diff_refs:)
......
......@@ -33,12 +33,12 @@ class MergeRequestDiff < ActiveRecord::Base
end
def size
real_size.presence || diffs.size
real_size.presence || raw_diffs.size
end
def diffs(options={})
def raw_diffs(options = {})
if options[:ignore_whitespace_change]
@diffs_no_whitespace ||= begin
@raw_diffs_no_whitespace ||= begin
compare = Gitlab::Git::Compare.new(
repository.raw_repository,
self.start_commit_sha || self.target_branch_sha,
......@@ -47,8 +47,8 @@ class MergeRequestDiff < ActiveRecord::Base
compare.diffs(options)
end
else
@diffs ||= {}
@diffs[options] ||= load_diffs(st_diffs, options)
@raw_diffs ||= {}
@raw_diffs[options] ||= load_diffs(st_diffs, options)
end
end
......@@ -82,6 +82,10 @@ class MergeRequestDiff < ActiveRecord::Base
project.commit(self.head_commit_sha)
end
def diff_refs_by_sha?
base_commit_sha? && head_commit_sha? && start_commit_sha?
end
def compare
@compare ||=
begin
......
......@@ -5,6 +5,7 @@ class Note < ActiveRecord::Base
include Mentionable
include Awardable
include Importable
include FasterCacheKeys
# Attribute containing rendered and redacted Markdown as generated by
# Banzai::ObjectRenderer.
......
......@@ -379,9 +379,10 @@ class Project < ActiveRecord::Base
joins(join_body).reorder('join_note_counts.amount DESC')
end
# Deletes gitlab project export files older than 24 hours
def remove_gitlab_exports!
Gitlab::Popen.popen(%W(find #{Gitlab::ImportExport.storage_path} -not -path #{Gitlab::ImportExport.storage_path} -mmin +1440 -delete))
def cached_count
Rails.cache.fetch('total_project_count', expires_in: 5.minutes) do
Project.count
end
end
end
......@@ -870,10 +871,16 @@ class Project < ActiveRecord::Base
# Check if current branch name is marked as protected in the system
def protected_branch?(branch_name)
return true if empty_repo? && default_branch_protected?
@protected_branches ||= self.protected_branches.to_a
ProtectedBranch.matching(branch_name, protected_branches: @protected_branches).present?
end
def user_can_push_to_empty_repo?(user)
!default_branch_protected? || team.max_member_access(user.id) > Gitlab::Access::DEVELOPER
end
def forked?
!(forked_project_link.nil? || forked_project_link.forked_from_project.nil?)
end
......@@ -1154,16 +1161,6 @@ class Project < ActiveRecord::Base
@wiki ||= ProjectWiki.new(self, self.owner)
end
def schedule_delete!(user_id, params)
# Queue this task for after the commit, so once we mark pending_delete it will run
run_after_commit do
job_id = ProjectDestroyWorker.perform_async(id, user_id, params)
Rails.logger.info("User #{user_id} scheduled destruction of project #{path_with_namespace} with job ID #{job_id}")
end
update_attribute(:pending_delete, true)
end
def running_or_pending_build_count(force: false)
Rails.cache.fetch(['projects', id, 'running_or_pending_build_count'], force: force) do
builds.running_or_pending.count(:all)
......@@ -1265,6 +1262,11 @@ class Project < ActiveRecord::Base
private
def default_branch_protected?
current_application_settings.default_branch_protection == Gitlab::Access::PROTECTION_FULL ||
current_application_settings.default_branch_protection == Gitlab::Access::PROTECTION_DEV_CAN_MERGE
end
def authorized_for_user_by_group?(user, min_access_level)
member = user.group_members.find_by(source_id: group)
......
......@@ -34,7 +34,7 @@ class ProjectTeam
end
def add_users(users, access, current_user = nil)
ProjectMember.add_users_into_projects(
ProjectMember.add_users_to_projects(
[project.id],
users,
access,
......@@ -138,8 +138,13 @@ class ProjectTeam
def max_member_access_for_user_ids(user_ids)
user_ids = user_ids.uniq
key = "max_member_access:#{project.id}"
RequestStore.store[key] ||= {}
access = RequestStore.store[key]
access = {}
if RequestStore.active?
RequestStore.store[key] ||= {}
access = RequestStore.store[key]
end
# Lookup only the IDs we need
user_ids = user_ids - access.keys
......
......@@ -372,7 +372,7 @@ class Repository
# We don't want to flush the cache if the commit didn't actually make any
# changes to any of the possible avatar files.
if revision && commit = self.commit(revision)
return unless commit.diffs.
return unless commit.raw_diffs(deltas_only: true).
any? { |diff| AVATAR_FILES.include?(diff.new_path) }
end
......@@ -601,7 +601,7 @@ class Repository
commit(sha)
end
def next_branch(name, opts={})
def next_branch(name, opts = {})
branch_ids = self.branch_names.map do |n|
next 1 if n == name
result = n.match(/\A#{name}-([0-9]+)\z/)
......@@ -636,9 +636,7 @@ class Repository
def tags_sorted_by(value)
case value
when 'name'
# Would be better to use `sort_by` but `version_sorter` only exposes
# `sort` and `rsort`
VersionSorter.rsort(tag_names).map { |tag_name| find_tag(tag_name) }
VersionSorter.rsort(tags) { |tag| tag.name }
when 'updated_desc'
tags_sorted_by_committed_date.reverse
when 'updated_asc'
......
......@@ -23,13 +23,13 @@ class User < ActiveRecord::Base
default_value_for :theme_id, gitlab_config.default_theme
attr_encrypted :otp_secret,
key: Gitlab::Application.config.secret_key_base,
key: Gitlab::Application.secrets.otp_key_base,
mode: :per_attribute_iv_and_salt,
insecure_mode: true,
algorithm: 'aes-256-cbc'
devise :two_factor_authenticatable,
otp_secret_encryption_key: Gitlab::Application.config.secret_key_base
otp_secret_encryption_key: Gitlab::Application.secrets.otp_key_base
devise :two_factor_backupable, otp_number_of_backup_codes: 10
serialize :otp_backup_codes, JSON
......
......@@ -20,10 +20,12 @@ class CompareService
)
end
Gitlab::Git::Compare.new(
raw_compare = Gitlab::Git::Compare.new(
target_project.repository.raw_repository,
target_branch,
source_sha,
source_sha
)
Compare.new(raw_compare, target_project)
end
end
......@@ -18,7 +18,7 @@ class DeleteUserService
user.personal_projects.each do |project|
# Skip repository removal because we remove directory with namespace
# that contain all this repositories
::Projects::DestroyService.new(project, current_user, skip_repo: true).pending_delete!
::Projects::DestroyService.new(project, current_user, skip_repo: true).async_execute
end
user.destroy
......
......@@ -9,7 +9,7 @@ class DestroyGroupService
group.projects.each do |project|
# Skip repository removal because we remove directory with namespace
# that contain all this repositories
::Projects::DestroyService.new(project, current_user, skip_repo: true).pending_delete!
::Projects::DestroyService.new(project, current_user, skip_repo: true).async_execute
end
group.destroy
......
class ImportExportCleanUpService
LAST_MODIFIED_TIME_IN_MINUTES = 1440
attr_reader :mmin, :path
def initialize(mmin = LAST_MODIFIED_TIME_IN_MINUTES)
@mmin = mmin
@path = Gitlab::ImportExport.storage_path
end
def execute
Gitlab::Metrics.measure(:import_export_clean_up) do
return unless File.directory?(path)
clean_up_export_files
end
end
private
def clean_up_export_files
Gitlab::Popen.popen(%W(find #{path} -not -path #{path} -mmin +#{mmin} -delete))
end
end
......@@ -34,7 +34,7 @@ module MergeRequests
# At this point we decide if merge request can be created
# If we have at least one commit to merge -> creation allowed
if commits.present?
merge_request.compare_commits = Commit.decorate(commits, merge_request.source_project)
merge_request.compare_commits = commits
merge_request.can_be_created = true
merge_request.compare = compare
else
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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