Merge branch 'master' into dz-merge-request-version
Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
|
|
@ -30,6 +30,7 @@
|
|||
/config/secrets.yml
|
||||
/config/sidekiq.yml
|
||||
/coverage/*
|
||||
/coverage-javascript/
|
||||
/db/*.sqlite3
|
||||
/db/*.sqlite3-journal
|
||||
/db/data.yml
|
||||
|
|
|
|||
101
.gitlab-ci.yml
|
|
@ -1,7 +1,7 @@
|
|||
image: "ruby:2.1"
|
||||
image: "ruby:2.3.1"
|
||||
|
||||
cache:
|
||||
key: "ruby21"
|
||||
key: "ruby-231"
|
||||
paths:
|
||||
- vendor/apt
|
||||
- vendor/ruby
|
||||
|
|
@ -15,6 +15,7 @@ variables:
|
|||
USE_DB: "true"
|
||||
USE_BUNDLE_INSTALL: "true"
|
||||
GIT_DEPTH: "20"
|
||||
PHANTOMJS_VERSION: "2.1.1"
|
||||
|
||||
before_script:
|
||||
- source ./scripts/prepare_build.sh
|
||||
|
|
@ -138,57 +139,57 @@ spinach 7 10: *spinach-knapsack
|
|||
spinach 8 10: *spinach-knapsack
|
||||
spinach 9 10: *spinach-knapsack
|
||||
|
||||
# Execute all testing suites against Ruby 2.3
|
||||
.ruby-23: &ruby-23
|
||||
image: "ruby:2.3"
|
||||
# Execute all testing suites against Ruby 2.1
|
||||
.ruby-21: &ruby-21
|
||||
image: "ruby:2.1"
|
||||
<<: *use-db
|
||||
only:
|
||||
- master
|
||||
cache:
|
||||
key: "ruby-23"
|
||||
key: "ruby21"
|
||||
paths:
|
||||
- vendor/apt
|
||||
- vendor/ruby
|
||||
|
||||
.rspec-knapsack-ruby23: &rspec-knapsack-ruby23
|
||||
.rspec-knapsack-ruby21: &rspec-knapsack-ruby21
|
||||
<<: *rspec-knapsack
|
||||
<<: *ruby-23
|
||||
<<: *ruby-21
|
||||
|
||||
.spinach-knapsack-ruby23: &spinach-knapsack-ruby23
|
||||
.spinach-knapsack-ruby21: &spinach-knapsack-ruby21
|
||||
<<: *spinach-knapsack
|
||||
<<: *ruby-23
|
||||
<<: *ruby-21
|
||||
|
||||
rspec 0 20 ruby23: *rspec-knapsack-ruby23
|
||||
rspec 1 20 ruby23: *rspec-knapsack-ruby23
|
||||
rspec 2 20 ruby23: *rspec-knapsack-ruby23
|
||||
rspec 3 20 ruby23: *rspec-knapsack-ruby23
|
||||
rspec 4 20 ruby23: *rspec-knapsack-ruby23
|
||||
rspec 5 20 ruby23: *rspec-knapsack-ruby23
|
||||
rspec 6 20 ruby23: *rspec-knapsack-ruby23
|
||||
rspec 7 20 ruby23: *rspec-knapsack-ruby23
|
||||
rspec 8 20 ruby23: *rspec-knapsack-ruby23
|
||||
rspec 9 20 ruby23: *rspec-knapsack-ruby23
|
||||
rspec 10 20 ruby23: *rspec-knapsack-ruby23
|
||||
rspec 11 20 ruby23: *rspec-knapsack-ruby23
|
||||
rspec 12 20 ruby23: *rspec-knapsack-ruby23
|
||||
rspec 13 20 ruby23: *rspec-knapsack-ruby23
|
||||
rspec 14 20 ruby23: *rspec-knapsack-ruby23
|
||||
rspec 15 20 ruby23: *rspec-knapsack-ruby23
|
||||
rspec 16 20 ruby23: *rspec-knapsack-ruby23
|
||||
rspec 17 20 ruby23: *rspec-knapsack-ruby23
|
||||
rspec 18 20 ruby23: *rspec-knapsack-ruby23
|
||||
rspec 19 20 ruby23: *rspec-knapsack-ruby23
|
||||
rspec 0 20 ruby21: *rspec-knapsack-ruby21
|
||||
rspec 1 20 ruby21: *rspec-knapsack-ruby21
|
||||
rspec 2 20 ruby21: *rspec-knapsack-ruby21
|
||||
rspec 3 20 ruby21: *rspec-knapsack-ruby21
|
||||
rspec 4 20 ruby21: *rspec-knapsack-ruby21
|
||||
rspec 5 20 ruby21: *rspec-knapsack-ruby21
|
||||
rspec 6 20 ruby21: *rspec-knapsack-ruby21
|
||||
rspec 7 20 ruby21: *rspec-knapsack-ruby21
|
||||
rspec 8 20 ruby21: *rspec-knapsack-ruby21
|
||||
rspec 9 20 ruby21: *rspec-knapsack-ruby21
|
||||
rspec 10 20 ruby21: *rspec-knapsack-ruby21
|
||||
rspec 11 20 ruby21: *rspec-knapsack-ruby21
|
||||
rspec 12 20 ruby21: *rspec-knapsack-ruby21
|
||||
rspec 13 20 ruby21: *rspec-knapsack-ruby21
|
||||
rspec 14 20 ruby21: *rspec-knapsack-ruby21
|
||||
rspec 15 20 ruby21: *rspec-knapsack-ruby21
|
||||
rspec 16 20 ruby21: *rspec-knapsack-ruby21
|
||||
rspec 17 20 ruby21: *rspec-knapsack-ruby21
|
||||
rspec 18 20 ruby21: *rspec-knapsack-ruby21
|
||||
rspec 19 20 ruby21: *rspec-knapsack-ruby21
|
||||
|
||||
spinach 0 10 ruby23: *spinach-knapsack-ruby23
|
||||
spinach 1 10 ruby23: *spinach-knapsack-ruby23
|
||||
spinach 2 10 ruby23: *spinach-knapsack-ruby23
|
||||
spinach 3 10 ruby23: *spinach-knapsack-ruby23
|
||||
spinach 4 10 ruby23: *spinach-knapsack-ruby23
|
||||
spinach 5 10 ruby23: *spinach-knapsack-ruby23
|
||||
spinach 6 10 ruby23: *spinach-knapsack-ruby23
|
||||
spinach 7 10 ruby23: *spinach-knapsack-ruby23
|
||||
spinach 8 10 ruby23: *spinach-knapsack-ruby23
|
||||
spinach 9 10 ruby23: *spinach-knapsack-ruby23
|
||||
spinach 0 10 ruby21: *spinach-knapsack-ruby21
|
||||
spinach 1 10 ruby21: *spinach-knapsack-ruby21
|
||||
spinach 2 10 ruby21: *spinach-knapsack-ruby21
|
||||
spinach 3 10 ruby21: *spinach-knapsack-ruby21
|
||||
spinach 4 10 ruby21: *spinach-knapsack-ruby21
|
||||
spinach 5 10 ruby21: *spinach-knapsack-ruby21
|
||||
spinach 6 10 ruby21: *spinach-knapsack-ruby21
|
||||
spinach 7 10 ruby21: *spinach-knapsack-ruby21
|
||||
spinach 8 10 ruby21: *spinach-knapsack-ruby21
|
||||
spinach 9 10 ruby21: *spinach-knapsack-ruby21
|
||||
|
||||
# Other generic tests
|
||||
|
||||
|
|
@ -222,7 +223,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 +268,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 +285,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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
#
|
||||
# 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>
|
||||
12
.rubocop.yml
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
2.1.8
|
||||
2.3.1
|
||||
|
|
|
|||
80
CHANGELOG
|
|
@ -1,51 +1,127 @@
|
|||
Please view this file on the master branch, on stable branches it's out of date.
|
||||
|
||||
v 8.11.0 (unreleased)
|
||||
- Remove the http_parser.rb dependency by removing the tinder gem. !5758 (tbalthazar)
|
||||
- Ability to specify branches for Pivotal Tracker integration (Egor Lynko)
|
||||
- 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)
|
||||
- Rename `markdown_preview` routes to `preview_markdown`. (Christopher Bartz)
|
||||
- Update to Ruby 2.3.1. !4948
|
||||
- Improve diff performance by eliminating redundant checks for text blobs
|
||||
- Ensure that branch names containing escapable characters (e.g. %20) aren't unescaped indiscriminately. !5770 (ewiltshi)
|
||||
- Convert switch icon into icon font (ClemMakesApps)
|
||||
- API: Endpoints for enabling and disabling deploy keys
|
||||
- API: List access requests, request access, approve, and deny access requests to a project or a group. !4833
|
||||
- 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
|
||||
- Pre-create all builds for a Pipeline when the new Pipeline is created !5295
|
||||
- Fix of 'Commits being passed to custom hooks are already reachable when using the UI'
|
||||
- Show member roles to all users on members page
|
||||
- Project.visible_to_user is instrumented again
|
||||
- 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
|
||||
- Fix branches page dropdown sort initial state (ClemMakesApps)
|
||||
- Environments have an url to link to
|
||||
- Various redundant database indexes have been removed
|
||||
- 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
|
||||
- Trigram indexes for the "ci_runners" table have been removed to speed up UPDATE queries
|
||||
- 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 archived badge to project list !5798
|
||||
- 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`
|
||||
- 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
|
||||
- Improve OAuth2 client documentation (muteor)
|
||||
- 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 bug where destroying a namespace would not always destroy projects
|
||||
- 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
|
||||
- Each `File::exists?` replaced to `File::exist?` because of deprecate since ruby version 2.2.0
|
||||
- Add auto-completition in pipeline (Katarzyna Kobierska Ula Budziszewska)
|
||||
- Fix a memory leak caused by Banzai::Filter::SanitizationFilter
|
||||
- Speed up todos queries by limiting the projects set we join with
|
||||
|
||||
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
|
||||
|
|
@ -134,6 +210,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)
|
||||
|
|
@ -196,6 +275,7 @@ v 8.10.0
|
|||
- Fix new snippet style bug (elliotec)
|
||||
- Instrument Rinku usage
|
||||
- Be explicit to define merge request discussion variables
|
||||
- Use cache for todos counter calling TodoService
|
||||
- Metrics for Rouge::Plugins::Redcarpet and Rouge::Formatters::HTMLGitlab
|
||||
- RailsCache metris now includes fetch_hit/fetch_miss and read_hit/read_miss info.
|
||||
- Allow [ci skip] to be in any case and allow [skip ci]. !4785 (simon_w)
|
||||
|
|
|
|||
|
|
@ -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/
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
3.2.1
|
||||
3.3.3
|
||||
|
|
|
|||
13
Gemfile
|
|
@ -1,6 +1,6 @@
|
|||
source 'https://rubygems.org'
|
||||
|
||||
gem 'rails', '4.2.7'
|
||||
gem 'rails', '4.2.7.1'
|
||||
gem 'rails-deprecated_sanitizer', '~> 1.0.3'
|
||||
|
||||
# Responders respond_to and respond_with
|
||||
|
|
@ -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'
|
||||
|
|
@ -163,9 +163,6 @@ gem 'redis-rails', '~> 4.0.0'
|
|||
gem 'redis', '~> 3.2'
|
||||
gem 'connection_pool', '~> 2.0'
|
||||
|
||||
# Campfire integration
|
||||
gem 'tinder', '~> 1.10.0'
|
||||
|
||||
# HipChat integration
|
||||
gem 'hipchat', '~> 1.5.0'
|
||||
|
||||
|
|
@ -326,7 +323,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'
|
||||
|
||||
|
|
|
|||
94
Gemfile.lock
|
|
@ -3,34 +3,34 @@ GEM
|
|||
specs:
|
||||
RedCloth (4.3.2)
|
||||
ace-rails-ap (4.0.2)
|
||||
actionmailer (4.2.7)
|
||||
actionpack (= 4.2.7)
|
||||
actionview (= 4.2.7)
|
||||
activejob (= 4.2.7)
|
||||
actionmailer (4.2.7.1)
|
||||
actionpack (= 4.2.7.1)
|
||||
actionview (= 4.2.7.1)
|
||||
activejob (= 4.2.7.1)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
rails-dom-testing (~> 1.0, >= 1.0.5)
|
||||
actionpack (4.2.7)
|
||||
actionview (= 4.2.7)
|
||||
activesupport (= 4.2.7)
|
||||
actionpack (4.2.7.1)
|
||||
actionview (= 4.2.7.1)
|
||||
activesupport (= 4.2.7.1)
|
||||
rack (~> 1.6)
|
||||
rack-test (~> 0.6.2)
|
||||
rails-dom-testing (~> 1.0, >= 1.0.5)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
||||
actionview (4.2.7)
|
||||
activesupport (= 4.2.7)
|
||||
actionview (4.2.7.1)
|
||||
activesupport (= 4.2.7.1)
|
||||
builder (~> 3.1)
|
||||
erubis (~> 2.7.0)
|
||||
rails-dom-testing (~> 1.0, >= 1.0.5)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
||||
activejob (4.2.7)
|
||||
activesupport (= 4.2.7)
|
||||
activejob (4.2.7.1)
|
||||
activesupport (= 4.2.7.1)
|
||||
globalid (>= 0.3.0)
|
||||
activemodel (4.2.7)
|
||||
activesupport (= 4.2.7)
|
||||
activemodel (4.2.7.1)
|
||||
activesupport (= 4.2.7.1)
|
||||
builder (~> 3.1)
|
||||
activerecord (4.2.7)
|
||||
activemodel (= 4.2.7)
|
||||
activesupport (= 4.2.7)
|
||||
activerecord (4.2.7.1)
|
||||
activemodel (= 4.2.7.1)
|
||||
activesupport (= 4.2.7.1)
|
||||
arel (~> 6.0)
|
||||
activerecord-session_store (1.0.0)
|
||||
actionpack (>= 4.0, < 5.1)
|
||||
|
|
@ -38,7 +38,7 @@ GEM
|
|||
multi_json (~> 1.11, >= 1.11.2)
|
||||
rack (>= 1.5.2, < 3)
|
||||
railties (>= 4.0, < 5.1)
|
||||
activesupport (4.2.7)
|
||||
activesupport (4.2.7.1)
|
||||
i18n (~> 0.7)
|
||||
json (~> 1.7, >= 1.7.7)
|
||||
minitest (~> 5.1)
|
||||
|
|
@ -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)
|
||||
|
|
@ -289,7 +289,7 @@ GEM
|
|||
omniauth (~> 1.0)
|
||||
pyu-ruby-sasl (~> 0.0.3.1)
|
||||
rubyntlm (~> 0.3)
|
||||
globalid (0.3.6)
|
||||
globalid (0.3.7)
|
||||
activesupport (>= 4.1.0)
|
||||
gollum-grit_adapter (1.0.1)
|
||||
gitlab-grit (~> 2.7, >= 2.7.1)
|
||||
|
|
@ -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)
|
||||
|
|
@ -335,7 +335,6 @@ GEM
|
|||
activesupport (>= 2)
|
||||
nokogiri (~> 1.4)
|
||||
htmlentities (4.3.4)
|
||||
http_parser.rb (0.5.3)
|
||||
httparty (0.13.7)
|
||||
json (~> 1.8)
|
||||
multi_xml (>= 0.5.2)
|
||||
|
|
@ -404,7 +403,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)
|
||||
|
|
@ -519,16 +518,16 @@ GEM
|
|||
rack
|
||||
rack-test (0.6.3)
|
||||
rack (>= 1.0)
|
||||
rails (4.2.7)
|
||||
actionmailer (= 4.2.7)
|
||||
actionpack (= 4.2.7)
|
||||
actionview (= 4.2.7)
|
||||
activejob (= 4.2.7)
|
||||
activemodel (= 4.2.7)
|
||||
activerecord (= 4.2.7)
|
||||
activesupport (= 4.2.7)
|
||||
rails (4.2.7.1)
|
||||
actionmailer (= 4.2.7.1)
|
||||
actionpack (= 4.2.7.1)
|
||||
actionview (= 4.2.7.1)
|
||||
activejob (= 4.2.7.1)
|
||||
activemodel (= 4.2.7.1)
|
||||
activerecord (= 4.2.7.1)
|
||||
activesupport (= 4.2.7.1)
|
||||
bundler (>= 1.3.0, < 2.0)
|
||||
railties (= 4.2.7)
|
||||
railties (= 4.2.7.1)
|
||||
sprockets-rails
|
||||
rails-deprecated_sanitizer (1.0.3)
|
||||
activesupport (>= 4.2.0.alpha)
|
||||
|
|
@ -538,9 +537,9 @@ GEM
|
|||
rails-deprecated_sanitizer (>= 1.0.1)
|
||||
rails-html-sanitizer (1.0.3)
|
||||
loofah (~> 2.0)
|
||||
railties (4.2.7)
|
||||
actionpack (= 4.2.7)
|
||||
activesupport (= 4.2.7)
|
||||
railties (4.2.7.1)
|
||||
actionpack (= 4.2.7.1)
|
||||
activesupport (= 4.2.7.1)
|
||||
rake (>= 0.8.7)
|
||||
thor (>= 0.18.1, < 2.0)
|
||||
rainbow (2.1.0)
|
||||
|
|
@ -672,7 +671,6 @@ GEM
|
|||
redis-namespace (>= 1.5.2)
|
||||
rufus-scheduler (>= 2.0.24)
|
||||
sidekiq (>= 4.0.0)
|
||||
simple_oauth (0.1.9)
|
||||
simplecov (0.12.0)
|
||||
docile (~> 1.1.0)
|
||||
json (>= 1.8, < 3)
|
||||
|
|
@ -742,21 +740,8 @@ GEM
|
|||
tilt (2.0.5)
|
||||
timecop (0.8.1)
|
||||
timfel-krb5-auth (0.8.3)
|
||||
tinder (1.10.1)
|
||||
eventmachine (~> 1.0)
|
||||
faraday (~> 0.9.0)
|
||||
faraday_middleware (~> 0.9)
|
||||
hashie (>= 1.0)
|
||||
json (~> 1.8.0)
|
||||
mime-types
|
||||
multi_json (~> 1.7)
|
||||
twitter-stream (~> 0.1)
|
||||
turbolinks (2.5.3)
|
||||
coffee-rails
|
||||
twitter-stream (0.1.16)
|
||||
eventmachine (>= 0.12.8)
|
||||
http_parser.rb (~> 0.5.1)
|
||||
simple_oauth (~> 0.1.4)
|
||||
tzinfo (1.2.2)
|
||||
thread_safe (~> 0.1)
|
||||
u2f (0.2.1)
|
||||
|
|
@ -778,7 +763,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 +855,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 +887,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)
|
||||
|
|
@ -929,7 +914,7 @@ DEPENDENCIES
|
|||
rack-attack (~> 4.3.1)
|
||||
rack-cors (~> 0.4.0)
|
||||
rack-oauth2 (~> 1.2.1)
|
||||
rails (= 4.2.7)
|
||||
rails (= 4.2.7.1)
|
||||
rails-deprecated_sanitizer (~> 1.0.3)
|
||||
rainbow (~> 2.1.0)
|
||||
rblineprof (~> 0.3.6)
|
||||
|
|
@ -981,7 +966,6 @@ DEPENDENCIES
|
|||
teaspoon-jasmine (~> 2.2.0)
|
||||
test_after_commit (~> 0.4.2)
|
||||
thin (~> 1.7.0)
|
||||
tinder (~> 1.10.0)
|
||||
turbolinks (~> 2.5.0)
|
||||
u2f (~> 0.2.1)
|
||||
uglifier (~> 2.7.2)
|
||||
|
|
@ -989,7 +973,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
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 90 B |
|
Before Width: | Height: | Size: 167 B |
|
Before Width: | Height: | Size: 367 B |
|
Before Width: | Height: | Size: 418 B |
|
Before Width: | Height: | Size: 222 B |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 197 B |
|
Before Width: | Height: | Size: 494 B |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 231 B |
|
Before Width: | Height: | Size: 49 B |
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
/*= require markdown_preview */
|
||||
/*= require preview_markdown */
|
||||
|
||||
(function() {
|
||||
this.DropzoneInput = (function() {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -5,13 +5,10 @@
|
|||
|
||||
this.Issuable = {
|
||||
init: function() {
|
||||
if (!issuable_created) {
|
||||
issuable_created = true;
|
||||
Issuable.initTemplates();
|
||||
Issuable.initSearch();
|
||||
Issuable.initChecks();
|
||||
return Issuable.initLabelFilterRemove();
|
||||
}
|
||||
Issuable.initTemplates();
|
||||
Issuable.initSearch();
|
||||
Issuable.initChecks();
|
||||
return Issuable.initLabelFilterRemove();
|
||||
},
|
||||
initTemplates: function() {
|
||||
return Issuable.labelRow = _.template('<% _.each(labels, function(label){ %> <span class="label-row btn-group" role="group" aria-label="<%- label.title %>" style="color: <%- label.text_color %>;"> <a href="#" class="btn btn-transparent has-tooltip" style="background-color: <%- label.color %>;" title="<%- label.description %>" data-container="body"> <%- label.title %> </a> <button type="button" class="btn btn-transparent label-remove js-label-filter-remove" style="background-color: <%- label.color %>;" data-label="<%- label.title %>"> <i class="fa fa-times"></i> </button> </span> <% }); %>');
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -1,211 +0,0 @@
|
|||
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();
|
||||
}
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
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;
|
||||
}
|
||||
|
|
@ -28,7 +28,7 @@
|
|||
};
|
||||
|
||||
MarkdownPreview.prototype.renderMarkdown = function(text, success) {
|
||||
if (!window.markdown_preview_path) {
|
||||
if (!window.preview_markdown_path) {
|
||||
return;
|
||||
}
|
||||
if (text === this.ajaxCache.text) {
|
||||
|
|
@ -36,7 +36,7 @@
|
|||
}
|
||||
return $.ajax({
|
||||
type: 'POST',
|
||||
url: window.markdown_preview_path,
|
||||
url: window.preview_markdown_path,
|
||||
data: {
|
||||
text: text
|
||||
},
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
(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);
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
(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);
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
(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);
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
(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);
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
(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);
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
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) {
|
||||
|
|
|
|||
|
|
@ -78,6 +78,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
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
.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 {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,35 @@
|
|||
.environments {
|
||||
|
||||
.commit-title {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.fa-play {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.dropdown-new {
|
||||
color: $table-text-gray;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
|
||||
.fa {
|
||||
margin-right: 6px;
|
||||
color: $table-text-gray;
|
||||
}
|
||||
}
|
||||
|
||||
.branch-name {
|
||||
color: $gl-dark-link-color;
|
||||
}
|
||||
}
|
||||
|
||||
.table.builds.environments {
|
||||
min-width: 500px;
|
||||
|
||||
.icon-container {
|
||||
width: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -48,9 +48,9 @@ class Admin::GroupsController < Admin::ApplicationController
|
|||
end
|
||||
|
||||
def destroy
|
||||
DestroyGroupService.new(@group, current_user).execute
|
||||
DestroyGroupService.new(@group, current_user).async_execute
|
||||
|
||||
redirect_to admin_groups_path, notice: 'Group was successfully deleted.'
|
||||
redirect_to admin_groups_path, alert: "Group '#{@group.name}' was scheduled for deletion."
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,8 +37,8 @@ class Dashboard::TodosController < Dashboard::ApplicationController
|
|||
|
||||
def todos_counts
|
||||
{
|
||||
count: TodosFinder.new(current_user, state: :pending).execute.count,
|
||||
done_count: TodosFinder.new(current_user, state: :done).execute.count
|
||||
count: current_user.todos_pending_count,
|
||||
done_count: current_user.todos_done_count
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -87,9 +87,9 @@ class GroupsController < Groups::ApplicationController
|
|||
end
|
||||
|
||||
def destroy
|
||||
DestroyGroupService.new(@group, current_user).execute
|
||||
DestroyGroupService.new(@group, current_user).async_execute
|
||||
|
||||
redirect_to root_path, alert: "Group '#{@group.name}' was successfully deleted."
|
||||
redirect_to root_path, alert: "Group '#{@group.name}' was scheduled for deletion."
|
||||
end
|
||||
|
||||
protected
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
class Projects::BranchesController < Projects::ApplicationController
|
||||
include ActionView::Helpers::SanitizeHelper
|
||||
include SortingHelper
|
||||
# Authorize
|
||||
before_action :require_non_empty_project
|
||||
before_action :authorize_download_code!
|
||||
before_action :authorize_push_code!, only: [:new, :create, :destroy]
|
||||
|
||||
def index
|
||||
@sort = params[:sort].presence || 'name'
|
||||
@sort = params[:sort].presence || sort_value_name
|
||||
@branches = BranchesFinder.new(@repository, params).execute
|
||||
@branches = Kaminari.paginate_array(@branches).page(params[:page])
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ class Projects::BuildsController < Projects::ApplicationController
|
|||
|
||||
def index
|
||||
@scope = params[:scope]
|
||||
@all_builds = project.builds
|
||||
@all_builds = project.builds.relevant
|
||||
@builds = @all_builds.order('created_at DESC')
|
||||
@builds =
|
||||
case @scope
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -134,8 +134,8 @@ class Projects::CommitController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def define_status_vars
|
||||
@statuses = CommitStatus.where(pipeline: pipelines)
|
||||
@builds = Ci::Build.where(pipeline: pipelines)
|
||||
@statuses = CommitStatus.where(pipeline: pipelines).relevant
|
||||
@builds = Ci::Build.where(pipeline: pipelines).relevant
|
||||
end
|
||||
|
||||
def assign_change_commit_vars(mr_source_branch)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,110 @@
|
|||
# This file should be identical in GitLab Community Edition and Enterprise Edition
|
||||
|
||||
class Projects::GitHttpClientController < Projects::ApplicationController
|
||||
include ActionController::HttpAuthentication::Basic
|
||||
include KerberosSpnegoHelper
|
||||
|
||||
attr_reader :user
|
||||
|
||||
# Git clients will not know what authenticity token to send along
|
||||
skip_before_action :verify_authenticity_token
|
||||
skip_before_action :repository
|
||||
before_action :authenticate_user
|
||||
before_action :ensure_project_found!
|
||||
|
||||
private
|
||||
|
||||
def authenticate_user
|
||||
if project && project.public? && download_request?
|
||||
return # Allow access
|
||||
end
|
||||
|
||||
if allow_basic_auth? && basic_auth_provided?
|
||||
login, password = user_name_and_password(request)
|
||||
auth_result = Gitlab::Auth.find_for_git_client(login, password, project: project, ip: request.ip)
|
||||
|
||||
if auth_result.type == :ci && download_request?
|
||||
@ci = true
|
||||
elsif auth_result.type == :oauth && !download_request?
|
||||
# Not allowed
|
||||
else
|
||||
@user = auth_result.user
|
||||
end
|
||||
|
||||
if ci? || user
|
||||
return # Allow access
|
||||
end
|
||||
elsif allow_kerberos_spnego_auth? && spnego_provided?
|
||||
@user = find_kerberos_user
|
||||
|
||||
if user
|
||||
send_final_spnego_response
|
||||
return # Allow access
|
||||
end
|
||||
end
|
||||
|
||||
send_challenges
|
||||
render plain: "HTTP Basic: Access denied\n", status: 401
|
||||
end
|
||||
|
||||
def basic_auth_provided?
|
||||
has_basic_credentials?(request)
|
||||
end
|
||||
|
||||
def send_challenges
|
||||
challenges = []
|
||||
challenges << 'Basic realm="GitLab"' if allow_basic_auth?
|
||||
challenges << spnego_challenge if allow_kerberos_spnego_auth?
|
||||
headers['Www-Authenticate'] = challenges.join("\n") if challenges.any?
|
||||
end
|
||||
|
||||
def ensure_project_found!
|
||||
render_not_found if project.blank?
|
||||
end
|
||||
|
||||
def project
|
||||
return @project if defined?(@project)
|
||||
|
||||
project_id, _ = project_id_with_suffix
|
||||
if project_id.blank?
|
||||
@project = nil
|
||||
else
|
||||
@project = Project.find_with_namespace("#{params[:namespace_id]}/#{project_id}")
|
||||
end
|
||||
end
|
||||
|
||||
# This method returns two values so that we can parse
|
||||
# params[:project_id] (untrusted input!) in exactly one place.
|
||||
def project_id_with_suffix
|
||||
id = params[:project_id] || ''
|
||||
|
||||
%w[.wiki.git .git].each do |suffix|
|
||||
if id.end_with?(suffix)
|
||||
# Be careful to only remove the suffix from the end of 'id'.
|
||||
# Accidentally removing it from the middle is how security
|
||||
# vulnerabilities happen!
|
||||
return [id.slice(0, id.length - suffix.length), suffix]
|
||||
end
|
||||
end
|
||||
|
||||
# Something is wrong with params[:project_id]; do not pass it on.
|
||||
[nil, nil]
|
||||
end
|
||||
|
||||
def repository
|
||||
_, suffix = project_id_with_suffix
|
||||
if suffix == '.wiki.git'
|
||||
project.wiki.repository
|
||||
else
|
||||
project.repository
|
||||
end
|
||||
end
|
||||
|
||||
def render_not_found
|
||||
render plain: 'Not Found', status: :not_found
|
||||
end
|
||||
|
||||
def ci?
|
||||
@ci.present?
|
||||
end
|
||||
end
|
||||
|
|
@ -1,17 +1,6 @@
|
|||
# This file should be identical in GitLab Community Edition and Enterprise Edition
|
||||
|
||||
class Projects::GitHttpController < Projects::ApplicationController
|
||||
include ActionController::HttpAuthentication::Basic
|
||||
include KerberosSpnegoHelper
|
||||
|
||||
attr_reader :user
|
||||
|
||||
# Git clients will not know what authenticity token to send along
|
||||
skip_before_action :verify_authenticity_token
|
||||
skip_before_action :repository
|
||||
before_action :authenticate_user
|
||||
before_action :ensure_project_found!
|
||||
|
||||
class Projects::GitHttpController < Projects::GitHttpClientController
|
||||
# GET /foo/bar.git/info/refs?service=git-upload-pack (git pull)
|
||||
# GET /foo/bar.git/info/refs?service=git-receive-pack (git push)
|
||||
def info_refs
|
||||
|
|
@ -20,9 +9,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 +20,7 @@ class Projects::GitHttpController < Projects::ApplicationController
|
|||
if upload_pack? && upload_pack_allowed?
|
||||
render_ok
|
||||
else
|
||||
render_not_found
|
||||
render_denied
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -40,87 +29,14 @@ class Projects::GitHttpController < Projects::ApplicationController
|
|||
if receive_pack? && receive_pack_allowed?
|
||||
render_ok
|
||||
else
|
||||
render_not_found
|
||||
render_denied
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def authenticate_user
|
||||
if project && project.public? && upload_pack?
|
||||
return # Allow access
|
||||
end
|
||||
|
||||
if allow_basic_auth? && basic_auth_provided?
|
||||
login, password = user_name_and_password(request)
|
||||
auth_result = Gitlab::Auth.find_for_git_client(login, password, project: project, ip: request.ip)
|
||||
|
||||
if auth_result.type == :ci && upload_pack?
|
||||
@ci = true
|
||||
elsif auth_result.type == :oauth && !upload_pack?
|
||||
# Not allowed
|
||||
else
|
||||
@user = auth_result.user
|
||||
end
|
||||
|
||||
if ci? || user
|
||||
return # Allow access
|
||||
end
|
||||
elsif allow_kerberos_spnego_auth? && spnego_provided?
|
||||
@user = find_kerberos_user
|
||||
|
||||
if user
|
||||
send_final_spnego_response
|
||||
return # Allow access
|
||||
end
|
||||
end
|
||||
|
||||
send_challenges
|
||||
render plain: "HTTP Basic: Access denied\n", status: 401
|
||||
end
|
||||
|
||||
def basic_auth_provided?
|
||||
has_basic_credentials?(request)
|
||||
end
|
||||
|
||||
def send_challenges
|
||||
challenges = []
|
||||
challenges << 'Basic realm="GitLab"' if allow_basic_auth?
|
||||
challenges << spnego_challenge if allow_kerberos_spnego_auth?
|
||||
headers['Www-Authenticate'] = challenges.join("\n") if challenges.any?
|
||||
end
|
||||
|
||||
def ensure_project_found!
|
||||
render_not_found if project.blank?
|
||||
end
|
||||
|
||||
def project
|
||||
return @project if defined?(@project)
|
||||
|
||||
project_id, _ = project_id_with_suffix
|
||||
if project_id.blank?
|
||||
@project = nil
|
||||
else
|
||||
@project = Project.find_with_namespace("#{params[:namespace_id]}/#{project_id}")
|
||||
end
|
||||
end
|
||||
|
||||
# This method returns two values so that we can parse
|
||||
# params[:project_id] (untrusted input!) in exactly one place.
|
||||
def project_id_with_suffix
|
||||
id = params[:project_id] || ''
|
||||
|
||||
%w[.wiki.git .git].each do |suffix|
|
||||
if id.end_with?(suffix)
|
||||
# Be careful to only remove the suffix from the end of 'id'.
|
||||
# Accidentally removing it from the middle is how security
|
||||
# vulnerabilities happen!
|
||||
return [id.slice(0, id.length - suffix.length), suffix]
|
||||
end
|
||||
end
|
||||
|
||||
# Something is wrong with params[:project_id]; do not pass it on.
|
||||
[nil, nil]
|
||||
def download_request?
|
||||
upload_pack?
|
||||
end
|
||||
|
||||
def upload_pack?
|
||||
|
|
@ -143,47 +59,37 @@ class Projects::GitHttpController < Projects::ApplicationController
|
|||
render json: Gitlab::Workhorse.git_http_ok(repository, user)
|
||||
end
|
||||
|
||||
def repository
|
||||
_, suffix = project_id_with_suffix
|
||||
if suffix == '.wiki.git'
|
||||
project.wiki.repository
|
||||
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
|
||||
project.repository
|
||||
# Do not leak information about project existence
|
||||
render_not_found
|
||||
end
|
||||
end
|
||||
|
||||
def render_not_found
|
||||
render plain: 'Not Found', status: :not_found
|
||||
end
|
||||
|
||||
def render_not_allowed
|
||||
render plain: download_access.message, status: :forbidden
|
||||
end
|
||||
|
||||
def ci?
|
||||
@ci.present?
|
||||
end
|
||||
|
||||
def upload_pack_allowed?
|
||||
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 +99,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.
|
||||
#
|
||||
|
|
|
|||
|
|
@ -0,0 +1,94 @@
|
|||
class Projects::LfsApiController < Projects::GitHttpClientController
|
||||
include LfsHelper
|
||||
|
||||
before_action :require_lfs_enabled!
|
||||
before_action :lfs_check_access!, except: [:deprecated]
|
||||
|
||||
def batch
|
||||
unless objects.present?
|
||||
render_lfs_not_found
|
||||
return
|
||||
end
|
||||
|
||||
if download_request?
|
||||
render json: { objects: download_objects! }
|
||||
elsif upload_request?
|
||||
render json: { objects: upload_objects! }
|
||||
else
|
||||
raise "Never reached"
|
||||
end
|
||||
end
|
||||
|
||||
def deprecated
|
||||
render(
|
||||
json: {
|
||||
message: 'Server supports batch API only, please update your Git LFS client to version 1.0.1 and up.',
|
||||
documentation_url: "#{Gitlab.config.gitlab.url}/help",
|
||||
},
|
||||
status: 501
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def objects
|
||||
@objects ||= (params[:objects] || []).to_a
|
||||
end
|
||||
|
||||
def existing_oids
|
||||
@existing_oids ||= begin
|
||||
storage_project.lfs_objects.where(oid: objects.map { |o| o['oid'].to_s }).pluck(:oid)
|
||||
end
|
||||
end
|
||||
|
||||
def download_objects!
|
||||
objects.each do |object|
|
||||
if existing_oids.include?(object[:oid])
|
||||
object[:actions] = download_actions(object)
|
||||
else
|
||||
object[:error] = {
|
||||
code: 404,
|
||||
message: "Object does not exist on the server or you don't have permissions to access it",
|
||||
}
|
||||
end
|
||||
end
|
||||
objects
|
||||
end
|
||||
|
||||
def upload_objects!
|
||||
objects.each do |object|
|
||||
object[:actions] = upload_actions(object) unless existing_oids.include?(object[:oid])
|
||||
end
|
||||
objects
|
||||
end
|
||||
|
||||
def download_actions(object)
|
||||
{
|
||||
download: {
|
||||
href: "#{project.http_url_to_repo}/gitlab-lfs/objects/#{object[:oid]}",
|
||||
header: {
|
||||
Authorization: request.headers['Authorization']
|
||||
}.compact
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def upload_actions(object)
|
||||
{
|
||||
upload: {
|
||||
href: "#{project.http_url_to_repo}/gitlab-lfs/objects/#{object[:oid]}/#{object[:size]}",
|
||||
header: {
|
||||
Authorization: request.headers['Authorization']
|
||||
}.compact
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def download_request?
|
||||
params[:operation] == 'download'
|
||||
end
|
||||
|
||||
def upload_request?
|
||||
params[:operation] == 'upload'
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
class Projects::LfsStorageController < Projects::GitHttpClientController
|
||||
include LfsHelper
|
||||
|
||||
before_action :require_lfs_enabled!
|
||||
before_action :lfs_check_access!
|
||||
|
||||
def download
|
||||
lfs_object = LfsObject.find_by_oid(oid)
|
||||
unless lfs_object && lfs_object.file.exists?
|
||||
render_lfs_not_found
|
||||
return
|
||||
end
|
||||
|
||||
send_file lfs_object.file.path, content_type: "application/octet-stream"
|
||||
end
|
||||
|
||||
def upload_authorize
|
||||
render(
|
||||
json: {
|
||||
StoreLFSPath: "#{Gitlab.config.lfs.storage_path}/tmp/upload",
|
||||
LfsOid: oid,
|
||||
LfsSize: size,
|
||||
},
|
||||
content_type: 'application/json; charset=utf-8'
|
||||
)
|
||||
end
|
||||
|
||||
def upload_finalize
|
||||
unless tmp_filename
|
||||
render_lfs_forbidden
|
||||
return
|
||||
end
|
||||
|
||||
if store_file(oid, size, tmp_filename)
|
||||
head 200
|
||||
else
|
||||
render plain: 'Unprocessable entity', status: 422
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def download_request?
|
||||
action_name == 'download'
|
||||
end
|
||||
|
||||
def upload_request?
|
||||
%w[upload_authorize upload_finalize].include? action_name
|
||||
end
|
||||
|
||||
def oid
|
||||
params[:oid].to_s
|
||||
end
|
||||
|
||||
def size
|
||||
params[:size].to_i
|
||||
end
|
||||
|
||||
def tmp_filename
|
||||
name = request.headers['X-Gitlab-Lfs-Tmp']
|
||||
return if name.include?('/')
|
||||
return unless oid.present? && name.start_with?(oid)
|
||||
name
|
||||
end
|
||||
|
||||
def store_file(oid, size, tmp_file)
|
||||
# Define tmp_file_path early because we use it in "ensure"
|
||||
tmp_file_path = File.join("#{Gitlab.config.lfs.storage_path}/tmp/upload", tmp_file)
|
||||
|
||||
object = LfsObject.find_or_create_by(oid: oid, size: size)
|
||||
file_exists = object.file.exists? || move_tmp_file_to_storage(object, tmp_file_path)
|
||||
file_exists && link_to_project(object)
|
||||
ensure
|
||||
FileUtils.rm_f(tmp_file_path)
|
||||
end
|
||||
|
||||
def move_tmp_file_to_storage(object, path)
|
||||
File.open(path) do |f|
|
||||
object.file = f
|
||||
end
|
||||
|
||||
object.file.store!
|
||||
object.save
|
||||
end
|
||||
|
||||
def link_to_project(object)
|
||||
if object && !object.projects.exists?(storage_project.id)
|
||||
object.projects << storage_project
|
||||
object.save
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -90,7 +90,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
|
||||
|
||||
|
|
@ -108,9 +112,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
|
||||
|
|
@ -158,11 +161,11 @@ 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
|
||||
@statuses = @pipeline.statuses if @pipeline
|
||||
@statuses = @pipeline.statuses.relevant if @pipeline
|
||||
|
||||
@note_counts = Note.where(commit_id: @commits.map(&:id)).
|
||||
group(:commit_id).count
|
||||
|
|
@ -364,7 +367,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
|||
@commits_count = @merge_request.commits.count
|
||||
|
||||
@pipeline = @merge_request.pipeline
|
||||
@statuses = @pipeline.statuses if @pipeline
|
||||
@statuses = @pipeline.statuses.relevant if @pipeline
|
||||
|
||||
if @merge_request.locked_long_ago?
|
||||
@merge_request.unlock_mr
|
||||
|
|
@ -383,6 +386,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),
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ class Projects::PipelinesController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def create
|
||||
@pipeline = Ci::CreatePipelineService.new(project, current_user, create_params).execute
|
||||
@pipeline = Ci::CreatePipelineService.new(project, current_user, create_params).execute(ignore_skip_ci: true, save_on_errors: false)
|
||||
unless @pipeline.persisted?
|
||||
render 'new'
|
||||
return
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ class Projects::WikisController < Projects::ApplicationController
|
|||
)
|
||||
end
|
||||
|
||||
def markdown_preview
|
||||
def preview_markdown
|
||||
text = params[:text]
|
||||
|
||||
ext = Gitlab::ReferenceExtractor.new(@project, current_user)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -238,7 +238,7 @@ class ProjectsController < Projects::ApplicationController
|
|||
}
|
||||
end
|
||||
|
||||
def markdown_preview
|
||||
def preview_markdown
|
||||
text = params[:text]
|
||||
|
||||
ext = Gitlab::ReferenceExtractor.new(@project, current_user)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
class ProjectsFinder < UnionFinder
|
||||
def execute(current_user = nil, options = {})
|
||||
def execute(current_user = nil, project_ids_relation = nil)
|
||||
segments = all_projects(current_user)
|
||||
segments.map! { |s| s.where(id: project_ids_relation) } if project_ids_relation
|
||||
|
||||
find_union(segments, Project)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -27,9 +27,11 @@ class TodosFinder
|
|||
items = by_action_id(items)
|
||||
items = by_action(items)
|
||||
items = by_author(items)
|
||||
items = by_project(items)
|
||||
items = by_state(items)
|
||||
items = by_type(items)
|
||||
# Filtering by project HAS TO be the last because we use
|
||||
# the project IDs yielded by the todos query thus far
|
||||
items = by_project(items)
|
||||
|
||||
items.reorder(id: :desc)
|
||||
end
|
||||
|
|
@ -91,14 +93,9 @@ class TodosFinder
|
|||
@project
|
||||
end
|
||||
|
||||
def projects
|
||||
return @projects if defined?(@projects)
|
||||
|
||||
if project?
|
||||
@projects = project
|
||||
else
|
||||
@projects = ProjectsFinder.new.execute(current_user)
|
||||
end
|
||||
def projects(items)
|
||||
item_project_ids = items.reorder(nil).select(:project_id)
|
||||
ProjectsFinder.new.execute(current_user, item_project_ids)
|
||||
end
|
||||
|
||||
def type?
|
||||
|
|
@ -136,8 +133,9 @@ class TodosFinder
|
|||
def by_project(items)
|
||||
if project?
|
||||
items = items.where(project: project)
|
||||
elsif projects
|
||||
items = items.merge(projects).joins(:project)
|
||||
else
|
||||
item_projects = projects(items)
|
||||
items = items.merge(item_projects).joins(:project)
|
||||
end
|
||||
|
||||
items
|
||||
|
|
|
|||
|
|
@ -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' }
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
module AvatarsHelper
|
||||
|
||||
def author_avatar(commit_or_event, options = {})
|
||||
user_avatar(options.merge({
|
||||
user: commit_or_event.author,
|
||||
|
|
@ -8,8 +7,6 @@ module AvatarsHelper
|
|||
}))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def user_avatar(options = {})
|
||||
avatar_size = options[:size] || 16
|
||||
user_name = options[:user].try(:name) || options[:user_name]
|
||||
|
|
@ -26,5 +23,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
|
||||
|
||||
def unfold_class(unfold)
|
||||
unfold ? 'unfold js-unfold' : ''
|
||||
if new_pos
|
||||
html << content_tag(:td, '...', class: cls + ['new_line'], data: { linenumber: new_pos })
|
||||
html << content
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
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?
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,67 @@
|
|||
module LfsHelper
|
||||
def require_lfs_enabled!
|
||||
return if Gitlab.config.lfs.enabled
|
||||
|
||||
render(
|
||||
json: {
|
||||
message: 'Git LFS is not enabled on this GitLab server, contact your admin.',
|
||||
documentation_url: "#{Gitlab.config.gitlab.url}/help",
|
||||
},
|
||||
status: 501
|
||||
)
|
||||
end
|
||||
|
||||
def lfs_check_access!
|
||||
return if download_request? && lfs_download_access?
|
||||
return if upload_request? && lfs_upload_access?
|
||||
|
||||
if project.public? || (user && user.can?(:read_project, project))
|
||||
render_lfs_forbidden
|
||||
else
|
||||
render_lfs_not_found
|
||||
end
|
||||
end
|
||||
|
||||
def lfs_download_access?
|
||||
project.public? || ci? || (user && user.can?(:download_code, project))
|
||||
end
|
||||
|
||||
def lfs_upload_access?
|
||||
user && user.can?(:push_code, project)
|
||||
end
|
||||
|
||||
def render_lfs_forbidden
|
||||
render(
|
||||
json: {
|
||||
message: 'Access forbidden. Check your access level.',
|
||||
documentation_url: "#{Gitlab.config.gitlab.url}/help",
|
||||
},
|
||||
content_type: "application/vnd.git-lfs+json",
|
||||
status: 403
|
||||
)
|
||||
end
|
||||
|
||||
def render_lfs_not_found
|
||||
render(
|
||||
json: {
|
||||
message: 'Not found.',
|
||||
documentation_url: "#{Gitlab.config.gitlab.url}/help",
|
||||
},
|
||||
content_type: "application/vnd.git-lfs+json",
|
||||
status: 404
|
||||
)
|
||||
end
|
||||
|
||||
def storage_project
|
||||
@storage_project ||= begin
|
||||
result = project
|
||||
|
||||
loop do
|
||||
break unless result.forked?
|
||||
result = result.forked_from_project
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -6,12 +6,6 @@ module MembersHelper
|
|||
"#{action}_#{member.type.underscore}".to_sym
|
||||
end
|
||||
|
||||
def default_show_roles(member)
|
||||
can?(current_user, action_member_permission(:update, member), member) ||
|
||||
can?(current_user, action_member_permission(:destroy, member), member) ||
|
||||
can?(current_user, action_member_permission(:admin, member), member.source)
|
||||
end
|
||||
|
||||
def remove_member_message(member, user: nil)
|
||||
user = current_user if defined?(current_user)
|
||||
|
||||
|
|
|
|||
|
|
@ -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],
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
module TodosHelper
|
||||
def todos_pending_count
|
||||
@todos_pending_count ||= TodosFinder.new(current_user, state: :pending).execute.count
|
||||
@todos_pending_count ||= current_user.todos_pending_count
|
||||
end
|
||||
|
||||
def todos_done_count
|
||||
@todos_done_count ||= TodosFinder.new(current_user, state: :done).execute.count
|
||||
@todos_done_count ||= current_user.todos_done_count
|
||||
end
|
||||
|
||||
def todo_action_name(todo)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||