Commit ccde2155 authored by Taylor A Murphy, PhD's avatar Taylor A Murphy, PhD

Merge branch 'master' into 'remove-pseudo-manifest'

# Conflicts:
#   config/pseudonymizer.yml
parents e21cdfdb d4cb6614

Too many changes to show.

To preserve performance only 1000 of 1000+ files are displayed.

...@@ -29,8 +29,6 @@ rules: ...@@ -29,8 +29,6 @@ rules:
import/no-useless-path-segments: off import/no-useless-path-segments: off
lines-between-class-members: off lines-between-class-members: off
# Disabled for now, to make the plugin-vue 4.5 -> 5.0 update smoother # Disabled for now, to make the plugin-vue 4.5 -> 5.0 update smoother
vue/html-closing-bracket-newline: off
vue/html-closing-bracket-spacing: off
vue/no-confusing-v-for-v-if: error vue/no-confusing-v-for-v-if: error
vue/no-unused-components: off vue/no-unused-components: off
vue/no-use-v-if-with-v-for: off vue/no-use-v-if-with-v-for: off
......
This diff is collapsed.
### Background:
(Include problem, use cases, benefits, and/or goals)
**What questions are you trying to answer?**
**Are you looking to verify an existing hypothesis or uncover new issues you should be exploring?**
**What is the backstory of this project and how does it impact the approach?**
**What do you already know about the areas you are exploring?**
**What does success look like at the end of the project?**
### Links / references:
/label ~"UX research"
...@@ -64,7 +64,7 @@ Some features might be simple enough that they only involve one Component, while ...@@ -64,7 +64,7 @@ Some features might be simple enough that they only involve one Component, while
more complex features could involve multiple or even all. more complex features could involve multiple or even all.
Example (from https://gitlab.com/gitlab-org/gitlab-ce/issues/50353): Example (from https://gitlab.com/gitlab-org/gitlab-ce/issues/50353):
* Respository is * Repository is
* Intuitive * Intuitive
* It's easy to select the desired file template * It's easy to select the desired file template
* It doesn't require unnecessary actions to save the change * It doesn't require unnecessary actions to save the change
...@@ -93,4 +93,4 @@ When adding new automated tests, please keep [testing levels](https://docs.gitla ...@@ -93,4 +93,4 @@ When adding new automated tests, please keep [testing levels](https://docs.gitla
in mind. in mind.
--> -->
/label ~Quality ~"test plan" /label ~Quality ~"test plan"
\ No newline at end of file
...@@ -50,7 +50,6 @@ Style/FrozenStringLiteralComment: ...@@ -50,7 +50,6 @@ Style/FrozenStringLiteralComment:
- 'danger/**/*' - 'danger/**/*'
- 'db/**/*' - 'db/**/*'
- 'ee/**/*' - 'ee/**/*'
- 'lib/gitlab/**/*'
- 'lib/tasks/**/*' - 'lib/tasks/**/*'
- 'qa/**/*' - 'qa/**/*'
- 'rubocop/**/*' - 'rubocop/**/*'
......
Please view this file on the master branch, on stable branches it's out of date. Please view this file on the master branch, on stable branches it's out of date.
## 11.5.1 (2018-11-26)
### Security (6 changes)
- Sanitize tracing external_urls before saving to DB and when displaying the URL to prevent XSS issues.
- Prevent reporter roles from viewing the Jaeger tracing settings page.
- Fix IDOR at /drafts/publish.
- Authorize users when listing board users and milestones.
- Resolve: Guest can set weight of a new issue.
- Fixes XSS with merge request approvers selection.
## 11.5.0 (2018-11-22)
### Security (2 changes)
- Escape entity title while autocomplete template rendering to prevent XSS. !696
- Prevent templated services from being imported.
### Removed (1 change)
- Remove security report summary from pipelines view. !7844
### Fixed (25 changes, 3 of them are from the community)
- Geo: Remove connectivity check from primary to secondary from gitlab:geo:check rake task. !7821
- Include (closed) for closed epics in parsed text. !7946
- Add new state to the cluster application vue app. !7954
- Do not allow to assign an issue to an epic twice. !8004
- [Geo] Fix: Deleting a project leaves orphaned LFS objects and CI Job artifacts around. !8031
- Support `/client/features` Unleash endpoint. !8045
- Fix button rendering in license management in FF. !8046
- Geo: Handle orphaned Uploads records. !8054
- Geo - Redirect user back to the secondary after a logout & re-login via the primary. !8157
- Fix approver removal still being conducted even when "Cancel" is clicked in confirmation prompt. !8178
- Link project short SHA to commit url. !8214
- Update ops dashboard remove dropdown button. !8236 (George Tsiolis)
- Clear ops dashboard project search input on submit. !8239 (George Tsiolis)
- Fixes a dismissed vulnerability bug on the group security dashboard. !8343
- Fixes missing fields on the group security dashboard. !8360
- Fixes the view issue button in the Group Security Dashboard. !8385
- Ops Dashboard should be available for public projects on GitLab.com. !8399
- Update draft comments design to match new design. !8405
- Change issues analytics breadcrumb. !8414 (George Tsiolis)
- Include classification label in project API. !8426
- Fix Pod Log topbar position when perf bar is disabled.
- Always proxy reports downloads.
- Removes extra rigth margin from job page.
- Geo: Rails console message display primary/secondary state incorrectly.
- Disable Feature Flags and Packages if repository is disabled.
### Changed (13 changes, 1 of them is from the community)
- Add test button to Group SAML settings. !5622
- Group SAML status badges on members page. !5807
- Update related issues list styling to be more space efficient. !7784
- Refactor test reports to use new artifact architecture. !7827
- Add timeline icon for issue weights. !7847 (George Tsiolis)
- Added a search bar to `Admin > Geo > Projects`. !8079
- Geo: Deprecate source installations instructions. !8134
- Does not synchronize default branch for pull mirrors. !8138
- Adds split error states for the group security dashboard. !8208
- Geo: Improve read-only message in secondary nodes for actionable screens. !8238
- Improve error messages for operations dashboard. !8244
- Add documentation link to ops dashboard. !8296
- Issue board card design. !21229
### Added (24 changes, 1 of them is from the community)
- Group-level file templates. !7391
- Adds group-level Security Dashboard counts. !7564
- Parse SAST reports and store vulnerabilities in database. !7578
- elasticsearch 6 support - migrate from parent/child relationships to join. !7618
- Geo: Admin > Geo > Projects support for batch operations. !7806
- Create system notes for epic close and reopen. !7850
- Add Tracing landing and settings page. !7903
- Add modals and actions to the vulnerabilities in the Group security dashboard. !7910
- Assign code owner as approver. !7933
- Enable previewing of draft review comments. !7936
- Audit log: Add logging for project feature changes. !7962
- Add project operations dashboard. !7973
- Audit log: Add audit events for group setting changes. !7987
- Add approve quick action. !7989
- Show actual Milestone dates within tooltips for Milestones in Epics sidebar. !8048
- Allow filtering by weight in issues API. !8140 (Heinrich Lee Yu)
- Filter epics by state in API. !8179
- Support epics autocomplete for project objects. !8180
- Add 'l', 'r' and 'e' keyboard shortcuts support in Epic. !8203
- Configurable GitHub static context for statuses integration. !8235
- Send notifications for epic status change. !8247
- Support license management and performance using new reports syntax.
- Support reports: for project security dashboard.
- Add chart of issues created per month.
### Other (17 changes, 11 of them are from the community)
- Update boards list selector specs. !6266 (George Tsiolis)
- Write some Geo development documentation. !7452
- Connects the Group Security Dashboard API and Frontend. !7793
- Rails5: Fix epics finder count_key method In Rails5, the state enum value is passed instead of the database integer. !7822 (Jasper Maes)
- Rails 5: fix presence message validation for prometheus_alert. !7823 (Jasper Maes)
- Rails 5: fix mysql milliseconds problem in prometheus alert event spec. !7828 (Jasper Maes)
- Rails5: fix VulnerabilitySummaryEntity. !7893 (Jasper Maes)
- Update feature flags empty state. !7967 (George Tsiolis)
- Adds the security dashboard link. !7974
- Remove tooltip on sidebar text buttons. !8021 (George Tsiolis)
- Add a metric to the usage ping data to track the number of projects with at least one alert. !8058
- Remove unneeded permission checks from the mirror repositories partial. !8077
- Rails5: fix flaky mysql reset pipeline minutes spec. !8122 (Jasper Maes)
- Move `prepend` outside the `class` block for finders. !8192 (George Tsiolis)
- Rails5: fix operations controller spec nil parameter. !8209 (Jasper Maes)
- Update related issues title typography. !8267 (George Tsiolis)
- Geo: Clarify Geo HA documentation.
## 11.4.8 (2018-11-27)
### Security (5 changes)
- Escape entity title while autocomplete template rendering to prevent XSS. !707
- Authorize users when listing board users and milestones.
- Fix IDOR at /drafts/publish.
- Resolve: Guest can set weight of a new issue.
- Fixes XSS with merge request approvers selection.
## 11.4.7 (2018-11-20)
### Fixed (1 change)
- Fix code owner as merge request suggestion not available under Starter plan. !8248
## 11.4.6 (2018-11-18)
### Security (1 change)
- Prevent templated services from being imported.
## 11.4.5 (2018-11-04)
### Fixed (1 change)
- Stops showing review actions on commit discussions in merge requests. !8007
### Performance (1 change)
- Add indexes to all geo event foreign keys. !7990
## 11.4.4 (2018-10-30) ## 11.4.4 (2018-10-30)
- No changes. - No changes.
...@@ -108,6 +259,23 @@ Please view this file on the master branch, on stable branches it's out of date. ...@@ -108,6 +259,23 @@ Please view this file on the master branch, on stable branches it's out of date.
- API: Allow issue weight parameter to be greater than or equal to zero. - API: Allow issue weight parameter to be greater than or equal to zero.
## 11.3.11 (2018-11-26)
### Security (7 changes)
- Escape entity title while autocomplete template rendering to prevent XSS. !697
- Properly filter private references from system notes.
- Authorize users when listing board users and milestones.
- Project groups approvers no longer leak private groups info.
- Resolve: Guest can set weight of a new issue.
- Fixes XSS with merge request approvers selection.
- Protect against CSRF attacks when adding Slack app.
## 11.3.10 (2018-11-18)
- No changes.
## 11.3.9 (2018-10-31) ## 11.3.9 (2018-10-31)
- No changes. - No changes.
......
This diff is collapsed.
7.0.0 7.2.1
\ No newline at end of file
# --- Special code for migrating to Rails 5.0 --- # --- Special code for migrating to Rails 5.0 ---
def rails5? def rails5?
%w[1 true].include?(ENV["RAILS5"]) !%w[0 false].include?(ENV["RAILS5"])
end end
gem_versions = {} gem_versions = {}
gem_versions['activerecord_sane_schema_dumper'] = rails5? ? '1.0' : '0.2' gem_versions['activerecord_sane_schema_dumper'] = rails5? ? '1.0' : '0.2'
gem_versions['default_value_for'] = rails5? ? '~> 3.0.5' : '~> 3.0.0' gem_versions['rails'] = rails5? ? '5.0.7' : '4.2.10'
gem_versions['rails'] = rails5? ? '5.0.7' : '4.2.10' gem_versions['rails-i18n'] = rails5? ? '~> 5.1' : '~> 4.0.9'
gem_versions['rails-i18n'] = rails5? ? '~> 5.1' : '~> 4.0.9'
# The 2.0.6 version of rack requires monkeypatch to be present in
# `config.ru`. This can be removed once a new update for Rack
# is available that contains https://github.com/rack/rack/pull/1201.
gem_versions['rack'] = rails5? ? '2.0.6' : '1.6.11'
# --- The end of special code for migrating to Rails 5.0 --- # --- The end of special code for migrating to Rails 5.0 ---
source 'https://rubygems.org' source 'https://rubygems.org'
...@@ -15,13 +19,20 @@ source 'https://rubygems.org' ...@@ -15,13 +19,20 @@ source 'https://rubygems.org'
gem 'rails', gem_versions['rails'] gem 'rails', gem_versions['rails']
gem 'rails-deprecated_sanitizer', '~> 1.0.3' gem 'rails-deprecated_sanitizer', '~> 1.0.3'
# Improves copy-on-write performance for MRI
gem 'nakayoshi_fork', '~> 0.0.4'
# Responders respond_to and respond_with # Responders respond_to and respond_with
gem 'responders', '~> 2.0' gem 'responders', '~> 2.0'
gem 'sprockets', '~> 3.7.0' gem 'sprockets', '~> 3.7.0'
# Default values for AR models # Default values for AR models
gem 'default_value_for', gem_versions['default_value_for'] if rails5?
gem 'gitlab-default_value_for', '~> 3.1.1', require: 'default_value_for'
else
gem 'default_value_for', '~> 3.0.0'
end
# Supported DBs # Supported DBs
gem 'mysql2', '~> 0.4.10', group: :mysql gem 'mysql2', '~> 0.4.10', group: :mysql
...@@ -134,7 +145,7 @@ gem 'faraday_middleware-aws-signers-v4' ...@@ -134,7 +145,7 @@ gem 'faraday_middleware-aws-signers-v4'
# Markdown and HTML processing # Markdown and HTML processing
gem 'html-pipeline', '~> 2.8' gem 'html-pipeline', '~> 2.8'
gem 'deckar01-task_list', '2.0.0' gem 'deckar01-task_list', '2.0.0'
gem 'gitlab-markup', '~> 1.6.4' gem 'gitlab-markup', '~> 1.6.5'
gem 'github-markup', '~> 1.7.0', require: 'github/markup' gem 'github-markup', '~> 1.7.0', require: 'github/markup'
gem 'redcarpet', '~> 3.4' gem 'redcarpet', '~> 3.4'
gem 'commonmarker', '~> 0.17' gem 'commonmarker', '~> 0.17'
...@@ -143,7 +154,7 @@ gem 'rdoc', '~> 6.0' ...@@ -143,7 +154,7 @@ gem 'rdoc', '~> 6.0'
gem 'org-ruby', '~> 0.9.12' gem 'org-ruby', '~> 0.9.12'
gem 'creole', '~> 0.5.0' gem 'creole', '~> 0.5.0'
gem 'wikicloth', '0.8.1' gem 'wikicloth', '0.8.1'
gem 'asciidoctor', '~> 1.5.6' gem 'asciidoctor', '~> 1.5.8'
gem 'asciidoctor-plantuml', '0.0.8' gem 'asciidoctor-plantuml', '0.0.8'
gem 'rouge', '~> 3.1' gem 'rouge', '~> 3.1'
gem 'truncato', '~> 0.7.9' gem 'truncato', '~> 0.7.9'
...@@ -158,6 +169,8 @@ gem 'icalendar' ...@@ -158,6 +169,8 @@ gem 'icalendar'
gem 'diffy', '~> 3.1.0' gem 'diffy', '~> 3.1.0'
# Application server # Application server
gem 'rack', gem_versions['rack']
group :unicorn do group :unicorn do
gem 'unicorn', '~> 5.1.0' gem 'unicorn', '~> 5.1.0'
gem 'unicorn-worker-killer', '~> 0.4.4' gem 'unicorn-worker-killer', '~> 0.4.4'
...@@ -214,6 +227,9 @@ gem 'redis-rails', '~> 5.0.2' ...@@ -214,6 +227,9 @@ gem 'redis-rails', '~> 5.0.2'
gem 'redis', '~> 3.2' gem 'redis', '~> 3.2'
gem 'connection_pool', '~> 2.0' gem 'connection_pool', '~> 2.0'
# Discord integration
gem 'discordrb-webhooks-blackst0ne', '~> 3.3', require: false
# HipChat integration # HipChat integration
gem 'hipchat', '~> 1.5.0' gem 'hipchat', '~> 1.5.0'
...@@ -230,13 +246,13 @@ gem 'slack-notifier', '~> 1.5.1' ...@@ -230,13 +246,13 @@ gem 'slack-notifier', '~> 1.5.1'
gem 'hangouts-chat', '~> 0.0.5' gem 'hangouts-chat', '~> 0.0.5'
# Asana integration # Asana integration
gem 'asana', '~> 0.6.0' gem 'asana', '~> 0.8.1'
# FogBugz integration # FogBugz integration
gem 'ruby-fogbugz', '~> 0.2.1' gem 'ruby-fogbugz', '~> 0.2.1'
# Kubernetes integration # Kubernetes integration
gem 'kubeclient', '~> 3.1.0' gem 'kubeclient', '~> 4.0.0'
# Sanitize user input # Sanitize user input
gem 'sanitize', '~> 4.6' gem 'sanitize', '~> 4.6'
...@@ -323,8 +339,8 @@ group :development do ...@@ -323,8 +339,8 @@ group :development do
gem 'rblineprof', '~> 0.3.6', platform: :mri, require: false gem 'rblineprof', '~> 0.3.6', platform: :mri, require: false
# Better errors handler # Better errors handler
gem 'better_errors', '~> 2.1.0' gem 'better_errors', '~> 2.5.0'
gem 'binding_of_caller', '~> 0.7.2' gem 'binding_of_caller', '~> 0.8.0'
# thin instead webrick # thin instead webrick
gem 'thin', '~> 1.7.0' gem 'thin', '~> 1.7.0'
...@@ -351,7 +367,7 @@ group :development, :test do ...@@ -351,7 +367,7 @@ group :development, :test do
gem 'minitest', '~> 5.7.0' gem 'minitest', '~> 5.7.0'
# Generate Fake data # Generate Fake data
gem 'ffaker', '~> 2.4' gem 'ffaker', '~> 2.10'
gem 'capybara', '~> 2.15' gem 'capybara', '~> 2.15'
gem 'capybara-screenshot', '~> 1.0.0' gem 'capybara-screenshot', '~> 1.0.0'
...@@ -366,14 +382,14 @@ group :development, :test do ...@@ -366,14 +382,14 @@ group :development, :test do
gem 'rubocop-rspec', '~> 1.22.1' gem 'rubocop-rspec', '~> 1.22.1'
gem 'scss_lint', '~> 0.56.0', require: false gem 'scss_lint', '~> 0.56.0', require: false
gem 'haml_lint', '~> 0.26.0', require: false gem 'haml_lint', '~> 0.28.0', require: false
gem 'simplecov', '~> 0.14.0', require: false gem 'simplecov', '~> 0.14.0', require: false
gem 'bundler-audit', '~> 0.5.0', require: false gem 'bundler-audit', '~> 0.5.0', require: false
gem 'benchmark-ips', '~> 2.3.0', require: false gem 'benchmark-ips', '~> 2.3.0', require: false
gem 'license_finder', '~> 5.4', require: false gem 'license_finder', '~> 5.4', require: false
gem 'knapsack', '~> 1.16' gem 'knapsack', '~> 1.17'
gem 'activerecord_sane_schema_dumper', gem_versions['activerecord_sane_schema_dumper'] gem 'activerecord_sane_schema_dumper', gem_versions['activerecord_sane_schema_dumper']
...@@ -392,7 +408,7 @@ group :test do ...@@ -392,7 +408,7 @@ group :test do
gem 'rails-controller-testing' if rails5? # Rails5 only gem. gem 'rails-controller-testing' if rails5? # Rails5 only gem.
gem 'test_after_commit', '~> 1.1' unless rails5? # Remove this gem when migrated to rails 5.0. It's been integrated to rails 5.0. gem 'test_after_commit', '~> 1.1' unless rails5? # Remove this gem when migrated to rails 5.0. It's been integrated to rails 5.0.
gem 'sham_rack', '~> 1.3.6' gem 'sham_rack', '~> 1.3.6'
gem 'concurrent-ruby', '~> 1.0.5' gem 'concurrent-ruby', '~> 1.1'
gem 'test-prof', '~> 0.2.5' gem 'test-prof', '~> 0.2.5'
gem 'rspec_junit_formatter' gem 'rspec_junit_formatter'
end end
...@@ -431,7 +447,7 @@ group :ed25519 do ...@@ -431,7 +447,7 @@ group :ed25519 do
end end
# Gitaly GRPC client # Gitaly GRPC client
gem 'gitaly-proto', '~> 0.118.1', require: 'gitaly' gem 'gitaly-proto', '~> 1.2.0', require: 'gitaly'
gem 'grpc', '~> 1.15.0' gem 'grpc', '~> 1.15.0'
gem 'google-protobuf', '~> 3.6' gem 'google-protobuf', '~> 3.6'
...@@ -446,6 +462,3 @@ gem 'flipper-active_support_cache_store', '~> 0.13.0' ...@@ -446,6 +462,3 @@ gem 'flipper-active_support_cache_store', '~> 0.13.0'
# Structured logging # Structured logging
gem 'lograge', '~> 0.5' gem 'lograge', '~> 0.5'
gem 'grape_logging', '~> 1.7' gem 'grape_logging', '~> 1.7'
# Asset synchronization
gem 'asset_sync', '~> 2.4'
This diff is collapsed.
# BUNDLE_GEMFILE=Gemfile.rails4 bundle install
ENV["RAILS5"] = "false"
gemfile = File.expand_path("../Gemfile", __FILE__)
eval(File.read(gemfile), nil, gemfile)
This diff is collapsed.
# BUNDLE_GEMFILE=Gemfile.rails5 bundle install
ENV["RAILS5"] = "true"
gemfile = File.expand_path("../Gemfile", __FILE__)
eval(File.read(gemfile), nil, gemfile)
This diff is collapsed.
This document is intended to communicate the product philosophy GitLab uses in creating GitLab Enterprise Edition. The principles can be found in the [Product Section of the GitLab Handbook](https://about.gitlab.com/handbook/product/#product-at-gitlab).
...@@ -103,6 +103,12 @@ picked into the stable branches) up to the 19th of the month. Such merge ...@@ -103,6 +103,12 @@ picked into the stable branches) up to the 19th of the month. Such merge
requests should have the ~"feature flag" label assigned, and don't require a requests should have the ~"feature flag" label assigned, and don't require a
corresponding exception request to be created. corresponding exception request to be created.
In order to build the final package and present the feature for self-hosted
customers, the feature flag should be removed. This should happen before the
22nd, ideally _at least_ 2 days before. That means MRs with feature
flags being picked at the 19th would have a quite tight schedule, so picking
these _earlier_ is preferable.
While rare, release managers may decide to reject picking a change into a stable While rare, release managers may decide to reject picking a change into a stable
branch, even when feature flags are used. This might be necessary if the changes branch, even when feature flags are used. This might be necessary if the changes
are deemed problematic, too invasive, or there simply isn't enough time to are deemed problematic, too invasive, or there simply isn't enough time to
......
11.5.0-pre 11.6.0-pre
...@@ -10,10 +10,10 @@ const Api = { ...@@ -10,10 +10,10 @@ const Api = {
projectsPath: '/api/:version/projects.json', projectsPath: '/api/:version/projects.json',
projectPath: '/api/:version/projects/:id', projectPath: '/api/:version/projects/:id',
projectLabelsPath: '/:namespace_path/:project_path/labels', projectLabelsPath: '/:namespace_path/:project_path/labels',
mergeRequestPath: '/api/:version/projects/:id/merge_requests/:mrid', projectMergeRequestPath: '/api/:version/projects/:id/merge_requests/:mrid',
projectMergeRequestChangesPath: '/api/:version/projects/:id/merge_requests/:mrid/changes',
projectMergeRequestVersionsPath: '/api/:version/projects/:id/merge_requests/:mrid/versions',
mergeRequestsPath: '/api/:version/merge_requests', mergeRequestsPath: '/api/:version/merge_requests',
mergeRequestChangesPath: '/api/:version/projects/:id/merge_requests/:mrid/changes',
mergeRequestVersionsPath: '/api/:version/projects/:id/merge_requests/:mrid/versions',
groupLabelsPath: '/groups/:namespace_path/-/labels', groupLabelsPath: '/groups/:namespace_path/-/labels',
ldapGroupsPath: '/api/:version/ldap/:provider/groups.json', ldapGroupsPath: '/api/:version/ldap/:provider/groups.json',
issuableTemplatePath: '/:namespace_path/:project_path/templates/:type/:key', issuableTemplatePath: '/:namespace_path/:project_path/templates/:type/:key',
...@@ -101,36 +101,36 @@ const Api = { ...@@ -101,36 +101,36 @@ const Api = {
}, },
// Return Merge Request for project // Return Merge Request for project
mergeRequest(projectPath, mergeRequestId, params = {}) { projectMergeRequest(projectPath, mergeRequestId, params = {}) {
const url = Api.buildUrl(Api.mergeRequestPath) const url = Api.buildUrl(Api.projectMergeRequestPath)
.replace(':id', encodeURIComponent(projectPath)) .replace(':id', encodeURIComponent(projectPath))
.replace(':mrid', mergeRequestId); .replace(':mrid', mergeRequestId);
return axios.get(url, { params }); return axios.get(url, { params });
}, },
mergeRequests(params = {}) { projectMergeRequestChanges(projectPath, mergeRequestId) {
const url = Api.buildUrl(Api.mergeRequestsPath); const url = Api.buildUrl(Api.projectMergeRequestChangesPath)
return axios.get(url, { params });
},
mergeRequestChanges(projectPath, mergeRequestId) {
const url = Api.buildUrl(Api.mergeRequestChangesPath)
.replace(':id', encodeURIComponent(projectPath)) .replace(':id', encodeURIComponent(projectPath))
.replace(':mrid', mergeRequestId); .replace(':mrid', mergeRequestId);
return axios.get(url); return axios.get(url);
}, },
mergeRequestVersions(projectPath, mergeRequestId) { projectMergeRequestVersions(projectPath, mergeRequestId) {
const url = Api.buildUrl(Api.mergeRequestVersionsPath) const url = Api.buildUrl(Api.projectMergeRequestVersionsPath)
.replace(':id', encodeURIComponent(projectPath)) .replace(':id', encodeURIComponent(projectPath))
.replace(':mrid', mergeRequestId); .replace(':mrid', mergeRequestId);
return axios.get(url); return axios.get(url);
}, },
mergeRequests(params = {}) {
const url = Api.buildUrl(Api.mergeRequestsPath);
return axios.get(url, { params });
},
newLabel(namespacePath, projectPath, data, callback) { newLabel(namespacePath, projectPath, data, callback) {
let url; let url;
......
<script> <script>
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import Tooltip from '~/vue_shared/directives/tooltip'; import { GlLoadingIcon, GlTooltipDirective } from '@gitlab/ui';
export default { export default {
name: 'Badge', name: 'Badge',
components: { components: {
Icon, Icon,
Tooltip, GlLoadingIcon,
}, },
directives: { directives: {
Tooltip, GlTooltip: GlTooltipDirective,
}, },
props: { props: {
imageUrl: { imageUrl: {
...@@ -63,12 +63,7 @@ export default { ...@@ -63,12 +63,7 @@ export default {
<template> <template>
<div> <div>
<a <a v-show="!isLoading && !hasError" :href="linkUrl" target="_blank" rel="noopener noreferrer">
v-show="!isLoading && !hasError"
:href="linkUrl"
target="_blank"
rel="noopener noreferrer"
>
<img <img
:src="imageUrlWithRetries" :src="imageUrlWithRetries"
class="project-badge" class="project-badge"
...@@ -78,15 +73,9 @@ export default { ...@@ -78,15 +73,9 @@ export default {
/> />
</a> </a>
<gl-loading-icon <gl-loading-icon v-show="isLoading" :inline="true" />
v-show="isLoading"
:inline="true"
/>
<div <div v-show="hasError" class="btn-group">
v-show="hasError"
class="btn-group"
>
<div class="btn btn-default btn-sm disabled"> <div class="btn btn-default btn-sm disabled">
<icon <icon
:size="16" :size="16"
...@@ -95,25 +84,20 @@ export default { ...@@ -95,25 +84,20 @@ export default {
aria-hidden="true" aria-hidden="true"
/> />
</div> </div>
<div <div class="btn btn-default btn-sm disabled">
class="btn btn-default btn-sm disabled"
>
<span class="prepend-left-8 append-right-8">{{ s__('Badges|No badge image') }}</span> <span class="prepend-left-8 append-right-8">{{ s__('Badges|No badge image') }}</span>
</div> </div>
</div> </div>
<button <button
v-show="hasError" v-show="hasError"
v-tooltip v-gl-tooltip.hover
:title="s__('Badges|Reload badge image')" :title="s__('Badges|Reload badge image')"
class="btn btn-transparent btn-sm text-primary" class="btn btn-transparent btn-sm text-primary"
type="button" type="button"
@click="reloadImage" @click="reloadImage"
> >
<icon <icon :size="16" name="retry" />
:size="16"
name="retry"
/>
</button> </button>
</div> </div>
</template> </template>
...@@ -4,6 +4,7 @@ import { mapActions, mapState } from 'vuex'; ...@@ -4,6 +4,7 @@ import { mapActions, mapState } from 'vuex';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { s__, sprintf } from '~/locale'; import { s__, sprintf } from '~/locale';
import LoadingButton from '~/vue_shared/components/loading_button.vue'; import LoadingButton from '~/vue_shared/components/loading_button.vue';
import { GlLoadingIcon } from '@gitlab/ui';
import createEmptyBadge from '../empty_badge'; import createEmptyBadge from '../empty_badge';
import Badge from './badge.vue'; import Badge from './badge.vue';
...@@ -14,6 +15,7 @@ export default { ...@@ -14,6 +15,7 @@ export default {
components: { components: {
Badge, Badge,
LoadingButton, LoadingButton,
GlLoadingIcon,
}, },
props: { props: {
isEditing: { isEditing: {
...@@ -153,10 +155,7 @@ export default { ...@@ -153,10 +155,7 @@ export default {
@submit.prevent.stop="onSubmit" @submit.prevent.stop="onSubmit"
> >
<div class="form-group"> <div class="form-group">
<label <label for="badge-link-url" class="label-bold">{{ s__('Badges|Link') }}</label>
for="badge-link-url"
class="label-bold"
>{{ s__('Badges|Link') }}</label>
<p v-html="helpText"></p> <p v-html="helpText"></p>
<input <input
id="badge-link-url" id="badge-link-url"
...@@ -166,19 +165,12 @@ export default { ...@@ -166,19 +165,12 @@ export default {
required required
@input="debouncedPreview" @input="debouncedPreview"
/> />
<div class="invalid-feedback"> <div class="invalid-feedback">{{ s__('Badges|Please fill in a valid URL') }}</div>
{{ s__('Badges|Please fill in a valid URL') }} <span class="form-text text-muted"> {{ badgeLinkUrlExample }} </span>
</div>
<span class="form-text text-muted">
{{ badgeLinkUrlExample }}
</span>
</div> </div>
<div class="form-group"> <div class="form-group">
<label <label for="badge-image-url" class="label-bold">{{ s__('Badges|Badge image URL') }}</label>
for="badge-image-url"
class="label-bold"
>{{ s__('Badges|Badge image URL') }}</label>
<p v-html="helpText"></p> <p v-html="helpText"></p>
<input <input
id="badge-image-url" id="badge-image-url"
...@@ -188,12 +180,8 @@ export default { ...@@ -188,12 +180,8 @@ export default {
required required
@input="debouncedPreview" @input="debouncedPreview"
/> />
<div class="invalid-feedback"> <div class="invalid-feedback">{{ s__('Badges|Please fill in a valid URL') }}</div>
{{ s__('Badges|Please fill in a valid URL') }} <span class="form-text text-muted"> {{ badgeImageUrlExample }} </span>
</div>
<span class="form-text text-muted">
{{ badgeImageUrlExample }}
</span>
</div> </div>
<div class="form-group"> <div class="form-group">
...@@ -204,37 +192,22 @@ export default { ...@@ -204,37 +192,22 @@ export default {
:image-url="renderedImageUrl" :image-url="renderedImageUrl"
:link-url="renderedLinkUrl" :link-url="renderedLinkUrl"
/> />
<p v-show="isRendering"> <p v-show="isRendering"><gl-loading-icon :inline="true" /></p>
<gl-loading-icon <p v-show="!renderedBadge && !isRendering" class="disabled-content">
:inline="true" {{ s__('Badges|No image to preview') }}
/>
</p> </p>
<p
v-show="!renderedBadge && !isRendering"
class="disabled-content"
>{{ s__('Badges|No image to preview') }}</p>
</div> </div>
<div <div v-if="isEditing" class="row-content-block">
v-if="isEditing"
class="row-content-block"
>
<loading-button <loading-button
:loading="isSaving" :loading="isSaving"
:label="s__('Badges|Save changes')" :label="s__('Badges|Save changes')"
type="submit" type="submit"
container-class="btn btn-success" container-class="btn btn-success"
/> />
<button <button class="btn btn-cancel" type="button" @click="onCancel">{{ __('Cancel') }}</button>
class="btn btn-cancel"
type="button"
@click="onCancel"
>{{ __('Cancel') }}</button>
</div> </div>
<div <div v-else class="form-group">
v-else
class="form-group"
>
<loading-button <loading-button
:loading="isSaving" :loading="isSaving"
:label="s__('Badges|Add badge')" :label="s__('Badges|Add badge')"
......
<script> <script>
import { mapState } from 'vuex'; import { mapState } from 'vuex';
import { GlLoadingIcon } from '@gitlab/ui';
import BadgeListRow from './badge_list_row.vue'; import BadgeListRow from './badge_list_row.vue';
import { GROUP_BADGE } from '../constants'; import { GROUP_BADGE } from '../constants';
...@@ -7,6 +8,7 @@ export default { ...@@ -7,6 +8,7 @@ export default {
name: 'BadgeList', name: 'BadgeList',
components: { components: {
BadgeListRow, BadgeListRow,
GlLoadingIcon,
}, },
computed: { computed: {
...mapState(['badges', 'isLoading', 'kind']), ...mapState(['badges', 'isLoading', 'kind']),
...@@ -24,32 +26,15 @@ export default { ...@@ -24,32 +26,15 @@ export default {
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">
{{ s__('Badges|Your badges') }} {{ s__('Badges|Your badges') }}
<span <span v-show="!isLoading" class="badge badge-pill">{{ badges.length }}</span>
v-show="!isLoading"
class="badge badge-pill"
>{{ badges.length }}</span>
</div> </div>
<gl-loading-icon <gl-loading-icon v-show="isLoading" :size="2" class="card-body" />
v-show="isLoading" <div v-if="hasNoBadges" class="card-body">
:size="2"
class="card-body"
/>
<div
v-if="hasNoBadges"
class="card-body"
>
<span v-if="isGroupBadge">{{ s__('Badges|This group has no badges') }}</span> <span v-if="isGroupBadge">{{ s__('Badges|This group has no badges') }}</span>
<span v-else>{{ s__('Badges|This project has no badges') }}</span> <span v-else>{{ s__('Badges|This project has no badges') }}</span>
</div> </div>
<div <div v-else class="card-body">
v-else <badge-list-row v-for="badge in badges" :key="badge.id" :badge="badge" />
class="card-body"
>
<badge-list-row
v-for="badge in badges"
:key="badge.id"
:badge="badge"
/>
</div> </div>
</div> </div>
</template> </template>
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
import { mapActions, mapState } from 'vuex'; import { mapActions, mapState } from 'vuex';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import { GlLoadingIcon } from '@gitlab/ui';
import { PROJECT_BADGE } from '../constants'; import { PROJECT_BADGE } from '../constants';
import Badge from './badge.vue'; import Badge from './badge.vue';
...@@ -10,6 +11,7 @@ export default { ...@@ -10,6 +11,7 @@ export default {
components: { components: {
Badge, Badge,
Icon, Icon,
GlLoadingIcon,
}, },
props: { props: {
badge: { badge: {
...@@ -48,20 +50,14 @@ export default { ...@@ -48,20 +50,14 @@ export default {
<span class="badge badge-pill">{{ badgeKindText }}</span> <span class="badge badge-pill">{{ badgeKindText }}</span>
</div> </div>
<div class="table-section section-15 table-button-footer"> <div class="table-section section-15 table-button-footer">
<div <div v-if="canEditBadge" class="table-action-buttons">
v-if="canEditBadge"
class="table-action-buttons">
<button <button
:disabled="badge.isDeleting" :disabled="badge.isDeleting"
class="btn btn-default append-right-8" class="btn btn-default append-right-8"
type="button" type="button"
@click="editBadge(badge)" @click="editBadge(badge);"
> >
<icon <icon :size="16" :aria-label="__('Edit')" name="pencil" />
:size="16"
:aria-label="__('Edit')"
name="pencil"
/>
</button> </button>
<button <button
:disabled="badge.isDeleting" :disabled="badge.isDeleting"
...@@ -69,18 +65,11 @@ export default { ...@@ -69,18 +65,11 @@ export default {
type="button" type="button"
data-toggle="modal" data-toggle="modal"
data-target="#delete-badge-modal" data-target="#delete-badge-modal"
@click="updateBadgeInModal(badge)" @click="updateBadgeInModal(badge);"
> >
<icon <icon :size="16" :aria-label="__('Delete')" name="remove" />
:size="16"
:aria-label="__('Delete')"
name="remove"
/>
</button> </button>
<gl-loading-icon <gl-loading-icon v-show="badge.isDeleting" :inline="true" />
v-show="badge.isDeleting"
:inline="true"
/>
</div> </div>
</div> </div>
</div> </div>
......
...@@ -46,7 +46,8 @@ export default { ...@@ -46,7 +46,8 @@ export default {
:header-title-text="s__('Badges|Delete badge?')" :header-title-text="s__('Badges|Delete badge?')"
:footer-primary-button-text="s__('Badges|Delete badge')" :footer-primary-button-text="s__('Badges|Delete badge')"
footer-primary-button-variant="danger" footer-primary-button-variant="danger"
@submit="onSubmitModal"> @submit="onSubmitModal"
>
<div class="well"> <div class="well">
<badge <badge
:image-url="badgeInModal ? badgeInModal.renderedImageUrl : ''" :image-url="badgeInModal ? badgeInModal.renderedImageUrl : ''"
...@@ -56,15 +57,9 @@ export default { ...@@ -56,15 +57,9 @@ export default {
<p v-html="deleteModalText"></p> <p v-html="deleteModalText"></p>
</gl-modal> </gl-modal>
<badge-form <badge-form v-show="isEditing" :is-editing="true" />
v-show="isEditing"
:is-editing="true"
/>
<badge-form <badge-form v-show="!isEditing" :is-editing="false" />
v-show="!isEditing"
:is-editing="false"
/>
<badge-list v-show="!isEditing" /> <badge-list v-show="!isEditing" />
</div> </div>
</template> </template>
import $ from 'jquery'; import $ from 'jquery';
import { convertPermissionToBoolean } from '~/lib/utils/common_utils'; import { parseBoolean } from '~/lib/utils/common_utils';
import GfmAutoComplete from '~/gfm_auto_complete'; import GfmAutoComplete from '~/gfm_auto_complete';
export default function initGFMInput() { export default function initGFMInput() {
$('.js-gfm-input:not(.js-vue-textarea)').each((i, el) => { $('.js-gfm-input:not(.js-vue-textarea)').each((i, el) => {
const gfm = new GfmAutoComplete(gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources); const gfm = new GfmAutoComplete(gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources);
const enableGFM = convertPermissionToBoolean(el.dataset.supportsAutocomplete); const enableGFM = parseBoolean(el.dataset.supportsAutocomplete);
gfm.setup($(el), { gfm.setup($(el), {
emojis: true, emojis: true,
......
...@@ -26,6 +26,9 @@ export default function renderMermaid($els) { ...@@ -26,6 +26,9 @@ export default function renderMermaid($els) {
}, },
// mermaidAPI options // mermaidAPI options
theme: 'neutral', theme: 'neutral',
flowchart: {
htmlLabels: false,
},
}); });
$els.each((i, el) => { $els.each((i, el) => {
......
import { n__ } from '../locale'; import { n__ } from '../locale';
import { convertPermissionToBoolean } from '../lib/utils/common_utils'; import { parseBoolean } from '../lib/utils/common_utils';
export default class SecretValues { export default class SecretValues {
constructor({ constructor({
...@@ -16,7 +16,7 @@ export default class SecretValues { ...@@ -16,7 +16,7 @@ export default class SecretValues {
this.revealButton = this.container.querySelector('.js-secret-value-reveal-button'); this.revealButton = this.container.querySelector('.js-secret-value-reveal-button');
if (this.revealButton) { if (this.revealButton) {
const isRevealed = convertPermissionToBoolean(this.revealButton.dataset.secretRevealStatus); const isRevealed = parseBoolean(this.revealButton.dataset.secretRevealStatus);
this.updateDom(isRevealed); this.updateDom(isRevealed);
this.revealButton.addEventListener('click', this.onRevealButtonClicked.bind(this)); this.revealButton.addEventListener('click', this.onRevealButtonClicked.bind(this));
...@@ -24,9 +24,7 @@ export default class SecretValues { ...@@ -24,9 +24,7 @@ export default class SecretValues {
} }
onRevealButtonClicked() { onRevealButtonClicked() {
const previousIsRevealed = convertPermissionToBoolean( const previousIsRevealed = parseBoolean(this.revealButton.dataset.secretRevealStatus);
this.revealButton.dataset.secretRevealStatus,
);
this.updateDom(!previousIsRevealed); this.updateDom(!previousIsRevealed);
} }
......
...@@ -4,6 +4,7 @@ import Mousetrap from 'mousetrap'; ...@@ -4,6 +4,7 @@ import Mousetrap from 'mousetrap';
import axios from '../../lib/utils/axios_utils'; import axios from '../../lib/utils/axios_utils';
import { refreshCurrentPage, visitUrl } from '../../lib/utils/url_utility'; import { refreshCurrentPage, visitUrl } from '../../lib/utils/url_utility';
import findAndFollowLink from '../../lib/utils/navigation_utility'; import findAndFollowLink from '../../lib/utils/navigation_utility';
import { parseBoolean } from '~/lib/utils/common_utils';
const defaultStopCallback = Mousetrap.stopCallback; const defaultStopCallback = Mousetrap.stopCallback;
Mousetrap.stopCallback = (e, element, combo) => { Mousetrap.stopCallback = (e, element, combo) => {
...@@ -61,7 +62,7 @@ export default class Shortcuts { ...@@ -61,7 +62,7 @@ export default class Shortcuts {
static onTogglePerfBar(e) { static onTogglePerfBar(e) {
e.preventDefault(); e.preventDefault();
const performanceBarCookieName = 'perf_bar_enabled'; const performanceBarCookieName = 'perf_bar_enabled';
if (Cookies.get(performanceBarCookieName) === 'true') { if (parseBoolean(Cookies.get(performanceBarCookieName))) {
Cookies.set(performanceBarCookieName, 'false', { path: '/' }); Cookies.set(performanceBarCookieName, 'false', { path: '/' });
} else { } else {
Cookies.set(performanceBarCookieName, 'true', { path: '/' }); Cookies.set(performanceBarCookieName, 'true', { path: '/' });
......
...@@ -4,6 +4,7 @@ import _ from 'underscore'; ...@@ -4,6 +4,7 @@ import _ from 'underscore';
import Sidebar from '../../right_sidebar'; import Sidebar from '../../right_sidebar';
import Shortcuts from './shortcuts'; import Shortcuts from './shortcuts';
import { CopyAsGFM } from '../markdown/copy_as_gfm'; import { CopyAsGFM } from '../markdown/copy_as_gfm';
import { getSelectedFragment } from '~/lib/utils/common_utils';
export default class ShortcutsIssuable extends Shortcuts { export default class ShortcutsIssuable extends Shortcuts {
constructor(isMergeRequest) { constructor(isMergeRequest) {
...@@ -24,17 +25,43 @@ export default class ShortcutsIssuable extends Shortcuts { ...@@ -24,17 +25,43 @@ export default class ShortcutsIssuable extends Shortcuts {
static replyWithSelectedText() { static replyWithSelectedText() {
const $replyField = $('.js-main-target-form .js-vue-comment-form'); const $replyField = $('.js-main-target-form .js-vue-comment-form');
const documentFragment = window.gl.utils.getSelectedFragment();
if (!$replyField.length) { if (!$replyField.length || $replyField.is(':hidden') /* Other tab selected in MR */) {
return false; return false;
} }
const documentFragment = getSelectedFragment(document.querySelector('#content-body'));
if (!documentFragment) { if (!documentFragment) {
$replyField.focus(); $replyField.focus();
return false; return false;
} }
// Sanity check: Make sure the selected text comes from a discussion : it can either contain a message...
let foundMessage = !!documentFragment.querySelector('.md, .wiki');
// ... Or come from a message
if (!foundMessage) {
if (documentFragment.originalNodes) {
documentFragment.originalNodes.forEach(e => {
let node = e;
do {
// Text nodes don't define the `matches` method
if (node.matches && node.matches('.md, .wiki')) {
foundMessage = true;
}
node = node.parentNode;
} while (node && !foundMessage);
});
}
// If there is no message, just select the reply field
if (!foundMessage) {
$replyField.focus();
return false;
}
}
const el = CopyAsGFM.transformGFMSelection(documentFragment.cloneNode(true)); const el = CopyAsGFM.transformGFMSelection(documentFragment.cloneNode(true));
const selected = CopyAsGFM.nodeToGFM(el); const selected = CopyAsGFM.nodeToGFM(el);
......
...@@ -124,7 +124,7 @@ export default class FileTemplateMediator { ...@@ -124,7 +124,7 @@ export default class FileTemplateMediator {
selectTemplateFile(selector, query, data) { selectTemplateFile(selector, query, data) {
selector.renderLoading(); selector.renderLoading();
// in case undo menu is already already there // in case undo menu is already there
this.destroyUndoMenu(); this.destroyUndoMenu();
this.fetchFileTemplate(selector.config.type, query, data) this.fetchFileTemplate(selector.config.type, query, data)
.then(file => { .then(file => {
......
...@@ -23,10 +23,12 @@ export default class BlobViewer { ...@@ -23,10 +23,12 @@ export default class BlobViewer {
if (!viewer || !viewer.dataset.richType) return; if (!viewer || !viewer.dataset.richType) return;
const initViewer = promise => const initViewer = promise =>
promise.then(module => module.default(viewer)).catch(error => { promise
Flash('Error loading file viewer.'); .then(module => module.default(viewer))
throw error; .catch(error => {
}); Flash('Error loading file viewer.');
throw error;
});
switch (viewer.dataset.richType) { switch (viewer.dataset.richType) {
case 'balsamiq': case 'balsamiq':
......
...@@ -16,9 +16,17 @@ export default () => { ...@@ -16,9 +16,17 @@ export default () => {
const filePath = editBlobForm.data('blobFilename'); const filePath = editBlobForm.data('blobFilename');
const currentAction = $('.js-file-title').data('currentAction'); const currentAction = $('.js-file-title').data('currentAction');
const projectId = editBlobForm.data('project-id'); const projectId = editBlobForm.data('project-id');
const commitButton = $('.js-commit-button');
commitButton.on('click', () => {
window.onbeforeunload = null;
});
new EditBlob(`${urlRoot}${assetsPath}`, filePath, currentAction, projectId); new EditBlob(`${urlRoot}${assetsPath}`, filePath, currentAction, projectId);
new NewCommitForm(editBlobForm); new NewCommitForm(editBlobForm);
// returning here blocks page navigation
window.onbeforeunload = () => '';
} }
if (uploadBlobForm.length) { if (uploadBlobForm.length) {
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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