New upstream version 11.5.3+dfsg
This commit is contained in:
parent
4aecc802ff
commit
e0c922f897
3195 changed files with 301857 additions and 41743 deletions
|
@ -1,90 +1,28 @@
|
||||||
---
|
|
||||||
env:
|
|
||||||
browser: true
|
|
||||||
es6: true
|
|
||||||
extends:
|
extends:
|
||||||
- airbnb-base
|
- '@gitlab'
|
||||||
- plugin:vue/recommended
|
|
||||||
globals:
|
globals:
|
||||||
__webpack_public_path__: true
|
__webpack_public_path__: true
|
||||||
gl: false
|
gl: false
|
||||||
gon: false
|
gon: false
|
||||||
localStorage: false
|
localStorage: false
|
||||||
parserOptions:
|
|
||||||
parser: babel-eslint
|
|
||||||
plugins:
|
plugins:
|
||||||
- filenames
|
|
||||||
- import
|
- import
|
||||||
- html
|
- html
|
||||||
- promise
|
|
||||||
settings:
|
settings:
|
||||||
html/html-extensions:
|
html/html-extensions:
|
||||||
- ".html"
|
- '.html'
|
||||||
- ".html.raw"
|
- '.html.raw'
|
||||||
import/resolver:
|
import/resolver:
|
||||||
webpack:
|
webpack:
|
||||||
config: "./config/webpack.config.js"
|
config: './config/webpack.config.js'
|
||||||
rules:
|
rules:
|
||||||
filenames/match-regex:
|
|
||||||
- error
|
|
||||||
- "^[a-z0-9_]+$"
|
|
||||||
import/no-commonjs: error
|
import/no-commonjs: error
|
||||||
no-multiple-empty-lines:
|
|
||||||
- error
|
|
||||||
- max: 1
|
|
||||||
promise/catch-or-return: error
|
|
||||||
no-param-reassign:
|
|
||||||
- error
|
|
||||||
- props: true
|
|
||||||
ignorePropertyModificationsFor:
|
|
||||||
- "acc" # for reduce accumulators
|
|
||||||
- "accumulator" # for reduce accumulators
|
|
||||||
- "el" # for DOM elements
|
|
||||||
- "element" # for DOM elements
|
|
||||||
- "state" # for Vuex mutations
|
|
||||||
no-underscore-dangle:
|
no-underscore-dangle:
|
||||||
- error
|
- error
|
||||||
- allow:
|
- allow:
|
||||||
- __
|
- __
|
||||||
- _links
|
- _links
|
||||||
no-mixed-operators: off
|
|
||||||
vue/html-self-closing:
|
|
||||||
- error
|
|
||||||
- html:
|
|
||||||
void: always
|
|
||||||
normal: never
|
|
||||||
component: always
|
|
||||||
svg: always
|
|
||||||
math: always
|
|
||||||
camelcase:
|
|
||||||
- error
|
|
||||||
- properties: never
|
|
||||||
ignoreDestructuring: true
|
|
||||||
## Conflicting rules with prettier:
|
|
||||||
space-before-function-paren: off
|
|
||||||
curly: off
|
|
||||||
arrow-parens: off
|
|
||||||
function-paren-newline: off
|
|
||||||
object-curly-newline: off
|
|
||||||
padded-blocks: off
|
|
||||||
# Disabled for now, to make the eslint 3 -> eslint 5 update smoother
|
|
||||||
## Indent rule. We are using the old for now: https://eslint.org/docs/user-guide/migrating-to-4.0.0#indent-rewrite
|
|
||||||
indent: off
|
|
||||||
indent-legacy:
|
|
||||||
- error
|
|
||||||
- 2
|
|
||||||
- SwitchCase: 1
|
|
||||||
VariableDeclarator: 1
|
|
||||||
outerIIFEBody: 1
|
|
||||||
FunctionDeclaration:
|
|
||||||
parameters: 1
|
|
||||||
body: 1
|
|
||||||
FunctionExpression:
|
|
||||||
parameters: 1
|
|
||||||
body: 1
|
|
||||||
# Disabled for now, to make the airbnb-base 12.1.0 -> 13.1.0 update smoother
|
# Disabled for now, to make the airbnb-base 12.1.0 -> 13.1.0 update smoother
|
||||||
operator-linebreak: off
|
|
||||||
implicit-arrow-linebreak: off
|
|
||||||
no-else-return:
|
no-else-return:
|
||||||
- error
|
- error
|
||||||
- allowElseIf: true
|
- allowElseIf: true
|
||||||
|
|
1
.gitattributes
vendored
1
.gitattributes
vendored
|
@ -1 +1,2 @@
|
||||||
Dangerfile gitlab-language=ruby
|
Dangerfile gitlab-language=ruby
|
||||||
|
db/schema.rb merge=merge_db_schema
|
||||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -40,6 +40,7 @@ eslint-report.html
|
||||||
/config/redis.queues.yml
|
/config/redis.queues.yml
|
||||||
/config/redis.shared_state.yml
|
/config/redis.shared_state.yml
|
||||||
/config/unicorn.rb
|
/config/unicorn.rb
|
||||||
|
/config/puma.rb
|
||||||
/config/secrets.yml
|
/config/secrets.yml
|
||||||
/config/sidekiq.yml
|
/config/sidekiq.yml
|
||||||
/config/registry.key
|
/config/registry.key
|
||||||
|
|
119
.gitlab-ci.yml
119
.gitlab-ci.yml
|
@ -1,4 +1,4 @@
|
||||||
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.4.4-golang-1.9-git-2.18-chrome-69.0-node-8.x-yarn-1.2-postgresql-9.6-graphicsmagick-1.3.29"
|
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.4.5-golang-1.9-git-2.18-chrome-69.0-node-8.x-yarn-1.2-postgresql-9.6-graphicsmagick-1.3.29"
|
||||||
|
|
||||||
.dedicated-runner: &dedicated-runner
|
.dedicated-runner: &dedicated-runner
|
||||||
retry: 1
|
retry: 1
|
||||||
|
@ -6,7 +6,7 @@ image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.4.4-golang-1.9-git
|
||||||
- gitlab-org
|
- gitlab-org
|
||||||
|
|
||||||
.default-cache: &default-cache
|
.default-cache: &default-cache
|
||||||
key: "ruby-2.4.4-debian-stretch-with-yarn"
|
key: "ruby-2.4.5-debian-stretch-with-yarn"
|
||||||
paths:
|
paths:
|
||||||
- vendor/ruby
|
- vendor/ruby
|
||||||
- .yarn-cache/
|
- .yarn-cache/
|
||||||
|
@ -75,11 +75,6 @@ stages:
|
||||||
- mysql:5.7
|
- mysql:5.7
|
||||||
- redis:alpine
|
- redis:alpine
|
||||||
|
|
||||||
.rails5-variables: &rails5-variables
|
|
||||||
script:
|
|
||||||
- export RAILS5=${RAILS5}
|
|
||||||
- export BUNDLE_GEMFILE=${BUNDLE_GEMFILE}
|
|
||||||
|
|
||||||
.rails5: &rails5
|
.rails5: &rails5
|
||||||
allow_failure: true
|
allow_failure: true
|
||||||
only:
|
only:
|
||||||
|
@ -139,7 +134,7 @@ stages:
|
||||||
- export SCRIPT_NAME="${SCRIPT_NAME:-$CI_JOB_NAME}"
|
- export SCRIPT_NAME="${SCRIPT_NAME:-$CI_JOB_NAME}"
|
||||||
- apk add --update openssl
|
- apk add --update openssl
|
||||||
- wget $CI_PROJECT_URL/raw/$CI_COMMIT_SHA/scripts/$SCRIPT_NAME
|
- wget $CI_PROJECT_URL/raw/$CI_COMMIT_SHA/scripts/$SCRIPT_NAME
|
||||||
- chmod 755 $SCRIPT_NAME
|
- chmod 755 $(basename $SCRIPT_NAME)
|
||||||
|
|
||||||
.rake-exec: &rake-exec
|
.rake-exec: &rake-exec
|
||||||
<<: *dedicated-no-docs-no-db-pull-cache-job
|
<<: *dedicated-no-docs-no-db-pull-cache-job
|
||||||
|
@ -150,7 +145,6 @@ stages:
|
||||||
<<: *dedicated-runner
|
<<: *dedicated-runner
|
||||||
<<: *except-docs-and-qa
|
<<: *except-docs-and-qa
|
||||||
<<: *pull-cache
|
<<: *pull-cache
|
||||||
<<: *rails5-variables
|
|
||||||
stage: test
|
stage: test
|
||||||
script:
|
script:
|
||||||
- JOB_NAME=( $CI_JOB_NAME )
|
- JOB_NAME=( $CI_JOB_NAME )
|
||||||
|
@ -271,7 +265,7 @@ package-and-qa:
|
||||||
SCRIPT_NAME: trigger-build-docs
|
SCRIPT_NAME: trigger-build-docs
|
||||||
environment:
|
environment:
|
||||||
name: review-docs/$CI_COMMIT_REF_SLUG
|
name: review-docs/$CI_COMMIT_REF_SLUG
|
||||||
# DOCS_REVIEW_APPS_DOMAIN and DOCS_GITLAB_REPO_SUFFIX are secret variables
|
# DOCS_REVIEW_APPS_DOMAIN and DOCS_GITLAB_REPO_SUFFIX are CI variables
|
||||||
# Discussion: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/14236/diffs#note_40140693
|
# Discussion: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/14236/diffs#note_40140693
|
||||||
url: http://$CI_ENVIRONMENT_SLUG.$DOCS_REVIEW_APPS_DOMAIN/$DOCS_GITLAB_REPO_SUFFIX
|
url: http://$CI_ENVIRONMENT_SLUG.$DOCS_REVIEW_APPS_DOMAIN/$DOCS_GITLAB_REPO_SUFFIX
|
||||||
on_stop: review-docs-cleanup
|
on_stop: review-docs-cleanup
|
||||||
|
@ -324,7 +318,8 @@ review-docs-cleanup:
|
||||||
cloud-native-image:
|
cloud-native-image:
|
||||||
image: ruby:2.4-alpine
|
image: ruby:2.4-alpine
|
||||||
before_script: []
|
before_script: []
|
||||||
stage: test
|
dependencies: []
|
||||||
|
stage: post-test
|
||||||
allow_failure: true
|
allow_failure: true
|
||||||
variables:
|
variables:
|
||||||
GIT_DEPTH: "1"
|
GIT_DEPTH: "1"
|
||||||
|
@ -594,7 +589,7 @@ static-analysis:
|
||||||
script:
|
script:
|
||||||
- scripts/static-analysis
|
- scripts/static-analysis
|
||||||
cache:
|
cache:
|
||||||
key: "ruby-2.4.4-debian-stretch-with-yarn-and-rubocop"
|
key: "ruby-2.4.5-debian-stretch-with-yarn-and-rubocop"
|
||||||
paths:
|
paths:
|
||||||
- vendor/ruby
|
- vendor/ruby
|
||||||
- .yarn-cache/
|
- .yarn-cache/
|
||||||
|
@ -700,7 +695,10 @@ gitlab:setup-mysql:
|
||||||
# Frontend-related jobs
|
# Frontend-related jobs
|
||||||
gitlab:assets:compile:
|
gitlab:assets:compile:
|
||||||
<<: *dedicated-no-docs-and-no-qa-pull-cache-job
|
<<: *dedicated-no-docs-and-no-qa-pull-cache-job
|
||||||
|
image: dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.4.4-git-2.18-chrome-69.0-node-8.x-yarn-1.2-graphicsmagick-1.3.29-docker-18.06.1
|
||||||
dependencies: []
|
dependencies: []
|
||||||
|
services:
|
||||||
|
- docker:stable-dind
|
||||||
variables:
|
variables:
|
||||||
NODE_ENV: "production"
|
NODE_ENV: "production"
|
||||||
RAILS_ENV: "production"
|
RAILS_ENV: "production"
|
||||||
|
@ -709,21 +707,26 @@ gitlab:assets:compile:
|
||||||
WEBPACK_REPORT: "true"
|
WEBPACK_REPORT: "true"
|
||||||
# we override the max_old_space_size to prevent OOM errors
|
# we override the max_old_space_size to prevent OOM errors
|
||||||
NODE_OPTIONS: --max_old_space_size=3584
|
NODE_OPTIONS: --max_old_space_size=3584
|
||||||
|
DOCKER_DRIVER: overlay2
|
||||||
|
DOCKER_HOST: tcp://docker:2375
|
||||||
script:
|
script:
|
||||||
- date
|
- date
|
||||||
- yarn install --frozen-lockfile --production --cache-folder .yarn-cache
|
- yarn install --frozen-lockfile --production --cache-folder .yarn-cache
|
||||||
- date
|
- date
|
||||||
- free -m
|
- free -m
|
||||||
- bundle exec rake gitlab:assets:compile
|
- bundle exec rake gitlab:assets:compile
|
||||||
|
- scripts/build_assets_image
|
||||||
artifacts:
|
artifacts:
|
||||||
name: webpack-report
|
name: webpack-report
|
||||||
expire_in: 31d
|
expire_in: 31d
|
||||||
paths:
|
paths:
|
||||||
- webpack-report/
|
- webpack-report/
|
||||||
- public/assets/
|
- public/assets/
|
||||||
|
tags:
|
||||||
|
- docker
|
||||||
|
|
||||||
karma:
|
karma:
|
||||||
<<: *dedicated-no-docs-and-no-qa-pull-cache-job
|
<<: *dedicated-no-docs-pull-cache-job
|
||||||
<<: *use-pg
|
<<: *use-pg
|
||||||
dependencies:
|
dependencies:
|
||||||
- compile-assets
|
- compile-assets
|
||||||
|
@ -929,3 +932,93 @@ no_ee_check:
|
||||||
- scripts/no-ee-check
|
- scripts/no-ee-check
|
||||||
only:
|
only:
|
||||||
- //@gitlab-org/gitlab-ce
|
- //@gitlab-org/gitlab-ce
|
||||||
|
|
||||||
|
# GitLab Review apps
|
||||||
|
review:
|
||||||
|
image: registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-charts-build-base
|
||||||
|
stage: test
|
||||||
|
allow_failure: true
|
||||||
|
before_script:
|
||||||
|
- gem install gitlab --no-document
|
||||||
|
variables:
|
||||||
|
GIT_DEPTH: "1"
|
||||||
|
HOST_SUFFIX: "$CI_ENVIRONMENT_SLUG"
|
||||||
|
DOMAIN: "-$CI_ENVIRONMENT_SLUG.$REVIEW_APPS_DOMAIN"
|
||||||
|
GITLAB_HELM_CHART_REF: "master"
|
||||||
|
script:
|
||||||
|
- export GITLAB_SHELL_VERSION=$(<GITLAB_SHELL_VERSION)
|
||||||
|
- export GITALY_VERSION=$(<GITALY_SERVER_VERSION)
|
||||||
|
- export GITLAB_WORKHORSE_VERSION=$(<GITLAB_WORKHORSE_VERSION)
|
||||||
|
- source ./scripts/review_apps/review-apps.sh
|
||||||
|
- BUILD_TRIGGER_TOKEN=$REVIEW_APPS_BUILD_TRIGGER_TOKEN ./scripts/trigger-build cng
|
||||||
|
- check_kube_domain
|
||||||
|
- download_gitlab_chart
|
||||||
|
- ensure_namespace
|
||||||
|
- install_tiller
|
||||||
|
- create_secret
|
||||||
|
- install_external_dns
|
||||||
|
- deploy
|
||||||
|
environment:
|
||||||
|
name: review/$CI_COMMIT_REF_NAME
|
||||||
|
url: https://gitlab-$CI_ENVIRONMENT_SLUG.$REVIEW_APPS_DOMAIN
|
||||||
|
on_stop: stop_review
|
||||||
|
only:
|
||||||
|
refs:
|
||||||
|
- branches@gitlab-org/gitlab-ce
|
||||||
|
- branches@gitlab-org/gitlab-ee
|
||||||
|
kubernetes: active
|
||||||
|
except:
|
||||||
|
refs:
|
||||||
|
- master
|
||||||
|
- /(^docs[\/-].*|.*-docs$)/
|
||||||
|
|
||||||
|
stop_review:
|
||||||
|
<<: *single-script-job
|
||||||
|
image: registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-charts-build-base
|
||||||
|
stage: test
|
||||||
|
allow_failure: true
|
||||||
|
cache: {}
|
||||||
|
dependencies: []
|
||||||
|
variables:
|
||||||
|
SCRIPT_NAME: "review_apps/review-apps.sh"
|
||||||
|
script:
|
||||||
|
- source $(basename "${SCRIPT_NAME}")
|
||||||
|
- delete
|
||||||
|
- cleanup
|
||||||
|
when: manual
|
||||||
|
environment:
|
||||||
|
name: review/$CI_COMMIT_REF_NAME
|
||||||
|
action: stop
|
||||||
|
only:
|
||||||
|
refs:
|
||||||
|
- branches@gitlab-org/gitlab-ce
|
||||||
|
- branches@gitlab-org/gitlab-ee
|
||||||
|
kubernetes: active
|
||||||
|
except:
|
||||||
|
- master
|
||||||
|
- /(^docs[\/-].*|.*-docs$)/
|
||||||
|
|
||||||
|
schedule:review_apps_cleanup:
|
||||||
|
<<: *dedicated-no-docs-pull-cache-job
|
||||||
|
image: registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-charts-build-base
|
||||||
|
stage: build
|
||||||
|
allow_failure: true
|
||||||
|
cache: {}
|
||||||
|
dependencies: []
|
||||||
|
before_script:
|
||||||
|
- gem install gitlab --no-document
|
||||||
|
variables:
|
||||||
|
GIT_DEPTH: "1"
|
||||||
|
script:
|
||||||
|
- ruby -rrubygems scripts/review_apps/automated_cleanup.rb
|
||||||
|
environment:
|
||||||
|
name: review/auto-cleanup
|
||||||
|
action: stop
|
||||||
|
only:
|
||||||
|
refs:
|
||||||
|
- schedules@gitlab-org/gitlab-ce
|
||||||
|
- schedules@gitlab-org/gitlab-ee
|
||||||
|
kubernetes: active
|
||||||
|
except:
|
||||||
|
- tags
|
||||||
|
- /(^docs[\/-].*|.*-docs$)/
|
||||||
|
|
|
@ -16,7 +16,6 @@ Set the title to: `[Security] Description of the original issue`
|
||||||
- [ ] Add a link to the MR to the [links section](#links)
|
- [ ] Add a link to the MR to the [links section](#links)
|
||||||
- [ ] Add a link to an EE MR if required
|
- [ ] Add a link to an EE MR if required
|
||||||
- [ ] Make sure the MR remains in-progress and gets approved after the review cycle, **but never merged**.
|
- [ ] Make sure the MR remains in-progress and gets approved after the review cycle, **but never merged**.
|
||||||
- [ ] Assign the MR to a RM once is reviewed and ready to be merged. Check the [RM list] to see who to ping.
|
|
||||||
|
|
||||||
#### Backports
|
#### Backports
|
||||||
|
|
||||||
|
@ -26,7 +25,8 @@ Set the title to: `[Security] Description of the original issue`
|
||||||
- [ ] Create the branch `security-X-Y` from `X-Y-stable` if it doesn't exist (and make sure it's up to date with stable)
|
- [ ] Create the branch `security-X-Y` from `X-Y-stable` if it doesn't exist (and make sure it's up to date with stable)
|
||||||
- [ ] Create each MR targetting the security branch `security-X-Y`
|
- [ ] Create each MR targetting the security branch `security-X-Y`
|
||||||
- [ ] Add the ~security label and prefix with the version `WIP: [X.Y]` the title of the MR
|
- [ ] Add the ~security label and prefix with the version `WIP: [X.Y]` the title of the MR
|
||||||
- [ ] Make sure all MRs have a link in the [links section](#links) and are assigned to a Release Manager.
|
- [ ] Add the ~"Merge into Security" label to all of the MRs.
|
||||||
|
- [ ] Make sure all MRs have a link in the [links section](#links)
|
||||||
|
|
||||||
[secpick documentation]: https://gitlab.com/gitlab-org/release/docs/blob/master/general/security/developer.md#secpick-script
|
[secpick documentation]: https://gitlab.com/gitlab-org/release/docs/blob/master/general/security/developer.md#secpick-script
|
||||||
|
|
||||||
|
|
|
@ -38,22 +38,22 @@ test plan](https://testing.googleblog.com/2011/09/10-minute-test-plan.html) and
|
||||||
[this wiki page from an open-source tool that implements the ACC
|
[this wiki page from an open-source tool that implements the ACC
|
||||||
model](https://code.google.com/archive/p/test-analytics/wikis/AccExplained.wiki). -->
|
model](https://code.google.com/archive/p/test-analytics/wikis/AccExplained.wiki). -->
|
||||||
|
|
||||||
| | Simple | Secure | Responsive | Obvious | Stable |
|
| | Secure | Responsive | Intuitive | Reliable |
|
||||||
|------------|:------:|:------:|:----------:|:-------:|:------:|
|
|------------|:------:|:----------:|:---------:|:--------:|
|
||||||
| Admin | | | | | |
|
| Admin | | | | |
|
||||||
| Groups | | | | | |
|
| Groups | | | | |
|
||||||
| Project | | | | | |
|
| Project | | | | |
|
||||||
| Repository | | | | | |
|
| Repository | | | | |
|
||||||
| Issues | | | | | |
|
| Issues | | | | |
|
||||||
| MRs | | | | | |
|
| MRs | | | | |
|
||||||
| CI/CD | | | | | |
|
| CI/CD | | | | |
|
||||||
| Ops | | | | | |
|
| Ops | | | | |
|
||||||
| Registry | | | | | |
|
| Registry | | | | |
|
||||||
| Wiki | | | | | |
|
| Wiki | | | | |
|
||||||
| Snippets | | | | | |
|
| Snippets | | | | |
|
||||||
| Settings | | | | | |
|
| Settings | | | | |
|
||||||
| Tracking | | | | | |
|
| Tracking | | | | |
|
||||||
| API | | | | | |
|
| API | | | | |
|
||||||
|
|
||||||
## Capabilities
|
## Capabilities
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ more complex features could involve multiple or even all.
|
||||||
|
|
||||||
Example (from https://gitlab.com/gitlab-org/gitlab-ce/issues/50353):
|
Example (from https://gitlab.com/gitlab-org/gitlab-ce/issues/50353):
|
||||||
* Respository is
|
* Respository is
|
||||||
* Simple
|
* Intuitive
|
||||||
* It's easy to select the desired file template
|
* It's easy to select the desired file template
|
||||||
* It doesn't require unnecessary actions to save the change
|
* It doesn't require unnecessary actions to save the change
|
||||||
* It's easy to undo the change after selecting a template
|
* It's easy to undo the change after selecting a template
|
||||||
|
@ -93,4 +93,4 @@ When adding new automated tests, please keep [testing levels](https://docs.gitla
|
||||||
in mind.
|
in mind.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
/label ~Quality
|
/label ~Quality ~"test plan"
|
|
@ -1,8 +1,23 @@
|
||||||
Add a description of your merge request here. Merge requests without an adequate
|
## What does this MR do?
|
||||||
description will not be reviewed until one is added.
|
|
||||||
|
<!--
|
||||||
|
Describe in detail what your merge request does, why it does that, etc. Merge
|
||||||
|
requests without an adequate description will not be reviewed until one is
|
||||||
|
added.
|
||||||
|
|
||||||
|
Please also keep this description up-to-date with any discussion that takes
|
||||||
|
place so that reviewers can understand your intent. This is especially
|
||||||
|
important if they didn't participate in the discussion.
|
||||||
|
|
||||||
|
Make sure to remove this comment when you are done.
|
||||||
|
-->
|
||||||
|
|
||||||
|
Add a description of your merge request here.
|
||||||
|
|
||||||
## Database checklist
|
## Database checklist
|
||||||
|
|
||||||
|
- [ ] Conforms to the [database guides](https://docs.gitlab.com/ee/development/README.html#databases-guides)
|
||||||
|
|
||||||
When adding migrations:
|
When adding migrations:
|
||||||
|
|
||||||
- [ ] Updated `db/schema.rb`
|
- [ ] Updated `db/schema.rb`
|
||||||
|
@ -35,16 +50,9 @@ When removing columns, tables, indexes or other structures:
|
||||||
|
|
||||||
- [ ] [Changelog entry](https://docs.gitlab.com/ee/development/changelog.html) added, if necessary
|
- [ ] [Changelog entry](https://docs.gitlab.com/ee/development/changelog.html) added, if necessary
|
||||||
- [ ] [Documentation created/updated](https://docs.gitlab.com/ee/development/documentation/index.html#contributing-to-docs)
|
- [ ] [Documentation created/updated](https://docs.gitlab.com/ee/development/documentation/index.html#contributing-to-docs)
|
||||||
- [ ] [API support added](https://docs.gitlab.com/ee/development/api_styleguide.html)
|
|
||||||
- [ ] [Tests added for this feature/bug](https://docs.gitlab.com/ee/development/testing_guide/index.html)
|
- [ ] [Tests added for this feature/bug](https://docs.gitlab.com/ee/development/testing_guide/index.html)
|
||||||
- Conforms to the [code review guidelines](https://docs.gitlab.com/ee/development/code_review.html)
|
- [ ] Conforms to the [code review guidelines](https://docs.gitlab.com/ee/development/code_review.html)
|
||||||
- [ ] Has been reviewed by a Backend [maintainer](https://about.gitlab.com/handbook/engineering/#maintainer)
|
|
||||||
- [ ] Has been reviewed by a Database [specialist](https://about.gitlab.com/team/structure/#specialist)
|
|
||||||
- [ ] Conforms to the [merge request performance guidelines](https://docs.gitlab.com/ee/development/merge_request_performance_guidelines.html)
|
- [ ] Conforms to the [merge request performance guidelines](https://docs.gitlab.com/ee/development/merge_request_performance_guidelines.html)
|
||||||
- [ ] Conforms to the [style guides](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/CONTRIBUTING.md#style-guides)
|
- [ ] Conforms to the [style guides](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/CONTRIBUTING.md#style-guides)
|
||||||
- [ ] If you have multiple commits, please combine them into a few logically organized commits by [squashing them](https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits)
|
|
||||||
- [ ] [Internationalization required/considered](https://docs.gitlab.com/ee/development/i18n/index.html)
|
|
||||||
- [ ] For a paid feature, have we considered GitLab.com plans, how it works for groups, and is there a design for promoting it to users who aren't on the correct plan?
|
|
||||||
- [ ] [End-to-end tests](https://docs.gitlab.com/ee/development/testing_guide/end_to_end_tests.html#testing-code-in-merge-requests) pass (`package-and-qa` manual pipeline job)
|
|
||||||
|
|
||||||
/label ~database
|
/label ~database
|
||||||
|
|
|
@ -19,6 +19,7 @@ Closes
|
||||||
- [ ] [Apply the correct labels and milestone](https://docs.gitlab.com/ee/development/documentation/workflow.html#2-developer-s-role-in-the-documentation-process)
|
- [ ] [Apply the correct labels and milestone](https://docs.gitlab.com/ee/development/documentation/workflow.html#2-developer-s-role-in-the-documentation-process)
|
||||||
- [ ] Crosslink the document from the higher-level index
|
- [ ] Crosslink the document from the higher-level index
|
||||||
- [ ] Crosslink the document from other subject-related docs
|
- [ ] Crosslink the document from other subject-related docs
|
||||||
|
- [ ] Feature moving tiers? Make sure the change is also reflected in [`features.yml`](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/data/features.yml)
|
||||||
- [ ] Correctly apply the product [badges](https://docs.gitlab.com/ee/development/documentation/styleguide.html#product-badges) and [tiers](https://docs.gitlab.com/ee/development/documentation/styleguide.html#gitlab-versions-and-tiers)
|
- [ ] Correctly apply the product [badges](https://docs.gitlab.com/ee/development/documentation/styleguide.html#product-badges) and [tiers](https://docs.gitlab.com/ee/development/documentation/styleguide.html#gitlab-versions-and-tiers)
|
||||||
- [ ] [Port the MR to EE (or backport from CE)](https://docs.gitlab.com/ee/development/documentation/index.html#cherry-picking-from-ce-to-ee): _always recommended, required when the `ee-compat-check` job fails_
|
- [ ] [Port the MR to EE (or backport from CE)](https://docs.gitlab.com/ee/development/documentation/index.html#cherry-picking-from-ce-to-ee): _always recommended, required when the `ee-compat-check` job fails_
|
||||||
|
|
||||||
|
|
|
@ -3,3 +3,7 @@
|
||||||
/public/
|
/public/
|
||||||
/vendor/
|
/vendor/
|
||||||
/tmp/
|
/tmp/
|
||||||
|
|
||||||
|
# ignore stylesheets for now as this clashes with our linter
|
||||||
|
*.css
|
||||||
|
*.scss
|
||||||
|
|
19
.rubocop.yml
19
.rubocop.yml
|
@ -3,7 +3,9 @@ inherit_gem:
|
||||||
- rubocop-default.yml
|
- rubocop-default.yml
|
||||||
|
|
||||||
inherit_from: .rubocop_todo.yml
|
inherit_from: .rubocop_todo.yml
|
||||||
require: ./rubocop/rubocop
|
require:
|
||||||
|
- ./rubocop/rubocop
|
||||||
|
- rubocop-rspec
|
||||||
|
|
||||||
AllCops:
|
AllCops:
|
||||||
TargetRailsVersion: 4.2
|
TargetRailsVersion: 4.2
|
||||||
|
@ -48,12 +50,20 @@ Style/FrozenStringLiteralComment:
|
||||||
- 'danger/**/*'
|
- 'danger/**/*'
|
||||||
- 'db/**/*'
|
- 'db/**/*'
|
||||||
- 'ee/**/*'
|
- 'ee/**/*'
|
||||||
- 'lib/**/*'
|
- 'lib/gitlab/**/*'
|
||||||
|
- 'lib/tasks/**/*'
|
||||||
- 'qa/**/*'
|
- 'qa/**/*'
|
||||||
- 'rubocop/**/*'
|
- 'rubocop/**/*'
|
||||||
- 'scripts/**/*'
|
- 'scripts/**/*'
|
||||||
- 'spec/**/*'
|
- 'spec/**/*'
|
||||||
|
|
||||||
|
RSpec/FilePath:
|
||||||
|
Exclude:
|
||||||
|
- 'qa/**/*'
|
||||||
|
- 'spec/javascripts/fixtures/*'
|
||||||
|
- 'ee/spec/javascripts/fixtures/*'
|
||||||
|
- 'spec/requests/api/v3/*'
|
||||||
|
|
||||||
Naming/FileName:
|
Naming/FileName:
|
||||||
ExpectMatchingDefinition: true
|
ExpectMatchingDefinition: true
|
||||||
Exclude:
|
Exclude:
|
||||||
|
@ -66,15 +76,20 @@ Naming/FileName:
|
||||||
- 'qa/qa/specs/**/*'
|
- 'qa/qa/specs/**/*'
|
||||||
- 'qa/bin/*'
|
- 'qa/bin/*'
|
||||||
- 'config/**/*'
|
- 'config/**/*'
|
||||||
|
- 'ee/config/**/*'
|
||||||
- 'lib/generators/**/*'
|
- 'lib/generators/**/*'
|
||||||
- 'locale/unfound_translations.rb'
|
- 'locale/unfound_translations.rb'
|
||||||
- 'ee/locale/unfound_translations.rb'
|
- 'ee/locale/unfound_translations.rb'
|
||||||
- 'ee/lib/generators/**/*'
|
- 'ee/lib/generators/**/*'
|
||||||
|
- 'qa/qa/scenario/test/integration/ldap_no_tls.rb'
|
||||||
|
- 'qa/qa/scenario/test/integration/ldap_tls.rb'
|
||||||
|
|
||||||
IgnoreExecutableScripts: true
|
IgnoreExecutableScripts: true
|
||||||
AllowedAcronyms:
|
AllowedAcronyms:
|
||||||
- EE
|
- EE
|
||||||
- JSON
|
- JSON
|
||||||
- LDAP
|
- LDAP
|
||||||
|
- SAML
|
||||||
- IO
|
- IO
|
||||||
- HMAC
|
- HMAC
|
||||||
- QA
|
- QA
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
2.4.4
|
2.4.5
|
||||||
|
|
352
CHANGELOG.md
352
CHANGELOG.md
|
@ -2,53 +2,312 @@
|
||||||
documentation](doc/development/changelog.md) for instructions on adding your own
|
documentation](doc/development/changelog.md) for instructions on adding your own
|
||||||
entry.
|
entry.
|
||||||
|
|
||||||
## 11.4.9 (2018-12-03)
|
## 11.5.3 (2018-12-06)
|
||||||
|
|
||||||
### Fixed (2 changes)
|
### Security (1 change)
|
||||||
|
|
||||||
|
- Prevent a path traversal attack on global file templates.
|
||||||
|
|
||||||
|
|
||||||
|
## 11.5.2 (2018-12-03)
|
||||||
|
|
||||||
|
### Removed (1 change)
|
||||||
|
|
||||||
|
- Removed Site Statistics optimization as it was causing problems. !23314
|
||||||
|
|
||||||
|
### Fixed (6 changes, 1 of them is from the community)
|
||||||
|
|
||||||
- Display impersonation token value only after creation. !22916
|
- Display impersonation token value only after creation. !22916
|
||||||
|
- Fix not render emoji in filter dropdown. !23112 (Hiroyuki Sato)
|
||||||
|
- Fixes stuck tooltip on stop env button. !23244
|
||||||
- Correctly handle data-loss scenarios when encrypting columns. !23306
|
- Correctly handle data-loss scenarios when encrypting columns. !23306
|
||||||
|
- Clear BatchLoader context between Sidekiq jobs. !23308
|
||||||
|
- Fix handling of filenames with hash characters in tree view. !23368
|
||||||
|
|
||||||
|
|
||||||
## 11.4.8 (2018-11-27)
|
## 11.5.1 (2018-11-26)
|
||||||
|
|
||||||
### Security (24 changes)
|
### Security (17 changes)
|
||||||
|
|
||||||
- Escape entity title while autocomplete template rendering to prevent XSS. !2571
|
|
||||||
- Resolve reflected XSS in Ouath authorize window.
|
|
||||||
- Fix XSS in merge request source branch name.
|
|
||||||
- Escape user fullname while rendering autocomplete template to prevent XSS.
|
- Escape user fullname while rendering autocomplete template to prevent XSS.
|
||||||
- Fix CRLF vulnerability in Project hooks.
|
- Fix CRLF vulnerability in Project hooks.
|
||||||
- Fix possible XSS attack in Markdown urls with spaces.
|
- Fix possible XSS attack in Markdown urls with spaces.
|
||||||
- Redact sensitive information on gitlab-workhorse log.
|
- Redact sensitive information on gitlab-workhorse log.
|
||||||
- Do not follow redirects in Prometheus service when making http requests to the configured api url.
|
- Do not follow redirects in Prometheus service when making http requests to the configured api url.
|
||||||
- Persist only SHA digest of PersonalAccessToken#token.
|
|
||||||
- Don't expose confidential information in commit message list.
|
- Don't expose confidential information in commit message list.
|
||||||
- Provide email notification when a user changes their email address.
|
- Provide email notification when a user changes their email address.
|
||||||
- Restrict Personal Access Tokens to API scope on web requests.
|
- Restrict Personal Access Tokens to API scope on web requests.
|
||||||
- Redact personal tokens in unsubscribe links.
|
- Resolve reflected XSS in Ouath authorize window.
|
||||||
- Fix SSRF in project integrations.
|
- Fix SSRF in project integrations.
|
||||||
- Fixed ability to comment on locked/confidential issues.
|
- Fixed ability to comment on locked/confidential issues.
|
||||||
- Fixed ability of guest users to edit/delete comments on locked or confidential issues.
|
- Fixed ability of guest users to edit/delete comments on locked or confidential issues.
|
||||||
- Fix milestone promotion authorization check.
|
- Fix milestone promotion authorization check.
|
||||||
- Monkey kubeclient to not follow any redirects.
|
|
||||||
- Configure mermaid to not render HTML content in diagrams.
|
- Configure mermaid to not render HTML content in diagrams.
|
||||||
- Fix a possible symlink time of check to time of use race condition in GitLab Pages.
|
- Fix a possible symlink time of check to time of use race condition in GitLab Pages.
|
||||||
- Removed ability to see private group names when the group id is entered in the url.
|
- Removed ability to see private group names when the group id is entered in the url.
|
||||||
- Fix stored XSS for Environments.
|
- Fix stored XSS for Environments.
|
||||||
- Prevent SSRF attacks in HipChat integration.
|
|
||||||
- Validate Wiki attachments are valid temporary files.
|
|
||||||
|
|
||||||
|
|
||||||
## 11.4.7 (2018-11-20)
|
## 11.5.0 (2018-11-22)
|
||||||
|
|
||||||
- No changes.
|
### Security (10 changes, 1 of them is from the community)
|
||||||
|
|
||||||
## 11.4.6 (2018-11-18)
|
|
||||||
|
|
||||||
### Security (1 change)
|
|
||||||
|
|
||||||
|
- Escape entity title while autocomplete template rendering to prevent XSS. !2556
|
||||||
|
- Update moment to 2.22.2. !22648 (Takuya Noguchi)
|
||||||
|
- Redact personal tokens in unsubscribe links.
|
||||||
- Escape user fullname while rendering autocomplete template to prevent XSS.
|
- Escape user fullname while rendering autocomplete template to prevent XSS.
|
||||||
|
- Persist only SHA digest of PersonalAccessToken#token.
|
||||||
|
- Monkey kubeclient to not follow any redirects.
|
||||||
|
- Prevent SSRF attacks in HipChat integration.
|
||||||
|
- Prevent templated services from being imported.
|
||||||
|
- Validate Wiki attachments are valid temporary files.
|
||||||
|
- Fix XSS in merge request source branch name.
|
||||||
|
|
||||||
|
### Removed (2 changes)
|
||||||
|
|
||||||
|
- Remove Git circuit breaker. !22212
|
||||||
|
- Remove Koding integration and documentation. !22334
|
||||||
|
|
||||||
|
### Fixed (74 changes, 15 of them are from the community)
|
||||||
|
|
||||||
|
- Hide all tables on Pipeline when no Jobs for the Pipeline. !18540 (Takuya Noguchi)
|
||||||
|
- Fixing count on Milestones. !21446
|
||||||
|
- Use case insensitve username lookups. !21728 (William George)
|
||||||
|
- Correctly process Bamboo API result array. !21970 (Alex Lossent)
|
||||||
|
- Fix 'merged with' UI being displayed when merge request has no merge commit. !22022
|
||||||
|
- Fix broken file name navigation on MRs. !22109
|
||||||
|
- Fix incorrect spacing between buttons when commenting on a MR. !22135
|
||||||
|
- Vertical align Pipeline Graph in Commit Page. !22173 (Johann Hubert Sonntagbauer)
|
||||||
|
- Reject invalid branch names in repository compare controller. !22186
|
||||||
|
- Fix size of emojis of user status in user menu. !22194
|
||||||
|
- Use the standard PIP_CACHE_DIR for Python dependency caching template. !22211 (Takuya Noguchi)
|
||||||
|
- Fix bug with wiki attachments content disposition. !22220
|
||||||
|
- Does not allow a SSH URI when importing new projects. !22309
|
||||||
|
- fix duplicated key in license management job auto devops gitlab ci template. !22311 (Adam Lemanski)
|
||||||
|
- Fix commit signature error when project is disabled. !22344
|
||||||
|
- Show available clusters when installed or updated. !22356
|
||||||
|
- Fix auto-corrected upload URLs in webhooks. !22361
|
||||||
|
- Fix a bug displaying certain wiki pages. !22377
|
||||||
|
- Fix prometheus graphs in firefox. !22400
|
||||||
|
- Resolve assign-me quick action doesn't work if there is extra white space. !22402
|
||||||
|
- Remove base64 encoding from files that contain plain text. !22425
|
||||||
|
- Strip whitespace around GitHub personal access tokens. !22432
|
||||||
|
- Fix 500 error when testing webhooks with redirect loops. !22447 (Heinrich Lee Yu)
|
||||||
|
- Fix rendering of 'Protected' value on Runner details page. !22459
|
||||||
|
- Fix bug stopping non-admin users from changing visibility level on group creation. !22468
|
||||||
|
- Make Issue Board sidebar show project-specific labels based on selected Issue. !22475
|
||||||
|
- Fix EOF detection with CI artifacts metadata. !22479
|
||||||
|
- Fix transient spec error in the bar_chart component. !22495
|
||||||
|
- Resolve LFS not correctly showing enabled. !22501
|
||||||
|
- If user was not found, service hooks won't run on post receive background job. !22519
|
||||||
|
- Fix broken "Show whitespace changes" button on MRs. !22539
|
||||||
|
- Always show new issue button in boards' Open list. !22557 (Heinrich Lee Yu)
|
||||||
|
- Add transparent background to markdown header tabs. !22565 (George Tsiolis)
|
||||||
|
- Use gitlab_environment for ldap rake task. !22582
|
||||||
|
- Add commit message to commit tree anchor title. !22585
|
||||||
|
- Cache pipeline status per SHA. !22589
|
||||||
|
- Change HELM_HOST in Auto-DevOps template to work behind proxy. !22596 (Sergej Nikolaev <kinolaev@gmail.com>)
|
||||||
|
- Show user status for label events in system notes. !22609
|
||||||
|
- Fix extra merge request versions created from forked merge requests. !22611
|
||||||
|
- Remove PersonalAccessTokensFinder#find_by method. !22617
|
||||||
|
- Fix search "all in GitLab" not working with relative URLs. !22644
|
||||||
|
- Fix quick links button styles. !22657 (George Tsiolis)
|
||||||
|
- Fix #53298: JupyterHub restarts should work without errors. !22671 (Amit Rathi)
|
||||||
|
- Fix incompatibility with IE11 due to non-transpiled gitlab-ui components. !22695
|
||||||
|
- Fix bug when links in tabs of the labels index pages ends with .html. !22716
|
||||||
|
- Fixed label removal from issue. !22762
|
||||||
|
- Align toggle sidebar button across all browsers and OSs. !22771
|
||||||
|
- Disable replication lag check for Aurora PostgreSQL databases. !22786
|
||||||
|
- Render unescaped link for failed pipeline status. !22807
|
||||||
|
- Fix misaligned approvers dropdown. !22832
|
||||||
|
- Fix bug with wiki page create message. !22849
|
||||||
|
- Fix rendering of filter bar tokens for special values. !22865 (Heinrich Lee Yu)
|
||||||
|
- Align sign in button. !22888 (George Tsiolis)
|
||||||
|
- Fix error handling bugs in kubernetes integration. !22922
|
||||||
|
- Fix deployment jobs using nil KUBE_TOKEN due to migration issue. !23009
|
||||||
|
- Avoid returning deployment metrics url to MR widget when the deployment is not successful. !23010
|
||||||
|
- Fix a race condition intermittently breaking GitLab startup. !23028
|
||||||
|
- Adds margin after a deleted branch name in the activity feed. !23038
|
||||||
|
- Ignore environment validation failure. !23100
|
||||||
|
- Fixes broken borders for reports section in MR widget.
|
||||||
|
- Adds CI favicon back to jobs page.
|
||||||
|
- Redirect to the pipeline builds page when a build is canceled. (Eva Kadlecova)
|
||||||
|
- Fixed diff stats not showing when performance bar is enabled.
|
||||||
|
- Show expand all diffs button when a single diff file is collapsed.
|
||||||
|
- Clear fetched file templates when changing template type in Web IDE.
|
||||||
|
- Fix bug causing not all emails to show up in commit email selectbox.
|
||||||
|
- Remove duplicate escape in job sidebar.
|
||||||
|
- Fixing styling issues on the scheduled pipelines page.
|
||||||
|
- Renders stuck block when runners are stuck.
|
||||||
|
- Removes extra border from test reports in the merge request widget.
|
||||||
|
- Only render link to branch when branch still exists in pipeline page.
|
||||||
|
- Fixed source project not filtering in merge request creation compare form.
|
||||||
|
- Do not reload self on hooks when creating deployment.
|
||||||
|
- Fixes broken test in master.
|
||||||
|
|
||||||
|
### Changed (38 changes, 12 of them are from the community)
|
||||||
|
|
||||||
|
- Link button in markdown editor recognize URLs. !1983 (Johann Hubert Sonntagbauer)
|
||||||
|
- Replace i to icons in vue components. !20748 (George Tsiolis)
|
||||||
|
- Remove Linguist gem, reducing Rails memory usage by 128MB per process. !21008
|
||||||
|
- Issue board card design. !21229
|
||||||
|
- On deletion of a file in sub directory in web IDE redirect to the sub directory instead of project root. !21465 (George Thomas @thegeorgeous)
|
||||||
|
- Change single-item breadcrumbs to page titles. !22155
|
||||||
|
- Improving branch filter sorting by listing exact matches first and added support for begins_with (^) and ends_with ($) matching. !22166 (Jason Rutherford)
|
||||||
|
- Remove legacy unencrypted webhook columns from the database. !22199
|
||||||
|
- Show canary status in the performance bar. !22222
|
||||||
|
- Add failure reason for execution timeout. !22224
|
||||||
|
- Rename "scheduled" label/badge of delayed jobs to "delayed". !22245
|
||||||
|
- Update the empty state on wiki-only projects to display an empty state that is more consistent with the rest of the system. !22262
|
||||||
|
- Add IID headers to E-Mail notifications. !22263
|
||||||
|
- Allow finding the common ancestor for multiple revisions through the API. !22295
|
||||||
|
- Add status to Deployment. !22380
|
||||||
|
- Add dynamic timer to delayed jobs. !22382
|
||||||
|
- No longer require a deploy to start Prometheus monitoring. !22401
|
||||||
|
- Secret Variables renamed to CI Variables in the codebase, to match UX. !22414 (Marcel Amirault @ravlen)
|
||||||
|
- Automatically navigate to last board visited. !22430
|
||||||
|
- Use merge request prefix symbol in event feed title. !22449 (George Tsiolis)
|
||||||
|
- Update Ruby version in README. !22466 (J.D. Bean)
|
||||||
|
- Reword error message for internal CI unknown pipeline status. !22474
|
||||||
|
- Bump mermaid to 8.0.0-rc.8. !22509 (@blackst0ne)
|
||||||
|
- Update Todo icons in collapsed sidebar for Issues and MRs. !22534
|
||||||
|
- Support backward compatibility when introduce new failure reason. !22566
|
||||||
|
- Add dynamic timer for delayed jobs in pipelines list. !22621
|
||||||
|
- Truncate milestone title on collapsed sidebar. !22624 (George Tsiolis)
|
||||||
|
- Standardize milestones filter in APIs to None / Any. !22637 (Heinrich Lee Yu)
|
||||||
|
- Add dynamic timer for delayed jobs in job list. !22656
|
||||||
|
- Allowing issues with single letter identifiers to be linked to external issue tracker (f.ex T-123). !22717 (Dídac Rodríguez Arbonès)
|
||||||
|
- Update project and group labels empty state. !22745 (George Tsiolis)
|
||||||
|
- Fix environment status in merge request widget. !22799
|
||||||
|
- Paginate Bitbucket Server importer projects. !22825
|
||||||
|
- Drop `allow_overflow` option in `TimeHelper.duration_in_numbers`. !52284
|
||||||
|
- Add 'only history' option to notes filter.
|
||||||
|
- Adds filtered dropdown with changed files in review.
|
||||||
|
- Expose {closed,merged}_{at,by} in merge requests API index.
|
||||||
|
- Make all legacy security reports to use raw format.
|
||||||
|
|
||||||
|
### Performance (27 changes, 6 of them are from the community)
|
||||||
|
|
||||||
|
- Add preload for routes and namespaces for issues controller. !21651
|
||||||
|
- Enhance performance of counting local LFS objects. !22143
|
||||||
|
- Use cached readme contents when available. !22325
|
||||||
|
- Experimental support for running Puma multithreaded web-server. !22372
|
||||||
|
- Enhance performance of counting local Uploads. !22522
|
||||||
|
- Reduce SQL queries needed to load open merge requests. !22709
|
||||||
|
- Significantly cut memory usage and SQL queries when reloading diffs. !22725
|
||||||
|
- Optimize merge request refresh by using the database to check commit SHAs. !22731
|
||||||
|
- Remove dind from license_management auto-devops job definition. !22732
|
||||||
|
- Add index to find stuck merge requests. !22749
|
||||||
|
- Allow Rails concurrency when running in Puma. !22751
|
||||||
|
- Improve performance of rendering large reports. !22835
|
||||||
|
- Improves performance of stuck import jobs detection. !22879
|
||||||
|
- Rewrite SnippetsFinder to improve performance by a factor of 1500.
|
||||||
|
- Enable more frozen string in lib/**/*.rb. (gfyoung)
|
||||||
|
- Enable some frozen string in lib/gitlab. (gfyoung)
|
||||||
|
- Enable even more frozen string in lib/**/*.rb. (gfyoung)
|
||||||
|
- Improve performance of tree rendering in repositories with lots of items.
|
||||||
|
- Remove gitlab-ui's tooltip from global.
|
||||||
|
- Remove gitlab-ui's progress bar from global.
|
||||||
|
- Remove gitlab-ui's pagination from global.
|
||||||
|
- Remove gitlab-ui's modal from global.
|
||||||
|
- Remove gitlab-ui's loading icon from global.
|
||||||
|
- Enable frozen string for lib/gitlab/*.rb. (gfyoung)
|
||||||
|
- Enable frozen string for lib/gitlab/ci. (gfyoung)
|
||||||
|
- Enable frozen string for remaining lib/gitlab/ci/**/*.rb. (gfyoung)
|
||||||
|
- Adds pagination to pipelines table in merge request page.
|
||||||
|
|
||||||
|
### Added (33 changes, 11 of them are from the community)
|
||||||
|
|
||||||
|
- Add endpoint to update a git submodule reference. !20949
|
||||||
|
- Add license data to projects endpoint. !21606 (J.D. Bean (@jdbean))
|
||||||
|
- Allow to configure when to retry failed CI jobs. !21758 (Markus Doits)
|
||||||
|
- Add API endpoint to list issue related merge requests. !21806 (Helmut Januschka)
|
||||||
|
- Add the Play button for delayed jobs in environment page. !22106
|
||||||
|
- Switch between tree list & file list in diffs file browser. !22191
|
||||||
|
- Re-arrange help-related user menu items into new Help menu. !22195
|
||||||
|
- Adds trace of each access check when git push times out. !22265
|
||||||
|
- Add email for milestone change. !22279
|
||||||
|
- Show post-merge pipeline in merge request page. !22292
|
||||||
|
- Add Applications API endpoints for listing and deleting entries. !22296 (Jean-Baptiste Vasseur)
|
||||||
|
- Added `Any` option to milestones filter. !22351 (Heinrich Lee Yu)
|
||||||
|
- Improve validation errors for external CI/CD configuration. !22394
|
||||||
|
- Introduce new model to persist specific cluster information. !22404
|
||||||
|
- Add background migration to populate Kubernetes namespaces. !22433
|
||||||
|
- Add support for JSON logging for audit events. !22471
|
||||||
|
- Adds option to override commit email with a noreply private email. !22560
|
||||||
|
- Add None/Any option for assignee_id in Issues and Merge Requests API. !22598 (Heinrich Lee Yu)
|
||||||
|
- Add None/Any option for assignee_id in search bar. !22599 (Heinrich Lee Yu)
|
||||||
|
- Implement parallel job keyword. !22631
|
||||||
|
- Add None / Any options to reactions filter. !22638 (Heinrich Lee Yu)
|
||||||
|
- Make index.* render like README.* when it's present in a repository. !22639 (Jakub Jirutka)
|
||||||
|
- Allow adding patches when creating a merge request via email. !22723 (Serdar Dogruyol)
|
||||||
|
- Bump Gitaly to 0.129.0. !22868
|
||||||
|
- Allow commenting on any diff line in Merge Requests. !22914
|
||||||
|
- Add revert to commits API. !22919
|
||||||
|
- Introduce Knative support. !43959 (Chris Baumbauer)
|
||||||
|
- Reimplemented image commenting in merge request diffs.
|
||||||
|
- Soft-archive old jobs.
|
||||||
|
- Renders warning info when job is archieved.
|
||||||
|
- Support licenses and performance.
|
||||||
|
- Filter notes by comments or activity for issues and merge requests.
|
||||||
|
- Bump Gitaly to 0.128.0.
|
||||||
|
|
||||||
|
### Other (54 changes, 18 of them are from the community)
|
||||||
|
|
||||||
|
- Remove .card-title from .card-header for BS4 migration. !19335 (Takuya Noguchi)
|
||||||
|
- Update group settings/edit page to new design. !21115
|
||||||
|
- Change markdown header tab anchor links to buttons. !21988 (George Tsiolis)
|
||||||
|
- Replace tooltip in markdown component with gl-tooltip. !21989 (George Tsiolis)
|
||||||
|
- Extend RBAC by having a service account restricted to project's namespace. !22011
|
||||||
|
- Update images in group docs. !22031 (Marc Schwede)
|
||||||
|
- Add gitlab:gitaly:check task for Gitaly health check. !22063
|
||||||
|
- Add new sort option "most_stars" to "Group > Children" pages. !22121 (Rene Hennig)
|
||||||
|
- Fix inaccessible dropdown for code-less projects. !22137
|
||||||
|
- Rails5: fix user edit profile clear status spec. !22169 (Jasper Maes)
|
||||||
|
- Rails 5: fix mysql milliseconds problems in scheduled build specs. !22170 (Jasper Maes)
|
||||||
|
- Focus project slug on tab navigation. !22198
|
||||||
|
- Redesign activity feed. !22217
|
||||||
|
- Update used version of Runner Helm Chart to 0.1.34. !22274
|
||||||
|
- Update environments empty state. !22297 (George Tsiolis)
|
||||||
|
- Adds model and migrations to enable group level clusters. !22307
|
||||||
|
- Use literal instead of constructor for creating regex. !22367
|
||||||
|
- Remove prometheus configuration help text. !22413 (George Tsiolis)
|
||||||
|
- Rails5: fix deployment model spec. !22428 (Jasper Maes)
|
||||||
|
- Change to top level controller for clusters so that we can use it for project clusters (now) and group clusters (later). !22438
|
||||||
|
- Remove empty spec describe blocks. !22451 (George Tsiolis)
|
||||||
|
- Change branch font type in tag creation. !22454 (George Tsiolis)
|
||||||
|
- Rails5: fix delete blob. !22456 (Jasper Maes)
|
||||||
|
- Start tracking shards and pool repositories in the database. !22482
|
||||||
|
- Allow kubeclient to call RoleBinding methods. !22524
|
||||||
|
- Introduce new kubernetes helpers. !22525
|
||||||
|
- Adds container to pager to enable scoping. !22529
|
||||||
|
- Update used version of Runner Helm Chart to 0.1.35. !22541
|
||||||
|
- Removes experimental labels from cluster views. !22550
|
||||||
|
- Combine all datetime library functions into 'datetime_utility.js'. !22570
|
||||||
|
- Upgrade Prometheus to 2.4.3 and Alertmanager to 0.15.2. !22600
|
||||||
|
- Fix stage dropdown not rendering in different languages. !22604
|
||||||
|
- Remove asset_sync gem from Gemfile and related code from codebase. !22610
|
||||||
|
- Use key-value pair arrays for API query parameter logging instead of hashes. !22623
|
||||||
|
- Replace deprecated uniq on a Relation with distinct. !22625 (Jasper Maes)
|
||||||
|
- Remove mousetrap-rails gem. !22647 (Takuya Noguchi)
|
||||||
|
- Fix IDE typos in props. !22685 (George Tsiolis)
|
||||||
|
- Add scheduled flag to job entity. !22710
|
||||||
|
- Remove `ci_enable_scheduled_build` feature flag. !22742
|
||||||
|
- Add endpoints for simulating certain failure modes in the application. !22746
|
||||||
|
- Bump KUBERNETES_VERSION for Auto DevOps to latest 1.10 series. !22757
|
||||||
|
- Fix statement timeouts in RemoveRestrictedTodos migration. !22795
|
||||||
|
- Rails5: fix mysql milliseconds issue in deployment model specs. !22850 (Jasper Maes)
|
||||||
|
- Update GitLab-Workhorse to v7.1.0. !22883
|
||||||
|
- Update JIRA service UI to accept email and API token.
|
||||||
|
- Update wiki empty state. (George Tsiolis)
|
||||||
|
- Only renders dropdown for review app changes when we have a list of files to show. Otherwise will render the regular review app button.
|
||||||
|
- Associate Rakefile with Ruby icon in diffs.
|
||||||
|
- Uses gitlab-ui components in jobs components.
|
||||||
|
- Create new group: Rename form fields and update UI.
|
||||||
|
- Transform job page into a single Vue+Vuex application.
|
||||||
|
- Updates svg dependency.
|
||||||
|
- Adds missing i18n to pipelines table.
|
||||||
|
- Disables stop environment button while the deploy is in progress.
|
||||||
|
|
||||||
|
|
||||||
## 11.4.5 (2018-11-04)
|
## 11.4.5 (2018-11-04)
|
||||||
|
@ -320,6 +579,41 @@ entry.
|
||||||
- Check frozen string in style builds. (gfyoung)
|
- Check frozen string in style builds. (gfyoung)
|
||||||
|
|
||||||
|
|
||||||
|
## 11.3.9 (2018-10-31)
|
||||||
|
|
||||||
|
### Security (1 change)
|
||||||
|
|
||||||
|
- Monkey kubeclient to not follow any redirects.
|
||||||
|
|
||||||
|
|
||||||
|
## 11.3.8 (2018-10-27)
|
||||||
|
|
||||||
|
- No changes.
|
||||||
|
|
||||||
|
## 11.3.7 (2018-10-26)
|
||||||
|
|
||||||
|
### Security (6 changes)
|
||||||
|
|
||||||
|
- Escape entity title while autocomplete template rendering to prevent XSS. !2557
|
||||||
|
- Persist only SHA digest of PersonalAccessToken#token.
|
||||||
|
- Fix XSS in merge request source branch name.
|
||||||
|
- Redact personal tokens in unsubscribe links.
|
||||||
|
- Prevent SSRF attacks in HipChat integration.
|
||||||
|
- Validate Wiki attachments are valid temporary files.
|
||||||
|
|
||||||
|
|
||||||
|
## 11.3.6 (2018-10-17)
|
||||||
|
|
||||||
|
- No changes.
|
||||||
|
|
||||||
|
## 11.3.5 (2018-10-15)
|
||||||
|
|
||||||
|
### Fixed (2 changes)
|
||||||
|
|
||||||
|
- Fix loading issue on some merge request discussion. !21982
|
||||||
|
- Fix project deletion when there is a export available. !22276
|
||||||
|
|
||||||
|
|
||||||
## 11.3.3 (2018-10-04)
|
## 11.3.3 (2018-10-04)
|
||||||
|
|
||||||
- No changes.
|
- No changes.
|
||||||
|
@ -597,6 +891,28 @@ entry.
|
||||||
- Creates Vue component for artifacts block on job page.
|
- Creates Vue component for artifacts block on job page.
|
||||||
|
|
||||||
|
|
||||||
|
## 11.2.8 (2018-10-31)
|
||||||
|
|
||||||
|
### Security (1 change)
|
||||||
|
|
||||||
|
- Monkey kubeclient to not follow any redirects.
|
||||||
|
|
||||||
|
|
||||||
|
## 11.2.7 (2018-10-27)
|
||||||
|
|
||||||
|
- No changes.
|
||||||
|
|
||||||
|
## 11.2.6 (2018-10-26)
|
||||||
|
|
||||||
|
### Security (5 changes)
|
||||||
|
|
||||||
|
- Escape entity title while autocomplete template rendering to prevent XSS. !2558
|
||||||
|
- Fix XSS in merge request source branch name.
|
||||||
|
- Redact personal tokens in unsubscribe links.
|
||||||
|
- Persist only SHA digest of PersonalAccessToken#token.
|
||||||
|
- Prevent SSRF attacks in HipChat integration.
|
||||||
|
|
||||||
|
|
||||||
## 11.2.5 (2018-10-05)
|
## 11.2.5 (2018-10-05)
|
||||||
|
|
||||||
### Security (3 changes)
|
### Security (3 changes)
|
||||||
|
|
156
CONTRIBUTING.md
156
CONTRIBUTING.md
|
@ -64,184 +64,56 @@ As of July 2018, all the documentation for contributing to the GitLab project ha
|
||||||
|
|
||||||
## Contribute to GitLab
|
## Contribute to GitLab
|
||||||
|
|
||||||
Thank you for your interest in contributing to GitLab. This guide details how
|
This [documentation](doc/development/contributing/index.md#contribute-to-gitlab) has been moved.
|
||||||
to contribute to GitLab in a way that is easy for everyone.
|
|
||||||
|
|
||||||
For a first-time step-by-step guide to the contribution process, please see
|
|
||||||
["Contributing to GitLab"](https://about.gitlab.com/contributing/).
|
|
||||||
|
|
||||||
Looking for something to work on? Look for issues in the [Backlog (Accepting merge requests) milestone](#i-want-to-contribute).
|
|
||||||
|
|
||||||
GitLab comes in two flavors, GitLab Community Edition (CE) our free and open
|
|
||||||
source edition, and GitLab Enterprise Edition (EE) which is our commercial
|
|
||||||
edition. Throughout this guide you will see references to CE and EE for
|
|
||||||
abbreviation.
|
|
||||||
|
|
||||||
To get an overview of GitLab community membership including those that would be reviewing or merging your contributions, please visit [the community roles page](doc/development/contributing/community_roles.md).
|
|
||||||
|
|
||||||
If you 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/)
|
|
||||||
|
|
||||||
## Security vulnerability disclosure
|
## Security vulnerability disclosure
|
||||||
|
|
||||||
Please report suspected security vulnerabilities in private to
|
This [documentation](doc/development/contributing/index.md#security-vulnerability-disclosure) has been moved.
|
||||||
`support@gitlab.com`, also see the
|
|
||||||
[disclosure section on the GitLab.com website](https://about.gitlab.com/disclosure/).
|
|
||||||
Please do **NOT** create publicly viewable issues for suspected security
|
|
||||||
vulnerabilities.
|
|
||||||
|
|
||||||
## Code of conduct
|
## Code of Conduct
|
||||||
|
|
||||||
As contributors and maintainers of this project, we pledge to respect all
|
This [documentation](https://about.gitlab.com/contributing/code-of-conduct/) has been moved.
|
||||||
people who contribute through reporting issues, posting feature requests,
|
|
||||||
updating documentation, submitting pull requests or patches, and other
|
|
||||||
activities.
|
|
||||||
|
|
||||||
We are committed to making participation in this project a harassment-free
|
|
||||||
experience for everyone, regardless of level of experience, gender, gender
|
|
||||||
identity and expression, sexual orientation, disability, personal appearance,
|
|
||||||
body size, race, ethnicity, age, or religion.
|
|
||||||
|
|
||||||
Examples of unacceptable behavior by participants include the use of sexual
|
|
||||||
language or imagery, derogatory comments or personal attacks, trolling, public
|
|
||||||
or private harassment, insults, or other unprofessional conduct.
|
|
||||||
|
|
||||||
Project maintainers have the right and responsibility to remove, edit, or
|
|
||||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
|
||||||
that are not aligned to this Code of Conduct. Project maintainers who do not
|
|
||||||
follow the Code of Conduct may be removed from the project team.
|
|
||||||
|
|
||||||
This code of conduct applies both within project spaces and in public spaces
|
|
||||||
when an individual is representing the project or its community.
|
|
||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior can be
|
|
||||||
reported by emailing `contact@gitlab.com`.
|
|
||||||
|
|
||||||
This Code of Conduct is adapted from the [Contributor Covenant][contributor-covenant], version 1.1.0,
|
|
||||||
available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/).
|
|
||||||
|
|
||||||
## Closing policy for issues and merge requests
|
## Closing policy for issues and merge requests
|
||||||
|
|
||||||
GitLab is a popular open source project and the capacity to deal with issues
|
This [documentation](doc/development/contributing/index.md#closing-policy-for-issues-and-merge-requests) has been moved.
|
||||||
and merge requests is limited. Out of respect for our volunteers, issues and
|
|
||||||
merge requests not in line with the guidelines listed in this document may be
|
|
||||||
closed without notice.
|
|
||||||
|
|
||||||
Please treat our volunteers with courtesy and respect, it will go a long way
|
|
||||||
towards getting your issue resolved.
|
|
||||||
|
|
||||||
Issues and merge requests should be in English and contain appropriate language
|
|
||||||
for audiences of all ages.
|
|
||||||
|
|
||||||
If a contributor is no longer actively working on a submitted merge request
|
|
||||||
we can decide that the merge request will be finished by one of our
|
|
||||||
[Merge request coaches][team] or close the merge request. We make this decision
|
|
||||||
based on how important the change is for our product vision. If a Merge request
|
|
||||||
coach is going to finish the merge request we assign the
|
|
||||||
~"coach will finish" label.
|
|
||||||
|
|
||||||
## Helping others
|
## Helping others
|
||||||
|
|
||||||
Please help other GitLab users when you can.
|
This [documentation](doc/development/contributing/index.md#helping-others) has been moved.
|
||||||
The methods people will use to seek help can be found on the [getting help page][getting-help].
|
|
||||||
|
|
||||||
Sign up for the mailing list, answer GitLab questions on StackOverflow or
|
|
||||||
respond in the IRC channel. You can also sign up on [CodeTriage][codetriage] to help with
|
|
||||||
the remaining issues on the GitHub issue tracker.
|
|
||||||
|
|
||||||
## I want to contribute!
|
## I want to contribute!
|
||||||
|
|
||||||
If you want to contribute to GitLab, [issues in the Backlog (Accepting merge requests)](https://gitlab.com/gitlab-org/gitlab-ce/issues?scope=all&utf8=✓&state=opened&assignee_id=0&milestone_title=Backlog%20(Accepting%20merge%20requests))
|
This [documentation](doc/development/contributing/index.md#i-want-to-contribute) has been moved.
|
||||||
are a great place to start. Issues with a lower weight (1 or 2) are deemed
|
|
||||||
suitable for beginners. These issues will be of reasonable size and challenge,
|
|
||||||
for anyone to start contributing to GitLab. If you have any questions or need help visit [Getting Help](https://about.gitlab.com/getting-help/#discussion) to
|
|
||||||
learn how to communicate with GitLab. If you're looking for a Gitter or Slack channel
|
|
||||||
please consider we favor
|
|
||||||
[asynchronous communication](https://about.gitlab.com/handbook/communication/#internal-communication) over real time communication. Thanks for your contribution!
|
|
||||||
|
|
||||||
## Contribution Flow
|
## Contribution Flow
|
||||||
|
|
||||||
When contributing to GitLab, your merge request is subject to review by merge request maintainers of a particular specialty.
|
This [documentation](doc/development/contributing/index.md) has been moved.
|
||||||
|
|
||||||
When you submit code to GitLab, we really want it to get merged, but there will be times when it will not be merged.
|
|
||||||
|
|
||||||
When maintainers are reading through a merge request they may request guidance from other maintainers. If merge request maintainers conclude that the code should not be merged, our reasons will be fully disclosed. If it has been decided that the code quality is not up to GitLab’s standards, the merge request maintainer will refer the author to our docs and code style guides, and provide some guidance.
|
|
||||||
|
|
||||||
Sometimes style guides will be followed but the code will lack structural integrity, or the maintainer will have reservations about the code’s overall quality. When there is a reservation the maintainer will inform the author and provide some guidance. The author may then choose to update the merge request. Once the merge request has been updated and reassigned to the maintainer, they will review the code again. Once the code has been resubmitted any number of times, the maintainer may choose to close the merge request with a summary of why it will not be merged, as well as some guidance. If the merge request is closed the maintainer will be open to discussion as to how to improve the code so it can be approved in the future.
|
|
||||||
|
|
||||||
GitLab will do its best to review community contributions as quickly as possible. Specially appointed developers review community contributions daily. You may take a look at the [team page](https://about.gitlab.com/team/) for the merge request coach who specializes in the type of code you have written and mention them in the merge request. For example, if you have written some JavaScript in your code then you should mention the frontend merge request coach. If your code has multiple disciplines you may mention multiple merge request coaches.
|
|
||||||
|
|
||||||
GitLab receives a lot of community contributions, so if your code has not been reviewed within 4 days of its initial submission feel free to re-mention the appropriate merge request coach.
|
|
||||||
|
|
||||||
When submitting code to GitLab, you may feel that your contribution requires the aid of an external library. If your code includes an external library please provide a link to the library, as well as reasons for including it.
|
|
||||||
|
|
||||||
When your code contains more than 500 changes, any major breaking changes, or an external library, `@mention` a maintainer in the merge request. If you are not sure who to mention, the reviewer will add one early in the merge request process.
|
|
||||||
|
|
||||||
[core team]: https://about.gitlab.com/core-team/
|
|
||||||
[team]: https://about.gitlab.com/team/
|
|
||||||
[getting-help]: https://about.gitlab.com/getting-help/
|
|
||||||
[codetriage]: http://www.codetriage.com/gitlabhq/gitlabhq
|
|
||||||
[accepting-mrs-weight]: https://gitlab.com/gitlab-org/gitlab-ce/issues?assignee_id=0&label_name[]=Accepting%20Merge%20Requests&sort=weight_asc
|
|
||||||
[ce-tracker]: https://gitlab.com/gitlab-org/gitlab-ce/issues
|
|
||||||
[ee-tracker]: https://gitlab.com/gitlab-org/gitlab-ee/issues
|
|
||||||
[google-group]: https://groups.google.com/forum/#!forum/gitlabhq
|
|
||||||
[stackoverflow]: https://stackoverflow.com/questions/tagged/gitlab
|
|
||||||
[fpl]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=feature+proposal
|
|
||||||
[accepting-mrs-ce]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=Accepting+Merge+Requests
|
|
||||||
[accepting-mrs-ee]: https://gitlab.com/gitlab-org/gitlab-ee/issues?label_name=Accepting+Merge+Requests
|
|
||||||
[gitlab-mr-tracker]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests
|
|
||||||
[gdk]: https://gitlab.com/gitlab-org/gitlab-development-kit
|
|
||||||
[git-squash]: https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits
|
|
||||||
[closed-merge-requests]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests?assignee_id=&label_name=&milestone_id=&scope=&sort=&state=closed
|
|
||||||
[definition-of-done]: http://guide.agilealliance.org/guide/definition-of-done.html
|
|
||||||
[contributor-covenant]: http://contributor-covenant.org
|
|
||||||
[rss-source]: https://github.com/bbatsov/ruby-style-guide/blob/master/README.md#source-code-layout
|
|
||||||
[rss-naming]: https://github.com/bbatsov/ruby-style-guide/blob/master/README.md#naming
|
|
||||||
[changelog]: doc/development/changelog.md "Generate a changelog entry"
|
|
||||||
[doc-guidelines]: doc/development/documentation/index.md "Documentation guidelines"
|
|
||||||
[js-styleguide]: doc/development/fe_guide/style_guide_js.md "JavaScript styleguide"
|
|
||||||
[scss-styleguide]: doc/development/fe_guide/style_guide_scss.md "SCSS styleguide"
|
|
||||||
[newlines-styleguide]: doc/development/newlines_styleguide.md "Newlines styleguide"
|
|
||||||
[UX Guide for GitLab]: http://docs.gitlab.com/ce/development/ux_guide/
|
|
||||||
[license-finder-doc]: doc/development/licensing.md
|
|
||||||
[GitLab Inc engineering workflow]: https://about.gitlab.com/handbook/engineering/workflow/#labelling-issues
|
|
||||||
[polling-etag]: https://docs.gitlab.com/ce/development/polling.html
|
|
||||||
[testing]: doc/development/testing_guide/index.md
|
|
||||||
[us-english]: https://en.wikipedia.org/wiki/American_English
|
|
||||||
|
|
||||||
|
|
||||||
## Workflow labels
|
## Workflow labels
|
||||||
|
|
||||||
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
|
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
|
||||||
|
|
||||||
|
|
||||||
### Type labels
|
### Type labels
|
||||||
|
|
||||||
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
|
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
|
||||||
|
|
||||||
|
|
||||||
### Subject labels
|
### Subject labels
|
||||||
|
|
||||||
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
|
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
|
||||||
|
|
||||||
|
|
||||||
### Team labels
|
### Team labels
|
||||||
|
|
||||||
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
|
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
|
||||||
|
|
||||||
|
|
||||||
### Release Scoping labels
|
### Release Scoping labels
|
||||||
|
|
||||||
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
|
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
|
||||||
|
|
||||||
|
|
||||||
### Priority labels
|
### Priority labels
|
||||||
|
|
||||||
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
|
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
|
||||||
|
|
||||||
|
|
||||||
### Severity labels
|
### Severity labels
|
||||||
|
|
||||||
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
|
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
|
||||||
|
@ -250,17 +122,14 @@ This [documentation](doc/development/contributing/issue_workflow.md) has been mo
|
||||||
|
|
||||||
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
|
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
|
||||||
|
|
||||||
|
|
||||||
### Label for community contributors
|
### Label for community contributors
|
||||||
|
|
||||||
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
|
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
|
||||||
|
|
||||||
|
|
||||||
## Implement design & UI elements
|
## Implement design & UI elements
|
||||||
|
|
||||||
This [documentation](doc/development/contributing/design.md) has been moved.
|
This [documentation](doc/development/contributing/design.md) has been moved.
|
||||||
|
|
||||||
|
|
||||||
## Issue tracker
|
## Issue tracker
|
||||||
|
|
||||||
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
|
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
|
||||||
|
@ -269,7 +138,6 @@ This [documentation](doc/development/contributing/issue_workflow.md) has been mo
|
||||||
|
|
||||||
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
|
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
|
||||||
|
|
||||||
|
|
||||||
### Feature proposals
|
### Feature proposals
|
||||||
|
|
||||||
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
|
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
|
||||||
|
@ -278,32 +146,26 @@ This [documentation](doc/development/contributing/issue_workflow.md) has been mo
|
||||||
|
|
||||||
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
|
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
|
||||||
|
|
||||||
|
|
||||||
### Issue weight
|
### Issue weight
|
||||||
|
|
||||||
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
|
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
|
||||||
|
|
||||||
|
|
||||||
### Regression issues
|
### Regression issues
|
||||||
|
|
||||||
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
|
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
|
||||||
|
|
||||||
|
|
||||||
### Technical and UX debt
|
### Technical and UX debt
|
||||||
|
|
||||||
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
|
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
|
||||||
|
|
||||||
|
|
||||||
### Stewardship
|
### Stewardship
|
||||||
|
|
||||||
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
|
This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
|
||||||
|
|
||||||
|
|
||||||
## Merge requests
|
## Merge requests
|
||||||
|
|
||||||
This [documentation](doc/development/contributing/merge_request_workflow.md) has been moved.
|
This [documentation](doc/development/contributing/merge_request_workflow.md) has been moved.
|
||||||
|
|
||||||
|
|
||||||
### Merge request guidelines
|
### Merge request guidelines
|
||||||
|
|
||||||
This [documentation](doc/development/contributing/merge_request_workflow.md) has been moved.
|
This [documentation](doc/development/contributing/merge_request_workflow.md) has been moved.
|
||||||
|
@ -313,12 +175,10 @@ This [documentation](doc/development/contributing/merge_request_workflow.md) has
|
||||||
|
|
||||||
This [documentation](doc/development/contributing/merge_request_workflow.md) has been moved.
|
This [documentation](doc/development/contributing/merge_request_workflow.md) has been moved.
|
||||||
|
|
||||||
|
|
||||||
## Definition of done
|
## Definition of done
|
||||||
|
|
||||||
This [documentation](doc/development/contributing/merge_request_workflow.md) has been moved.
|
This [documentation](doc/development/contributing/merge_request_workflow.md) has been moved.
|
||||||
|
|
||||||
|
|
||||||
## Style guides
|
## Style guides
|
||||||
|
|
||||||
This [documentation](doc/development/contributing/design.md) has been moved.
|
This [documentation](doc/development/contributing/design.md) has been moved.
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
danger.import_plugin('danger/plugins/helper.rb')
|
||||||
danger.import_dangerfile(path: 'danger/metadata')
|
danger.import_dangerfile(path: 'danger/metadata')
|
||||||
danger.import_dangerfile(path: 'danger/changes_size')
|
danger.import_dangerfile(path: 'danger/changes_size')
|
||||||
danger.import_dangerfile(path: 'danger/changelog')
|
danger.import_dangerfile(path: 'danger/changelog')
|
||||||
|
|
4
Dockerfile.assets
Normal file
4
Dockerfile.assets
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
# Simple container to store assets for later use
|
||||||
|
FROM scratch
|
||||||
|
ADD public/assets /assets/
|
||||||
|
CMD /bin/true
|
|
@ -1 +1 @@
|
||||||
0.125.1
|
0.129.0
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
1.1.1
|
1.3.1
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
8.3.3
|
8.4.1
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
7.0.1
|
7.1.3
|
||||||
|
|
28
Gemfile
28
Gemfile
|
@ -79,13 +79,6 @@ gem 'gpgme'
|
||||||
gem 'gitlab_omniauth-ldap', '~> 2.0.4', require: 'omniauth-ldap'
|
gem 'gitlab_omniauth-ldap', '~> 2.0.4', require: 'omniauth-ldap'
|
||||||
gem 'net-ldap'
|
gem 'net-ldap'
|
||||||
|
|
||||||
# Git Wiki
|
|
||||||
# Only used to compute wiki page slugs
|
|
||||||
gem 'gitlab-gollum-lib', '~> 4.2', require: false
|
|
||||||
|
|
||||||
# Language detection
|
|
||||||
gem 'github-linguist', '~> 5.3.3', require: 'linguist'
|
|
||||||
|
|
||||||
# API
|
# API
|
||||||
gem 'grape', '~> 1.1'
|
gem 'grape', '~> 1.1'
|
||||||
gem 'grape-entity', '~> 0.7.1'
|
gem 'grape-entity', '~> 0.7.1'
|
||||||
|
@ -146,6 +139,7 @@ gem 'rouge', '~> 3.1'
|
||||||
gem 'truncato', '~> 0.7.9'
|
gem 'truncato', '~> 0.7.9'
|
||||||
gem 'bootstrap_form', '~> 2.7.0'
|
gem 'bootstrap_form', '~> 2.7.0'
|
||||||
gem 'nokogiri', '~> 1.8.2'
|
gem 'nokogiri', '~> 1.8.2'
|
||||||
|
gem 'escape_utils', '~> 1.1'
|
||||||
|
|
||||||
# Calendar rendering
|
# Calendar rendering
|
||||||
gem 'icalendar'
|
gem 'icalendar'
|
||||||
|
@ -159,6 +153,11 @@ group :unicorn do
|
||||||
gem 'unicorn-worker-killer', '~> 0.4.4'
|
gem 'unicorn-worker-killer', '~> 0.4.4'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
group :puma do
|
||||||
|
gem 'puma', '~> 3.12', require: false
|
||||||
|
gem 'puma_worker_killer', require: false
|
||||||
|
end
|
||||||
|
|
||||||
# State machine
|
# State machine
|
||||||
gem 'state_machines-activerecord', '~> 0.5.1'
|
gem 'state_machines-activerecord', '~> 0.5.1'
|
||||||
|
|
||||||
|
@ -212,7 +211,7 @@ gem 'hipchat', '~> 1.5.0'
|
||||||
gem 'jira-ruby', '~> 1.4'
|
gem 'jira-ruby', '~> 1.4'
|
||||||
|
|
||||||
# Flowdock integration
|
# Flowdock integration
|
||||||
gem 'gitlab-flowdock-git-hook', '~> 1.0.1'
|
gem 'flowdock', '~> 0.7'
|
||||||
|
|
||||||
# Slack integration
|
# Slack integration
|
||||||
gem 'slack-notifier', '~> 1.5.1'
|
gem 'slack-notifier', '~> 1.5.1'
|
||||||
|
@ -245,9 +244,6 @@ gem 'rack-attack', '~> 4.4.1'
|
||||||
# Ace editor
|
# Ace editor
|
||||||
gem 'ace-rails-ap', '~> 4.1.0'
|
gem 'ace-rails-ap', '~> 4.1.0'
|
||||||
|
|
||||||
# Keyboard shortcuts
|
|
||||||
gem 'mousetrap-rails', '~> 1.4.6'
|
|
||||||
|
|
||||||
# Detect and convert string character encoding
|
# Detect and convert string character encoding
|
||||||
gem 'charlock_holmes', '~> 0.7.5'
|
gem 'charlock_holmes', '~> 0.7.5'
|
||||||
|
|
||||||
|
@ -420,11 +416,10 @@ group :ed25519 do
|
||||||
end
|
end
|
||||||
|
|
||||||
# Gitaly GRPC client
|
# Gitaly GRPC client
|
||||||
gem 'gitaly-proto', '~> 0.118.1', require: 'gitaly'
|
gem 'gitaly-proto', '~> 0.123.0', require: 'gitaly'
|
||||||
gem 'grpc', '~> 1.11.0'
|
gem 'grpc', '~> 1.15.0'
|
||||||
|
|
||||||
# Locked until https://github.com/google/protobuf/issues/4210 is closed
|
gem 'google-protobuf', '~> 3.6'
|
||||||
gem 'google-protobuf', '= 3.5.1'
|
|
||||||
|
|
||||||
gem 'toml-rb', '~> 1.0.0', require: false
|
gem 'toml-rb', '~> 1.0.0', require: false
|
||||||
|
|
||||||
|
@ -436,6 +431,3 @@ gem 'flipper-active_support_cache_store', '~> 0.13.0'
|
||||||
# Structured logging
|
# Structured logging
|
||||||
gem 'lograge', '~> 0.5'
|
gem 'lograge', '~> 0.5'
|
||||||
gem 'grape_logging', '~> 1.7'
|
gem 'grape_logging', '~> 1.7'
|
||||||
|
|
||||||
# Asset synchronization
|
|
||||||
gem 'asset_sync', '~> 2.4'
|
|
||||||
|
|
82
Gemfile.lock
82
Gemfile.lock
|
@ -58,11 +58,6 @@ GEM
|
||||||
asciidoctor (1.5.6.2)
|
asciidoctor (1.5.6.2)
|
||||||
asciidoctor-plantuml (0.0.8)
|
asciidoctor-plantuml (0.0.8)
|
||||||
asciidoctor (~> 1.5)
|
asciidoctor (~> 1.5)
|
||||||
asset_sync (2.4.0)
|
|
||||||
activemodel (>= 4.1.0)
|
|
||||||
fog-core
|
|
||||||
mime-types (>= 2.99)
|
|
||||||
unf
|
|
||||||
ast (2.4.0)
|
ast (2.4.0)
|
||||||
atomic (1.1.99)
|
atomic (1.1.99)
|
||||||
attr_encrypted (3.1.0)
|
attr_encrypted (3.1.0)
|
||||||
|
@ -274,32 +269,9 @@ GEM
|
||||||
gettext_i18n_rails (>= 0.7.1)
|
gettext_i18n_rails (>= 0.7.1)
|
||||||
po_to_json (>= 1.0.0)
|
po_to_json (>= 1.0.0)
|
||||||
rails (>= 3.2.0)
|
rails (>= 3.2.0)
|
||||||
gitaly-proto (0.118.1)
|
gitaly-proto (0.123.0)
|
||||||
google-protobuf (~> 3.1)
|
grpc (~> 1.0)
|
||||||
grpc (~> 1.10)
|
|
||||||
github-linguist (5.3.3)
|
|
||||||
charlock_holmes (~> 0.7.5)
|
|
||||||
escape_utils (~> 1.1.0)
|
|
||||||
mime-types (>= 1.19)
|
|
||||||
rugged (>= 0.25.1)
|
|
||||||
github-markup (1.7.0)
|
github-markup (1.7.0)
|
||||||
gitlab-flowdock-git-hook (1.0.1)
|
|
||||||
flowdock (~> 0.7)
|
|
||||||
gitlab-grit (>= 2.4.1)
|
|
||||||
multi_json
|
|
||||||
gitlab-gollum-lib (4.2.7.5)
|
|
||||||
gemojione (~> 3.2)
|
|
||||||
github-markup (~> 1.6)
|
|
||||||
gollum-grit_adapter (~> 1.0)
|
|
||||||
nokogiri (>= 1.6.1, < 2.0)
|
|
||||||
rouge (~> 3.1)
|
|
||||||
sanitize (~> 4.6.4)
|
|
||||||
stringex (~> 2.6)
|
|
||||||
gitlab-grit (2.8.2)
|
|
||||||
charlock_holmes (~> 0.6)
|
|
||||||
diff-lcs (~> 1.1)
|
|
||||||
mime-types (>= 1.16)
|
|
||||||
posix-spawn (~> 0.3)
|
|
||||||
gitlab-markup (1.6.4)
|
gitlab-markup (1.6.4)
|
||||||
gitlab-sidekiq-fetcher (0.3.0)
|
gitlab-sidekiq-fetcher (0.3.0)
|
||||||
sidekiq (~> 5)
|
sidekiq (~> 5)
|
||||||
|
@ -314,8 +286,6 @@ GEM
|
||||||
rubyntlm (~> 0.5)
|
rubyntlm (~> 0.5)
|
||||||
globalid (0.4.1)
|
globalid (0.4.1)
|
||||||
activesupport (>= 4.2.0)
|
activesupport (>= 4.2.0)
|
||||||
gollum-grit_adapter (1.0.1)
|
|
||||||
gitlab-grit (~> 2.7, >= 2.7.1)
|
|
||||||
gon (6.2.0)
|
gon (6.2.0)
|
||||||
actionpack (>= 3.0)
|
actionpack (>= 3.0)
|
||||||
multi_json
|
multi_json
|
||||||
|
@ -327,16 +297,15 @@ GEM
|
||||||
mime-types (~> 3.0)
|
mime-types (~> 3.0)
|
||||||
representable (~> 3.0)
|
representable (~> 3.0)
|
||||||
retriable (>= 2.0, < 4.0)
|
retriable (>= 2.0, < 4.0)
|
||||||
google-protobuf (3.5.1)
|
google-protobuf (3.6.1)
|
||||||
googleapis-common-protos-types (1.0.1)
|
googleapis-common-protos-types (1.0.2)
|
||||||
google-protobuf (~> 3.0)
|
google-protobuf (~> 3.0)
|
||||||
googleauth (0.6.2)
|
googleauth (0.6.6)
|
||||||
faraday (~> 0.12)
|
faraday (~> 0.12)
|
||||||
jwt (>= 1.4, < 3.0)
|
jwt (>= 1.4, < 3.0)
|
||||||
logging (~> 2.0)
|
|
||||||
memoist (~> 0.12)
|
memoist (~> 0.12)
|
||||||
multi_json (~> 1.11)
|
multi_json (~> 1.11)
|
||||||
os (~> 0.9)
|
os (>= 0.9, < 2.0)
|
||||||
signet (~> 0.7)
|
signet (~> 0.7)
|
||||||
gpgme (2.0.13)
|
gpgme (2.0.13)
|
||||||
mini_portile2 (~> 2.1)
|
mini_portile2 (~> 2.1)
|
||||||
|
@ -360,10 +329,9 @@ GEM
|
||||||
railties
|
railties
|
||||||
sprockets-rails
|
sprockets-rails
|
||||||
graphql (1.8.1)
|
graphql (1.8.1)
|
||||||
grpc (1.11.0)
|
grpc (1.15.0)
|
||||||
google-protobuf (~> 3.1)
|
google-protobuf (~> 3.1)
|
||||||
googleapis-common-protos-types (~> 1.0.0)
|
googleapis-common-protos-types (~> 1.0.0)
|
||||||
googleauth (>= 0.5.1, < 0.7)
|
|
||||||
haml (5.0.4)
|
haml (5.0.4)
|
||||||
temple (>= 0.8.0)
|
temple (>= 0.8.0)
|
||||||
tilt
|
tilt
|
||||||
|
@ -465,11 +433,7 @@ GEM
|
||||||
xml-simple
|
xml-simple
|
||||||
licensee (8.9.2)
|
licensee (8.9.2)
|
||||||
rugged (~> 0.24)
|
rugged (~> 0.24)
|
||||||
little-plugger (1.1.4)
|
|
||||||
locale (2.1.2)
|
locale (2.1.2)
|
||||||
logging (2.2.2)
|
|
||||||
little-plugger (~> 1.1)
|
|
||||||
multi_json (~> 1.10)
|
|
||||||
lograge (0.10.0)
|
lograge (0.10.0)
|
||||||
actionpack (>= 4)
|
actionpack (>= 4)
|
||||||
activesupport (>= 4)
|
activesupport (>= 4)
|
||||||
|
@ -493,7 +457,6 @@ GEM
|
||||||
mini_mime (1.0.1)
|
mini_mime (1.0.1)
|
||||||
mini_portile2 (2.3.0)
|
mini_portile2 (2.3.0)
|
||||||
minitest (5.7.0)
|
minitest (5.7.0)
|
||||||
mousetrap-rails (1.4.6)
|
|
||||||
msgpack (1.2.4)
|
msgpack (1.2.4)
|
||||||
multi_json (1.13.1)
|
multi_json (1.13.1)
|
||||||
multi_xml (0.6.0)
|
multi_xml (0.6.0)
|
||||||
|
@ -575,9 +538,9 @@ GEM
|
||||||
org-ruby (0.9.12)
|
org-ruby (0.9.12)
|
||||||
rubypants (~> 0.2)
|
rubypants (~> 0.2)
|
||||||
orm_adapter (0.5.0)
|
orm_adapter (0.5.0)
|
||||||
os (0.9.6)
|
os (1.0.0)
|
||||||
parallel (1.12.1)
|
parallel (1.12.1)
|
||||||
parser (2.5.1.0)
|
parser (2.5.3.0)
|
||||||
ast (~> 2.4.0)
|
ast (~> 2.4.0)
|
||||||
parslet (1.8.2)
|
parslet (1.8.2)
|
||||||
peek (1.0.1)
|
peek (1.0.1)
|
||||||
|
@ -605,7 +568,6 @@ GEM
|
||||||
pg (0.18.4)
|
pg (0.18.4)
|
||||||
po_to_json (1.0.1)
|
po_to_json (1.0.1)
|
||||||
json (>= 1.6.0)
|
json (>= 1.6.0)
|
||||||
posix-spawn (0.3.13)
|
|
||||||
powerpack (0.1.1)
|
powerpack (0.1.1)
|
||||||
premailer (1.10.4)
|
premailer (1.10.4)
|
||||||
addressable
|
addressable
|
||||||
|
@ -629,6 +591,10 @@ GEM
|
||||||
pry-rails (0.3.6)
|
pry-rails (0.3.6)
|
||||||
pry (>= 0.10.4)
|
pry (>= 0.10.4)
|
||||||
public_suffix (3.0.3)
|
public_suffix (3.0.3)
|
||||||
|
puma (3.12.0)
|
||||||
|
puma_worker_killer (0.1.0)
|
||||||
|
get_process_mem (~> 0.2)
|
||||||
|
puma (>= 2.7, < 4)
|
||||||
pyu-ruby-sasl (0.0.3.3)
|
pyu-ruby-sasl (0.0.3.3)
|
||||||
rack (1.6.10)
|
rack (1.6.10)
|
||||||
rack-accept (0.4.5)
|
rack-accept (0.4.5)
|
||||||
|
@ -797,7 +763,7 @@ GEM
|
||||||
rubyzip (1.2.2)
|
rubyzip (1.2.2)
|
||||||
rufus-scheduler (3.4.0)
|
rufus-scheduler (3.4.0)
|
||||||
et-orbi (~> 1.0)
|
et-orbi (~> 1.0)
|
||||||
rugged (0.27.4)
|
rugged (0.27.5)
|
||||||
safe_yaml (1.0.4)
|
safe_yaml (1.0.4)
|
||||||
sanitize (4.6.6)
|
sanitize (4.6.6)
|
||||||
crass (~> 1.0.2)
|
crass (~> 1.0.2)
|
||||||
|
@ -843,7 +809,7 @@ GEM
|
||||||
sidekiq-cron (0.6.0)
|
sidekiq-cron (0.6.0)
|
||||||
rufus-scheduler (>= 3.3.0)
|
rufus-scheduler (>= 3.3.0)
|
||||||
sidekiq (>= 4.2.1)
|
sidekiq (>= 4.2.1)
|
||||||
signet (0.8.1)
|
signet (0.11.0)
|
||||||
addressable (~> 2.3)
|
addressable (~> 2.3)
|
||||||
faraday (~> 0.9)
|
faraday (~> 0.9)
|
||||||
jwt (>= 1.5, < 3.0)
|
jwt (>= 1.5, < 3.0)
|
||||||
|
@ -876,7 +842,6 @@ GEM
|
||||||
state_machines-activerecord (0.5.1)
|
state_machines-activerecord (0.5.1)
|
||||||
activerecord (>= 4.1, < 6.0)
|
activerecord (>= 4.1, < 6.0)
|
||||||
state_machines-activemodel (>= 0.5.0)
|
state_machines-activemodel (>= 0.5.0)
|
||||||
stringex (2.8.4)
|
|
||||||
sys-filesystem (1.1.6)
|
sys-filesystem (1.1.6)
|
||||||
ffi
|
ffi
|
||||||
sysexits (1.2.0)
|
sysexits (1.2.0)
|
||||||
|
@ -968,7 +933,6 @@ DEPENDENCIES
|
||||||
asana (~> 0.6.0)
|
asana (~> 0.6.0)
|
||||||
asciidoctor (~> 1.5.6)
|
asciidoctor (~> 1.5.6)
|
||||||
asciidoctor-plantuml (= 0.0.8)
|
asciidoctor-plantuml (= 0.0.8)
|
||||||
asset_sync (~> 2.4)
|
|
||||||
attr_encrypted (~> 3.1.0)
|
attr_encrypted (~> 3.1.0)
|
||||||
awesome_print
|
awesome_print
|
||||||
babosa (~> 1.0.2)
|
babosa (~> 1.0.2)
|
||||||
|
@ -1006,6 +970,7 @@ DEPENDENCIES
|
||||||
ed25519 (~> 1.2)
|
ed25519 (~> 1.2)
|
||||||
email_reply_trimmer (~> 0.1)
|
email_reply_trimmer (~> 0.1)
|
||||||
email_spec (~> 2.2.0)
|
email_spec (~> 2.2.0)
|
||||||
|
escape_utils (~> 1.1)
|
||||||
factory_bot_rails (~> 4.8.2)
|
factory_bot_rails (~> 4.8.2)
|
||||||
faraday (~> 0.12)
|
faraday (~> 0.12)
|
||||||
fast_blank
|
fast_blank
|
||||||
|
@ -1013,6 +978,7 @@ DEPENDENCIES
|
||||||
flipper (~> 0.13.0)
|
flipper (~> 0.13.0)
|
||||||
flipper-active_record (~> 0.13.0)
|
flipper-active_record (~> 0.13.0)
|
||||||
flipper-active_support_cache_store (~> 0.13.0)
|
flipper-active_support_cache_store (~> 0.13.0)
|
||||||
|
flowdock (~> 0.7)
|
||||||
fog-aliyun (~> 0.2.0)
|
fog-aliyun (~> 0.2.0)
|
||||||
fog-aws (~> 2.0.1)
|
fog-aws (~> 2.0.1)
|
||||||
fog-core (~> 1.44)
|
fog-core (~> 1.44)
|
||||||
|
@ -1027,18 +993,15 @@ DEPENDENCIES
|
||||||
gettext (~> 3.2.2)
|
gettext (~> 3.2.2)
|
||||||
gettext_i18n_rails (~> 1.8.0)
|
gettext_i18n_rails (~> 1.8.0)
|
||||||
gettext_i18n_rails_js (~> 1.3)
|
gettext_i18n_rails_js (~> 1.3)
|
||||||
gitaly-proto (~> 0.118.1)
|
gitaly-proto (~> 0.123.0)
|
||||||
github-linguist (~> 5.3.3)
|
|
||||||
github-markup (~> 1.7.0)
|
github-markup (~> 1.7.0)
|
||||||
gitlab-flowdock-git-hook (~> 1.0.1)
|
|
||||||
gitlab-gollum-lib (~> 4.2)
|
|
||||||
gitlab-markup (~> 1.6.4)
|
gitlab-markup (~> 1.6.4)
|
||||||
gitlab-sidekiq-fetcher
|
gitlab-sidekiq-fetcher
|
||||||
gitlab-styles (~> 2.4)
|
gitlab-styles (~> 2.4)
|
||||||
gitlab_omniauth-ldap (~> 2.0.4)
|
gitlab_omniauth-ldap (~> 2.0.4)
|
||||||
gon (~> 6.2)
|
gon (~> 6.2)
|
||||||
google-api-client (~> 0.23)
|
google-api-client (~> 0.23)
|
||||||
google-protobuf (= 3.5.1)
|
google-protobuf (~> 3.6)
|
||||||
gpgme
|
gpgme
|
||||||
grape (~> 1.1)
|
grape (~> 1.1)
|
||||||
grape-entity (~> 0.7.1)
|
grape-entity (~> 0.7.1)
|
||||||
|
@ -1046,7 +1009,7 @@ DEPENDENCIES
|
||||||
grape_logging (~> 1.7)
|
grape_logging (~> 1.7)
|
||||||
graphiql-rails (~> 1.4.10)
|
graphiql-rails (~> 1.4.10)
|
||||||
graphql (~> 1.8.0)
|
graphql (~> 1.8.0)
|
||||||
grpc (~> 1.11.0)
|
grpc (~> 1.15.0)
|
||||||
haml_lint (~> 0.26.0)
|
haml_lint (~> 0.26.0)
|
||||||
hamlit (~> 2.8.8)
|
hamlit (~> 2.8.8)
|
||||||
hangouts-chat (~> 0.0.5)
|
hangouts-chat (~> 0.0.5)
|
||||||
|
@ -1075,7 +1038,6 @@ DEPENDENCIES
|
||||||
method_source (~> 0.8)
|
method_source (~> 0.8)
|
||||||
mini_magick
|
mini_magick
|
||||||
minitest (~> 5.7.0)
|
minitest (~> 5.7.0)
|
||||||
mousetrap-rails (~> 1.4.6)
|
|
||||||
mysql2 (~> 0.4.10)
|
mysql2 (~> 0.4.10)
|
||||||
net-ldap
|
net-ldap
|
||||||
net-ssh (~> 5.0)
|
net-ssh (~> 5.0)
|
||||||
|
@ -1109,6 +1071,8 @@ DEPENDENCIES
|
||||||
prometheus-client-mmap (~> 0.9.4)
|
prometheus-client-mmap (~> 0.9.4)
|
||||||
pry-byebug (~> 3.4.1)
|
pry-byebug (~> 3.4.1)
|
||||||
pry-rails (~> 0.3.4)
|
pry-rails (~> 0.3.4)
|
||||||
|
puma (~> 3.12)
|
||||||
|
puma_worker_killer
|
||||||
rack-attack (~> 4.4.1)
|
rack-attack (~> 4.4.1)
|
||||||
rack-cors (~> 1.0.0)
|
rack-cors (~> 1.0.0)
|
||||||
rack-oauth2 (~> 1.2.1)
|
rack-oauth2 (~> 1.2.1)
|
||||||
|
@ -1187,4 +1151,4 @@ DEPENDENCIES
|
||||||
wikicloth (= 0.8.1)
|
wikicloth (= 0.8.1)
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
1.16.4
|
1.17.1
|
||||||
|
|
|
@ -61,11 +61,6 @@ GEM
|
||||||
asciidoctor (1.5.6.2)
|
asciidoctor (1.5.6.2)
|
||||||
asciidoctor-plantuml (0.0.8)
|
asciidoctor-plantuml (0.0.8)
|
||||||
asciidoctor (~> 1.5)
|
asciidoctor (~> 1.5)
|
||||||
asset_sync (2.4.0)
|
|
||||||
activemodel (>= 4.1.0)
|
|
||||||
fog-core
|
|
||||||
mime-types (>= 2.99)
|
|
||||||
unf
|
|
||||||
ast (2.4.0)
|
ast (2.4.0)
|
||||||
atomic (1.1.99)
|
atomic (1.1.99)
|
||||||
attr_encrypted (3.1.0)
|
attr_encrypted (3.1.0)
|
||||||
|
@ -277,32 +272,9 @@ GEM
|
||||||
gettext_i18n_rails (>= 0.7.1)
|
gettext_i18n_rails (>= 0.7.1)
|
||||||
po_to_json (>= 1.0.0)
|
po_to_json (>= 1.0.0)
|
||||||
rails (>= 3.2.0)
|
rails (>= 3.2.0)
|
||||||
gitaly-proto (0.118.1)
|
gitaly-proto (0.123.0)
|
||||||
google-protobuf (~> 3.1)
|
grpc (~> 1.0)
|
||||||
grpc (~> 1.10)
|
|
||||||
github-linguist (5.3.3)
|
|
||||||
charlock_holmes (~> 0.7.5)
|
|
||||||
escape_utils (~> 1.1.0)
|
|
||||||
mime-types (>= 1.19)
|
|
||||||
rugged (>= 0.25.1)
|
|
||||||
github-markup (1.7.0)
|
github-markup (1.7.0)
|
||||||
gitlab-flowdock-git-hook (1.0.1)
|
|
||||||
flowdock (~> 0.7)
|
|
||||||
gitlab-grit (>= 2.4.1)
|
|
||||||
multi_json
|
|
||||||
gitlab-gollum-lib (4.2.7.5)
|
|
||||||
gemojione (~> 3.2)
|
|
||||||
github-markup (~> 1.6)
|
|
||||||
gollum-grit_adapter (~> 1.0)
|
|
||||||
nokogiri (>= 1.6.1, < 2.0)
|
|
||||||
rouge (~> 3.1)
|
|
||||||
sanitize (~> 4.6.4)
|
|
||||||
stringex (~> 2.6)
|
|
||||||
gitlab-grit (2.8.2)
|
|
||||||
charlock_holmes (~> 0.6)
|
|
||||||
diff-lcs (~> 1.1)
|
|
||||||
mime-types (>= 1.16)
|
|
||||||
posix-spawn (~> 0.3)
|
|
||||||
gitlab-markup (1.6.4)
|
gitlab-markup (1.6.4)
|
||||||
gitlab-sidekiq-fetcher (0.3.0)
|
gitlab-sidekiq-fetcher (0.3.0)
|
||||||
sidekiq (~> 5)
|
sidekiq (~> 5)
|
||||||
|
@ -317,8 +289,6 @@ GEM
|
||||||
rubyntlm (~> 0.5)
|
rubyntlm (~> 0.5)
|
||||||
globalid (0.4.1)
|
globalid (0.4.1)
|
||||||
activesupport (>= 4.2.0)
|
activesupport (>= 4.2.0)
|
||||||
gollum-grit_adapter (1.0.1)
|
|
||||||
gitlab-grit (~> 2.7, >= 2.7.1)
|
|
||||||
gon (6.2.0)
|
gon (6.2.0)
|
||||||
actionpack (>= 3.0)
|
actionpack (>= 3.0)
|
||||||
multi_json
|
multi_json
|
||||||
|
@ -330,16 +300,15 @@ GEM
|
||||||
mime-types (~> 3.0)
|
mime-types (~> 3.0)
|
||||||
representable (~> 3.0)
|
representable (~> 3.0)
|
||||||
retriable (>= 2.0, < 4.0)
|
retriable (>= 2.0, < 4.0)
|
||||||
google-protobuf (3.5.1)
|
google-protobuf (3.6.1)
|
||||||
googleapis-common-protos-types (1.0.1)
|
googleapis-common-protos-types (1.0.2)
|
||||||
google-protobuf (~> 3.0)
|
google-protobuf (~> 3.0)
|
||||||
googleauth (0.6.2)
|
googleauth (0.6.6)
|
||||||
faraday (~> 0.12)
|
faraday (~> 0.12)
|
||||||
jwt (>= 1.4, < 3.0)
|
jwt (>= 1.4, < 3.0)
|
||||||
logging (~> 2.0)
|
|
||||||
memoist (~> 0.12)
|
memoist (~> 0.12)
|
||||||
multi_json (~> 1.11)
|
multi_json (~> 1.11)
|
||||||
os (~> 0.9)
|
os (>= 0.9, < 2.0)
|
||||||
signet (~> 0.7)
|
signet (~> 0.7)
|
||||||
gpgme (2.0.13)
|
gpgme (2.0.13)
|
||||||
mini_portile2 (~> 2.1)
|
mini_portile2 (~> 2.1)
|
||||||
|
@ -363,10 +332,9 @@ GEM
|
||||||
railties
|
railties
|
||||||
sprockets-rails
|
sprockets-rails
|
||||||
graphql (1.8.1)
|
graphql (1.8.1)
|
||||||
grpc (1.11.0)
|
grpc (1.15.0)
|
||||||
google-protobuf (~> 3.1)
|
google-protobuf (~> 3.1)
|
||||||
googleapis-common-protos-types (~> 1.0.0)
|
googleapis-common-protos-types (~> 1.0.0)
|
||||||
googleauth (>= 0.5.1, < 0.7)
|
|
||||||
haml (5.0.4)
|
haml (5.0.4)
|
||||||
temple (>= 0.8.0)
|
temple (>= 0.8.0)
|
||||||
tilt
|
tilt
|
||||||
|
@ -468,11 +436,7 @@ GEM
|
||||||
xml-simple
|
xml-simple
|
||||||
licensee (8.9.2)
|
licensee (8.9.2)
|
||||||
rugged (~> 0.24)
|
rugged (~> 0.24)
|
||||||
little-plugger (1.1.4)
|
|
||||||
locale (2.1.2)
|
locale (2.1.2)
|
||||||
logging (2.2.2)
|
|
||||||
little-plugger (~> 1.1)
|
|
||||||
multi_json (~> 1.10)
|
|
||||||
lograge (0.10.0)
|
lograge (0.10.0)
|
||||||
actionpack (>= 4)
|
actionpack (>= 4)
|
||||||
activesupport (>= 4)
|
activesupport (>= 4)
|
||||||
|
@ -496,7 +460,6 @@ GEM
|
||||||
mini_mime (1.0.1)
|
mini_mime (1.0.1)
|
||||||
mini_portile2 (2.3.0)
|
mini_portile2 (2.3.0)
|
||||||
minitest (5.7.0)
|
minitest (5.7.0)
|
||||||
mousetrap-rails (1.4.6)
|
|
||||||
msgpack (1.2.4)
|
msgpack (1.2.4)
|
||||||
multi_json (1.13.1)
|
multi_json (1.13.1)
|
||||||
multi_xml (0.6.0)
|
multi_xml (0.6.0)
|
||||||
|
@ -579,9 +542,9 @@ GEM
|
||||||
org-ruby (0.9.12)
|
org-ruby (0.9.12)
|
||||||
rubypants (~> 0.2)
|
rubypants (~> 0.2)
|
||||||
orm_adapter (0.5.0)
|
orm_adapter (0.5.0)
|
||||||
os (0.9.6)
|
os (1.0.0)
|
||||||
parallel (1.12.1)
|
parallel (1.12.1)
|
||||||
parser (2.5.1.0)
|
parser (2.5.1.2)
|
||||||
ast (~> 2.4.0)
|
ast (~> 2.4.0)
|
||||||
parslet (1.8.2)
|
parslet (1.8.2)
|
||||||
peek (1.0.1)
|
peek (1.0.1)
|
||||||
|
@ -609,7 +572,6 @@ GEM
|
||||||
pg (0.18.4)
|
pg (0.18.4)
|
||||||
po_to_json (1.0.1)
|
po_to_json (1.0.1)
|
||||||
json (>= 1.6.0)
|
json (>= 1.6.0)
|
||||||
posix-spawn (0.3.13)
|
|
||||||
powerpack (0.1.1)
|
powerpack (0.1.1)
|
||||||
premailer (1.10.4)
|
premailer (1.10.4)
|
||||||
addressable
|
addressable
|
||||||
|
@ -633,6 +595,10 @@ GEM
|
||||||
pry-rails (0.3.6)
|
pry-rails (0.3.6)
|
||||||
pry (>= 0.10.4)
|
pry (>= 0.10.4)
|
||||||
public_suffix (3.0.3)
|
public_suffix (3.0.3)
|
||||||
|
puma (3.12.0)
|
||||||
|
puma_worker_killer (0.1.0)
|
||||||
|
get_process_mem (~> 0.2)
|
||||||
|
puma (>= 2.7, < 4)
|
||||||
pyu-ruby-sasl (0.0.3.3)
|
pyu-ruby-sasl (0.0.3.3)
|
||||||
rack (2.0.5)
|
rack (2.0.5)
|
||||||
rack-accept (0.4.5)
|
rack-accept (0.4.5)
|
||||||
|
@ -805,7 +771,7 @@ GEM
|
||||||
rubyzip (1.2.2)
|
rubyzip (1.2.2)
|
||||||
rufus-scheduler (3.4.0)
|
rufus-scheduler (3.4.0)
|
||||||
et-orbi (~> 1.0)
|
et-orbi (~> 1.0)
|
||||||
rugged (0.27.4)
|
rugged (0.27.5)
|
||||||
safe_yaml (1.0.4)
|
safe_yaml (1.0.4)
|
||||||
sanitize (4.6.6)
|
sanitize (4.6.6)
|
||||||
crass (~> 1.0.2)
|
crass (~> 1.0.2)
|
||||||
|
@ -851,7 +817,7 @@ GEM
|
||||||
sidekiq-cron (0.6.0)
|
sidekiq-cron (0.6.0)
|
||||||
rufus-scheduler (>= 3.3.0)
|
rufus-scheduler (>= 3.3.0)
|
||||||
sidekiq (>= 4.2.1)
|
sidekiq (>= 4.2.1)
|
||||||
signet (0.8.1)
|
signet (0.11.0)
|
||||||
addressable (~> 2.3)
|
addressable (~> 2.3)
|
||||||
faraday (~> 0.9)
|
faraday (~> 0.9)
|
||||||
jwt (>= 1.5, < 3.0)
|
jwt (>= 1.5, < 3.0)
|
||||||
|
@ -884,7 +850,6 @@ GEM
|
||||||
state_machines-activerecord (0.5.1)
|
state_machines-activerecord (0.5.1)
|
||||||
activerecord (>= 4.1, < 6.0)
|
activerecord (>= 4.1, < 6.0)
|
||||||
state_machines-activemodel (>= 0.5.0)
|
state_machines-activemodel (>= 0.5.0)
|
||||||
stringex (2.8.4)
|
|
||||||
sys-filesystem (1.1.6)
|
sys-filesystem (1.1.6)
|
||||||
ffi
|
ffi
|
||||||
sysexits (1.2.0)
|
sysexits (1.2.0)
|
||||||
|
@ -977,7 +942,6 @@ DEPENDENCIES
|
||||||
asana (~> 0.6.0)
|
asana (~> 0.6.0)
|
||||||
asciidoctor (~> 1.5.6)
|
asciidoctor (~> 1.5.6)
|
||||||
asciidoctor-plantuml (= 0.0.8)
|
asciidoctor-plantuml (= 0.0.8)
|
||||||
asset_sync (~> 2.4)
|
|
||||||
attr_encrypted (~> 3.1.0)
|
attr_encrypted (~> 3.1.0)
|
||||||
awesome_print
|
awesome_print
|
||||||
babosa (~> 1.0.2)
|
babosa (~> 1.0.2)
|
||||||
|
@ -1015,6 +979,7 @@ DEPENDENCIES
|
||||||
ed25519 (~> 1.2)
|
ed25519 (~> 1.2)
|
||||||
email_reply_trimmer (~> 0.1)
|
email_reply_trimmer (~> 0.1)
|
||||||
email_spec (~> 2.2.0)
|
email_spec (~> 2.2.0)
|
||||||
|
escape_utils (~> 1.1)
|
||||||
factory_bot_rails (~> 4.8.2)
|
factory_bot_rails (~> 4.8.2)
|
||||||
faraday (~> 0.12)
|
faraday (~> 0.12)
|
||||||
fast_blank
|
fast_blank
|
||||||
|
@ -1022,6 +987,7 @@ DEPENDENCIES
|
||||||
flipper (~> 0.13.0)
|
flipper (~> 0.13.0)
|
||||||
flipper-active_record (~> 0.13.0)
|
flipper-active_record (~> 0.13.0)
|
||||||
flipper-active_support_cache_store (~> 0.13.0)
|
flipper-active_support_cache_store (~> 0.13.0)
|
||||||
|
flowdock (~> 0.7)
|
||||||
fog-aliyun (~> 0.2.0)
|
fog-aliyun (~> 0.2.0)
|
||||||
fog-aws (~> 2.0.1)
|
fog-aws (~> 2.0.1)
|
||||||
fog-core (~> 1.44)
|
fog-core (~> 1.44)
|
||||||
|
@ -1036,18 +1002,15 @@ DEPENDENCIES
|
||||||
gettext (~> 3.2.2)
|
gettext (~> 3.2.2)
|
||||||
gettext_i18n_rails (~> 1.8.0)
|
gettext_i18n_rails (~> 1.8.0)
|
||||||
gettext_i18n_rails_js (~> 1.3)
|
gettext_i18n_rails_js (~> 1.3)
|
||||||
gitaly-proto (~> 0.118.1)
|
gitaly-proto (~> 0.123.0)
|
||||||
github-linguist (~> 5.3.3)
|
|
||||||
github-markup (~> 1.7.0)
|
github-markup (~> 1.7.0)
|
||||||
gitlab-flowdock-git-hook (~> 1.0.1)
|
|
||||||
gitlab-gollum-lib (~> 4.2)
|
|
||||||
gitlab-markup (~> 1.6.4)
|
gitlab-markup (~> 1.6.4)
|
||||||
gitlab-sidekiq-fetcher
|
gitlab-sidekiq-fetcher
|
||||||
gitlab-styles (~> 2.4)
|
gitlab-styles (~> 2.4)
|
||||||
gitlab_omniauth-ldap (~> 2.0.4)
|
gitlab_omniauth-ldap (~> 2.0.4)
|
||||||
gon (~> 6.2)
|
gon (~> 6.2)
|
||||||
google-api-client (~> 0.23)
|
google-api-client (~> 0.23)
|
||||||
google-protobuf (= 3.5.1)
|
google-protobuf (~> 3.6)
|
||||||
gpgme
|
gpgme
|
||||||
grape (~> 1.1)
|
grape (~> 1.1)
|
||||||
grape-entity (~> 0.7.1)
|
grape-entity (~> 0.7.1)
|
||||||
|
@ -1055,7 +1018,7 @@ DEPENDENCIES
|
||||||
grape_logging (~> 1.7)
|
grape_logging (~> 1.7)
|
||||||
graphiql-rails (~> 1.4.10)
|
graphiql-rails (~> 1.4.10)
|
||||||
graphql (~> 1.8.0)
|
graphql (~> 1.8.0)
|
||||||
grpc (~> 1.11.0)
|
grpc (~> 1.15.0)
|
||||||
haml_lint (~> 0.26.0)
|
haml_lint (~> 0.26.0)
|
||||||
hamlit (~> 2.8.8)
|
hamlit (~> 2.8.8)
|
||||||
hangouts-chat (~> 0.0.5)
|
hangouts-chat (~> 0.0.5)
|
||||||
|
@ -1084,7 +1047,6 @@ DEPENDENCIES
|
||||||
method_source (~> 0.8)
|
method_source (~> 0.8)
|
||||||
mini_magick
|
mini_magick
|
||||||
minitest (~> 5.7.0)
|
minitest (~> 5.7.0)
|
||||||
mousetrap-rails (~> 1.4.6)
|
|
||||||
mysql2 (~> 0.4.10)
|
mysql2 (~> 0.4.10)
|
||||||
net-ldap
|
net-ldap
|
||||||
net-ssh (~> 5.0)
|
net-ssh (~> 5.0)
|
||||||
|
@ -1118,6 +1080,8 @@ DEPENDENCIES
|
||||||
prometheus-client-mmap (~> 0.9.4)
|
prometheus-client-mmap (~> 0.9.4)
|
||||||
pry-byebug (~> 3.4.1)
|
pry-byebug (~> 3.4.1)
|
||||||
pry-rails (~> 0.3.4)
|
pry-rails (~> 0.3.4)
|
||||||
|
puma (~> 3.12)
|
||||||
|
puma_worker_killer
|
||||||
rack-attack (~> 4.4.1)
|
rack-attack (~> 4.4.1)
|
||||||
rack-cors (~> 1.0.0)
|
rack-cors (~> 1.0.0)
|
||||||
rack-oauth2 (~> 1.2.1)
|
rack-oauth2 (~> 1.2.1)
|
||||||
|
@ -1196,4 +1160,4 @@ DEPENDENCIES
|
||||||
wikicloth (= 0.8.1)
|
wikicloth (= 0.8.1)
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
1.16.4
|
1.17.1
|
||||||
|
|
|
@ -208,6 +208,7 @@ the stable branch are:
|
||||||
* Fixes or improvements to automated QA scenarios
|
* Fixes or improvements to automated QA scenarios
|
||||||
* [Documentation updates](https://docs.gitlab.com/ee/development/documentation/workflow.html#documentation-shipped-late) for changes in the same release
|
* [Documentation updates](https://docs.gitlab.com/ee/development/documentation/workflow.html#documentation-shipped-late) for changes in the same release
|
||||||
* New or updated translations (as long as they do not touch application code)
|
* New or updated translations (as long as they do not touch application code)
|
||||||
|
* Changes that are behind a feature flag and have the ~"feature flag" label
|
||||||
|
|
||||||
During the feature freeze all merge requests that are meant to go into the
|
During the feature freeze all merge requests that are meant to go into the
|
||||||
upcoming release should have the correct milestone assigned _and_ the
|
upcoming release should have the correct milestone assigned _and_ the
|
||||||
|
|
|
@ -83,7 +83,7 @@ Instructions on how to start GitLab and how to run the tests can be found in the
|
||||||
GitLab is a Ruby on Rails application that runs on the following software:
|
GitLab is a Ruby on Rails application that runs on the following software:
|
||||||
|
|
||||||
- Ubuntu/Debian/CentOS/RHEL/OpenSUSE
|
- Ubuntu/Debian/CentOS/RHEL/OpenSUSE
|
||||||
- Ruby (MRI) 2.3
|
- Ruby (MRI) 2.4
|
||||||
- Git 2.8.4+
|
- Git 2.8.4+
|
||||||
- Redis 2.8+
|
- Redis 2.8+
|
||||||
- PostgreSQL (preferred) or MySQL
|
- PostgreSQL (preferred) or MySQL
|
||||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
11.4.9
|
11.5.3
|
||||||
|
|
BIN
app/assets/images/cluster_app_logos/knative.png
Normal file
BIN
app/assets/images/cluster_app_logos/knative.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
|
@ -1,8 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 14">
|
|
||||||
<g fill="#d6d7d9">
|
|
||||||
<path d="M8.7 0L5.3.3l3.2 6.8-3.2 6.6 3.5.3L12 6.9z"/>
|
|
||||||
<ellipse cx="1.7" cy="11.1" rx="1.7" ry="1.7"/>
|
|
||||||
<ellipse cx="1.7" cy="5.6" rx="1.7" ry="1.7"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 312 B |
|
@ -6,10 +6,12 @@ import Pager from './pager';
|
||||||
import { localTimeAgo } from './lib/utils/datetime_utility';
|
import { localTimeAgo } from './lib/utils/datetime_utility';
|
||||||
|
|
||||||
export default class Activities {
|
export default class Activities {
|
||||||
constructor() {
|
constructor(container = '') {
|
||||||
Pager.init(20, true, false, data => data, this.updateTooltips);
|
this.container = container;
|
||||||
|
|
||||||
$('.event-filter-link').on('click', (e) => {
|
Pager.init(20, true, false, data => data, this.updateTooltips, this.container);
|
||||||
|
|
||||||
|
$('.event-filter-link').on('click', e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.toggleFilter(e.currentTarget);
|
this.toggleFilter(e.currentTarget);
|
||||||
this.reloadActivities();
|
this.reloadActivities();
|
||||||
|
@ -22,7 +24,7 @@ export default class Activities {
|
||||||
|
|
||||||
reloadActivities() {
|
reloadActivities() {
|
||||||
$('.content_list').html('');
|
$('.content_list').html('');
|
||||||
Pager.init(20, true, false, data => data, this.updateTooltips);
|
Pager.init(20, true, false, data => data, this.updateTooltips, this.container);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleFilter(sender) {
|
toggleFilter(sender) {
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
<script>
|
<script>
|
||||||
import Icon from '~/vue_shared/components/icon.vue';
|
import Icon from '~/vue_shared/components/icon.vue';
|
||||||
import Tooltip from '~/vue_shared/directives/tooltip';
|
import Tooltip from '~/vue_shared/directives/tooltip';
|
||||||
|
import { GlLoadingIcon } from '@gitlab-org/gitlab-ui';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Badge',
|
name: 'Badge',
|
||||||
components: {
|
components: {
|
||||||
Icon,
|
Icon,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
|
GlLoadingIcon,
|
||||||
},
|
},
|
||||||
directives: {
|
directives: {
|
||||||
Tooltip,
|
Tooltip,
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { mapActions, mapState } from 'vuex';
|
||||||
import createFlash from '~/flash';
|
import createFlash from '~/flash';
|
||||||
import { s__, sprintf } from '~/locale';
|
import { s__, sprintf } from '~/locale';
|
||||||
import LoadingButton from '~/vue_shared/components/loading_button.vue';
|
import LoadingButton from '~/vue_shared/components/loading_button.vue';
|
||||||
|
import { GlLoadingIcon } from '@gitlab-org/gitlab-ui';
|
||||||
import createEmptyBadge from '../empty_badge';
|
import createEmptyBadge from '../empty_badge';
|
||||||
import Badge from './badge.vue';
|
import Badge from './badge.vue';
|
||||||
|
|
||||||
|
@ -14,6 +15,7 @@ export default {
|
||||||
components: {
|
components: {
|
||||||
Badge,
|
Badge,
|
||||||
LoadingButton,
|
LoadingButton,
|
||||||
|
GlLoadingIcon,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
isEditing: {
|
isEditing: {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from 'vuex';
|
import { mapState } from 'vuex';
|
||||||
|
import { GlLoadingIcon } from '@gitlab-org/gitlab-ui';
|
||||||
import BadgeListRow from './badge_list_row.vue';
|
import BadgeListRow from './badge_list_row.vue';
|
||||||
import { GROUP_BADGE } from '../constants';
|
import { GROUP_BADGE } from '../constants';
|
||||||
|
|
||||||
|
@ -7,6 +8,7 @@ export default {
|
||||||
name: 'BadgeList',
|
name: 'BadgeList',
|
||||||
components: {
|
components: {
|
||||||
BadgeListRow,
|
BadgeListRow,
|
||||||
|
GlLoadingIcon,
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['badges', 'isLoading', 'kind']),
|
...mapState(['badges', 'isLoading', 'kind']),
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
import { mapActions, mapState } from 'vuex';
|
import { mapActions, mapState } from 'vuex';
|
||||||
import { s__ } from '~/locale';
|
import { s__ } from '~/locale';
|
||||||
import Icon from '~/vue_shared/components/icon.vue';
|
import Icon from '~/vue_shared/components/icon.vue';
|
||||||
|
import { GlLoadingIcon } from '@gitlab-org/gitlab-ui';
|
||||||
import { PROJECT_BADGE } from '../constants';
|
import { PROJECT_BADGE } from '../constants';
|
||||||
import Badge from './badge.vue';
|
import Badge from './badge.vue';
|
||||||
|
|
||||||
|
@ -10,6 +11,7 @@ export default {
|
||||||
components: {
|
components: {
|
||||||
Badge,
|
Badge,
|
||||||
Icon,
|
Icon,
|
||||||
|
GlLoadingIcon,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
badge: {
|
badge: {
|
||||||
|
|
|
@ -51,7 +51,7 @@ export default function initCopyToClipboard() {
|
||||||
* the last minute to deconstruct this JSON hash and set the `text/plain` and `text/x-gfm` copy
|
* the last minute to deconstruct this JSON hash and set the `text/plain` and `text/x-gfm` copy
|
||||||
* data types to the intended values.
|
* data types to the intended values.
|
||||||
*/
|
*/
|
||||||
$(document).on('copy', 'body > textarea[readonly]', (e) => {
|
$(document).on('copy', 'body > textarea[readonly]', e => {
|
||||||
const { clipboardData } = e.originalEvent;
|
const { clipboardData } = e.originalEvent;
|
||||||
if (!clipboardData) return;
|
if (!clipboardData) return;
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,9 @@ import $ from 'jquery';
|
||||||
|
|
||||||
$(() => {
|
$(() => {
|
||||||
$('body').on('click', '.js-details-target', function target() {
|
$('body').on('click', '.js-details-target', function target() {
|
||||||
$(this).closest('.js-details-container').toggleClass('open');
|
$(this)
|
||||||
|
.closest('.js-details-container')
|
||||||
|
.toggleClass('open');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Show details content. Hides link after click.
|
// Show details content. Hides link after click.
|
||||||
|
@ -13,7 +15,9 @@ $(() => {
|
||||||
//
|
//
|
||||||
$('body').on('click', '.js-details-expand', function expand(e) {
|
$('body').on('click', '.js-details-expand', function expand(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
$(this).next('.js-details-content').removeClass('hide');
|
$(this)
|
||||||
|
.next('.js-details-content')
|
||||||
|
.removeClass('hide');
|
||||||
$(this).hide();
|
$(this).hide();
|
||||||
|
|
||||||
const truncatedItem = $(this).siblings('.js-details-short');
|
const truncatedItem = $(this).siblings('.js-details-short');
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* eslint-disable object-shorthand, no-unused-vars, no-use-before-define, max-len, no-restricted-syntax, guard-for-in, no-continue */
|
/* eslint-disable object-shorthand, no-unused-vars, no-use-before-define, no-restricted-syntax, guard-for-in, no-continue */
|
||||||
|
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import _ from 'underscore';
|
import _ from 'underscore';
|
||||||
|
@ -34,7 +34,7 @@ const gfmRules = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
AutolinkFilter: {
|
AutolinkFilter: {
|
||||||
'a'(el, text) {
|
a(el, text) {
|
||||||
// Fallback on the regular MarkdownFilter's `a` handler.
|
// Fallback on the regular MarkdownFilter's `a` handler.
|
||||||
if (text !== el.getAttribute('href')) return false;
|
if (text !== el.getAttribute('href')) return false;
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ const gfmRules = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ImageLazyLoadFilter: {
|
ImageLazyLoadFilter: {
|
||||||
'img'(el, text) {
|
img(el, text) {
|
||||||
return `![${el.getAttribute('alt')}](${el.getAttribute('src')})`;
|
return `![${el.getAttribute('alt')}](${el.getAttribute('src')})`;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -71,7 +71,7 @@ const gfmRules = {
|
||||||
|
|
||||||
return CopyAsGFM.nodeToGFM(videoEl);
|
return CopyAsGFM.nodeToGFM(videoEl);
|
||||||
},
|
},
|
||||||
'video'(el) {
|
video(el) {
|
||||||
return `![${el.dataset.title}](${el.getAttribute('src')})`;
|
return `![${el.dataset.title}](${el.getAttribute('src')})`;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -118,11 +118,14 @@ const gfmRules = {
|
||||||
'a[name]:not([href]):empty'(el) {
|
'a[name]:not([href]):empty'(el) {
|
||||||
return el.outerHTML;
|
return el.outerHTML;
|
||||||
},
|
},
|
||||||
'dl'(el, text) {
|
dl(el, text) {
|
||||||
let lines = text.replace(/\n\n/g, '\n').trim().split('\n');
|
let lines = text
|
||||||
|
.replace(/\n\n/g, '\n')
|
||||||
|
.trim()
|
||||||
|
.split('\n');
|
||||||
// Add two spaces to the front of subsequent list items lines,
|
// Add two spaces to the front of subsequent list items lines,
|
||||||
// or leave the line entirely blank.
|
// or leave the line entirely blank.
|
||||||
lines = lines.map((l) => {
|
lines = lines.map(l => {
|
||||||
const line = l.trim();
|
const line = l.trim();
|
||||||
if (line.length === 0) return '';
|
if (line.length === 0) return '';
|
||||||
|
|
||||||
|
@ -151,12 +154,15 @@ const gfmRules = {
|
||||||
|
|
||||||
// Prefixes lines with 4 spaces if the code contains triple backticks
|
// Prefixes lines with 4 spaces if the code contains triple backticks
|
||||||
if (lang === '' && text.match(/^```/gm)) {
|
if (lang === '' && text.match(/^```/gm)) {
|
||||||
return text.split('\n').map((l) => {
|
return text
|
||||||
|
.split('\n')
|
||||||
|
.map(l => {
|
||||||
const line = l.trim();
|
const line = l.trim();
|
||||||
if (line.length === 0) return '';
|
if (line.length === 0) return '';
|
||||||
|
|
||||||
return ` ${line}`;
|
return ` ${line}`;
|
||||||
}).join('\n');
|
})
|
||||||
|
.join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
return `\`\`\`${lang}\n${text}\n\`\`\``;
|
return `\`\`\`${lang}\n${text}\n\`\`\``;
|
||||||
|
@ -167,11 +173,11 @@ const gfmRules = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
MarkdownFilter: {
|
MarkdownFilter: {
|
||||||
'br'(el) {
|
br(el) {
|
||||||
// Two spaces at the end of a line are turned into a BR
|
// Two spaces at the end of a line are turned into a BR
|
||||||
return ' ';
|
return ' ';
|
||||||
},
|
},
|
||||||
'code'(el, text) {
|
code(el, text) {
|
||||||
let backtickCount = 1;
|
let backtickCount = 1;
|
||||||
const backtickMatch = text.match(/`+/);
|
const backtickMatch = text.match(/`+/);
|
||||||
if (backtickMatch) {
|
if (backtickMatch) {
|
||||||
|
@ -183,27 +189,31 @@ const gfmRules = {
|
||||||
|
|
||||||
return backticks + spaceOrNoSpace + text.trim() + spaceOrNoSpace + backticks;
|
return backticks + spaceOrNoSpace + text.trim() + spaceOrNoSpace + backticks;
|
||||||
},
|
},
|
||||||
'blockquote'(el, text) {
|
blockquote(el, text) {
|
||||||
return text.trim().split('\n').map(s => `> ${s}`.trim()).join('\n');
|
return text
|
||||||
|
.trim()
|
||||||
|
.split('\n')
|
||||||
|
.map(s => `> ${s}`.trim())
|
||||||
|
.join('\n');
|
||||||
},
|
},
|
||||||
'img'(el) {
|
img(el) {
|
||||||
const imageSrc = el.src;
|
const imageSrc = el.src;
|
||||||
const imageUrl = imageSrc && imageSrc !== placeholderImage ? imageSrc : (el.dataset.src || '');
|
const imageUrl = imageSrc && imageSrc !== placeholderImage ? imageSrc : el.dataset.src || '';
|
||||||
return `![${el.getAttribute('alt')}](${imageUrl})`;
|
return `![${el.getAttribute('alt')}](${imageUrl})`;
|
||||||
},
|
},
|
||||||
'a.anchor'(el, text) {
|
'a.anchor'(el, text) {
|
||||||
// Don't render a Markdown link for the anchor link inside a heading
|
// Don't render a Markdown link for the anchor link inside a heading
|
||||||
return text;
|
return text;
|
||||||
},
|
},
|
||||||
'a'(el, text) {
|
a(el, text) {
|
||||||
return `[${text}](${el.getAttribute('href')})`;
|
return `[${text}](${el.getAttribute('href')})`;
|
||||||
},
|
},
|
||||||
'li'(el, text) {
|
li(el, text) {
|
||||||
const lines = text.trim().split('\n');
|
const lines = text.trim().split('\n');
|
||||||
const firstLine = `- ${lines.shift()}`;
|
const firstLine = `- ${lines.shift()}`;
|
||||||
// Add four spaces to the front of subsequent list items lines,
|
// Add four spaces to the front of subsequent list items lines,
|
||||||
// or leave the line entirely blank.
|
// or leave the line entirely blank.
|
||||||
const nextLines = lines.map((s) => {
|
const nextLines = lines.map(s => {
|
||||||
if (s.trim().length === 0) return '';
|
if (s.trim().length === 0) return '';
|
||||||
|
|
||||||
return ` ${s}`;
|
return ` ${s}`;
|
||||||
|
@ -211,49 +221,49 @@ const gfmRules = {
|
||||||
|
|
||||||
return `${firstLine}\n${nextLines.join('\n')}`;
|
return `${firstLine}\n${nextLines.join('\n')}`;
|
||||||
},
|
},
|
||||||
'ul'(el, text) {
|
ul(el, text) {
|
||||||
return text;
|
return text;
|
||||||
},
|
},
|
||||||
'ol'(el, text) {
|
ol(el, text) {
|
||||||
// LIs get a `- ` prefix by default, which we replace by `1. ` for ordered lists.
|
// LIs get a `- ` prefix by default, which we replace by `1. ` for ordered lists.
|
||||||
return text.replace(/^- /mg, '1. ');
|
return text.replace(/^- /gm, '1. ');
|
||||||
},
|
},
|
||||||
'h1'(el, text) {
|
h1(el, text) {
|
||||||
return `# ${text.trim()}\n`;
|
return `# ${text.trim()}\n`;
|
||||||
},
|
},
|
||||||
'h2'(el, text) {
|
h2(el, text) {
|
||||||
return `## ${text.trim()}\n`;
|
return `## ${text.trim()}\n`;
|
||||||
},
|
},
|
||||||
'h3'(el, text) {
|
h3(el, text) {
|
||||||
return `### ${text.trim()}\n`;
|
return `### ${text.trim()}\n`;
|
||||||
},
|
},
|
||||||
'h4'(el, text) {
|
h4(el, text) {
|
||||||
return `#### ${text.trim()}\n`;
|
return `#### ${text.trim()}\n`;
|
||||||
},
|
},
|
||||||
'h5'(el, text) {
|
h5(el, text) {
|
||||||
return `##### ${text.trim()}\n`;
|
return `##### ${text.trim()}\n`;
|
||||||
},
|
},
|
||||||
'h6'(el, text) {
|
h6(el, text) {
|
||||||
return `###### ${text.trim()}\n`;
|
return `###### ${text.trim()}\n`;
|
||||||
},
|
},
|
||||||
'strong'(el, text) {
|
strong(el, text) {
|
||||||
return `**${text}**`;
|
return `**${text}**`;
|
||||||
},
|
},
|
||||||
'em'(el, text) {
|
em(el, text) {
|
||||||
return `_${text}_`;
|
return `_${text}_`;
|
||||||
},
|
},
|
||||||
'del'(el, text) {
|
del(el, text) {
|
||||||
return `~~${text}~~`;
|
return `~~${text}~~`;
|
||||||
},
|
},
|
||||||
'hr'(el) {
|
hr(el) {
|
||||||
// extra leading \n is to ensure that there is a blank line between
|
// extra leading \n is to ensure that there is a blank line between
|
||||||
// a list followed by an hr, otherwise this breaks old redcarpet rendering
|
// a list followed by an hr, otherwise this breaks old redcarpet rendering
|
||||||
return '\n-----\n';
|
return '\n-----\n';
|
||||||
},
|
},
|
||||||
'p'(el, text) {
|
p(el, text) {
|
||||||
return `${text.trim()}\n`;
|
return `${text.trim()}\n`;
|
||||||
},
|
},
|
||||||
'table'(el) {
|
table(el) {
|
||||||
const theadEl = el.querySelector('thead');
|
const theadEl = el.querySelector('thead');
|
||||||
const tbodyEl = el.querySelector('tbody');
|
const tbodyEl = el.querySelector('tbody');
|
||||||
if (!theadEl || !tbodyEl) return false;
|
if (!theadEl || !tbodyEl) return false;
|
||||||
|
@ -263,8 +273,8 @@ const gfmRules = {
|
||||||
|
|
||||||
return [theadText, tbodyText].join('\n');
|
return [theadText, tbodyText].join('\n');
|
||||||
},
|
},
|
||||||
'thead'(el, text) {
|
thead(el, text) {
|
||||||
const cells = _.map(el.querySelectorAll('th'), (cell) => {
|
const cells = _.map(el.querySelectorAll('th'), cell => {
|
||||||
let chars = CopyAsGFM.nodeToGFM(cell).length + 2;
|
let chars = CopyAsGFM.nodeToGFM(cell).length + 2;
|
||||||
|
|
||||||
let before = '';
|
let before = '';
|
||||||
|
@ -296,7 +306,7 @@ const gfmRules = {
|
||||||
|
|
||||||
return [text, separatorRow].join('\n');
|
return [text, separatorRow].join('\n');
|
||||||
},
|
},
|
||||||
'tr'(el) {
|
tr(el) {
|
||||||
const cellEls = el.querySelectorAll('td, th');
|
const cellEls = el.querySelectorAll('td, th');
|
||||||
if (cellEls.length === 0) return false;
|
if (cellEls.length === 0) return false;
|
||||||
|
|
||||||
|
@ -315,8 +325,12 @@ export class CopyAsGFM {
|
||||||
const isIOS = /\b(iPad|iPhone|iPod)(?=;)/.test(userAgent);
|
const isIOS = /\b(iPad|iPhone|iPod)(?=;)/.test(userAgent);
|
||||||
if (isIOS) return;
|
if (isIOS) return;
|
||||||
|
|
||||||
$(document).on('copy', '.md, .wiki', (e) => { CopyAsGFM.copyAsGFM(e, CopyAsGFM.transformGFMSelection); });
|
$(document).on('copy', '.md, .wiki', e => {
|
||||||
$(document).on('copy', 'pre.code.highlight, .diff-content .line_content', (e) => { CopyAsGFM.copyAsGFM(e, CopyAsGFM.transformCodeSelection); });
|
CopyAsGFM.copyAsGFM(e, CopyAsGFM.transformGFMSelection);
|
||||||
|
});
|
||||||
|
$(document).on('copy', 'pre.code.highlight, .diff-content .line_content', e => {
|
||||||
|
CopyAsGFM.copyAsGFM(e, CopyAsGFM.transformCodeSelection);
|
||||||
|
});
|
||||||
$(document).on('paste', '.js-gfm-input', CopyAsGFM.pasteGFM);
|
$(document).on('paste', '.js-gfm-input', CopyAsGFM.pasteGFM);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -356,7 +370,7 @@ export class CopyAsGFM {
|
||||||
// This will break down when the actual code block contains an uneven
|
// This will break down when the actual code block contains an uneven
|
||||||
// number of backticks, but this is a rare edge case.
|
// number of backticks, but this is a rare edge case.
|
||||||
const backtickMatch = textBefore.match(/`/g);
|
const backtickMatch = textBefore.match(/`/g);
|
||||||
const insideCodeBlock = backtickMatch && (backtickMatch.length % 2) === 1;
|
const insideCodeBlock = backtickMatch && backtickMatch.length % 2 === 1;
|
||||||
|
|
||||||
if (insideCodeBlock) {
|
if (insideCodeBlock) {
|
||||||
return text;
|
return text;
|
||||||
|
@ -393,7 +407,9 @@ export class CopyAsGFM {
|
||||||
let lineSelector = '.line';
|
let lineSelector = '.line';
|
||||||
|
|
||||||
if (target) {
|
if (target) {
|
||||||
const lineClass = ['left-side', 'right-side'].filter(name => target.classList.contains(name))[0];
|
const lineClass = ['left-side', 'right-side'].filter(name =>
|
||||||
|
target.classList.contains(name),
|
||||||
|
)[0];
|
||||||
if (lineClass) {
|
if (lineClass) {
|
||||||
lineSelector = `.line_content.${lineClass} ${lineSelector}`;
|
lineSelector = `.line_content.${lineClass} ${lineSelector}`;
|
||||||
}
|
}
|
||||||
|
@ -436,7 +452,8 @@ export class CopyAsGFM {
|
||||||
return node.textContent;
|
return node.textContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
const respectWhitespace = respectWhitespaceParam || (node.nodeName === 'PRE' || node.nodeName === 'CODE');
|
const respectWhitespace =
|
||||||
|
respectWhitespaceParam || (node.nodeName === 'PRE' || node.nodeName === 'CODE');
|
||||||
|
|
||||||
const text = this.innerGFM(node, respectWhitespace);
|
const text = this.innerGFM(node, respectWhitespace);
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,9 @@ export default function renderMath($els) {
|
||||||
Promise.all([
|
Promise.all([
|
||||||
import(/* webpackChunkName: 'katex' */ 'katex'),
|
import(/* webpackChunkName: 'katex' */ 'katex'),
|
||||||
import(/* webpackChunkName: 'katex' */ 'katex/dist/katex.min.css'),
|
import(/* webpackChunkName: 'katex' */ 'katex/dist/katex.min.css'),
|
||||||
]).then(([katex]) => {
|
])
|
||||||
|
.then(([katex]) => {
|
||||||
renderWithKaTeX($els, katex);
|
renderWithKaTeX($els, katex);
|
||||||
}).catch(() => flash(__('An error occurred while rendering KaTeX')));
|
})
|
||||||
|
.catch(() => flash(__('An error occurred while rendering KaTeX')));
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,8 @@ import flash from '~/flash';
|
||||||
export default function renderMermaid($els) {
|
export default function renderMermaid($els) {
|
||||||
if (!$els.length) return;
|
if (!$els.length) return;
|
||||||
|
|
||||||
import(/* webpackChunkName: 'mermaid' */ 'blackst0ne-mermaid').then((mermaid) => {
|
import(/* webpackChunkName: 'mermaid' */ 'mermaid')
|
||||||
|
.then(mermaid => {
|
||||||
mermaid.initialize({
|
mermaid.initialize({
|
||||||
// mermaid core options
|
// mermaid core options
|
||||||
mermaid: {
|
mermaid: {
|
||||||
|
@ -36,7 +37,7 @@ export default function renderMermaid($els) {
|
||||||
// Remove any extra spans added by the backend syntax highlighting.
|
// Remove any extra spans added by the backend syntax highlighting.
|
||||||
Object.assign(el, { textContent: source });
|
Object.assign(el, { textContent: source });
|
||||||
|
|
||||||
mermaid.init(undefined, el, (id) => {
|
mermaid.init(undefined, el, id => {
|
||||||
const svg = document.getElementById(id);
|
const svg = document.getElementById(id);
|
||||||
|
|
||||||
svg.classList.add('mermaid');
|
svg.classList.add('mermaid');
|
||||||
|
@ -54,7 +55,8 @@ export default function renderMermaid($els) {
|
||||||
svg.appendChild(sourceEl);
|
svg.appendChild(sourceEl);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}).catch((err) => {
|
})
|
||||||
|
.catch(err => {
|
||||||
flash(`Can't load mermaid module: ${err}`);
|
flash(`Can't load mermaid module: ${err}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,10 @@ MarkdownPreview.prototype.showPreview = function ($form) {
|
||||||
this.hideReferencedUsers($form);
|
this.hideReferencedUsers($form);
|
||||||
} else {
|
} else {
|
||||||
preview.addClass('md-preview-loading').text('Loading...');
|
preview.addClass('md-preview-loading').text('Loading...');
|
||||||
this.fetchMarkdownPreview(mdText, url, (function (response) {
|
this.fetchMarkdownPreview(
|
||||||
|
mdText,
|
||||||
|
url,
|
||||||
|
function(response) {
|
||||||
var body;
|
var body;
|
||||||
if (response.body.length > 0) {
|
if (response.body.length > 0) {
|
||||||
({ body } = response);
|
({ body } = response);
|
||||||
|
@ -59,7 +62,8 @@ MarkdownPreview.prototype.showPreview = function ($form) {
|
||||||
if (response.references.commands) {
|
if (response.references.commands) {
|
||||||
this.renderReferencedCommands(response.references.commands, $form);
|
this.renderReferencedCommands(response.references.commands, $form);
|
||||||
}
|
}
|
||||||
}).bind(this));
|
}.bind(this),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -68,7 +72,9 @@ MarkdownPreview.prototype.versionedPreviewPath = function (markdownPreviewPath,
|
||||||
return markdownPreviewPath;
|
return markdownPreviewPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${markdownPreviewPath}${markdownPreviewPath.indexOf('?') === -1 ? '?' : '&'}markdown_version=${markdownVersion}`;
|
return `${markdownPreviewPath}${
|
||||||
|
markdownPreviewPath.indexOf('?') === -1 ? '?' : '&'
|
||||||
|
}markdown_version=${markdownVersion}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
MarkdownPreview.prototype.fetchMarkdownPreview = function(text, url, success) {
|
MarkdownPreview.prototype.fetchMarkdownPreview = function(text, url, success) {
|
||||||
|
@ -79,7 +85,8 @@ MarkdownPreview.prototype.fetchMarkdownPreview = function (text, url, success) {
|
||||||
success(this.ajaxCache.response);
|
success(this.ajaxCache.response);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
axios.post(url, {
|
axios
|
||||||
|
.post(url, {
|
||||||
text,
|
text,
|
||||||
})
|
})
|
||||||
.then(({ data }) => {
|
.then(({ data }) => {
|
||||||
|
@ -148,8 +155,14 @@ $(document).on('markdown-preview:show', function (e, $form) {
|
||||||
lastTextareaHeight = lastTextareaPreviewed.height();
|
lastTextareaHeight = lastTextareaPreviewed.height();
|
||||||
|
|
||||||
// toggle tabs
|
// toggle tabs
|
||||||
$form.find(writeButtonSelector).parent().removeClass('active');
|
$form
|
||||||
$form.find(previewButtonSelector).parent().addClass('active');
|
.find(writeButtonSelector)
|
||||||
|
.parent()
|
||||||
|
.removeClass('active');
|
||||||
|
$form
|
||||||
|
.find(previewButtonSelector)
|
||||||
|
.parent()
|
||||||
|
.addClass('active');
|
||||||
|
|
||||||
// toggle content
|
// toggle content
|
||||||
$form.find('.md-write-holder').hide();
|
$form.find('.md-write-holder').hide();
|
||||||
|
@ -169,8 +182,14 @@ $(document).on('markdown-preview:hide', function (e, $form) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// toggle tabs
|
// toggle tabs
|
||||||
$form.find(writeButtonSelector).parent().addClass('active');
|
$form
|
||||||
$form.find(previewButtonSelector).parent().removeClass('active');
|
.find(writeButtonSelector)
|
||||||
|
.parent()
|
||||||
|
.addClass('active');
|
||||||
|
$form
|
||||||
|
.find(previewButtonSelector)
|
||||||
|
.parent()
|
||||||
|
.removeClass('active');
|
||||||
|
|
||||||
// toggle content
|
// toggle content
|
||||||
$form.find('.md-write-holder').show();
|
$form.find('.md-write-holder').show();
|
||||||
|
|
|
@ -28,7 +28,7 @@ function keyCodeIs(e, keyCode) {
|
||||||
return e.keyCode === keyCode;
|
return e.keyCode === keyCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).on('keydown.quick_submit', '.js-quick-submit', (e) => {
|
$(document).on('keydown.quick_submit', '.js-quick-submit', e => {
|
||||||
// Enter
|
// Enter
|
||||||
if (!keyCodeIs(e, 13)) {
|
if (!keyCodeIs(e, 13)) {
|
||||||
return;
|
return;
|
||||||
|
@ -55,16 +55,17 @@ $(document).on('keydown.quick_submit', '.js-quick-submit', (e) => {
|
||||||
|
|
||||||
// If the user tabs to a submit button on a `js-quick-submit` form, display a
|
// If the user tabs to a submit button on a `js-quick-submit` form, display a
|
||||||
// tooltip to let them know they could've used the hotkey
|
// tooltip to let them know they could've used the hotkey
|
||||||
$(document).on('keyup.quick_submit', '.js-quick-submit input[type=submit], .js-quick-submit button[type=submit]', function displayTooltip(e) {
|
$(document).on(
|
||||||
|
'keyup.quick_submit',
|
||||||
|
'.js-quick-submit input[type=submit], .js-quick-submit button[type=submit]',
|
||||||
|
function displayTooltip(e) {
|
||||||
// Tab
|
// Tab
|
||||||
if (!keyCodeIs(e, 9)) {
|
if (!keyCodeIs(e, 9)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const $this = $(this);
|
const $this = $(this);
|
||||||
const title = isMac() ?
|
const title = isMac() ? 'You can also press ⌘-Enter' : 'You can also press Ctrl-Enter';
|
||||||
'You can also press ⌘-Enter' :
|
|
||||||
'You can also press Ctrl-Enter';
|
|
||||||
|
|
||||||
$this.tooltip({
|
$this.tooltip({
|
||||||
container: 'body',
|
container: 'body',
|
||||||
|
@ -74,4 +75,5 @@ $(document).on('keyup.quick_submit', '.js-quick-submit input[type=submit], .js-q
|
||||||
trigger: 'manual',
|
trigger: 'manual',
|
||||||
});
|
});
|
||||||
$this.tooltip('show').one('blur click', () => $this.tooltip('hide'));
|
$this.tooltip('show').one('blur click', () => $this.tooltip('hide'));
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
|
@ -18,7 +18,8 @@ import '../commons/bootstrap';
|
||||||
$.fn.requiresInput = function requiresInput() {
|
$.fn.requiresInput = function requiresInput() {
|
||||||
const $form = $(this);
|
const $form = $(this);
|
||||||
const $button = $('button[type=submit], input[type=submit]', $form);
|
const $button = $('button[type=submit], input[type=submit]', $form);
|
||||||
const fieldSelector = 'input[required=required], select[required=required], textarea[required=required]';
|
const fieldSelector =
|
||||||
|
'input[required=required], select[required=required], textarea[required=required]';
|
||||||
|
|
||||||
function requireInput() {
|
function requireInput() {
|
||||||
// Collect the input values of *all* required fields
|
// Collect the input values of *all* required fields
|
||||||
|
|
|
@ -32,16 +32,18 @@ export default class SecretValues {
|
||||||
|
|
||||||
updateDom(isRevealed) {
|
updateDom(isRevealed) {
|
||||||
const values = this.container.querySelectorAll(this.valueSelector);
|
const values = this.container.querySelectorAll(this.valueSelector);
|
||||||
values.forEach((value) => {
|
values.forEach(value => {
|
||||||
value.classList.toggle('hide', !isRevealed);
|
value.classList.toggle('hide', !isRevealed);
|
||||||
});
|
});
|
||||||
|
|
||||||
const placeholders = this.container.querySelectorAll(this.placeholderSelector);
|
const placeholders = this.container.querySelectorAll(this.placeholderSelector);
|
||||||
placeholders.forEach((placeholder) => {
|
placeholders.forEach(placeholder => {
|
||||||
placeholder.classList.toggle('hide', isRevealed);
|
placeholder.classList.toggle('hide', isRevealed);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.revealButton.textContent = isRevealed ? n__('Hide value', 'Hide values', values.length) : n__('Reveal value', 'Reveal values', values.length);
|
this.revealButton.textContent = isRevealed
|
||||||
|
? n__('Hide value', 'Hide values', values.length)
|
||||||
|
: n__('Reveal value', 'Reveal values', values.length);
|
||||||
this.revealButton.dataset.secretRevealStatus = isRevealed;
|
this.revealButton.dataset.secretRevealStatus = isRevealed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,9 +88,11 @@ export default class Shortcuts {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return axios.get(gon.shortcuts_path, {
|
return axios
|
||||||
|
.get(gon.shortcuts_path, {
|
||||||
responseType: 'text',
|
responseType: 'text',
|
||||||
}).then(({ data }) => {
|
})
|
||||||
|
.then(({ data }) => {
|
||||||
$.globalEval(data);
|
$.globalEval(data);
|
||||||
|
|
||||||
if (location && location.length > 0) {
|
if (location && location.length > 0) {
|
||||||
|
|
|
@ -18,9 +18,7 @@ $(() => {
|
||||||
.toggleClass('fa-chevron-up', toggleState)
|
.toggleClass('fa-chevron-up', toggleState)
|
||||||
.toggleClass('fa-chevron-down', toggleState !== undefined ? !toggleState : undefined);
|
.toggleClass('fa-chevron-down', toggleState !== undefined ? !toggleState : undefined);
|
||||||
|
|
||||||
$container
|
$container.find('.js-toggle-content').toggle(toggleState);
|
||||||
.find('.js-toggle-content')
|
|
||||||
.toggle(toggleState);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$('body').on('click', '.js-toggle-button', function toggleButton(e) {
|
$('body').on('click', '.js-toggle-button', function toggleButton(e) {
|
||||||
|
|
|
@ -18,12 +18,7 @@ export default class Renderer {
|
||||||
this.loader = new STLLoader();
|
this.loader = new STLLoader();
|
||||||
|
|
||||||
this.fov = 45;
|
this.fov = 45;
|
||||||
this.camera = new THREE.PerspectiveCamera(
|
this.camera = new THREE.PerspectiveCamera(this.fov, this.width / this.height, 1, 1000);
|
||||||
this.fov,
|
|
||||||
this.width / this.height,
|
|
||||||
1,
|
|
||||||
1000,
|
|
||||||
);
|
|
||||||
|
|
||||||
this.scene = new THREE.Scene();
|
this.scene = new THREE.Scene();
|
||||||
|
|
||||||
|
@ -35,10 +30,7 @@ export default class Renderer {
|
||||||
this.setupLight();
|
this.setupLight();
|
||||||
|
|
||||||
// Set up OrbitControls
|
// Set up OrbitControls
|
||||||
this.controls = new OrbitControls(
|
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
|
||||||
this.camera,
|
|
||||||
this.renderer.domElement,
|
|
||||||
);
|
|
||||||
this.controls.minDistance = 5;
|
this.controls.minDistance = 5;
|
||||||
this.controls.maxDistance = 30;
|
this.controls.maxDistance = 30;
|
||||||
this.controls.enableKeys = false;
|
this.controls.enableKeys = false;
|
||||||
|
@ -51,47 +43,32 @@ export default class Renderer {
|
||||||
antialias: true,
|
antialias: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.renderer.setClearColor(0xFFFFFF);
|
this.renderer.setClearColor(0xffffff);
|
||||||
this.renderer.setPixelRatio(window.devicePixelRatio);
|
this.renderer.setPixelRatio(window.devicePixelRatio);
|
||||||
this.renderer.setSize(
|
this.renderer.setSize(this.width, this.height);
|
||||||
this.width,
|
|
||||||
this.height,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setupLight() {
|
setupLight() {
|
||||||
// Point light illuminates the object
|
// Point light illuminates the object
|
||||||
const pointLight = new THREE.PointLight(
|
const pointLight = new THREE.PointLight(0xffffff, 2, 0);
|
||||||
0xFFFFFF,
|
|
||||||
2,
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
|
|
||||||
pointLight.castShadow = true;
|
pointLight.castShadow = true;
|
||||||
|
|
||||||
this.camera.add(pointLight);
|
this.camera.add(pointLight);
|
||||||
|
|
||||||
// Ambient light illuminates the scene
|
// Ambient light illuminates the scene
|
||||||
const ambientLight = new THREE.AmbientLight(
|
const ambientLight = new THREE.AmbientLight(0xffffff, 1);
|
||||||
0xFFFFFF,
|
|
||||||
1,
|
|
||||||
);
|
|
||||||
this.scene.add(ambientLight);
|
this.scene.add(ambientLight);
|
||||||
}
|
}
|
||||||
|
|
||||||
setupGrid() {
|
setupGrid() {
|
||||||
this.grid = new THREE.GridHelper(
|
this.grid = new THREE.GridHelper(20, 20, 0x000000, 0x000000);
|
||||||
20,
|
|
||||||
20,
|
|
||||||
0x000000,
|
|
||||||
0x000000,
|
|
||||||
);
|
|
||||||
|
|
||||||
this.scene.add(this.grid);
|
this.scene.add(this.grid);
|
||||||
}
|
}
|
||||||
|
|
||||||
loadFile() {
|
loadFile() {
|
||||||
this.loader.load(this.container.dataset.endpoint, (geo) => {
|
this.loader.load(this.container.dataset.endpoint, geo => {
|
||||||
const obj = new MeshObject(geo);
|
const obj = new MeshObject(geo);
|
||||||
|
|
||||||
this.objects.push(obj);
|
this.objects.push(obj);
|
||||||
|
@ -116,30 +93,23 @@ export default class Renderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
this.renderer.render(
|
this.renderer.render(this.scene, this.camera);
|
||||||
this.scene,
|
|
||||||
this.camera,
|
|
||||||
);
|
|
||||||
|
|
||||||
requestAnimationFrame(this.renderWrapper);
|
requestAnimationFrame(this.renderWrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
changeObjectMaterials(type) {
|
changeObjectMaterials(type) {
|
||||||
this.objects.forEach((obj) => {
|
this.objects.forEach(obj => {
|
||||||
obj.changeMaterial(type);
|
obj.changeMaterial(type);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setDefaultCameraPosition() {
|
setDefaultCameraPosition() {
|
||||||
const obj = this.objects[0];
|
const obj = this.objects[0];
|
||||||
const radius = (obj.geometry.boundingSphere.radius / 1.5);
|
const radius = obj.geometry.boundingSphere.radius / 1.5;
|
||||||
const dist = radius / (Math.sin((this.fov * (Math.PI / 180)) / 2));
|
const dist = radius / Math.sin((this.fov * (Math.PI / 180)) / 2);
|
||||||
|
|
||||||
this.camera.position.set(
|
this.camera.position.set(0, dist + 1, dist);
|
||||||
0,
|
|
||||||
dist + 1,
|
|
||||||
dist,
|
|
||||||
);
|
|
||||||
|
|
||||||
this.camera.lookAt(this.grid);
|
this.camera.lookAt(this.grid);
|
||||||
this.controls.update();
|
this.controls.update();
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
import {
|
import { Matrix4, MeshLambertMaterial, Mesh } from 'three/build/three.module';
|
||||||
Matrix4,
|
|
||||||
MeshLambertMaterial,
|
|
||||||
Mesh,
|
|
||||||
} from 'three/build/three.module';
|
|
||||||
|
|
||||||
const defaultColor = 0xE24329;
|
const defaultColor = 0xe24329;
|
||||||
const materials = {
|
const materials = {
|
||||||
default: new MeshLambertMaterial({
|
default: new MeshLambertMaterial({
|
||||||
color: defaultColor,
|
color: defaultColor,
|
||||||
|
@ -17,10 +13,7 @@ const materials = {
|
||||||
|
|
||||||
export default class MeshObject extends Mesh {
|
export default class MeshObject extends Mesh {
|
||||||
constructor(geo) {
|
constructor(geo) {
|
||||||
super(
|
super(geo, materials.default);
|
||||||
geo,
|
|
||||||
materials.default,
|
|
||||||
);
|
|
||||||
|
|
||||||
this.geometry.computeBoundingSphere();
|
this.geometry.computeBoundingSphere();
|
||||||
|
|
||||||
|
@ -29,13 +22,7 @@ export default class MeshObject extends Mesh {
|
||||||
if (this.geometry.boundingSphere.radius > 4) {
|
if (this.geometry.boundingSphere.radius > 4) {
|
||||||
const scale = 4 / this.geometry.boundingSphere.radius;
|
const scale = 4 / this.geometry.boundingSphere.radius;
|
||||||
|
|
||||||
this.geometry.applyMatrix(
|
this.geometry.applyMatrix(new Matrix4().makeScale(scale, scale, scale));
|
||||||
new Matrix4().makeScale(
|
|
||||||
scale,
|
|
||||||
scale,
|
|
||||||
scale,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
this.geometry.computeBoundingSphere();
|
this.geometry.computeBoundingSphere();
|
||||||
|
|
||||||
this.position.x = -this.geometry.boundingSphere.center.x;
|
this.position.x = -this.geometry.boundingSphere.center.x;
|
||||||
|
|
|
@ -42,7 +42,7 @@ class BalsamiqViewer {
|
||||||
this.initDatabase(loadEvent.target.response);
|
this.initDatabase(loadEvent.target.response);
|
||||||
|
|
||||||
const previews = this.getPreviews();
|
const previews = this.getPreviews();
|
||||||
previews.forEach((preview) => {
|
previews.forEach(preview => {
|
||||||
const renderedPreview = this.renderPreview(preview);
|
const renderedPreview = this.renderPreview(preview);
|
||||||
|
|
||||||
container.appendChild(renderedPreview);
|
container.appendChild(renderedPreview);
|
||||||
|
|
|
@ -45,7 +45,9 @@ export default class BlobFileDropzone {
|
||||||
this.on('addedfile', function() {
|
this.on('addedfile', function() {
|
||||||
toggleLoading(submitButton, submitButtonLoadingIcon, false);
|
toggleLoading(submitButton, submitButtonLoadingIcon, false);
|
||||||
dropzoneMessage.addClass(HIDDEN_CLASS);
|
dropzoneMessage.addClass(HIDDEN_CLASS);
|
||||||
$('.dropzone-alerts').html('').hide();
|
$('.dropzone-alerts')
|
||||||
|
.html('')
|
||||||
|
.hide();
|
||||||
});
|
});
|
||||||
this.on('removedfile', function() {
|
this.on('removedfile', function() {
|
||||||
toggleLoading(submitButton, submitButtonLoadingIcon, false);
|
toggleLoading(submitButton, submitButtonLoadingIcon, false);
|
||||||
|
@ -67,13 +69,17 @@ export default class BlobFileDropzone {
|
||||||
},
|
},
|
||||||
// Override behavior of adding error underneath preview
|
// Override behavior of adding error underneath preview
|
||||||
error: function(file, errorMessage) {
|
error: function(file, errorMessage) {
|
||||||
const stripped = $('<div/>').html(errorMessage).text();
|
const stripped = $('<div/>')
|
||||||
$('.dropzone-alerts').html(`Error uploading file: "${stripped}"`).show();
|
.html(errorMessage)
|
||||||
|
.text();
|
||||||
|
$('.dropzone-alerts')
|
||||||
|
.html(`Error uploading file: "${stripped}"`)
|
||||||
|
.show();
|
||||||
this.removeFile(file);
|
this.removeFile(file);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
submitButton.on('click', (e) => {
|
submitButton.on('click', e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if (dropzone[0].dropzone.getQueuedFiles().length === 0) {
|
if (dropzone[0].dropzone.getQueuedFiles().length === 0) {
|
||||||
|
|
|
@ -2,13 +2,15 @@ import { getLocationHash } from '../lib/utils/url_utility';
|
||||||
|
|
||||||
const lineNumberRe = /^L[0-9]+/;
|
const lineNumberRe = /^L[0-9]+/;
|
||||||
|
|
||||||
const updateLineNumbersOnBlobPermalinks = (linksToUpdate) => {
|
const updateLineNumbersOnBlobPermalinks = linksToUpdate => {
|
||||||
const hash = getLocationHash();
|
const hash = getLocationHash();
|
||||||
if (hash && lineNumberRe.test(hash)) {
|
if (hash && lineNumberRe.test(hash)) {
|
||||||
const hashUrlString = `#${hash}`;
|
const hashUrlString = `#${hash}`;
|
||||||
|
|
||||||
[].concat(Array.prototype.slice.call(linksToUpdate)).forEach((permalinkButton) => {
|
[].concat(Array.prototype.slice.call(linksToUpdate)).forEach(permalinkButton => {
|
||||||
const baseHref = permalinkButton.getAttribute('data-original-href') || (() => {
|
const baseHref =
|
||||||
|
permalinkButton.getAttribute('data-original-href') ||
|
||||||
|
(() => {
|
||||||
const href = permalinkButton.getAttribute('href');
|
const href = permalinkButton.getAttribute('href');
|
||||||
permalinkButton.setAttribute('data-original-href', href);
|
permalinkButton.setAttribute('data-original-href', href);
|
||||||
return href;
|
return href;
|
||||||
|
@ -26,7 +28,7 @@ function BlobLinePermalinkUpdater(blobContentHolder, lineNumberSelector, element
|
||||||
}, 0);
|
}, 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
blobContentHolder.addEventListener('click', (e) => {
|
blobContentHolder.addEventListener('click', e => {
|
||||||
if (e.target.matches(lineNumberSelector)) {
|
if (e.target.matches(lineNumberSelector)) {
|
||||||
updateBlameAndBlobPermalinkCb();
|
updateBlameAndBlobPermalinkCb();
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,15 +45,11 @@ export default class FileTemplateSelector {
|
||||||
}
|
}
|
||||||
|
|
||||||
renderLoading() {
|
renderLoading() {
|
||||||
this.$loadingIcon
|
this.$loadingIcon.addClass('fa-spinner fa-spin').removeClass('fa-chevron-down');
|
||||||
.addClass('fa-spinner fa-spin')
|
|
||||||
.removeClass('fa-chevron-down');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderLoaded() {
|
renderLoaded() {
|
||||||
this.$loadingIcon
|
this.$loadingIcon.addClass('fa-chevron-down').removeClass('fa-spinner fa-spin');
|
||||||
.addClass('fa-chevron-down')
|
|
||||||
.removeClass('fa-spinner fa-spin');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
reportSelection(options) {
|
reportSelection(options) {
|
||||||
|
|
|
@ -40,13 +40,14 @@ export default () => {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
loadFile() {
|
loadFile() {
|
||||||
axios.get(el.dataset.endpoint)
|
axios
|
||||||
|
.get(el.dataset.endpoint)
|
||||||
.then(res => res.data)
|
.then(res => res.data)
|
||||||
.then((data) => {
|
.then(data => {
|
||||||
this.json = data;
|
this.json = data;
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch(e => {
|
||||||
if (e.status !== 200) {
|
if (e.status !== 200) {
|
||||||
this.loadError = true;
|
this.loadError = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ export default class SketchLoader {
|
||||||
return this.getZipFile()
|
return this.getZipFile()
|
||||||
.then(data => JSZip.loadAsync(data))
|
.then(data => JSZip.loadAsync(data))
|
||||||
.then(asyncResult => asyncResult.files['previews/preview.png'].async('uint8array'))
|
.then(asyncResult => asyncResult.files['previews/preview.png'].async('uint8array'))
|
||||||
.then((content) => {
|
.then(content => {
|
||||||
const url = window.URL || window.webkitURL;
|
const url = window.URL || window.webkitURL;
|
||||||
const blob = new Blob([new Uint8Array(content)], {
|
const blob = new Blob([new Uint8Array(content)], {
|
||||||
type: 'image/png',
|
type: 'image/png',
|
||||||
|
|
|
@ -3,8 +3,8 @@ import Renderer from './3d_viewer';
|
||||||
export default () => {
|
export default () => {
|
||||||
const viewer = new Renderer(document.getElementById('js-stl-viewer'));
|
const viewer = new Renderer(document.getElementById('js-stl-viewer'));
|
||||||
|
|
||||||
[].slice.call(document.querySelectorAll('.js-material-changer')).forEach((el) => {
|
[].slice.call(document.querySelectorAll('.js-material-changer')).forEach(el => {
|
||||||
el.addEventListener('click', (e) => {
|
el.addEventListener('click', e => {
|
||||||
const { target } = e;
|
const { target } = e;
|
||||||
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
|
@ -81,14 +81,10 @@ export default class TemplateSelector {
|
||||||
}
|
}
|
||||||
|
|
||||||
startLoadingSpinner() {
|
startLoadingSpinner() {
|
||||||
this.$dropdownIcon
|
this.$dropdownIcon.addClass('fa-spinner fa-spin').removeClass('fa-chevron-down');
|
||||||
.addClass('fa-spinner fa-spin')
|
|
||||||
.removeClass('fa-chevron-down');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stopLoadingSpinner() {
|
stopLoadingSpinner() {
|
||||||
this.$dropdownIcon
|
this.$dropdownIcon.addClass('fa-chevron-down').removeClass('fa-spinner fa-spin');
|
||||||
.addClass('fa-chevron-down')
|
|
||||||
.removeClass('fa-spinner fa-spin');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ export default class BlobLicenseSelector extends FileTemplateSelector {
|
||||||
search: {
|
search: {
|
||||||
fields: ['name'],
|
fields: ['name'],
|
||||||
},
|
},
|
||||||
clicked: (options) => {
|
clicked: options => {
|
||||||
const { e } = options;
|
const { e } = options;
|
||||||
const el = options.$el;
|
const el = options.$el;
|
||||||
const query = options.selectedObj;
|
const query = options.selectedObj;
|
||||||
|
|
|
@ -21,5 +21,4 @@ export default class FileTemplateTypeSelector extends FileTemplateSelector {
|
||||||
text: item => item.name,
|
text: item => item.name,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,9 +22,8 @@ export default class BlobViewer {
|
||||||
const viewer = document.querySelector('.blob-viewer[data-type="rich"]');
|
const viewer = document.querySelector('.blob-viewer[data-type="rich"]');
|
||||||
if (!viewer || !viewer.dataset.richType) return;
|
if (!viewer || !viewer.dataset.richType) return;
|
||||||
|
|
||||||
const initViewer = promise => promise
|
const initViewer = promise =>
|
||||||
.then(module => module.default(viewer))
|
promise.then(module => module.default(viewer)).catch(error => {
|
||||||
.catch((error) => {
|
|
||||||
Flash('Error loading file viewer.');
|
Flash('Error loading file viewer.');
|
||||||
throw error;
|
throw error;
|
||||||
});
|
});
|
||||||
|
@ -79,8 +78,7 @@ export default class BlobViewer {
|
||||||
|
|
||||||
initBindings() {
|
initBindings() {
|
||||||
if (this.switcherBtns.length) {
|
if (this.switcherBtns.length) {
|
||||||
Array.from(this.switcherBtns)
|
Array.from(this.switcherBtns).forEach(el => {
|
||||||
.forEach((el) => {
|
|
||||||
el.addEventListener('click', this.switchViewHandler.bind(this));
|
el.addEventListener('click', this.switchViewHandler.bind(this));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -109,7 +107,10 @@ export default class BlobViewer {
|
||||||
this.copySourceBtn.setAttribute('title', 'Copy source to clipboard');
|
this.copySourceBtn.setAttribute('title', 'Copy source to clipboard');
|
||||||
this.copySourceBtn.classList.remove('disabled');
|
this.copySourceBtn.classList.remove('disabled');
|
||||||
} else if (this.activeViewer === this.simpleViewer) {
|
} else if (this.activeViewer === this.simpleViewer) {
|
||||||
this.copySourceBtn.setAttribute('title', 'Wait for the source to load to copy it to the clipboard');
|
this.copySourceBtn.setAttribute(
|
||||||
|
'title',
|
||||||
|
'Wait for the source to load to copy it to the clipboard',
|
||||||
|
);
|
||||||
this.copySourceBtn.classList.add('disabled');
|
this.copySourceBtn.classList.add('disabled');
|
||||||
} else {
|
} else {
|
||||||
this.copySourceBtn.setAttribute('title', 'Switch to the source to copy it to the clipboard');
|
this.copySourceBtn.setAttribute('title', 'Switch to the source to copy it to the clipboard');
|
||||||
|
@ -147,7 +148,7 @@ export default class BlobViewer {
|
||||||
this.toggleCopyButtonState();
|
this.toggleCopyButtonState();
|
||||||
|
|
||||||
BlobViewer.loadViewer(newViewer)
|
BlobViewer.loadViewer(newViewer)
|
||||||
.then((viewer) => {
|
.then(viewer => {
|
||||||
$(viewer).renderGFM();
|
$(viewer).renderGFM();
|
||||||
|
|
||||||
this.$fileHolder.trigger('highlight:line');
|
this.$fileHolder.trigger('highlight:line');
|
||||||
|
@ -168,8 +169,7 @@ export default class BlobViewer {
|
||||||
|
|
||||||
viewer.setAttribute('data-loading', 'true');
|
viewer.setAttribute('data-loading', 'true');
|
||||||
|
|
||||||
return axios.get(url)
|
return axios.get(url).then(({ data }) => {
|
||||||
.then(({ data }) => {
|
|
||||||
viewer.innerHTML = data.html;
|
viewer.innerHTML = data.html;
|
||||||
viewer.setAttribute('data-loaded', 'true');
|
viewer.setAttribute('data-loaded', 'true');
|
||||||
|
|
||||||
|
|
|
@ -13,11 +13,11 @@ export default () => {
|
||||||
if (editBlobForm.length) {
|
if (editBlobForm.length) {
|
||||||
const urlRoot = editBlobForm.data('relativeUrlRoot');
|
const urlRoot = editBlobForm.data('relativeUrlRoot');
|
||||||
const assetsPath = editBlobForm.data('assetsPrefix');
|
const assetsPath = editBlobForm.data('assetsPrefix');
|
||||||
const blobLanguage = editBlobForm.data('blobLanguage');
|
const filePath = editBlobForm.data('blobFilename');
|
||||||
const currentAction = $('.js-file-title').data('currentAction');
|
const currentAction = $('.js-file-title').data('currentAction');
|
||||||
const projectId = editBlobForm.data('project-id');
|
const projectId = editBlobForm.data('project-id');
|
||||||
|
|
||||||
new EditBlob(`${urlRoot}${assetsPath}`, blobLanguage, currentAction, projectId);
|
new EditBlob(`${urlRoot}${assetsPath}`, filePath, currentAction, projectId);
|
||||||
new NewCommitForm(editBlobForm);
|
new NewCommitForm(editBlobForm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import axios from '~/lib/utils/axios_utils';
|
||||||
import createFlash from '~/flash';
|
import createFlash from '~/flash';
|
||||||
import { __ } from '~/locale';
|
import { __ } from '~/locale';
|
||||||
import TemplateSelectorMediator from '../blob/file_template_mediator';
|
import TemplateSelectorMediator from '../blob/file_template_mediator';
|
||||||
|
import getModeByFileExtension from '~/lib/utils/ace_utils';
|
||||||
|
|
||||||
export default class EditBlob {
|
export default class EditBlob {
|
||||||
constructor(assetsPath, aceMode, currentAction, projectId) {
|
constructor(assetsPath, aceMode, currentAction, projectId) {
|
||||||
|
@ -14,9 +15,10 @@ export default class EditBlob {
|
||||||
this.initFileSelectors(currentAction, projectId);
|
this.initFileSelectors(currentAction, projectId);
|
||||||
}
|
}
|
||||||
|
|
||||||
configureAceEditor(aceMode, assetsPath) {
|
configureAceEditor(filePath, assetsPath) {
|
||||||
ace.config.set('modePath', `${assetsPath}/ace`);
|
ace.config.set('modePath', `${assetsPath}/ace`);
|
||||||
ace.config.loadModule('ace/ext/searchbox');
|
ace.config.loadModule('ace/ext/searchbox');
|
||||||
|
ace.config.loadModule('ace/ext/modelist');
|
||||||
|
|
||||||
this.editor = ace.edit('editor');
|
this.editor = ace.edit('editor');
|
||||||
|
|
||||||
|
@ -25,8 +27,8 @@ export default class EditBlob {
|
||||||
|
|
||||||
this.editor.focus();
|
this.editor.focus();
|
||||||
|
|
||||||
if (aceMode) {
|
if (filePath) {
|
||||||
this.editor.getSession().setMode(`ace/mode/${aceMode}`);
|
this.editor.getSession().setMode(getModeByFileExtension(filePath));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,25 +1,20 @@
|
||||||
/* eslint-disable comma-dangle */
|
|
||||||
|
|
||||||
import Sortable from 'sortablejs';
|
import Sortable from 'sortablejs';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import { n__ } from '~/locale';
|
import { n__ } from '~/locale';
|
||||||
import Icon from '~/vue_shared/components/icon.vue';
|
import Icon from '~/vue_shared/components/icon.vue';
|
||||||
import Tooltip from '~/vue_shared/directives/tooltip';
|
import Tooltip from '~/vue_shared/directives/tooltip';
|
||||||
import AccessorUtilities from '../../lib/utils/accessor';
|
import AccessorUtilities from '../../lib/utils/accessor';
|
||||||
import boardList from './board_list.vue';
|
|
||||||
import BoardBlankState from './board_blank_state.vue';
|
import BoardBlankState from './board_blank_state.vue';
|
||||||
import './board_delete';
|
import BoardDelete from './board_delete';
|
||||||
|
import BoardList from './board_list.vue';
|
||||||
|
import boardsStore from '../stores/boards_store';
|
||||||
|
import { getBoardSortableDefaultOptions, sortableEnd } from '../mixins/sortable_default_options';
|
||||||
|
|
||||||
const Store = gl.issueBoards.BoardsStore;
|
export default Vue.extend({
|
||||||
|
|
||||||
window.gl = window.gl || {};
|
|
||||||
window.gl.issueBoards = window.gl.issueBoards || {};
|
|
||||||
|
|
||||||
gl.issueBoards.Board = Vue.extend({
|
|
||||||
components: {
|
components: {
|
||||||
boardList,
|
|
||||||
'board-delete': gl.issueBoards.BoardDelete,
|
|
||||||
BoardBlankState,
|
BoardBlankState,
|
||||||
|
BoardDelete,
|
||||||
|
BoardList,
|
||||||
Icon,
|
Icon,
|
||||||
},
|
},
|
||||||
directives: {
|
directives: {
|
||||||
|
@ -49,8 +44,8 @@ gl.issueBoards.Board = Vue.extend({
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
detailIssue: Store.detail,
|
detailIssue: boardsStore.detail,
|
||||||
filter: Store.filter,
|
filter: boardsStore.filter,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -58,44 +53,47 @@ gl.issueBoards.Board = Vue.extend({
|
||||||
const { issuesSize } = this.list;
|
const { issuesSize } = this.list;
|
||||||
return `${n__('%d issue', '%d issues', issuesSize)}`;
|
return `${n__('%d issue', '%d issues', issuesSize)}`;
|
||||||
},
|
},
|
||||||
|
isNewIssueShown() {
|
||||||
|
return this.list.type === 'backlog' || (!this.disabled && this.list.type !== 'closed');
|
||||||
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
filter: {
|
filter: {
|
||||||
handler() {
|
handler() {
|
||||||
this.list.page = 1;
|
this.list.page = 1;
|
||||||
this.list.getIssues(true)
|
this.list.getIssues(true).catch(() => {
|
||||||
.catch(() => {
|
|
||||||
// TODO: handle request error
|
// TODO: handle request error
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
deep: true,
|
deep: true,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.sortableOptions = gl.issueBoards.getBoardSortableDefaultOptions({
|
this.sortableOptions = getBoardSortableDefaultOptions({
|
||||||
disabled: this.disabled,
|
disabled: this.disabled,
|
||||||
group: 'boards',
|
group: 'boards',
|
||||||
draggable: '.is-draggable',
|
draggable: '.is-draggable',
|
||||||
handle: '.js-board-handle',
|
handle: '.js-board-handle',
|
||||||
onEnd: (e) => {
|
onEnd: e => {
|
||||||
gl.issueBoards.onEnd();
|
sortableEnd();
|
||||||
|
|
||||||
if (e.newIndex !== undefined && e.oldIndex !== e.newIndex) {
|
if (e.newIndex !== undefined && e.oldIndex !== e.newIndex) {
|
||||||
const order = this.sortable.toArray();
|
const order = this.sortable.toArray();
|
||||||
const list = Store.findList('id', parseInt(e.item.dataset.id, 10));
|
const list = boardsStore.findList('id', parseInt(e.item.dataset.id, 10));
|
||||||
|
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
Store.moveList(list, order);
|
boardsStore.moveList(list, order);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
this.sortable = Sortable.create(this.$el.parentNode, this.sortableOptions);
|
this.sortable = Sortable.create(this.$el.parentNode, this.sortableOptions);
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
if (this.list.isExpandable && AccessorUtilities.isLocalStorageAccessSafe()) {
|
if (this.list.isExpandable && AccessorUtilities.isLocalStorageAccessSafe()) {
|
||||||
const isCollapsed = localStorage.getItem(`boards.${this.boardId}.${this.list.type}.expanded`) === 'false';
|
const isCollapsed =
|
||||||
|
localStorage.getItem(`boards.${this.boardId}.${this.list.type}.expanded`) === 'false';
|
||||||
|
|
||||||
this.list.isExpanded = !isCollapsed;
|
this.list.isExpanded = !isCollapsed;
|
||||||
}
|
}
|
||||||
|
@ -109,7 +107,10 @@ gl.issueBoards.Board = Vue.extend({
|
||||||
this.list.isExpanded = !this.list.isExpanded;
|
this.list.isExpanded = !this.list.isExpanded;
|
||||||
|
|
||||||
if (AccessorUtilities.isLocalStorageAccessSafe()) {
|
if (AccessorUtilities.isLocalStorageAccessSafe()) {
|
||||||
localStorage.setItem(`boards.${this.boardId}.${this.list.type}.expanded`, this.list.isExpanded);
|
localStorage.setItem(
|
||||||
|
`boards.${this.boardId}.${this.list.type}.expanded`,
|
||||||
|
this.list.isExpanded,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,8 +2,7 @@
|
||||||
/* global ListLabel */
|
/* global ListLabel */
|
||||||
import _ from 'underscore';
|
import _ from 'underscore';
|
||||||
import Cookies from 'js-cookie';
|
import Cookies from 'js-cookie';
|
||||||
|
import boardsStore from '../stores/boards_store';
|
||||||
const Store = gl.issueBoards.BoardsStore;
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
|
@ -19,7 +18,7 @@ export default {
|
||||||
this.clearBlankState();
|
this.clearBlankState();
|
||||||
|
|
||||||
this.predefinedLabels.forEach((label, i) => {
|
this.predefinedLabels.forEach((label, i) => {
|
||||||
Store.addList({
|
boardsStore.addList({
|
||||||
title: label.title,
|
title: label.title,
|
||||||
position: i,
|
position: i,
|
||||||
list_type: 'label',
|
list_type: 'label',
|
||||||
|
@ -30,35 +29,34 @@ export default {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
Store.state.lists = _.sortBy(Store.state.lists, 'position');
|
boardsStore.state.lists = _.sortBy(boardsStore.state.lists, 'position');
|
||||||
|
|
||||||
// Save the labels
|
// Save the labels
|
||||||
gl.boardService.generateDefaultLists()
|
gl.boardService
|
||||||
|
.generateDefaultLists()
|
||||||
.then(res => res.data)
|
.then(res => res.data)
|
||||||
.then((data) => {
|
.then(data => {
|
||||||
data.forEach((listObj) => {
|
data.forEach(listObj => {
|
||||||
const list = Store.findList('title', listObj.title);
|
const list = boardsStore.findList('title', listObj.title);
|
||||||
|
|
||||||
list.id = listObj.id;
|
list.id = listObj.id;
|
||||||
list.label.id = listObj.label.id;
|
list.label.id = listObj.label.id;
|
||||||
list.getIssues()
|
list.getIssues().catch(() => {
|
||||||
.catch(() => {
|
|
||||||
// TODO: handle request error
|
// TODO: handle request error
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
Store.removeList(undefined, 'label');
|
boardsStore.removeList(undefined, 'label');
|
||||||
Cookies.remove('issue_board_welcome_hidden', {
|
Cookies.remove('issue_board_welcome_hidden', {
|
||||||
path: '',
|
path: '',
|
||||||
});
|
});
|
||||||
Store.addBlankState();
|
boardsStore.addBlankState();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
clearBlankState: Store.removeBlankState.bind(Store),
|
clearBlankState: boardsStore.removeBlankState.bind(boardsStore),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -2,8 +2,7 @@
|
||||||
/* eslint-disable vue/require-default-prop */
|
/* eslint-disable vue/require-default-prop */
|
||||||
import IssueCardInner from './issue_card_inner.vue';
|
import IssueCardInner from './issue_card_inner.vue';
|
||||||
import eventHub from '../eventhub';
|
import eventHub from '../eventhub';
|
||||||
|
import boardsStore from '../stores/boards_store';
|
||||||
const Store = gl.issueBoards.BoardsStore;
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'BoardsIssueCard',
|
name: 'BoardsIssueCard',
|
||||||
|
@ -42,7 +41,7 @@
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
showDetail: false,
|
showDetail: false,
|
||||||
detailIssue: Store.detail,
|
detailIssue: boardsStore.detail,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -63,11 +62,11 @@
|
||||||
if (this.showDetail) {
|
if (this.showDetail) {
|
||||||
this.showDetail = false;
|
this.showDetail = false;
|
||||||
|
|
||||||
if (Store.detail.issue && Store.detail.issue.id === this.issue.id) {
|
if (boardsStore.detail.issue && boardsStore.detail.issue.id === this.issue.id) {
|
||||||
eventHub.$emit('clearDetailIssue');
|
eventHub.$emit('clearDetailIssue');
|
||||||
} else {
|
} else {
|
||||||
eventHub.$emit('newDetailIssue', this.issue);
|
eventHub.$emit('newDetailIssue', this.issue);
|
||||||
Store.detail.list = this.list;
|
boardsStore.detail.list = this.list;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,12 +1,7 @@
|
||||||
/* eslint-disable comma-dangle, no-alert */
|
|
||||||
|
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
window.gl = window.gl || {};
|
export default Vue.extend({
|
||||||
window.gl.issueBoards = window.gl.issueBoards || {};
|
|
||||||
|
|
||||||
gl.issueBoards.BoardDelete = Vue.extend({
|
|
||||||
props: {
|
props: {
|
||||||
list: {
|
list: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -17,9 +12,10 @@ gl.issueBoards.BoardDelete = Vue.extend({
|
||||||
deleteBoard() {
|
deleteBoard() {
|
||||||
$(this.$el).tooltip('hide');
|
$(this.$el).tooltip('hide');
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-alert
|
||||||
if (window.confirm('Are you sure you want to delete this list?')) {
|
if (window.confirm('Are you sure you want to delete this list?')) {
|
||||||
this.list.destroy();
|
this.list.destroy();
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,16 +1,18 @@
|
||||||
<script>
|
<script>
|
||||||
import Sortable from 'sortablejs';
|
import Sortable from 'sortablejs';
|
||||||
|
import { GlLoadingIcon } from '@gitlab-org/gitlab-ui';
|
||||||
import boardNewIssue from './board_new_issue.vue';
|
import boardNewIssue from './board_new_issue.vue';
|
||||||
import boardCard from './board_card.vue';
|
import boardCard from './board_card.vue';
|
||||||
import eventHub from '../eventhub';
|
import eventHub from '../eventhub';
|
||||||
|
import boardsStore from '../stores/boards_store';
|
||||||
const Store = gl.issueBoards.BoardsStore;
|
import { getBoardSortableDefaultOptions, sortableStart } from '../mixins/sortable_default_options';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'BoardList',
|
name: 'BoardList',
|
||||||
components: {
|
components: {
|
||||||
boardCard,
|
boardCard,
|
||||||
boardNewIssue,
|
boardNewIssue,
|
||||||
|
GlLoadingIcon,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
groupId: {
|
groupId: {
|
||||||
|
@ -46,7 +48,7 @@ export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
scrollOffset: 250,
|
scrollOffset: 250,
|
||||||
filters: Store.state.filters,
|
filters: boardsStore.state.filters,
|
||||||
showCount: false,
|
showCount: false,
|
||||||
showIssueForm: false,
|
showIssueForm: false,
|
||||||
};
|
};
|
||||||
|
@ -61,11 +63,12 @@ export default {
|
||||||
},
|
},
|
||||||
issues() {
|
issues() {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
if (this.scrollHeight() <= this.listHeight() &&
|
if (
|
||||||
this.list.issuesSize > this.list.issues.length) {
|
this.scrollHeight() <= this.listHeight() &&
|
||||||
|
this.list.issuesSize > this.list.issues.length
|
||||||
|
) {
|
||||||
this.list.page += 1;
|
this.list.page += 1;
|
||||||
this.list.getIssues(false)
|
this.list.getIssues(false).catch(() => {
|
||||||
.catch(() => {
|
|
||||||
// TODO: handle request error
|
// TODO: handle request error
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -83,7 +86,7 @@ export default {
|
||||||
eventHub.$on(`scroll-board-list-${this.list.id}`, this.scrollToTop);
|
eventHub.$on(`scroll-board-list-${this.list.id}`, this.scrollToTop);
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
const options = gl.issueBoards.getBoardSortableDefaultOptions({
|
const options = getBoardSortableDefaultOptions({
|
||||||
scroll: true,
|
scroll: true,
|
||||||
disabled: this.disabled,
|
disabled: this.disabled,
|
||||||
filter: '.board-list-count, .is-disabled',
|
filter: '.board-list-count, .is-disabled',
|
||||||
|
@ -108,7 +111,8 @@ export default {
|
||||||
// So from there, we can get reference to actual container
|
// So from there, we can get reference to actual container
|
||||||
// and thus the container type to enable Copy or Move
|
// and thus the container type to enable Copy or Move
|
||||||
if (e.target) {
|
if (e.target) {
|
||||||
const containerEl = e.target.closest('.js-board-list') || e.target.querySelector('.js-board-list');
|
const containerEl =
|
||||||
|
e.target.closest('.js-board-list') || e.target.querySelector('.js-board-list');
|
||||||
const toBoardType = containerEl.dataset.boardType;
|
const toBoardType = containerEl.dataset.boardType;
|
||||||
const cloneActions = {
|
const cloneActions = {
|
||||||
label: ['milestone', 'assignee'],
|
label: ['milestone', 'assignee'],
|
||||||
|
@ -120,8 +124,9 @@ export default {
|
||||||
const fromBoardType = this.list.type;
|
const fromBoardType = this.list.type;
|
||||||
// For each list we check if the destination list is
|
// For each list we check if the destination list is
|
||||||
// a the list were we should clone the issue
|
// a the list were we should clone the issue
|
||||||
const shouldClone = Object.entries(cloneActions).some(entry => (
|
const shouldClone = Object.entries(cloneActions).some(
|
||||||
fromBoardType === entry[0] && entry[1].includes(toBoardType)));
|
entry => fromBoardType === entry[0] && entry[1].includes(toBoardType),
|
||||||
|
);
|
||||||
|
|
||||||
if (shouldClone) {
|
if (shouldClone) {
|
||||||
return 'clone';
|
return 'clone';
|
||||||
|
@ -133,28 +138,36 @@ export default {
|
||||||
},
|
},
|
||||||
revertClone: true,
|
revertClone: true,
|
||||||
},
|
},
|
||||||
onStart: (e) => {
|
onStart: e => {
|
||||||
const card = this.$refs.issue[e.oldIndex];
|
const card = this.$refs.issue[e.oldIndex];
|
||||||
|
|
||||||
card.showDetail = false;
|
card.showDetail = false;
|
||||||
Store.moving.list = card.list;
|
boardsStore.moving.list = card.list;
|
||||||
Store.moving.issue = Store.moving.list.findIssue(+e.item.dataset.issueId);
|
boardsStore.moving.issue = boardsStore.moving.list.findIssue(+e.item.dataset.issueId);
|
||||||
|
|
||||||
gl.issueBoards.onStart();
|
sortableStart();
|
||||||
},
|
},
|
||||||
onAdd: (e) => {
|
onAdd: e => {
|
||||||
gl.issueBoards.BoardsStore
|
boardsStore.moveIssueToList(
|
||||||
.moveIssueToList(Store.moving.list, this.list, Store.moving.issue, e.newIndex);
|
boardsStore.moving.list,
|
||||||
|
this.list,
|
||||||
|
boardsStore.moving.issue,
|
||||||
|
e.newIndex,
|
||||||
|
);
|
||||||
|
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
e.item.remove();
|
e.item.remove();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onUpdate: (e) => {
|
onUpdate: e => {
|
||||||
const sortedArray = this.sortable.toArray()
|
const sortedArray = this.sortable.toArray().filter(id => id !== '-1');
|
||||||
.filter(id => id !== '-1');
|
boardsStore.moveIssueInList(
|
||||||
gl.issueBoards.BoardsStore
|
this.list,
|
||||||
.moveIssueInList(this.list, Store.moving.issue, e.oldIndex, e.newIndex, sortedArray);
|
boardsStore.moving.issue,
|
||||||
|
e.oldIndex,
|
||||||
|
e.newIndex,
|
||||||
|
sortedArray,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
onMove(e) {
|
onMove(e) {
|
||||||
return !e.related.classList.contains('board-list-count');
|
return !e.related.classList.contains('board-list-count');
|
||||||
|
@ -192,16 +205,14 @@ export default {
|
||||||
|
|
||||||
if (getIssues) {
|
if (getIssues) {
|
||||||
this.list.loadingMore = true;
|
this.list.loadingMore = true;
|
||||||
getIssues
|
getIssues.then(loadingDone).catch(loadingDone);
|
||||||
.then(loadingDone)
|
|
||||||
.catch(loadingDone);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
toggleForm() {
|
toggleForm() {
|
||||||
this.showIssueForm = !this.showIssueForm;
|
this.showIssueForm = !this.showIssueForm;
|
||||||
},
|
},
|
||||||
onScroll() {
|
onScroll() {
|
||||||
if (!this.list.loadingMore && (this.scrollTop() > this.scrollHeight() - this.scrollOffset)) {
|
if (!this.list.loadingMore && this.scrollTop() > this.scrollHeight() - this.scrollOffset) {
|
||||||
this.loadNextPage();
|
this.loadNextPage();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,17 +1,16 @@
|
||||||
<script>
|
<script>
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import { Button } from '@gitlab-org/gitlab-ui';
|
import { GlButton } from '@gitlab-org/gitlab-ui';
|
||||||
import eventHub from '../eventhub';
|
import eventHub from '../eventhub';
|
||||||
import ProjectSelect from './project_select.vue';
|
import ProjectSelect from './project_select.vue';
|
||||||
import ListIssue from '../models/issue';
|
import ListIssue from '../models/issue';
|
||||||
|
import boardsStore from '../stores/boards_store';
|
||||||
const Store = gl.issueBoards.BoardsStore;
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'BoardNewIssue',
|
name: 'BoardNewIssue',
|
||||||
components: {
|
components: {
|
||||||
ProjectSelect,
|
ProjectSelect,
|
||||||
'gl-button': Button,
|
GlButton,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
groupId: {
|
groupId: {
|
||||||
|
@ -63,13 +62,14 @@ export default {
|
||||||
eventHub.$emit(`scroll-board-list-${this.list.id}`);
|
eventHub.$emit(`scroll-board-list-${this.list.id}`);
|
||||||
this.cancel();
|
this.cancel();
|
||||||
|
|
||||||
return this.list.newIssue(issue)
|
return this.list
|
||||||
|
.newIssue(issue)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// Need this because our jQuery very kindly disables buttons on ALL form submissions
|
// Need this because our jQuery very kindly disables buttons on ALL form submissions
|
||||||
$(this.$refs.submitButton).enable();
|
$(this.$refs.submitButton).enable();
|
||||||
|
|
||||||
Store.detail.issue = issue;
|
boardsStore.detail.issue = issue;
|
||||||
Store.detail.list = this.list;
|
boardsStore.detail.list = this.list;
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
// Need this because our jQuery very kindly disables buttons on ALL form submissions
|
// Need this because our jQuery very kindly disables buttons on ALL form submissions
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* eslint-disable comma-dangle, no-new */
|
/* eslint-disable no-new */
|
||||||
|
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
@ -14,13 +14,9 @@ import IssuableContext from '../../issuable_context';
|
||||||
import LabelsSelect from '../../labels_select';
|
import LabelsSelect from '../../labels_select';
|
||||||
import Subscriptions from '../../sidebar/components/subscriptions/subscriptions.vue';
|
import Subscriptions from '../../sidebar/components/subscriptions/subscriptions.vue';
|
||||||
import MilestoneSelect from '../../milestone_select';
|
import MilestoneSelect from '../../milestone_select';
|
||||||
|
import boardsStore from '../stores/boards_store';
|
||||||
|
|
||||||
const Store = gl.issueBoards.BoardsStore;
|
export default Vue.extend({
|
||||||
|
|
||||||
window.gl = window.gl || {};
|
|
||||||
window.gl.issueBoards = window.gl.issueBoards || {};
|
|
||||||
|
|
||||||
gl.issueBoards.BoardSidebar = Vue.extend({
|
|
||||||
components: {
|
components: {
|
||||||
AssigneeTitle,
|
AssigneeTitle,
|
||||||
Assignees,
|
Assignees,
|
||||||
|
@ -35,7 +31,7 @@ gl.issueBoards.BoardSidebar = Vue.extend({
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
detail: Store.detail,
|
detail: boardsStore.detail,
|
||||||
issue: {},
|
issue: {},
|
||||||
list: {},
|
list: {},
|
||||||
loadingAssignees: false,
|
loadingAssignees: false,
|
||||||
|
@ -55,14 +51,16 @@ gl.issueBoards.BoardSidebar = Vue.extend({
|
||||||
return this.issue.labels && this.issue.labels.length;
|
return this.issue.labels && this.issue.labels.length;
|
||||||
},
|
},
|
||||||
labelDropdownTitle() {
|
labelDropdownTitle() {
|
||||||
return this.hasLabels ? sprintf(__('%{firstLabel} +%{labelCount} more'), {
|
return this.hasLabels
|
||||||
|
? sprintf(__('%{firstLabel} +%{labelCount} more'), {
|
||||||
firstLabel: this.issue.labels[0].title,
|
firstLabel: this.issue.labels[0].title,
|
||||||
labelCount: this.issue.labels.length - 1
|
labelCount: this.issue.labels.length - 1,
|
||||||
}) : __('Label');
|
})
|
||||||
|
: __('Label');
|
||||||
},
|
},
|
||||||
selectedLabels() {
|
selectedLabels() {
|
||||||
return this.hasLabels ? this.issue.labels.map(l => l.title).join(',') : '';
|
return this.hasLabels ? this.issue.labels.map(l => l.title).join(',') : '';
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
detail: {
|
detail: {
|
||||||
|
@ -75,14 +73,16 @@ gl.issueBoards.BoardSidebar = Vue.extend({
|
||||||
});
|
});
|
||||||
|
|
||||||
$('.js-issue-board-sidebar', this.$el).each((i, el) => {
|
$('.js-issue-board-sidebar', this.$el).each((i, el) => {
|
||||||
$(el).data('glDropdown').clearMenu();
|
$(el)
|
||||||
|
.data('glDropdown')
|
||||||
|
.clearMenu();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.issue = this.detail.issue;
|
this.issue = this.detail.issue;
|
||||||
this.list = this.detail.list;
|
this.list = this.detail.list;
|
||||||
},
|
},
|
||||||
deep: true
|
deep: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
|
@ -117,18 +117,19 @@ gl.issueBoards.BoardSidebar = Vue.extend({
|
||||||
this.saveAssignees();
|
this.saveAssignees();
|
||||||
},
|
},
|
||||||
removeAssignee(a) {
|
removeAssignee(a) {
|
||||||
gl.issueBoards.BoardsStore.detail.issue.removeAssignee(a);
|
boardsStore.detail.issue.removeAssignee(a);
|
||||||
},
|
},
|
||||||
addAssignee(a) {
|
addAssignee(a) {
|
||||||
gl.issueBoards.BoardsStore.detail.issue.addAssignee(a);
|
boardsStore.detail.issue.addAssignee(a);
|
||||||
},
|
},
|
||||||
removeAllAssignees() {
|
removeAllAssignees() {
|
||||||
gl.issueBoards.BoardsStore.detail.issue.removeAllAssignees();
|
boardsStore.detail.issue.removeAllAssignees();
|
||||||
},
|
},
|
||||||
saveAssignees() {
|
saveAssignees() {
|
||||||
this.loadingAssignees = true;
|
this.loadingAssignees = true;
|
||||||
|
|
||||||
gl.issueBoards.BoardsStore.detail.issue.update()
|
boardsStore.detail.issue
|
||||||
|
.update()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.loadingAssignees = false;
|
this.loadingAssignees = false;
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,17 +1,24 @@
|
||||||
<script>
|
<script>
|
||||||
import $ from 'jquery';
|
import { GlTooltipDirective } from '@gitlab-org/gitlab-ui';
|
||||||
|
import { sprintf, __ } from '~/locale';
|
||||||
|
import Icon from '~/vue_shared/components/icon.vue';
|
||||||
|
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue';
|
||||||
import UserAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
|
import UserAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
|
||||||
import eventHub from '../eventhub';
|
import eventHub from '../eventhub';
|
||||||
import tooltip from '../../vue_shared/directives/tooltip';
|
import IssueDueDate from './issue_due_date.vue';
|
||||||
|
import IssueTimeEstimate from './issue_time_estimate.vue';
|
||||||
const Store = gl.issueBoards.BoardsStore;
|
import boardsStore from '../stores/boards_store';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
Icon,
|
||||||
UserAvatarLink,
|
UserAvatarLink,
|
||||||
|
TooltipOnTruncate,
|
||||||
|
IssueDueDate,
|
||||||
|
IssueTimeEstimate,
|
||||||
},
|
},
|
||||||
directives: {
|
directives: {
|
||||||
tooltip,
|
GlTooltip: GlTooltipDirective,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
issue: {
|
issue: {
|
||||||
|
@ -44,8 +51,8 @@
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
limitBeforeCounter: 3,
|
limitBeforeCounter: 2,
|
||||||
maxRender: 4,
|
maxRender: 3,
|
||||||
maxCounter: 99,
|
maxCounter: 99,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -54,7 +61,9 @@
|
||||||
return this.issue.assignees.length - this.limitBeforeCounter;
|
return this.issue.assignees.length - this.limitBeforeCounter;
|
||||||
},
|
},
|
||||||
assigneeCounterTooltip() {
|
assigneeCounterTooltip() {
|
||||||
return `${this.assigneeCounterLabel} more`;
|
const { numberOverLimit, maxCounter } = this;
|
||||||
|
const count = numberOverLimit > maxCounter ? maxCounter : numberOverLimit;
|
||||||
|
return sprintf(__('%{count} more assignees'), { count });
|
||||||
},
|
},
|
||||||
assigneeCounterLabel() {
|
assigneeCounterLabel() {
|
||||||
if (this.numberOverLimit > this.maxCounter) {
|
if (this.numberOverLimit > this.maxCounter) {
|
||||||
|
@ -79,6 +88,10 @@
|
||||||
showLabelFooter() {
|
showLabelFooter() {
|
||||||
return this.issue.labels.find(l => this.showLabel(l)) !== undefined;
|
return this.issue.labels.find(l => this.showLabel(l)) !== undefined;
|
||||||
},
|
},
|
||||||
|
issueReferencePath() {
|
||||||
|
const { referencePath, groupId } = this.issue;
|
||||||
|
return !groupId ? referencePath.split('#')[0] : null;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
isIndexLessThanlimit(index) {
|
isIndexLessThanlimit(index) {
|
||||||
|
@ -95,11 +108,9 @@
|
||||||
return index < this.limitBeforeCounter;
|
return index < this.limitBeforeCounter;
|
||||||
},
|
},
|
||||||
assigneeUrl(assignee) {
|
assigneeUrl(assignee) {
|
||||||
|
if (!assignee) return '';
|
||||||
return `${this.rootPath}${assignee.username}`;
|
return `${this.rootPath}${assignee.username}`;
|
||||||
},
|
},
|
||||||
assigneeUrlTitle(assignee) {
|
|
||||||
return `Assigned to ${assignee.name}`;
|
|
||||||
},
|
|
||||||
avatarUrlTitle(assignee) {
|
avatarUrlTitle(assignee) {
|
||||||
return `Avatar for ${assignee.name}`;
|
return `Avatar for ${assignee.name}`;
|
||||||
},
|
},
|
||||||
|
@ -107,24 +118,34 @@
|
||||||
if (!label.id) return false;
|
if (!label.id) return false;
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
filterByLabel(label, e) {
|
filterByLabel(label) {
|
||||||
|
if (!this.updateFilters) return;
|
||||||
|
const labelTitle = encodeURIComponent(label.title);
|
||||||
|
const filter = `label_name[]=${labelTitle}`;
|
||||||
|
|
||||||
|
this.applyFilter(filter);
|
||||||
|
},
|
||||||
|
filterByWeight(weight) {
|
||||||
if (!this.updateFilters) return;
|
if (!this.updateFilters) return;
|
||||||
|
|
||||||
const filterPath = gl.issueBoards.BoardsStore.filter.path.split('&');
|
const issueWeight = encodeURIComponent(weight);
|
||||||
const labelTitle = encodeURIComponent(label.title);
|
const filter = `weight=${issueWeight}`;
|
||||||
const param = `label_name[]=${labelTitle}`;
|
|
||||||
const labelIndex = filterPath.indexOf(param);
|
|
||||||
$(e.currentTarget).tooltip('hide');
|
|
||||||
|
|
||||||
if (labelIndex === -1) {
|
this.applyFilter(filter);
|
||||||
filterPath.push(param);
|
},
|
||||||
|
applyFilter(filter) {
|
||||||
|
const filterPath = boardsStore.filter.path.split('&');
|
||||||
|
const filterIndex = filterPath.indexOf(filter);
|
||||||
|
|
||||||
|
if (filterIndex === -1) {
|
||||||
|
filterPath.push(filter);
|
||||||
} else {
|
} else {
|
||||||
filterPath.splice(labelIndex, 1);
|
filterPath.splice(filterIndex, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
gl.issueBoards.BoardsStore.filter.path = filterPath.join('&');
|
boardsStore.filter.path = filterPath.join('&');
|
||||||
|
|
||||||
Store.updateFiltersUrl();
|
boardsStore.updateFiltersUrl();
|
||||||
|
|
||||||
eventHub.$emit('updateTokens');
|
eventHub.$emit('updateTokens');
|
||||||
},
|
},
|
||||||
|
@ -140,24 +161,62 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="board-card-header">
|
<div class="board-card-header">
|
||||||
<h4 class="board-card-title">
|
<h4 class="board-card-title append-bottom-0 prepend-top-0">
|
||||||
<i
|
<icon
|
||||||
v-if="issue.confidential"
|
v-if="issue.confidential"
|
||||||
class="fa fa-eye-slash confidential-icon"
|
v-gl-tooltip
|
||||||
aria-hidden="true"
|
name="eye-slash"
|
||||||
></i>
|
:title="__('Confidential')"
|
||||||
<a
|
class="confidential-icon append-right-4"
|
||||||
|
:aria-label="__('Confidential')"
|
||||||
|
/><a
|
||||||
:href="issue.path"
|
:href="issue.path"
|
||||||
:title="issue.title"
|
:title="issue.title"
|
||||||
class="js-no-trigger"
|
class="js-no-trigger"
|
||||||
@mousemove.stop>{{ issue.title }}</a>
|
@mousemove.stop>{{ issue.title }}</a>
|
||||||
<span
|
|
||||||
v-if="issueId"
|
|
||||||
class="board-card-number append-right-5"
|
|
||||||
>
|
|
||||||
{{ issue.referencePath }}
|
|
||||||
</span>
|
|
||||||
</h4>
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="showLabelFooter"
|
||||||
|
class="board-card-labels prepend-top-4 d-flex flex-wrap"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
v-for="label in issue.labels"
|
||||||
|
v-if="showLabel(label)"
|
||||||
|
:key="label.id"
|
||||||
|
v-gl-tooltip
|
||||||
|
:style="labelStyle(label)"
|
||||||
|
:title="label.description"
|
||||||
|
class="badge color-label append-right-4 prepend-top-4"
|
||||||
|
type="button"
|
||||||
|
@click="filterByLabel(label)"
|
||||||
|
>
|
||||||
|
{{ label.title }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="board-card-footer d-flex justify-content-between align-items-end">
|
||||||
|
<div class="d-flex align-items-start flex-wrap-reverse board-card-number-container js-board-card-number-container">
|
||||||
|
<span
|
||||||
|
v-if="issue.referencePath"
|
||||||
|
class="board-card-number d-flex append-right-8 prepend-top-8"
|
||||||
|
>
|
||||||
|
<tooltip-on-truncate
|
||||||
|
v-if="issueReferencePath"
|
||||||
|
:title="issueReferencePath"
|
||||||
|
placement="bottom"
|
||||||
|
class="board-issue-path block-truncated bold"
|
||||||
|
>{{ issueReferencePath }}</tooltip-on-truncate>#{{ issue.iid }}
|
||||||
|
</span>
|
||||||
|
<span class="board-info-items prepend-top-8 d-inline-block">
|
||||||
|
<issue-due-date
|
||||||
|
v-if="issue.dueDate"
|
||||||
|
:date="issue.dueDate"
|
||||||
|
/><issue-time-estimate
|
||||||
|
v-if="issue.timeEstimate"
|
||||||
|
:estimate="issue.timeEstimate"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
<div class="board-card-assignee">
|
<div class="board-card-assignee">
|
||||||
<user-avatar-link
|
<user-avatar-link
|
||||||
v-for="(assignee, index) in issue.assignees"
|
v-for="(assignee, index) in issue.assignees"
|
||||||
|
@ -166,38 +225,26 @@
|
||||||
:link-href="assigneeUrl(assignee)"
|
:link-href="assigneeUrl(assignee)"
|
||||||
:img-alt="avatarUrlTitle(assignee)"
|
:img-alt="avatarUrlTitle(assignee)"
|
||||||
:img-src="assignee.avatar"
|
:img-src="assignee.avatar"
|
||||||
:tooltip-text="assigneeUrlTitle(assignee)"
|
:img-size="24"
|
||||||
class="js-no-trigger"
|
class="js-no-trigger"
|
||||||
tooltip-placement="bottom"
|
tooltip-placement="bottom"
|
||||||
/>
|
>
|
||||||
|
<span class="js-assignee-tooltip">
|
||||||
|
<span class="bold d-block">Assignee</span>
|
||||||
|
{{ assignee.name }}
|
||||||
|
<span class="text-white-50">@{{ assignee.username }}</span>
|
||||||
|
</span>
|
||||||
|
</user-avatar-link>
|
||||||
<span
|
<span
|
||||||
v-if="shouldRenderCounter"
|
v-if="shouldRenderCounter"
|
||||||
v-tooltip
|
v-gl-tooltip
|
||||||
:title="assigneeCounterTooltip"
|
:title="assigneeCounterTooltip"
|
||||||
class="avatar-counter"
|
class="avatar-counter"
|
||||||
|
data-placement="bottom"
|
||||||
>
|
>
|
||||||
{{ assigneeCounterLabel }}
|
{{ assigneeCounterLabel }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
v-if="showLabelFooter"
|
|
||||||
class="board-card-footer"
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
v-for="label in issue.labels"
|
|
||||||
v-if="showLabel(label)"
|
|
||||||
:key="label.id"
|
|
||||||
v-tooltip
|
|
||||||
:style="labelStyle(label)"
|
|
||||||
:title="label.description"
|
|
||||||
class="badge color-label"
|
|
||||||
type="button"
|
|
||||||
data-container="body"
|
|
||||||
@click="filterByLabel(label, $event)"
|
|
||||||
>
|
|
||||||
{{ label.title }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
90
app/assets/javascripts/boards/components/issue_due_date.vue
Normal file
90
app/assets/javascripts/boards/components/issue_due_date.vue
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
<script>
|
||||||
|
import dateFormat from 'dateformat';
|
||||||
|
import { GlTooltip } from '@gitlab-org/gitlab-ui';
|
||||||
|
import Icon from '~/vue_shared/components/icon.vue';
|
||||||
|
import { __ } from '~/locale';
|
||||||
|
import { getDayDifference, getTimeago, dateInWords } from '~/lib/utils/datetime_utility';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Icon,
|
||||||
|
GlTooltip,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
date: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
title() {
|
||||||
|
const timeago = getTimeago();
|
||||||
|
const { timeDifference, standardDateFormat } = this;
|
||||||
|
const formatedDate = standardDateFormat;
|
||||||
|
|
||||||
|
if (timeDifference >= -1 && timeDifference < 7) {
|
||||||
|
return `${timeago.format(this.issueDueDate)} (${formatedDate})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return timeago.format(this.issueDueDate);
|
||||||
|
},
|
||||||
|
body() {
|
||||||
|
const { timeDifference, issueDueDate, standardDateFormat } = this;
|
||||||
|
|
||||||
|
if (timeDifference === 0) {
|
||||||
|
return __('Today');
|
||||||
|
} else if (timeDifference === 1) {
|
||||||
|
return __('Tomorrow');
|
||||||
|
} else if (timeDifference === -1) {
|
||||||
|
return __('Yesterday');
|
||||||
|
} else if (timeDifference > 0 && timeDifference < 7) {
|
||||||
|
return dateFormat(issueDueDate, 'dddd', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return standardDateFormat;
|
||||||
|
},
|
||||||
|
issueDueDate() {
|
||||||
|
return new Date(this.date);
|
||||||
|
},
|
||||||
|
timeDifference() {
|
||||||
|
const today = new Date();
|
||||||
|
return getDayDifference(today, this.issueDueDate);
|
||||||
|
},
|
||||||
|
isPastDue() {
|
||||||
|
if (this.timeDifference >= 0) return false;
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
standardDateFormat() {
|
||||||
|
const today = new Date();
|
||||||
|
const isDueInCurrentYear = today.getFullYear() === this.issueDueDate.getFullYear();
|
||||||
|
|
||||||
|
return dateInWords(this.issueDueDate, true, isDueInCurrentYear);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<span>
|
||||||
|
<span
|
||||||
|
ref="issueDueDate"
|
||||||
|
class="board-card-info card-number"
|
||||||
|
>
|
||||||
|
<icon
|
||||||
|
:class="{'text-danger': isPastDue, 'board-card-info-icon': true}"
|
||||||
|
name="calendar"
|
||||||
|
/><time
|
||||||
|
:class="{'text-danger': isPastDue}"
|
||||||
|
datetime="date"
|
||||||
|
class="board-card-info-text">{{ body }}</time>
|
||||||
|
</span>
|
||||||
|
<gl-tooltip
|
||||||
|
:target="() => $refs.issueDueDate"
|
||||||
|
placement="bottom"
|
||||||
|
>
|
||||||
|
<span class="bold">{{ __('Due date') }}</span>
|
||||||
|
<br />
|
||||||
|
<span :class="{'text-danger-muted': isPastDue}">{{ title }}</span>
|
||||||
|
</gl-tooltip>
|
||||||
|
</span>
|
||||||
|
</template>
|
|
@ -0,0 +1,48 @@
|
||||||
|
<script>
|
||||||
|
import { GlTooltip } from '@gitlab-org/gitlab-ui';
|
||||||
|
import Icon from '~/vue_shared/components/icon.vue';
|
||||||
|
import { parseSeconds, stringifyTime } from '~/lib/utils/datetime_utility';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Icon,
|
||||||
|
GlTooltip,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
estimate: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
title() {
|
||||||
|
return stringifyTime(parseSeconds(this.estimate), true);
|
||||||
|
},
|
||||||
|
timeEstimate() {
|
||||||
|
return stringifyTime(parseSeconds(this.estimate));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<span>
|
||||||
|
<span
|
||||||
|
ref="issueTimeEstimate"
|
||||||
|
class="board-card-info card-number"
|
||||||
|
>
|
||||||
|
<icon
|
||||||
|
name="hourglass"
|
||||||
|
css-classes="board-card-info-icon"
|
||||||
|
/><time class="board-card-info-text">{{ timeEstimate }}</time>
|
||||||
|
</span>
|
||||||
|
<gl-tooltip
|
||||||
|
:target="() => $refs.issueTimeEstimate"
|
||||||
|
placement="bottom"
|
||||||
|
class="js-issue-time-estimate"
|
||||||
|
>
|
||||||
|
<span class="bold d-block">{{ __('Time estimate') }}</span>
|
||||||
|
{{ title }}
|
||||||
|
</gl-tooltip>
|
||||||
|
</span>
|
||||||
|
</template>
|
|
@ -20,7 +20,7 @@ export default {
|
||||||
computed: {
|
computed: {
|
||||||
contents() {
|
contents() {
|
||||||
const obj = {
|
const obj = {
|
||||||
title: 'You haven\'t added any issues to your project yet',
|
title: "You haven't added any issues to your project yet",
|
||||||
content: `
|
content: `
|
||||||
An issue can be a bug, a todo or a feature request that needs to be
|
An issue can be a bug, a todo or a feature request that needs to be
|
||||||
discussed in a project. Besides, issues are searchable and filterable.
|
discussed in a project. Besides, issues are searchable and filterable.
|
||||||
|
@ -28,7 +28,7 @@ export default {
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.activeTab === 'selected') {
|
if (this.activeTab === 'selected') {
|
||||||
obj.title = 'You haven\'t selected any issues yet';
|
obj.title = "You haven't selected any issues yet";
|
||||||
obj.content = `
|
obj.content = `
|
||||||
Go back to <strong>Open issues</strong> and select some issues
|
Go back to <strong>Open issues</strong> and select some issues
|
||||||
to add to your board.
|
to add to your board.
|
||||||
|
|
|
@ -5,6 +5,7 @@ import ListsDropdown from './lists_dropdown.vue';
|
||||||
import { pluralize } from '../../../lib/utils/text_utility';
|
import { pluralize } from '../../../lib/utils/text_utility';
|
||||||
import ModalStore from '../../stores/modal_store';
|
import ModalStore from '../../stores/modal_store';
|
||||||
import modalMixin from '../../mixins/modal_mixins';
|
import modalMixin from '../../mixins/modal_mixins';
|
||||||
|
import boardsStore from '../../stores/boards_store';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
@ -14,7 +15,7 @@ export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
modal: ModalStore.store,
|
modal: ModalStore.store,
|
||||||
state: gl.issueBoards.BoardsStore.state,
|
state: boardsStore.state,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -41,19 +42,17 @@ export default {
|
||||||
const req = this.buildUpdateRequest(list);
|
const req = this.buildUpdateRequest(list);
|
||||||
|
|
||||||
// Post the data to the backend
|
// Post the data to the backend
|
||||||
gl.boardService
|
gl.boardService.bulkUpdate(issueIds, req).catch(() => {
|
||||||
.bulkUpdate(issueIds, req)
|
|
||||||
.catch(() => {
|
|
||||||
Flash(__('Failed to update issues, please try again.'));
|
Flash(__('Failed to update issues, please try again.'));
|
||||||
|
|
||||||
selectedIssues.forEach((issue) => {
|
selectedIssues.forEach(issue => {
|
||||||
list.removeIssue(issue);
|
list.removeIssue(issue);
|
||||||
list.issuesSize -= 1;
|
list.issuesSize -= 1;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add the issues on the frontend
|
// Add the issues on the frontend
|
||||||
selectedIssues.forEach((issue) => {
|
selectedIssues.forEach(issue => {
|
||||||
list.addIssue(issue);
|
list.addIssue(issue);
|
||||||
list.issuesSize += 1;
|
list.issuesSize += 1;
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
import ModalFooter from './footer.vue';
|
import ModalFooter from './footer.vue';
|
||||||
import EmptyState from './empty_state.vue';
|
import EmptyState from './empty_state.vue';
|
||||||
import ModalStore from '../../stores/modal_store';
|
import ModalStore from '../../stores/modal_store';
|
||||||
|
import { GlLoadingIcon } from '@gitlab-org/gitlab-ui';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
@ -13,6 +14,7 @@
|
||||||
ModalHeader,
|
ModalHeader,
|
||||||
ModalList,
|
ModalList,
|
||||||
ModalFooter,
|
ModalFooter,
|
||||||
|
GlLoadingIcon,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
newIssuePath: {
|
newIssuePath: {
|
||||||
|
@ -107,7 +109,8 @@
|
||||||
loadIssues(clearIssues = false) {
|
loadIssues(clearIssues = false) {
|
||||||
if (!this.showAddIssuesModal) return false;
|
if (!this.showAddIssuesModal) return false;
|
||||||
|
|
||||||
return gl.boardService.getBacklog({
|
return gl.boardService
|
||||||
|
.getBacklog({
|
||||||
...urlParamsToObject(this.filter.path),
|
...urlParamsToObject(this.filter.path),
|
||||||
page: this.page,
|
page: this.page,
|
||||||
per: this.perPage,
|
per: this.perPage,
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
|
import Icon from '~/vue_shared/components/icon.vue';
|
||||||
import bp from '../../../breakpoints';
|
import bp from '../../../breakpoints';
|
||||||
import ModalStore from '../../stores/modal_store';
|
import ModalStore from '../../stores/modal_store';
|
||||||
import IssueCardInner from '../issue_card_inner.vue';
|
import IssueCardInner from '../issue_card_inner.vue';
|
||||||
|
@ -6,6 +7,7 @@
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
IssueCardInner,
|
IssueCardInner,
|
||||||
|
Icon,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
issueLinkBase: {
|
issueLinkBase: {
|
||||||
|
@ -147,13 +149,13 @@
|
||||||
:issue="issue"
|
:issue="issue"
|
||||||
:issue-link-base="issueLinkBase"
|
:issue-link-base="issueLinkBase"
|
||||||
:root-path="rootPath"/>
|
:root-path="rootPath"/>
|
||||||
<span
|
<icon
|
||||||
v-if="issue.selected"
|
v-if="issue.selected"
|
||||||
:aria-label="'Issue #' + issue.id + ' selected'"
|
:aria-label="'Issue #' + issue.id + ' selected'"
|
||||||
|
name="mobile-issue-close"
|
||||||
aria-checked="true"
|
aria-checked="true"
|
||||||
class="issue-card-selected text-center">
|
class="issue-card-selected text-center"
|
||||||
<i class="fa fa-check"></i>
|
/>
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,15 +1,18 @@
|
||||||
<script>
|
<script>
|
||||||
import { Link } from '@gitlab-org/gitlab-ui';
|
import { GlLink } from '@gitlab-org/gitlab-ui';
|
||||||
|
import Icon from '~/vue_shared/components/icon.vue';
|
||||||
import ModalStore from '../../stores/modal_store';
|
import ModalStore from '../../stores/modal_store';
|
||||||
|
import boardsStore from '../../stores/boards_store';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
'gl-link': Link,
|
GlLink,
|
||||||
|
Icon,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
modal: ModalStore.store,
|
modal: ModalStore.store,
|
||||||
state: gl.issueBoards.BoardsStore.state,
|
state: boardsStore.state,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -34,7 +37,9 @@ export default {
|
||||||
class="dropdown-label-box">
|
class="dropdown-label-box">
|
||||||
</span>
|
</span>
|
||||||
{{ selected.title }}
|
{{ selected.title }}
|
||||||
<i class="fa fa-chevron-down"></i>
|
<icon
|
||||||
|
name="chevron-down"
|
||||||
|
/>
|
||||||
</button>
|
</button>
|
||||||
<div class="dropdown-menu dropdown-menu-selectable dropdown-menu-drop-up">
|
<div class="dropdown-menu dropdown-menu-selectable dropdown-menu-drop-up">
|
||||||
<ul>
|
<ul>
|
||||||
|
|
|
@ -4,16 +4,14 @@ import $ from 'jquery';
|
||||||
import axios from '~/lib/utils/axios_utils';
|
import axios from '~/lib/utils/axios_utils';
|
||||||
import _ from 'underscore';
|
import _ from 'underscore';
|
||||||
import CreateLabelDropdown from '../../create_label';
|
import CreateLabelDropdown from '../../create_label';
|
||||||
|
import boardsStore from '../stores/boards_store';
|
||||||
|
|
||||||
window.gl = window.gl || {};
|
$(document)
|
||||||
window.gl.issueBoards = window.gl.issueBoards || {};
|
.off('created.label')
|
||||||
|
.on('created.label', (e, label) => {
|
||||||
const Store = gl.issueBoards.BoardsStore;
|
boardsStore.new({
|
||||||
|
|
||||||
$(document).off('created.label').on('created.label', (e, label) => {
|
|
||||||
Store.new({
|
|
||||||
title: label.title,
|
title: label.title,
|
||||||
position: Store.state.lists.length - 2,
|
position: boardsStore.state.lists.length - 2,
|
||||||
list_type: 'label',
|
list_type: 'label',
|
||||||
label: {
|
label: {
|
||||||
id: label.id,
|
id: label.id,
|
||||||
|
@ -23,23 +21,26 @@ $(document).off('created.label').on('created.label', (e, label) => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
gl.issueBoards.newListDropdownInit = () => {
|
export default function initNewListDropdown() {
|
||||||
$('.js-new-board-list').each(function() {
|
$('.js-new-board-list').each(function() {
|
||||||
const $this = $(this);
|
const $this = $(this);
|
||||||
new CreateLabelDropdown($this.closest('.dropdown').find('.dropdown-new-label'), $this.data('namespacePath'), $this.data('projectPath'));
|
new CreateLabelDropdown(
|
||||||
|
$this.closest('.dropdown').find('.dropdown-new-label'),
|
||||||
|
$this.data('namespacePath'),
|
||||||
|
$this.data('projectPath'),
|
||||||
|
);
|
||||||
|
|
||||||
$this.glDropdown({
|
$this.glDropdown({
|
||||||
data(term, callback) {
|
data(term, callback) {
|
||||||
axios.get($this.attr('data-list-labels-path'))
|
axios.get($this.attr('data-list-labels-path')).then(({ data }) => {
|
||||||
.then(({ data }) => {
|
|
||||||
callback(data);
|
callback(data);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
renderRow(label) {
|
renderRow(label) {
|
||||||
const active = Store.findList('title', label.title);
|
const active = boardsStore.findList('title', label.title);
|
||||||
const $li = $('<li />');
|
const $li = $('<li />');
|
||||||
const $a = $('<a />', {
|
const $a = $('<a />', {
|
||||||
class: (active ? `is-active js-board-list-${active.id}` : ''),
|
class: active ? `is-active js-board-list-${active.id}` : '',
|
||||||
text: label.title,
|
text: label.title,
|
||||||
href: '#',
|
href: '#',
|
||||||
});
|
});
|
||||||
|
@ -62,10 +63,10 @@ gl.issueBoards.newListDropdownInit = () => {
|
||||||
const label = options.selectedObj;
|
const label = options.selectedObj;
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
if (!Store.findList('title', label.title)) {
|
if (!boardsStore.findList('title', label.title)) {
|
||||||
Store.new({
|
boardsStore.new({
|
||||||
title: label.title,
|
title: label.title,
|
||||||
position: Store.state.lists.length - 2,
|
position: boardsStore.state.lists.length - 2,
|
||||||
list_type: 'label',
|
list_type: 'label',
|
||||||
label: {
|
label: {
|
||||||
id: label.id,
|
id: label.id,
|
||||||
|
@ -74,9 +75,9 @@ gl.issueBoards.newListDropdownInit = () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
Store.state.lists = _.sortBy(Store.state.lists, 'position');
|
boardsStore.state.lists = _.sortBy(boardsStore.state.lists, 'position');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,11 +1,17 @@
|
||||||
<script>
|
<script>
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import _ from 'underscore';
|
import _ from 'underscore';
|
||||||
|
import Icon from '~/vue_shared/components/icon.vue';
|
||||||
|
import { GlLoadingIcon } from '@gitlab-org/gitlab-ui';
|
||||||
import eventHub from '../eventhub';
|
import eventHub from '../eventhub';
|
||||||
import Api from '../../api';
|
import Api from '../../api';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'BoardProjectSelect',
|
name: 'BoardProjectSelect',
|
||||||
|
components: {
|
||||||
|
Icon,
|
||||||
|
GlLoadingIcon,
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
groupId: {
|
groupId: {
|
||||||
type: Number,
|
type: Number,
|
||||||
|
@ -42,7 +48,7 @@ export default {
|
||||||
selectable: true,
|
selectable: true,
|
||||||
data: (term, callback) => {
|
data: (term, callback) => {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
return Api.groupProjects(this.groupId, term, {}, projects => {
|
return Api.groupProjects(this.groupId, term, { with_issues_enabled: true }, projects => {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
callback(projects);
|
callback(projects);
|
||||||
});
|
});
|
||||||
|
@ -50,7 +56,9 @@ export default {
|
||||||
renderRow(project) {
|
renderRow(project) {
|
||||||
return `
|
return `
|
||||||
<li>
|
<li>
|
||||||
<a href='#' class='dropdown-menu-link' data-project-id="${project.id}" data-project-name="${project.name}">
|
<a href='#' class='dropdown-menu-link' data-project-id="${
|
||||||
|
project.id
|
||||||
|
}" data-project-name="${project.name}">
|
||||||
${_.escape(project.name)}
|
${_.escape(project.name)}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -78,11 +86,9 @@ export default {
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
>
|
>
|
||||||
{{ selectedProjectName }}
|
{{ selectedProjectName }}
|
||||||
<i
|
<icon
|
||||||
class="fa fa-chevron-down"
|
name="chevron-down"
|
||||||
aria-hidden="true"
|
/>
|
||||||
>
|
|
||||||
</i>
|
|
||||||
</button>
|
</button>
|
||||||
<div class="dropdown-menu dropdown-menu-selectable dropdown-menu-full-width">
|
<div class="dropdown-menu dropdown-menu-selectable dropdown-menu-full-width">
|
||||||
<div class="dropdown-title">
|
<div class="dropdown-title">
|
||||||
|
@ -92,12 +98,11 @@ export default {
|
||||||
type="button"
|
type="button"
|
||||||
class="dropdown-title-button dropdown-menu-close"
|
class="dropdown-title-button dropdown-menu-close"
|
||||||
>
|
>
|
||||||
<i
|
<icon
|
||||||
aria-hidden="true"
|
name="merge-request-close-m"
|
||||||
data-hidden="true"
|
data-hidden="true"
|
||||||
class="fa fa-times dropdown-menu-close-icon"
|
class="dropdown-menu-close-icon"
|
||||||
>
|
/>
|
||||||
</i>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="dropdown-input">
|
<div class="dropdown-input">
|
||||||
|
@ -106,12 +111,11 @@ export default {
|
||||||
type="search"
|
type="search"
|
||||||
placeholder="Search projects"
|
placeholder="Search projects"
|
||||||
/>
|
/>
|
||||||
<i
|
<icon
|
||||||
aria-hidden="true"
|
name="search"
|
||||||
|
class="dropdown-input-search"
|
||||||
data-hidden="true"
|
data-hidden="true"
|
||||||
class="fa fa-search dropdown-input-search"
|
/>
|
||||||
>
|
|
||||||
</i>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="dropdown-content"></div>
|
<div class="dropdown-content"></div>
|
||||||
<div class="dropdown-loading">
|
<div class="dropdown-loading">
|
||||||
|
|
|
@ -2,8 +2,7 @@
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import Flash from '../../../flash';
|
import Flash from '../../../flash';
|
||||||
import { __ } from '../../../locale';
|
import { __ } from '../../../locale';
|
||||||
|
import boardsStore from '../../stores/boards_store';
|
||||||
const Store = gl.issueBoards.BoardsStore;
|
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
props: {
|
props: {
|
||||||
|
@ -49,7 +48,7 @@
|
||||||
list.removeIssue(issue);
|
list.removeIssue(issue);
|
||||||
});
|
});
|
||||||
|
|
||||||
Store.detail.issue = {};
|
boardsStore.detail.issue = {};
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Build the default patch request.
|
* Build the default patch request.
|
||||||
|
@ -57,9 +56,7 @@
|
||||||
buildPatchRequest(issue, lists) {
|
buildPatchRequest(issue, lists) {
|
||||||
const listLabelIds = lists.map(list => list.label.id);
|
const listLabelIds = lists.map(list => list.label.id);
|
||||||
|
|
||||||
const labelIds = issue.labels
|
const labelIds = issue.labels.map(label => label.id).filter(id => !listLabelIds.includes(id));
|
||||||
.map(label => label.id)
|
|
||||||
.filter(id => !listLabelIds.includes(id));
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
label_ids: labelIds,
|
label_ids: labelIds,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import FilteredSearchContainer from '../filtered_search/container';
|
import FilteredSearchContainer from '../filtered_search/container';
|
||||||
import FilteredSearchManager from '../filtered_search/filtered_search_manager';
|
import FilteredSearchManager from '../filtered_search/filtered_search_manager';
|
||||||
|
import boardsStore from './stores/boards_store';
|
||||||
|
|
||||||
export default class FilteredSearchBoards extends FilteredSearchManager {
|
export default class FilteredSearchBoards extends FilteredSearchManager {
|
||||||
constructor(store, updateUrl = false, cantEdit = []) {
|
constructor(store, updateUrl = false, cantEdit = []) {
|
||||||
|
@ -23,7 +24,7 @@ export default class FilteredSearchBoards extends FilteredSearchManager {
|
||||||
this.store.path = path.substr(1);
|
this.store.path = path.substr(1);
|
||||||
|
|
||||||
if (this.updateUrl) {
|
if (this.updateUrl) {
|
||||||
gl.issueBoards.BoardsStore.updateFiltersUrl();
|
boardsStore.updateFiltersUrl();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +32,7 @@ export default class FilteredSearchBoards extends FilteredSearchManager {
|
||||||
const tokens = FilteredSearchContainer.container.querySelectorAll('.js-visual-token');
|
const tokens = FilteredSearchContainer.container.querySelectorAll('.js-visual-token');
|
||||||
|
|
||||||
// Remove all the tokens as they will be replaced by the search manager
|
// Remove all the tokens as they will be replaced by the search manager
|
||||||
[].forEach.call(tokens, (el) => {
|
[].forEach.call(tokens, el => {
|
||||||
el.parentNode.removeChild(el);
|
el.parentNode.removeChild(el);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -49,7 +50,10 @@ export default class FilteredSearchBoards extends FilteredSearchManager {
|
||||||
|
|
||||||
canEdit(tokenName, tokenValue) {
|
canEdit(tokenName, tokenValue) {
|
||||||
if (this.cantEdit.includes(tokenName)) return false;
|
if (this.cantEdit.includes(tokenName)) return false;
|
||||||
return this.cantEditWithValue.findIndex(token => token.name === tokenName &&
|
return (
|
||||||
token.value === tokenValue) === -1;
|
this.cantEditWithValue.findIndex(
|
||||||
|
token => token.name === tokenName && token.value === tokenValue,
|
||||||
|
) === -1
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,54 +14,48 @@ import './models/issue';
|
||||||
import './models/list';
|
import './models/list';
|
||||||
import './models/milestone';
|
import './models/milestone';
|
||||||
import './models/project';
|
import './models/project';
|
||||||
import './stores/boards_store';
|
import boardsStore from './stores/boards_store';
|
||||||
import ModalStore from './stores/modal_store';
|
import ModalStore from './stores/modal_store';
|
||||||
import BoardService from './services/board_service';
|
import BoardService from './services/board_service';
|
||||||
import modalMixin from './mixins/modal_mixins';
|
import modalMixin from './mixins/modal_mixins';
|
||||||
import './mixins/sortable_default_options';
|
|
||||||
import './filters/due_date_filters';
|
import './filters/due_date_filters';
|
||||||
import './components/board';
|
import Board from './components/board';
|
||||||
import './components/board_sidebar';
|
import BoardSidebar from './components/board_sidebar';
|
||||||
import './components/new_list_dropdown';
|
import initNewListDropdown from './components/new_list_dropdown';
|
||||||
import BoardAddIssuesModal from './components/modal/index.vue';
|
import BoardAddIssuesModal from './components/modal/index.vue';
|
||||||
import '~/vue_shared/vue_resource_interceptor';
|
import '~/vue_shared/vue_resource_interceptor';
|
||||||
import { NavigationType } from '~/lib/utils/common_utils';
|
import { NavigationType } from '~/lib/utils/common_utils';
|
||||||
|
|
||||||
|
let issueBoardsApp;
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
const $boardApp = document.getElementById('board-app');
|
const $boardApp = document.getElementById('board-app');
|
||||||
const Store = gl.issueBoards.BoardsStore;
|
|
||||||
|
|
||||||
window.gl = window.gl || {};
|
|
||||||
|
|
||||||
// check for browser back and trigger a hard reload to circumvent browser caching.
|
// check for browser back and trigger a hard reload to circumvent browser caching.
|
||||||
window.addEventListener('pageshow', (event) => {
|
window.addEventListener('pageshow', event => {
|
||||||
const isNavTypeBackForward = window.performance &&
|
const isNavTypeBackForward =
|
||||||
window.performance.navigation.type === NavigationType.TYPE_BACK_FORWARD;
|
window.performance && window.performance.navigation.type === NavigationType.TYPE_BACK_FORWARD;
|
||||||
|
|
||||||
if (event.persisted || isNavTypeBackForward) {
|
if (event.persisted || isNavTypeBackForward) {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (gl.IssueBoardsApp) {
|
if (issueBoardsApp) {
|
||||||
gl.IssueBoardsApp.$destroy(true);
|
issueBoardsApp.$destroy(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
Store.create();
|
boardsStore.create();
|
||||||
|
|
||||||
// hack to allow sidebar scripts like milestone_select manipulate the BoardsStore
|
issueBoardsApp = new Vue({
|
||||||
gl.issueBoards.boardStoreIssueSet = (...args) => Vue.set(Store.detail.issue, ...args);
|
|
||||||
gl.issueBoards.boardStoreIssueDelete = (...args) => Vue.delete(Store.detail.issue, ...args);
|
|
||||||
|
|
||||||
gl.IssueBoardsApp = new Vue({
|
|
||||||
el: $boardApp,
|
el: $boardApp,
|
||||||
components: {
|
components: {
|
||||||
board: gl.issueBoards.Board,
|
Board,
|
||||||
'board-sidebar': gl.issueBoards.BoardSidebar,
|
BoardSidebar,
|
||||||
BoardAddIssuesModal,
|
BoardAddIssuesModal,
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
state: Store.state,
|
state: boardsStore.state,
|
||||||
loading: true,
|
loading: true,
|
||||||
boardsEndpoint: $boardApp.dataset.boardsEndpoint,
|
boardsEndpoint: $boardApp.dataset.boardsEndpoint,
|
||||||
listsEndpoint: $boardApp.dataset.listsEndpoint,
|
listsEndpoint: $boardApp.dataset.listsEndpoint,
|
||||||
|
@ -70,7 +64,7 @@ export default () => {
|
||||||
issueLinkBase: $boardApp.dataset.issueLinkBase,
|
issueLinkBase: $boardApp.dataset.issueLinkBase,
|
||||||
rootPath: $boardApp.dataset.rootPath,
|
rootPath: $boardApp.dataset.rootPath,
|
||||||
bulkUpdatePath: $boardApp.dataset.bulkUpdatePath,
|
bulkUpdatePath: $boardApp.dataset.bulkUpdatePath,
|
||||||
detailIssue: Store.detail,
|
detailIssue: boardsStore.detail,
|
||||||
defaultAvatar: $boardApp.dataset.defaultAvatar,
|
defaultAvatar: $boardApp.dataset.defaultAvatar,
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -85,7 +79,7 @@ export default () => {
|
||||||
bulkUpdatePath: this.bulkUpdatePath,
|
bulkUpdatePath: this.bulkUpdatePath,
|
||||||
boardId: this.boardId,
|
boardId: this.boardId,
|
||||||
});
|
});
|
||||||
Store.rootPath = this.boardsEndpoint;
|
boardsStore.rootPath = this.boardsEndpoint;
|
||||||
|
|
||||||
eventHub.$on('updateTokens', this.updateTokens);
|
eventHub.$on('updateTokens', this.updateTokens);
|
||||||
eventHub.$on('newDetailIssue', this.updateDetailIssue);
|
eventHub.$on('newDetailIssue', this.updateDetailIssue);
|
||||||
|
@ -99,16 +93,16 @@ export default () => {
|
||||||
sidebarEventHub.$off('toggleSubscription', this.toggleSubscription);
|
sidebarEventHub.$off('toggleSubscription', this.toggleSubscription);
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.filterManager = new FilteredSearchBoards(Store.filter, true, Store.cantEdit);
|
this.filterManager = new FilteredSearchBoards(boardsStore.filter, true, boardsStore.cantEdit);
|
||||||
this.filterManager.setup();
|
this.filterManager.setup();
|
||||||
|
|
||||||
Store.disabled = this.disabled;
|
boardsStore.disabled = this.disabled;
|
||||||
gl.boardService
|
gl.boardService
|
||||||
.all()
|
.all()
|
||||||
.then(res => res.data)
|
.then(res => res.data)
|
||||||
.then(data => {
|
.then(data => {
|
||||||
data.forEach(board => {
|
data.forEach(board => {
|
||||||
const list = Store.addList(board, this.defaultAvatar);
|
const list = boardsStore.addList(board, this.defaultAvatar);
|
||||||
|
|
||||||
if (list.type === 'closed') {
|
if (list.type === 'closed') {
|
||||||
list.position = Infinity;
|
list.position = Infinity;
|
||||||
|
@ -119,7 +113,7 @@ export default () => {
|
||||||
|
|
||||||
this.state.lists = _.sortBy(this.state.lists, 'position');
|
this.state.lists = _.sortBy(this.state.lists, 'position');
|
||||||
|
|
||||||
Store.addBlankState();
|
boardsStore.addBlankState();
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
|
@ -148,13 +142,13 @@ export default () => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Store.detail.issue = newIssue;
|
boardsStore.detail.issue = newIssue;
|
||||||
},
|
},
|
||||||
clearDetailIssue() {
|
clearDetailIssue() {
|
||||||
Store.detail.issue = {};
|
boardsStore.detail.issue = {};
|
||||||
},
|
},
|
||||||
toggleSubscription(id) {
|
toggleSubscription(id) {
|
||||||
const { issue } = Store.detail;
|
const { issue } = boardsStore.detail;
|
||||||
if (issue.id === id && issue.toggleSubscriptionEndpoint) {
|
if (issue.id === id && issue.toggleSubscriptionEndpoint) {
|
||||||
issue.setFetchingState('subscriptions', true);
|
issue.setFetchingState('subscriptions', true);
|
||||||
BoardService.toggleIssueSubscription(issue.toggleSubscriptionEndpoint)
|
BoardService.toggleIssueSubscription(issue.toggleSubscriptionEndpoint)
|
||||||
|
@ -173,26 +167,28 @@ export default () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
gl.IssueBoardsSearch = new Vue({
|
// eslint-disable-next-line no-new
|
||||||
|
new Vue({
|
||||||
el: document.getElementById('js-add-list'),
|
el: document.getElementById('js-add-list'),
|
||||||
data: {
|
data: {
|
||||||
filters: Store.state.filters,
|
filters: boardsStore.state.filters,
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
gl.issueBoards.newListDropdownInit();
|
initNewListDropdown();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const issueBoardsModal = document.getElementById('js-add-issues-btn');
|
const issueBoardsModal = document.getElementById('js-add-issues-btn');
|
||||||
|
|
||||||
if (issueBoardsModal) {
|
if (issueBoardsModal) {
|
||||||
gl.IssueBoardsModalAddBtn = new Vue({
|
// eslint-disable-next-line no-new
|
||||||
|
new Vue({
|
||||||
el: issueBoardsModal,
|
el: issueBoardsModal,
|
||||||
mixins: [modalMixin],
|
mixins: [modalMixin],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
modal: ModalStore.store,
|
modal: ModalStore.store,
|
||||||
store: Store.state,
|
store: boardsStore.state,
|
||||||
canAdminList: this.$options.el.hasAttribute('data-can-admin-list'),
|
canAdminList: this.$options.el.hasAttribute('data-can-admin-list'),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
|
@ -3,32 +3,33 @@
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import sortableConfig from '../../sortable/sortable_config';
|
import sortableConfig from '../../sortable/sortable_config';
|
||||||
|
|
||||||
window.gl = window.gl || {};
|
export function sortableStart() {
|
||||||
window.gl.issueBoards = window.gl.issueBoards || {};
|
$('.has-tooltip')
|
||||||
|
.tooltip('hide')
|
||||||
gl.issueBoards.onStart = () => {
|
|
||||||
$('.has-tooltip').tooltip('hide')
|
|
||||||
.tooltip('disable');
|
.tooltip('disable');
|
||||||
document.body.classList.add('is-dragging');
|
document.body.classList.add('is-dragging');
|
||||||
};
|
}
|
||||||
|
|
||||||
gl.issueBoards.onEnd = () => {
|
export function sortableEnd() {
|
||||||
$('.has-tooltip').tooltip('enable');
|
$('.has-tooltip').tooltip('enable');
|
||||||
document.body.classList.remove('is-dragging');
|
document.body.classList.remove('is-dragging');
|
||||||
};
|
}
|
||||||
|
|
||||||
gl.issueBoards.touchEnabled = ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch;
|
export function getBoardSortableDefaultOptions(obj) {
|
||||||
|
const touchEnabled =
|
||||||
|
'ontouchstart' in window || (window.DocumentTouch && document instanceof DocumentTouch);
|
||||||
|
|
||||||
gl.issueBoards.getBoardSortableDefaultOptions = (obj) => {
|
|
||||||
const defaultSortOptions = Object.assign({}, sortableConfig, {
|
const defaultSortOptions = Object.assign({}, sortableConfig, {
|
||||||
filter: '.board-delete, .btn',
|
filter: '.board-delete, .btn',
|
||||||
delay: gl.issueBoards.touchEnabled ? 100 : 0,
|
delay: touchEnabled ? 100 : 0,
|
||||||
scrollSensitivity: gl.issueBoards.touchEnabled ? 60 : 100,
|
scrollSensitivity: touchEnabled ? 60 : 100,
|
||||||
scrollSpeed: 20,
|
scrollSpeed: 20,
|
||||||
onStart: gl.issueBoards.onStart,
|
onStart: sortableStart,
|
||||||
onEnd: gl.issueBoards.onEnd,
|
onEnd: sortableEnd,
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.keys(obj).forEach((key) => { defaultSortOptions[key] = obj[key]; });
|
Object.keys(obj).forEach(key => {
|
||||||
|
defaultSortOptions[key] = obj[key];
|
||||||
|
});
|
||||||
return defaultSortOptions;
|
return defaultSortOptions;
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* eslint-disable no-unused-vars, comma-dangle */
|
/* eslint-disable no-unused-vars */
|
||||||
/* global ListLabel */
|
/* global ListLabel */
|
||||||
/* global ListMilestone */
|
/* global ListMilestone */
|
||||||
/* global ListAssignee */
|
/* global ListAssignee */
|
||||||
|
@ -6,6 +6,7 @@
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import '~/vue_shared/models/label';
|
import '~/vue_shared/models/label';
|
||||||
import IssueProject from './project';
|
import IssueProject from './project';
|
||||||
|
import boardsStore from '../stores/boards_store';
|
||||||
|
|
||||||
class ListIssue {
|
class ListIssue {
|
||||||
constructor(obj, defaultAvatar) {
|
constructor(obj, defaultAvatar) {
|
||||||
|
@ -29,6 +30,8 @@ class ListIssue {
|
||||||
this.toggleSubscriptionEndpoint = obj.toggle_subscription_endpoint;
|
this.toggleSubscriptionEndpoint = obj.toggle_subscription_endpoint;
|
||||||
this.milestone_id = obj.milestone_id;
|
this.milestone_id = obj.milestone_id;
|
||||||
this.project_id = obj.project_id;
|
this.project_id = obj.project_id;
|
||||||
|
this.timeEstimate = obj.time_estimate;
|
||||||
|
this.assignableLabelsEndpoint = obj.assignable_labels_endpoint;
|
||||||
|
|
||||||
if (obj.project) {
|
if (obj.project) {
|
||||||
this.project = new IssueProject(obj.project);
|
this.project = new IssueProject(obj.project);
|
||||||
|
@ -38,7 +41,7 @@ class ListIssue {
|
||||||
this.milestone = new ListMilestone(obj.milestone);
|
this.milestone = new ListMilestone(obj.milestone);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj.labels.forEach((label) => {
|
obj.labels.forEach(label => {
|
||||||
this.labels.push(new ListLabel(label));
|
this.labels.push(new ListLabel(label));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -86,7 +89,7 @@ class ListIssue {
|
||||||
}
|
}
|
||||||
|
|
||||||
getLists() {
|
getLists() {
|
||||||
return gl.issueBoards.BoardsStore.state.lists.filter(list => list.findIssue(this.id));
|
return boardsStore.state.lists.filter(list => list.findIssue(this.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
updateData(newData) {
|
updateData(newData) {
|
||||||
|
@ -106,9 +109,9 @@ class ListIssue {
|
||||||
issue: {
|
issue: {
|
||||||
milestone_id: this.milestone ? this.milestone.id : null,
|
milestone_id: this.milestone ? this.milestone.id : null,
|
||||||
due_date: this.dueDate,
|
due_date: this.dueDate,
|
||||||
assignee_ids: this.assignees.length > 0 ? this.assignees.map((u) => u.id) : [0],
|
assignee_ids: this.assignees.length > 0 ? this.assignees.map(u => u.id) : [0],
|
||||||
label_ids: this.labels.map((label) => label.id)
|
label_ids: this.labels.map(label => label.id),
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!data.issue.label_ids.length) {
|
if (!data.issue.label_ids.length) {
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
/* eslint-disable no-underscore-dangle, class-methods-use-this, consistent-return, no-shadow, no-param-reassign, max-len */
|
/* eslint-disable no-underscore-dangle, class-methods-use-this, consistent-return, no-shadow, no-param-reassign */
|
||||||
/* global ListIssue */
|
/* global ListIssue */
|
||||||
|
|
||||||
import { __ } from '~/locale';
|
import { __ } from '~/locale';
|
||||||
import ListLabel from '~/vue_shared/models/label';
|
import ListLabel from '~/vue_shared/models/label';
|
||||||
import ListAssignee from '~/vue_shared/models/assignee';
|
import ListAssignee from '~/vue_shared/models/assignee';
|
||||||
import { urlParamsToObject } from '~/lib/utils/common_utils';
|
import { urlParamsToObject } from '~/lib/utils/common_utils';
|
||||||
|
import boardsStore from '../stores/boards_store';
|
||||||
|
|
||||||
const PER_PAGE = 20;
|
const PER_PAGE = 20;
|
||||||
|
|
||||||
|
@ -89,9 +90,9 @@ class List {
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
const index = gl.issueBoards.BoardsStore.state.lists.indexOf(this);
|
const index = boardsStore.state.lists.indexOf(this);
|
||||||
gl.issueBoards.BoardsStore.state.lists.splice(index, 1);
|
boardsStore.state.lists.splice(index, 1);
|
||||||
gl.issueBoards.BoardsStore.updateNewListDropdown(this.id);
|
boardsStore.updateNewListDropdown(this.id);
|
||||||
|
|
||||||
gl.boardService.destroyList(this.id).catch(() => {
|
gl.boardService.destroyList(this.id).catch(() => {
|
||||||
// TODO: handle request error
|
// TODO: handle request error
|
||||||
|
@ -116,7 +117,7 @@ class List {
|
||||||
|
|
||||||
getIssues(emptyIssues = true) {
|
getIssues(emptyIssues = true) {
|
||||||
const data = {
|
const data = {
|
||||||
...urlParamsToObject(gl.issueBoards.BoardsStore.filter.path),
|
...urlParamsToObject(boardsStore.filter.path),
|
||||||
page: this.page,
|
page: this.page,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,9 @@ export default class BoardService {
|
||||||
}
|
}
|
||||||
|
|
||||||
static generateIssuePath(boardId, id) {
|
static generateIssuePath(boardId, id) {
|
||||||
return `${gon.relative_url_root}/-/boards/${boardId ? `${boardId}` : ''}/issues${id ? `/${id}` : ''}`;
|
return `${gon.relative_url_root}/-/boards/${boardId ? `${boardId}` : ''}/issues${
|
||||||
|
id ? `/${id}` : ''
|
||||||
|
}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
all() {
|
all() {
|
||||||
|
@ -54,7 +56,9 @@ export default class BoardService {
|
||||||
|
|
||||||
getIssuesForList(id, filter = {}) {
|
getIssuesForList(id, filter = {}) {
|
||||||
const data = { id };
|
const data = { id };
|
||||||
Object.keys(filter).forEach((key) => { data[key] = filter[key]; });
|
Object.keys(filter).forEach(key => {
|
||||||
|
data[key] = filter[key];
|
||||||
|
});
|
||||||
|
|
||||||
return axios.get(mergeUrlParams(data, this.generateIssuesPath(id)));
|
return axios.get(mergeUrlParams(data, this.generateIssuesPath(id)));
|
||||||
}
|
}
|
||||||
|
@ -75,7 +79,9 @@ export default class BoardService {
|
||||||
}
|
}
|
||||||
|
|
||||||
getBacklog(data) {
|
getBacklog(data) {
|
||||||
return axios.get(mergeUrlParams(data, `${gon.relative_url_root}/-/boards/${this.boardId}/issues.json`));
|
return axios.get(
|
||||||
|
mergeUrlParams(data, `${gon.relative_url_root}/-/boards/${this.boardId}/issues.json`),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
bulkUpdate(issueIds, extraData = {}) {
|
bulkUpdate(issueIds, extraData = {}) {
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
/* eslint-disable comma-dangle, no-shadow */
|
/* eslint-disable no-shadow */
|
||||||
/* global List */
|
/* global List */
|
||||||
|
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import _ from 'underscore';
|
import _ from 'underscore';
|
||||||
|
import Vue from 'vue';
|
||||||
import Cookies from 'js-cookie';
|
import Cookies from 'js-cookie';
|
||||||
import { getUrlParamsArray } from '~/lib/utils/common_utils';
|
import { getUrlParamsArray } from '~/lib/utils/common_utils';
|
||||||
|
|
||||||
window.gl = window.gl || {};
|
const boardsStore = {
|
||||||
window.gl.issueBoards = window.gl.issueBoards || {};
|
|
||||||
|
|
||||||
gl.issueBoards.BoardsStore = {
|
|
||||||
disabled: false,
|
disabled: false,
|
||||||
filter: {
|
filter: {
|
||||||
path: '',
|
path: '',
|
||||||
|
@ -57,7 +55,7 @@ gl.issueBoards.BoardsStore = {
|
||||||
},
|
},
|
||||||
shouldAddBlankState() {
|
shouldAddBlankState() {
|
||||||
// Decide whether to add the blank state
|
// Decide whether to add the blank state
|
||||||
return !(this.state.lists.filter(list => list.type !== 'backlog' && list.type !== 'closed')[0]);
|
return !this.state.lists.filter(list => list.type !== 'backlog' && list.type !== 'closed')[0];
|
||||||
},
|
},
|
||||||
addBlankState() {
|
addBlankState() {
|
||||||
if (!this.shouldAddBlankState() || this.welcomeIsHidden() || this.disabled) return;
|
if (!this.shouldAddBlankState() || this.welcomeIsHidden() || this.disabled) return;
|
||||||
|
@ -66,7 +64,7 @@ gl.issueBoards.BoardsStore = {
|
||||||
id: 'blank',
|
id: 'blank',
|
||||||
list_type: 'blank',
|
list_type: 'blank',
|
||||||
title: 'Welcome to your Issue Board!',
|
title: 'Welcome to your Issue Board!',
|
||||||
position: 0
|
position: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.state.lists = _.sortBy(this.state.lists, 'position');
|
this.state.lists = _.sortBy(this.state.lists, 'position');
|
||||||
|
@ -76,7 +74,7 @@ gl.issueBoards.BoardsStore = {
|
||||||
|
|
||||||
Cookies.set('issue_board_welcome_hidden', 'true', {
|
Cookies.set('issue_board_welcome_hidden', 'true', {
|
||||||
expires: 365 * 10,
|
expires: 365 * 10,
|
||||||
path: ''
|
path: '',
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
welcomeIsHidden() {
|
welcomeIsHidden() {
|
||||||
|
@ -104,14 +102,17 @@ gl.issueBoards.BoardsStore = {
|
||||||
|
|
||||||
if (!issueTo) {
|
if (!issueTo) {
|
||||||
// Check if target list assignee is already present in this issue
|
// Check if target list assignee is already present in this issue
|
||||||
if ((listTo.type === 'assignee' && listFrom.type === 'assignee') &&
|
if (
|
||||||
issue.findAssignee(listTo.assignee)) {
|
listTo.type === 'assignee' &&
|
||||||
|
listFrom.type === 'assignee' &&
|
||||||
|
issue.findAssignee(listTo.assignee)
|
||||||
|
) {
|
||||||
const targetIssue = listTo.findIssue(issue.id);
|
const targetIssue = listTo.findIssue(issue.id);
|
||||||
targetIssue.removeAssignee(listFrom.assignee);
|
targetIssue.removeAssignee(listFrom.assignee);
|
||||||
} else if (listTo.type === 'milestone') {
|
} else if (listTo.type === 'milestone') {
|
||||||
const currentMilestone = issue.milestone;
|
const currentMilestone = issue.milestone;
|
||||||
const currentLists = this.state.lists
|
const currentLists = this.state.lists
|
||||||
.filter(list => (list.type === 'milestone' && list.id !== listTo.id))
|
.filter(list => list.type === 'milestone' && list.id !== listTo.id)
|
||||||
.filter(list => list.issues.some(listIssue => issue.id === listIssue.id));
|
.filter(list => list.issues.some(listIssue => issue.id === listIssue.id));
|
||||||
|
|
||||||
issue.removeMilestone(currentMilestone);
|
issue.removeMilestone(currentMilestone);
|
||||||
|
@ -128,7 +129,7 @@ gl.issueBoards.BoardsStore = {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (listTo.type === 'closed' && listFrom.type !== 'backlog') {
|
if (listTo.type === 'closed' && listFrom.type !== 'backlog') {
|
||||||
issueLists.forEach((list) => {
|
issueLists.forEach(list => {
|
||||||
list.removeIssue(issue);
|
list.removeIssue(issue);
|
||||||
});
|
});
|
||||||
issue.removeLabels(listLabels);
|
issue.removeLabels(listLabels);
|
||||||
|
@ -146,7 +147,7 @@ gl.issueBoards.BoardsStore = {
|
||||||
return (
|
return (
|
||||||
(listTo.type !== 'label' && listFrom.type === 'assignee') ||
|
(listTo.type !== 'label' && listFrom.type === 'assignee') ||
|
||||||
(listTo.type !== 'assignee' && listFrom.type === 'label') ||
|
(listTo.type !== 'assignee' && listFrom.type === 'label') ||
|
||||||
(listFrom.type === 'backlog')
|
listFrom.type === 'backlog'
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
moveIssueInList(list, issue, oldIndex, newIndex, idArray) {
|
moveIssueInList(list, issue, oldIndex, newIndex, idArray) {
|
||||||
|
@ -156,8 +157,10 @@ gl.issueBoards.BoardsStore = {
|
||||||
list.moveIssue(issue, oldIndex, newIndex, beforeId, afterId);
|
list.moveIssue(issue, oldIndex, newIndex, beforeId, afterId);
|
||||||
},
|
},
|
||||||
findList(key, val, type = 'label') {
|
findList(key, val, type = 'label') {
|
||||||
const filteredList = this.state.lists.filter((list) => {
|
const filteredList = this.state.lists.filter(list => {
|
||||||
const byType = type ? (list.type === type) || (list.type === 'assignee') || (list.type === 'milestone') : true;
|
const byType = type
|
||||||
|
? list.type === type || list.type === 'assignee' || list.type === 'milestone'
|
||||||
|
: true;
|
||||||
|
|
||||||
return list[key] === val && byType;
|
return list[key] === val && byType;
|
||||||
});
|
});
|
||||||
|
@ -165,5 +168,18 @@ gl.issueBoards.BoardsStore = {
|
||||||
},
|
},
|
||||||
updateFiltersUrl() {
|
updateFiltersUrl() {
|
||||||
window.history.pushState(null, null, `?${this.filter.path}`);
|
window.history.pushState(null, null, `?${this.filter.path}`);
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// hacks added in order to allow milestone_select to function properly
|
||||||
|
// TODO: remove these
|
||||||
|
|
||||||
|
export function boardStoreIssueSet(...args) {
|
||||||
|
Vue.set(boardsStore.detail.issue, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function boardStoreIssueDelete(...args) {
|
||||||
|
Vue.delete(boardsStore.detail.issue, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default boardsStore;
|
||||||
|
|
|
@ -40,7 +40,7 @@ class ModalStore {
|
||||||
toggleAll() {
|
toggleAll() {
|
||||||
const select = this.selectedCount() !== this.store.issues.length;
|
const select = this.selectedCount() !== this.store.issues.length;
|
||||||
|
|
||||||
this.store.issues.forEach((issue) => {
|
this.store.issues.forEach(issue => {
|
||||||
const issueUpdate = issue;
|
const issueUpdate = issue;
|
||||||
|
|
||||||
if (issueUpdate.selected !== select) {
|
if (issueUpdate.selected !== select) {
|
||||||
|
@ -69,13 +69,14 @@ class ModalStore {
|
||||||
|
|
||||||
removeSelectedIssue(issue, forcePurge = false) {
|
removeSelectedIssue(issue, forcePurge = false) {
|
||||||
if (this.store.activeTab === 'all' || forcePurge) {
|
if (this.store.activeTab === 'all' || forcePurge) {
|
||||||
this.store.selectedIssues = this.store.selectedIssues
|
this.store.selectedIssues = this.store.selectedIssues.filter(
|
||||||
.filter(fIssue => fIssue.id !== issue.id);
|
fIssue => fIssue.id !== issue.id,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
purgeUnselectedIssues() {
|
purgeUnselectedIssues() {
|
||||||
this.store.selectedIssues.forEach((issue) => {
|
this.store.selectedIssues.forEach(issue => {
|
||||||
if (!issue.selected) {
|
if (!issue.selected) {
|
||||||
this.removeSelectedIssue(issue, true);
|
this.removeSelectedIssue(issue, true);
|
||||||
}
|
}
|
||||||
|
@ -87,8 +88,7 @@ class ModalStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
findSelectedIssue(issue) {
|
findSelectedIssue(issue) {
|
||||||
return this.store.selectedIssues
|
return this.store.selectedIssues.filter(filteredIssue => filteredIssue.id === issue.id)[0];
|
||||||
.filter(filteredIssue => filteredIssue.id === issue.id)[0];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
|
|
||||||
export const addTooltipToEl = (el) => {
|
export const addTooltipToEl = el => {
|
||||||
const textEl = el.querySelector('.js-breadcrumb-item-text');
|
const textEl = el.querySelector('.js-breadcrumb-item-text');
|
||||||
|
|
||||||
if (textEl && textEl.scrollWidth > textEl.offsetWidth) {
|
if (textEl && textEl.scrollWidth > textEl.offsetWidth) {
|
||||||
|
@ -14,16 +14,17 @@ export default () => {
|
||||||
const breadcrumbs = document.querySelector('.js-breadcrumbs-list');
|
const breadcrumbs = document.querySelector('.js-breadcrumbs-list');
|
||||||
|
|
||||||
if (breadcrumbs) {
|
if (breadcrumbs) {
|
||||||
const topLevelLinks = [...breadcrumbs.children].filter(el => !el.classList.contains('dropdown'))
|
const topLevelLinks = [...breadcrumbs.children]
|
||||||
|
.filter(el => !el.classList.contains('dropdown'))
|
||||||
.map(el => el.querySelector('a'))
|
.map(el => el.querySelector('a'))
|
||||||
.filter(el => el);
|
.filter(el => el);
|
||||||
const $expander = $('.js-breadcrumbs-collapsed-expander');
|
const $expander = $('.js-breadcrumbs-collapsed-expander');
|
||||||
|
|
||||||
topLevelLinks.forEach(el => addTooltipToEl(el));
|
topLevelLinks.forEach(el => addTooltipToEl(el));
|
||||||
|
|
||||||
$expander.closest('.dropdown')
|
$expander.closest('.dropdown').on('show.bs.dropdown hide.bs.dropdown', e => {
|
||||||
.on('show.bs.dropdown hide.bs.dropdown', (e) => {
|
$('.js-breadcrumbs-collapsed-expander', e.currentTarget)
|
||||||
$('.js-breadcrumbs-collapsed-expander', e.currentTarget).toggleClass('open')
|
.toggleClass('open')
|
||||||
.tooltip('hide');
|
.tooltip('hide');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,11 +37,15 @@ export default class BuildArtifacts {
|
||||||
// We want the tooltip to show if you hover anywhere on the row
|
// We want the tooltip to show if you hover anywhere on the row
|
||||||
// But be placed below and in the middle of the file name
|
// But be placed below and in the middle of the file name
|
||||||
$('.js-artifact-tree-row')
|
$('.js-artifact-tree-row')
|
||||||
.on('mouseenter', (e) => {
|
.on('mouseenter', e => {
|
||||||
$(e.currentTarget).find('.js-artifact-tree-tooltip').tooltip('show');
|
$(e.currentTarget)
|
||||||
|
.find('.js-artifact-tree-tooltip')
|
||||||
|
.tooltip('show');
|
||||||
})
|
})
|
||||||
.on('mouseleave', (e) => {
|
.on('mouseleave', e => {
|
||||||
$(e.currentTarget).find('.js-artifact-tree-tooltip').tooltip('hide');
|
$(e.currentTarget)
|
||||||
|
.find('.js-artifact-tree-tooltip')
|
||||||
|
.tooltip('hide');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,11 +7,13 @@ import statusCodes from '../lib/utils/http_status';
|
||||||
import VariableList from './ci_variable_list';
|
import VariableList from './ci_variable_list';
|
||||||
|
|
||||||
function generateErrorBoxContent(errors) {
|
function generateErrorBoxContent(errors) {
|
||||||
const errorList = [].concat(errors).map(errorString => `
|
const errorList = [].concat(errors).map(
|
||||||
|
errorString => `
|
||||||
<li>
|
<li>
|
||||||
${_.escape(errorString)}
|
${_.escape(errorString)}
|
||||||
</li>
|
</li>
|
||||||
`);
|
`,
|
||||||
|
);
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<p>
|
<p>
|
||||||
|
@ -25,13 +27,7 @@ function generateErrorBoxContent(errors) {
|
||||||
|
|
||||||
// Used for the variable list on CI/CD projects/groups settings page
|
// Used for the variable list on CI/CD projects/groups settings page
|
||||||
export default class AjaxVariableList {
|
export default class AjaxVariableList {
|
||||||
constructor({
|
constructor({ container, saveButton, errorBox, formField = 'variables', saveEndpoint }) {
|
||||||
container,
|
|
||||||
saveButton,
|
|
||||||
errorBox,
|
|
||||||
formField = 'variables',
|
|
||||||
saveEndpoint,
|
|
||||||
}) {
|
|
||||||
this.container = container;
|
this.container = container;
|
||||||
this.saveButton = saveButton;
|
this.saveButton = saveButton;
|
||||||
this.errorBox = errorBox;
|
this.errorBox = errorBox;
|
||||||
|
@ -51,25 +47,28 @@ export default class AjaxVariableList {
|
||||||
}
|
}
|
||||||
|
|
||||||
onSaveClicked() {
|
onSaveClicked() {
|
||||||
const loadingIcon = this.saveButton.querySelector('.js-secret-variables-save-loading-icon');
|
const loadingIcon = this.saveButton.querySelector('.js-ci-variables-save-loading-icon');
|
||||||
loadingIcon.classList.toggle('hide', false);
|
loadingIcon.classList.toggle('hide', false);
|
||||||
this.errorBox.classList.toggle('hide', true);
|
this.errorBox.classList.toggle('hide', true);
|
||||||
// We use this to prevent a user from changing a key before we have a chance
|
// We use this to prevent a user from changing a key before we have a chance
|
||||||
// to match it up in `updateRowsWithPersistedVariables`
|
// to match it up in `updateRowsWithPersistedVariables`
|
||||||
this.variableList.toggleEnableRow(false);
|
this.variableList.toggleEnableRow(false);
|
||||||
|
|
||||||
return axios.patch(this.saveEndpoint, {
|
return axios
|
||||||
|
.patch(
|
||||||
|
this.saveEndpoint,
|
||||||
|
{
|
||||||
variables_attributes: this.variableList.getAllData(),
|
variables_attributes: this.variableList.getAllData(),
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
// We want to be able to process the `res.data` from a 400 error response
|
// We want to be able to process the `res.data` from a 400 error response
|
||||||
// and print the validation messages such as duplicate variable keys
|
// and print the validation messages such as duplicate variable keys
|
||||||
validateStatus: status => (
|
validateStatus: status =>
|
||||||
status >= statusCodes.OK &&
|
(status >= statusCodes.OK && status < statusCodes.MULTIPLE_CHOICES) ||
|
||||||
status < statusCodes.MULTIPLE_CHOICES
|
|
||||||
) ||
|
|
||||||
status === statusCodes.BAD_REQUEST,
|
status === statusCodes.BAD_REQUEST,
|
||||||
})
|
},
|
||||||
.then((res) => {
|
)
|
||||||
|
.then(res => {
|
||||||
loadingIcon.classList.toggle('hide', true);
|
loadingIcon.classList.toggle('hide', true);
|
||||||
this.variableList.toggleEnableRow(true);
|
this.variableList.toggleEnableRow(true);
|
||||||
|
|
||||||
|
@ -90,12 +89,15 @@ export default class AjaxVariableList {
|
||||||
}
|
}
|
||||||
|
|
||||||
updateRowsWithPersistedVariables(persistedVariables = []) {
|
updateRowsWithPersistedVariables(persistedVariables = []) {
|
||||||
const persistedVariableMap = [].concat(persistedVariables).reduce((variableMap, variable) => ({
|
const persistedVariableMap = [].concat(persistedVariables).reduce(
|
||||||
|
(variableMap, variable) => ({
|
||||||
...variableMap,
|
...variableMap,
|
||||||
[variable.key]: variable,
|
[variable.key]: variable,
|
||||||
}), {});
|
}),
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
|
||||||
this.container.querySelectorAll('.js-row').forEach((row) => {
|
this.container.querySelectorAll('.js-row').forEach(row => {
|
||||||
// If we submitted a row that was destroyed, remove it so we don't try
|
// If we submitted a row that was destroyed, remove it so we don't try
|
||||||
// to destroy it again which would cause a BE error
|
// to destroy it again which would cause a BE error
|
||||||
const destroyInput = row.querySelector('.js-ci-variable-input-destroy');
|
const destroyInput = row.querySelector('.js-ci-variable-input-destroy');
|
||||||
|
|
|
@ -16,10 +16,7 @@ function createEnvironmentItem(value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class VariableList {
|
export default class VariableList {
|
||||||
constructor({
|
constructor({ container, formField }) {
|
||||||
container,
|
|
||||||
formField,
|
|
||||||
}) {
|
|
||||||
this.$container = $(container);
|
this.$container = $(container);
|
||||||
this.formField = formField;
|
this.formField = formField;
|
||||||
this.environmentDropdownMap = new WeakMap();
|
this.environmentDropdownMap = new WeakMap();
|
||||||
|
@ -71,7 +68,7 @@ export default class VariableList {
|
||||||
this.initRow(rowEl);
|
this.initRow(rowEl);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$container.on('click', '.js-row-remove-button', (e) => {
|
this.$container.on('click', '.js-row-remove-button', e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.removeRow($(e.currentTarget).closest('.js-row'));
|
this.removeRow($(e.currentTarget).closest('.js-row'));
|
||||||
});
|
});
|
||||||
|
@ -81,7 +78,7 @@ export default class VariableList {
|
||||||
.join(',');
|
.join(',');
|
||||||
|
|
||||||
// Remove any empty rows except the last row
|
// Remove any empty rows except the last row
|
||||||
this.$container.on('blur', inputSelector, (e) => {
|
this.$container.on('blur', inputSelector, e => {
|
||||||
const $row = $(e.currentTarget).closest('.js-row');
|
const $row = $(e.currentTarget).closest('.js-row');
|
||||||
|
|
||||||
if ($row.is(':not(:last-child)') && !this.checkIfRowTouched($row)) {
|
if ($row.is(':not(:last-child)') && !this.checkIfRowTouched($row)) {
|
||||||
|
@ -136,7 +133,7 @@ export default class VariableList {
|
||||||
$rowClone.removeAttr('data-is-persisted');
|
$rowClone.removeAttr('data-is-persisted');
|
||||||
|
|
||||||
// Reset the inputs to their defaults
|
// Reset the inputs to their defaults
|
||||||
Object.keys(this.inputMap).forEach((name) => {
|
Object.keys(this.inputMap).forEach(name => {
|
||||||
const entry = this.inputMap[name];
|
const entry = this.inputMap[name];
|
||||||
$rowClone.find(entry.selector).val(entry.default);
|
$rowClone.find(entry.selector).val(entry.default);
|
||||||
});
|
});
|
||||||
|
@ -171,7 +168,7 @@ export default class VariableList {
|
||||||
}
|
}
|
||||||
|
|
||||||
checkIfRowTouched($row) {
|
checkIfRowTouched($row) {
|
||||||
return Object.keys(this.inputMap).some((name) => {
|
return Object.keys(this.inputMap).some(name => {
|
||||||
const entry = this.inputMap[name];
|
const entry = this.inputMap[name];
|
||||||
const $el = $row.find(entry.selector);
|
const $el = $row.find(entry.selector);
|
||||||
return $el.length && $el.val() !== entry.default;
|
return $el.length && $el.val() !== entry.default;
|
||||||
|
@ -190,11 +187,14 @@ export default class VariableList {
|
||||||
getAllData() {
|
getAllData() {
|
||||||
// Ignore the last empty row because we don't want to try persist
|
// Ignore the last empty row because we don't want to try persist
|
||||||
// a blank variable and run into validation problems.
|
// a blank variable and run into validation problems.
|
||||||
const validRows = this.$container.find('.js-row').toArray().slice(0, -1);
|
const validRows = this.$container
|
||||||
|
.find('.js-row')
|
||||||
|
.toArray()
|
||||||
|
.slice(0, -1);
|
||||||
|
|
||||||
return validRows.map((rowEl) => {
|
return validRows.map(rowEl => {
|
||||||
const resultant = {};
|
const resultant = {};
|
||||||
Object.keys(this.inputMap).forEach((name) => {
|
Object.keys(this.inputMap).forEach(name => {
|
||||||
const entry = this.inputMap[name];
|
const entry = this.inputMap[name];
|
||||||
const $input = $(rowEl).find(entry.selector);
|
const $input = $(rowEl).find(entry.selector);
|
||||||
if ($input.length) {
|
if ($input.length) {
|
||||||
|
@ -207,11 +207,16 @@ export default class VariableList {
|
||||||
}
|
}
|
||||||
|
|
||||||
getEnvironmentValues() {
|
getEnvironmentValues() {
|
||||||
const valueMap = this.$container.find(this.inputMap.environment_scope.selector).toArray()
|
const valueMap = this.$container
|
||||||
.reduce((prevValueMap, envInput) => ({
|
.find(this.inputMap.environment_scope.selector)
|
||||||
|
.toArray()
|
||||||
|
.reduce(
|
||||||
|
(prevValueMap, envInput) => ({
|
||||||
...prevValueMap,
|
...prevValueMap,
|
||||||
[envInput.value]: envInput.value,
|
[envInput.value]: envInput.value,
|
||||||
}), {});
|
}),
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
|
||||||
return Object.keys(valueMap).map(createEnvironmentItem);
|
return Object.keys(valueMap).map(createEnvironmentItem);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,7 @@ import $ from 'jquery';
|
||||||
import VariableList from './ci_variable_list';
|
import VariableList from './ci_variable_list';
|
||||||
|
|
||||||
// Used for the variable list on scheduled pipeline edit page
|
// Used for the variable list on scheduled pipeline edit page
|
||||||
export default function setupNativeFormVariableList({
|
export default function setupNativeFormVariableList({ container, formField = 'variables' }) {
|
||||||
container,
|
|
||||||
formField = 'variables',
|
|
||||||
}) {
|
|
||||||
const $container = $(container);
|
const $container = $(container);
|
||||||
|
|
||||||
const variableList = new VariableList({
|
const variableList = new VariableList({
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import Visibility from 'visibilityjs';
|
import Visibility from 'visibilityjs';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import PersistentUserCallout from '../persistent_user_callout';
|
import initDismissableCallout from '~/dismissable_callout';
|
||||||
import { s__, sprintf } from '../locale';
|
import { s__, sprintf } from '../locale';
|
||||||
import Flash from '../flash';
|
import Flash from '../flash';
|
||||||
import Poll from '../lib/utils/poll';
|
import Poll from '../lib/utils/poll';
|
||||||
|
@ -28,6 +28,7 @@ export default class Clusters {
|
||||||
installIngressPath,
|
installIngressPath,
|
||||||
installRunnerPath,
|
installRunnerPath,
|
||||||
installJupyterPath,
|
installJupyterPath,
|
||||||
|
installKnativePath,
|
||||||
installPrometheusPath,
|
installPrometheusPath,
|
||||||
managePrometheusPath,
|
managePrometheusPath,
|
||||||
clusterStatus,
|
clusterStatus,
|
||||||
|
@ -49,6 +50,7 @@ export default class Clusters {
|
||||||
installRunnerEndpoint: installRunnerPath,
|
installRunnerEndpoint: installRunnerPath,
|
||||||
installPrometheusEndpoint: installPrometheusPath,
|
installPrometheusEndpoint: installPrometheusPath,
|
||||||
installJupyterEndpoint: installJupyterPath,
|
installJupyterEndpoint: installJupyterPath,
|
||||||
|
installKnativeEndpoint: installKnativePath,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.installApplication = this.installApplication.bind(this);
|
this.installApplication = this.installApplication.bind(this);
|
||||||
|
@ -62,7 +64,7 @@ export default class Clusters {
|
||||||
this.showTokenButton = document.querySelector('.js-show-cluster-token');
|
this.showTokenButton = document.querySelector('.js-show-cluster-token');
|
||||||
this.tokenField = document.querySelector('.js-cluster-token');
|
this.tokenField = document.querySelector('.js-cluster-token');
|
||||||
|
|
||||||
Clusters.initDismissableCallout();
|
initDismissableCallout('.js-cluster-security-warning');
|
||||||
initSettingsPanels();
|
initSettingsPanels();
|
||||||
setupToggleButtons(document.querySelector('.js-cluster-enable-toggle-area'));
|
setupToggleButtons(document.querySelector('.js-cluster-enable-toggle-area'));
|
||||||
this.initApplications();
|
this.initApplications();
|
||||||
|
@ -105,12 +107,6 @@ export default class Clusters {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static initDismissableCallout() {
|
|
||||||
const callout = document.querySelector('.js-cluster-security-warning');
|
|
||||||
|
|
||||||
if (callout) new PersistentUserCallout(callout); // eslint-disable-line no-new
|
|
||||||
}
|
|
||||||
|
|
||||||
addListeners() {
|
addListeners() {
|
||||||
if (this.showTokenButton) this.showTokenButton.addEventListener('click', this.showToken);
|
if (this.showTokenButton) this.showTokenButton.addEventListener('click', this.showToken);
|
||||||
eventHub.$on('installApplication', this.installApplication);
|
eventHub.$on('installApplication', this.installApplication);
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
import createFlash from '~/flash';
|
import createFlash from '~/flash';
|
||||||
import { __ } from '~/locale';
|
import { __ } from '~/locale';
|
||||||
import setupToggleButtons from '~/toggle_buttons';
|
import setupToggleButtons from '~/toggle_buttons';
|
||||||
import PersistentUserCallout from '../persistent_user_callout';
|
import initDismissableCallout from '~/dismissable_callout';
|
||||||
|
|
||||||
import ClustersService from './services/clusters_service';
|
import ClustersService from './services/clusters_service';
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
const clusterList = document.querySelector('.js-clusters-list');
|
const clusterList = document.querySelector('.js-clusters-list');
|
||||||
|
|
||||||
const callout = document.querySelector('.gcp-signup-offer');
|
initDismissableCallout('.gcp-signup-offer');
|
||||||
if (callout) new PersistentUserCallout(callout); // eslint-disable-line no-new
|
|
||||||
|
|
||||||
// The empty state won't have a clusterList
|
// The empty state won't have a clusterList
|
||||||
if (clusterList) {
|
if (clusterList) {
|
||||||
|
|
|
@ -74,7 +74,9 @@
|
||||||
},
|
},
|
||||||
isInstalled() {
|
isInstalled() {
|
||||||
return (
|
return (
|
||||||
this.status === APPLICATION_STATUS.INSTALLED || this.status === APPLICATION_STATUS.UPDATED
|
this.status === APPLICATION_STATUS.INSTALLED ||
|
||||||
|
this.status === APPLICATION_STATUS.UPDATED ||
|
||||||
|
this.status === APPLICATION_STATUS.UPDATING
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
hasLogo() {
|
hasLogo() {
|
||||||
|
@ -88,19 +90,24 @@
|
||||||
return `js-cluster-application-row-${this.id}`;
|
return `js-cluster-application-row-${this.id}`;
|
||||||
},
|
},
|
||||||
installButtonLoading() {
|
installButtonLoading() {
|
||||||
return !this.status ||
|
return (
|
||||||
|
!this.status ||
|
||||||
this.status === APPLICATION_STATUS.SCHEDULED ||
|
this.status === APPLICATION_STATUS.SCHEDULED ||
|
||||||
this.status === APPLICATION_STATUS.INSTALLING ||
|
this.status === APPLICATION_STATUS.INSTALLING ||
|
||||||
this.requestStatus === REQUEST_LOADING;
|
this.requestStatus === REQUEST_LOADING
|
||||||
|
);
|
||||||
},
|
},
|
||||||
installButtonDisabled() {
|
installButtonDisabled() {
|
||||||
// Avoid the potential for the real-time data to say APPLICATION_STATUS.INSTALLABLE but
|
// Avoid the potential for the real-time data to say APPLICATION_STATUS.INSTALLABLE but
|
||||||
// we already made a request to install and are just waiting for the real-time
|
// we already made a request to install and are just waiting for the real-time
|
||||||
// to sync up.
|
// to sync up.
|
||||||
return ((this.status !== APPLICATION_STATUS.INSTALLABLE
|
return (
|
||||||
&& this.status !== APPLICATION_STATUS.ERROR) ||
|
((this.status !== APPLICATION_STATUS.INSTALLABLE &&
|
||||||
|
this.status !== APPLICATION_STATUS.ERROR) ||
|
||||||
this.requestStatus === REQUEST_LOADING ||
|
this.requestStatus === REQUEST_LOADING ||
|
||||||
this.requestStatus === REQUEST_SUCCESS) && this.isKnownStatus;
|
this.requestStatus === REQUEST_SUCCESS) &&
|
||||||
|
this.isKnownStatus
|
||||||
|
);
|
||||||
},
|
},
|
||||||
installButtonLabel() {
|
installButtonLabel() {
|
||||||
let label;
|
let label;
|
||||||
|
@ -111,11 +118,16 @@
|
||||||
this.isUnknownStatus
|
this.isUnknownStatus
|
||||||
) {
|
) {
|
||||||
label = s__('ClusterIntegration|Install');
|
label = s__('ClusterIntegration|Install');
|
||||||
} else if (this.status === APPLICATION_STATUS.SCHEDULED ||
|
} else if (
|
||||||
this.status === APPLICATION_STATUS.INSTALLING) {
|
this.status === APPLICATION_STATUS.SCHEDULED ||
|
||||||
|
this.status === APPLICATION_STATUS.INSTALLING
|
||||||
|
) {
|
||||||
label = s__('ClusterIntegration|Installing');
|
label = s__('ClusterIntegration|Installing');
|
||||||
} else if (this.status === APPLICATION_STATUS.INSTALLED ||
|
} else if (
|
||||||
this.status === APPLICATION_STATUS.UPDATED) {
|
this.status === APPLICATION_STATUS.INSTALLED ||
|
||||||
|
this.status === APPLICATION_STATUS.UPDATED ||
|
||||||
|
this.status === APPLICATION_STATUS.UPDATING
|
||||||
|
) {
|
||||||
label = s__('ClusterIntegration|Installed');
|
label = s__('ClusterIntegration|Installed');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,15 +140,12 @@
|
||||||
return s__('ClusterIntegration|Manage');
|
return s__('ClusterIntegration|Manage');
|
||||||
},
|
},
|
||||||
hasError() {
|
hasError() {
|
||||||
return this.status === APPLICATION_STATUS.ERROR ||
|
return this.status === APPLICATION_STATUS.ERROR || this.requestStatus === REQUEST_FAILURE;
|
||||||
this.requestStatus === REQUEST_FAILURE;
|
|
||||||
},
|
},
|
||||||
generalErrorDescription() {
|
generalErrorDescription() {
|
||||||
return sprintf(
|
return sprintf(s__('ClusterIntegration|Something went wrong while installing %{title}'), {
|
||||||
s__('ClusterIntegration|Something went wrong while installing %{title}'), {
|
|
||||||
title: this.title,
|
title: this.title,
|
||||||
},
|
});
|
||||||
);
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
<script>
|
<script>
|
||||||
import _ from 'underscore';
|
import _ from 'underscore';
|
||||||
import helmInstallIllustration from '@gitlab-org/gitlab-svgs/illustrations/kubernetes-installation.svg';
|
import helmInstallIllustration from '@gitlab/svgs/dist/illustrations/kubernetes-installation.svg';
|
||||||
import elasticsearchLogo from 'images/cluster_app_logos/elasticsearch.png';
|
import elasticsearchLogo from 'images/cluster_app_logos/elasticsearch.png';
|
||||||
import gitlabLogo from 'images/cluster_app_logos/gitlab.png';
|
import gitlabLogo from 'images/cluster_app_logos/gitlab.png';
|
||||||
import helmLogo from 'images/cluster_app_logos/helm.png';
|
import helmLogo from 'images/cluster_app_logos/helm.png';
|
||||||
import jeagerLogo from 'images/cluster_app_logos/jeager.png';
|
import jeagerLogo from 'images/cluster_app_logos/jeager.png';
|
||||||
import jupyterhubLogo from 'images/cluster_app_logos/jupyterhub.png';
|
import jupyterhubLogo from 'images/cluster_app_logos/jupyterhub.png';
|
||||||
import kubernetesLogo from 'images/cluster_app_logos/kubernetes.png';
|
import kubernetesLogo from 'images/cluster_app_logos/kubernetes.png';
|
||||||
|
import knativeLogo from 'images/cluster_app_logos/knative.png';
|
||||||
import meltanoLogo from 'images/cluster_app_logos/meltano.png';
|
import meltanoLogo from 'images/cluster_app_logos/meltano.png';
|
||||||
import prometheusLogo from 'images/cluster_app_logos/prometheus.png';
|
import prometheusLogo from 'images/cluster_app_logos/prometheus.png';
|
||||||
import { s__, sprintf } from '../../locale';
|
import { s__, sprintf } from '../../locale';
|
||||||
|
@ -53,6 +54,7 @@ export default {
|
||||||
jeagerLogo,
|
jeagerLogo,
|
||||||
jupyterhubLogo,
|
jupyterhubLogo,
|
||||||
kubernetesLogo,
|
kubernetesLogo,
|
||||||
|
knativeLogo,
|
||||||
meltanoLogo,
|
meltanoLogo,
|
||||||
prometheusLogo,
|
prometheusLogo,
|
||||||
}),
|
}),
|
||||||
|
@ -136,6 +138,9 @@ export default {
|
||||||
jupyterHostname() {
|
jupyterHostname() {
|
||||||
return this.applications.jupyter.hostname;
|
return this.applications.jupyter.hostname;
|
||||||
},
|
},
|
||||||
|
knativeInstalled() {
|
||||||
|
return this.applications.knative.status === APPLICATION_STATUS.INSTALLED;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.helmInstallIllustration = helmInstallIllustration;
|
this.helmInstallIllustration = helmInstallIllustration;
|
||||||
|
@ -321,7 +326,6 @@ export default {
|
||||||
:request-reason="applications.jupyter.requestReason"
|
:request-reason="applications.jupyter.requestReason"
|
||||||
:install-application-request-params="{ hostname: applications.jupyter.hostname }"
|
:install-application-request-params="{ hostname: applications.jupyter.hostname }"
|
||||||
:disabled="!helmInstalled"
|
:disabled="!helmInstalled"
|
||||||
class="hide-bottom-border rounded-bottom"
|
|
||||||
title-link="https://jupyterhub.readthedocs.io/en/stable/"
|
title-link="https://jupyterhub.readthedocs.io/en/stable/"
|
||||||
>
|
>
|
||||||
<div slot="description">
|
<div slot="description">
|
||||||
|
@ -371,6 +375,58 @@ export default {
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</application-row>
|
</application-row>
|
||||||
|
<application-row
|
||||||
|
id="knative"
|
||||||
|
:logo-url="knativeLogo"
|
||||||
|
:title="applications.knative.title"
|
||||||
|
:status="applications.knative.status"
|
||||||
|
:status-reason="applications.knative.statusReason"
|
||||||
|
:request-status="applications.knative.requestStatus"
|
||||||
|
:request-reason="applications.knative.requestReason"
|
||||||
|
:install-application-request-params="{ hostname: applications.knative.hostname}"
|
||||||
|
:disabled="!helmInstalled"
|
||||||
|
class="hide-bottom-border rounded-bottom"
|
||||||
|
title-link="https://github.com/knative/docs"
|
||||||
|
>
|
||||||
|
<div slot="description">
|
||||||
|
<p>
|
||||||
|
{{ s__(`ClusterIntegration|A Knative build extends Kubernetes
|
||||||
|
and utilizes existing Kubernetes primitives to provide you with
|
||||||
|
the ability to run on-cluster container builds from source.
|
||||||
|
For example, you can write a build that uses Kubernetes-native
|
||||||
|
resources to obtain your source code from a repository,
|
||||||
|
build it into container a image, and then run that image.`) }}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<template v-if="knativeInstalled">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="knative-domainname">
|
||||||
|
{{ s__('ClusterIntegration|Knative Domain Name:') }}
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="knative-domainname"
|
||||||
|
v-model="applications.knative.hostname"
|
||||||
|
type="text"
|
||||||
|
class="form-control js-domainname"
|
||||||
|
readonly
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="knative-domainname">
|
||||||
|
{{ s__('ClusterIntegration|Knative Domain Name:') }}
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="knative-domainname"
|
||||||
|
v-model="applications.knative.hostname"
|
||||||
|
type="text"
|
||||||
|
class="form-control js-domainname"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</application-row>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -6,6 +6,7 @@ export const APPLICATION_STATUS = {
|
||||||
INSTALLING: 'installing',
|
INSTALLING: 'installing',
|
||||||
INSTALLED: 'installed',
|
INSTALLED: 'installed',
|
||||||
UPDATED: 'updated',
|
UPDATED: 'updated',
|
||||||
|
UPDATING: 'updating',
|
||||||
ERROR: 'errored',
|
ERROR: 'errored',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -15,3 +16,4 @@ export const REQUEST_SUCCESS = 'request-success';
|
||||||
export const REQUEST_FAILURE = 'request-failure';
|
export const REQUEST_FAILURE = 'request-failure';
|
||||||
export const INGRESS = 'ingress';
|
export const INGRESS = 'ingress';
|
||||||
export const JUPYTER = 'jupyter';
|
export const JUPYTER = 'jupyter';
|
||||||
|
export const KNATIVE = 'knative';
|
||||||
|
|
|
@ -9,6 +9,7 @@ export default class ClusterService {
|
||||||
runner: this.options.installRunnerEndpoint,
|
runner: this.options.installRunnerEndpoint,
|
||||||
prometheus: this.options.installPrometheusEndpoint,
|
prometheus: this.options.installPrometheusEndpoint,
|
||||||
jupyter: this.options.installJupyterEndpoint,
|
jupyter: this.options.installJupyterEndpoint,
|
||||||
|
knative: this.options.installKnativeEndpoint,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { s__ } from '../../locale';
|
import { s__ } from '../../locale';
|
||||||
import { INGRESS, JUPYTER } from '../constants';
|
import { INGRESS, JUPYTER, KNATIVE } from '../constants';
|
||||||
|
|
||||||
export default class ClusterStore {
|
export default class ClusterStore {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -46,6 +46,14 @@ export default class ClusterStore {
|
||||||
requestReason: null,
|
requestReason: null,
|
||||||
hostname: null,
|
hostname: null,
|
||||||
},
|
},
|
||||||
|
knative: {
|
||||||
|
title: s__('ClusterIntegration|Knative'),
|
||||||
|
status: null,
|
||||||
|
statusReason: null,
|
||||||
|
requestStatus: null,
|
||||||
|
requestReason: null,
|
||||||
|
hostname: null,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -76,12 +84,8 @@ export default class ClusterStore {
|
||||||
this.state.status = serverState.status;
|
this.state.status = serverState.status;
|
||||||
this.state.statusReason = serverState.status_reason;
|
this.state.statusReason = serverState.status_reason;
|
||||||
|
|
||||||
serverState.applications.forEach((serverAppEntry) => {
|
serverState.applications.forEach(serverAppEntry => {
|
||||||
const {
|
const { name: appId, status, status_reason: statusReason } = serverAppEntry;
|
||||||
name: appId,
|
|
||||||
status,
|
|
||||||
status_reason: statusReason,
|
|
||||||
} = serverAppEntry;
|
|
||||||
|
|
||||||
this.state.applications[appId] = {
|
this.state.applications[appId] = {
|
||||||
...(this.state.applications[appId] || {}),
|
...(this.state.applications[appId] || {}),
|
||||||
|
@ -97,6 +101,9 @@ export default class ClusterStore {
|
||||||
(this.state.applications.ingress.externalIp
|
(this.state.applications.ingress.externalIp
|
||||||
? `jupyter.${this.state.applications.ingress.externalIp}.nip.io`
|
? `jupyter.${this.state.applications.ingress.externalIp}.nip.io`
|
||||||
: '');
|
: '');
|
||||||
|
} else if (appId === KNATIVE) {
|
||||||
|
this.state.applications.knative.hostname =
|
||||||
|
serverAppEntry.hostname || this.state.applications.knative.hostname;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,36 +24,44 @@ class CommentTypeToggle {
|
||||||
|
|
||||||
setConfig() {
|
setConfig() {
|
||||||
const config = {
|
const config = {
|
||||||
InputSetter: [{
|
InputSetter: [
|
||||||
|
{
|
||||||
input: this.noteTypeInput,
|
input: this.noteTypeInput,
|
||||||
valueAttribute: 'data-value',
|
valueAttribute: 'data-value',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: this.submitButton,
|
input: this.submitButton,
|
||||||
valueAttribute: 'data-submit-text',
|
valueAttribute: 'data-submit-text',
|
||||||
}],
|
},
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.closeButton) {
|
if (this.closeButton) {
|
||||||
config.InputSetter.push({
|
config.InputSetter.push(
|
||||||
|
{
|
||||||
input: this.closeButton,
|
input: this.closeButton,
|
||||||
valueAttribute: 'data-close-text',
|
valueAttribute: 'data-close-text',
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
input: this.closeButton,
|
input: this.closeButton,
|
||||||
valueAttribute: 'data-close-text',
|
valueAttribute: 'data-close-text',
|
||||||
inputAttribute: 'data-alternative-text',
|
inputAttribute: 'data-alternative-text',
|
||||||
});
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.reopenButton) {
|
if (this.reopenButton) {
|
||||||
config.InputSetter.push({
|
config.InputSetter.push(
|
||||||
|
{
|
||||||
input: this.reopenButton,
|
input: this.reopenButton,
|
||||||
valueAttribute: 'data-reopen-text',
|
valueAttribute: 'data-reopen-text',
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
input: this.reopenButton,
|
input: this.reopenButton,
|
||||||
valueAttribute: 'data-reopen-text',
|
valueAttribute: 'data-reopen-text',
|
||||||
inputAttribute: 'data-alternative-text',
|
inputAttribute: 'data-alternative-text',
|
||||||
});
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* eslint-disable func-names, wrap-iife, no-var, prefer-arrow-callback, no-else-return, consistent-return, prefer-template, quotes, one-var, one-var-declaration-per-line, no-unused-vars, no-return-assign, comma-dangle, quote-props, no-unused-expressions, no-sequences, max-len */
|
/* eslint-disable func-names, no-var, prefer-arrow-callback, no-else-return, consistent-return, prefer-template, one-var, no-unused-vars, no-return-assign, no-unused-expressions, no-sequences */
|
||||||
|
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
|
|
||||||
|
@ -9,9 +9,14 @@ const viewModes = ['two-up', 'swipe'];
|
||||||
export default class ImageFile {
|
export default class ImageFile {
|
||||||
constructor(file) {
|
constructor(file) {
|
||||||
this.file = file;
|
this.file = file;
|
||||||
this.requestImageInfo($('.two-up.view .frame.deleted img', this.file), (function(_this) {
|
this.requestImageInfo(
|
||||||
|
$('.two-up.view .frame.deleted img', this.file),
|
||||||
|
(function(_this) {
|
||||||
return function(deletedWidth, deletedHeight) {
|
return function(deletedWidth, deletedHeight) {
|
||||||
return _this.requestImageInfo($('.two-up.view .frame.added img', _this.file), function(width, height) {
|
return _this.requestImageInfo($('.two-up.view .frame.added img', _this.file), function(
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
) {
|
||||||
_this.initViewModes();
|
_this.initViewModes();
|
||||||
|
|
||||||
// Load two-up view after images are loaded
|
// Load two-up view after images are loaded
|
||||||
|
@ -23,30 +28,41 @@ export default class ImageFile {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
})(this));
|
})(this),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
initViewModes() {
|
initViewModes() {
|
||||||
const viewMode = viewModes[0];
|
const viewMode = viewModes[0];
|
||||||
$('.view-modes', this.file).removeClass('hide');
|
$('.view-modes', this.file).removeClass('hide');
|
||||||
$('.view-modes-menu', this.file).on('click', 'li', (function(_this) {
|
$('.view-modes-menu', this.file).on(
|
||||||
|
'click',
|
||||||
|
'li',
|
||||||
|
(function(_this) {
|
||||||
return function(event) {
|
return function(event) {
|
||||||
if (!$(event.currentTarget).hasClass('active')) {
|
if (!$(event.currentTarget).hasClass('active')) {
|
||||||
return _this.activateViewMode(event.currentTarget.className);
|
return _this.activateViewMode(event.currentTarget.className);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
})(this));
|
})(this),
|
||||||
|
);
|
||||||
return this.activateViewMode(viewMode);
|
return this.activateViewMode(viewMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
activateViewMode(viewMode) {
|
activateViewMode(viewMode) {
|
||||||
$('.view-modes-menu li', this.file).removeClass('active').filter("." + viewMode).addClass('active');
|
$('.view-modes-menu li', this.file)
|
||||||
return $(".view:visible:not(." + viewMode + ")", this.file).fadeOut(200, (function(_this) {
|
.removeClass('active')
|
||||||
|
.filter('.' + viewMode)
|
||||||
|
.addClass('active');
|
||||||
|
return $('.view:visible:not(.' + viewMode + ')', this.file).fadeOut(
|
||||||
|
200,
|
||||||
|
(function(_this) {
|
||||||
return function() {
|
return function() {
|
||||||
$(".view." + viewMode, _this.file).fadeIn(200);
|
$('.view.' + viewMode, _this.file).fadeIn(200);
|
||||||
return _this.initView(viewMode);
|
return _this.initView(viewMode);
|
||||||
};
|
};
|
||||||
})(this));
|
})(this),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
initView(viewMode) {
|
initView(viewMode) {
|
||||||
|
@ -63,7 +79,10 @@ export default class ImageFile {
|
||||||
$body.css('user-select', 'none');
|
$body.css('user-select', 'none');
|
||||||
});
|
});
|
||||||
|
|
||||||
$body.off('mouseup').off('mousemove').on('mouseup', function() {
|
$body
|
||||||
|
.off('mouseup')
|
||||||
|
.off('mousemove')
|
||||||
|
.on('mouseup', function() {
|
||||||
dragging = false;
|
dragging = false;
|
||||||
$body.css('user-select', '');
|
$body.css('user-select', '');
|
||||||
})
|
})
|
||||||
|
@ -81,24 +100,29 @@ export default class ImageFile {
|
||||||
var maxHeight, maxWidth;
|
var maxHeight, maxWidth;
|
||||||
maxWidth = 0;
|
maxWidth = 0;
|
||||||
maxHeight = 0;
|
maxHeight = 0;
|
||||||
$('.frame', view).each((function(_this) {
|
$('.frame', view)
|
||||||
|
.each(
|
||||||
|
(function(_this) {
|
||||||
return function(index, frame) {
|
return function(index, frame) {
|
||||||
var height, width;
|
var height, width;
|
||||||
width = $(frame).width();
|
width = $(frame).width();
|
||||||
height = $(frame).height();
|
height = $(frame).height();
|
||||||
maxWidth = width > maxWidth ? width : maxWidth;
|
maxWidth = width > maxWidth ? width : maxWidth;
|
||||||
return maxHeight = height > maxHeight ? height : maxHeight;
|
return (maxHeight = height > maxHeight ? height : maxHeight);
|
||||||
};
|
};
|
||||||
})(this)).css({
|
})(this),
|
||||||
|
)
|
||||||
|
.css({
|
||||||
width: maxWidth,
|
width: maxWidth,
|
||||||
height: maxHeight
|
height: maxHeight,
|
||||||
});
|
});
|
||||||
return [maxWidth, maxHeight];
|
return [maxWidth, maxHeight];
|
||||||
}
|
}
|
||||||
|
|
||||||
views = {
|
views = {
|
||||||
'two-up': function() {
|
'two-up': function() {
|
||||||
return $('.two-up.view .wrap', this.file).each((function(_this) {
|
return $('.two-up.view .wrap', this.file).each(
|
||||||
|
(function(_this) {
|
||||||
return function(index, wrap) {
|
return function(index, wrap) {
|
||||||
$('img', wrap).each(function() {
|
$('img', wrap).each(function() {
|
||||||
var currentWidth;
|
var currentWidth;
|
||||||
|
@ -108,58 +132,68 @@ export default class ImageFile {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return _this.requestImageInfo($('img', wrap), function(width, height) {
|
return _this.requestImageInfo($('img', wrap), function(width, height) {
|
||||||
$('.image-info .meta-width', wrap).text(width + "px");
|
$('.image-info .meta-width', wrap).text(width + 'px');
|
||||||
$('.image-info .meta-height', wrap).text(height + "px");
|
$('.image-info .meta-height', wrap).text(height + 'px');
|
||||||
return $('.image-info', wrap).removeClass('hide');
|
return $('.image-info', wrap).removeClass('hide');
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
})(this));
|
})(this),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
'swipe': function() {
|
swipe() {
|
||||||
var maxHeight, maxWidth;
|
var maxHeight, maxWidth;
|
||||||
maxWidth = 0;
|
maxWidth = 0;
|
||||||
maxHeight = 0;
|
maxHeight = 0;
|
||||||
return $('.swipe.view', this.file).each((function(_this) {
|
return $('.swipe.view', this.file).each(
|
||||||
|
(function(_this) {
|
||||||
return function(index, view) {
|
return function(index, view) {
|
||||||
var $swipeWrap, $swipeBar, $swipeFrame, wrapPadding, ref;
|
var $swipeWrap, $swipeBar, $swipeFrame, wrapPadding, ref;
|
||||||
ref = _this.prepareFrames(view), [maxWidth, maxHeight] = ref;
|
(ref = _this.prepareFrames(view)), ([maxWidth, maxHeight] = ref);
|
||||||
$swipeFrame = $('.swipe-frame', view);
|
$swipeFrame = $('.swipe-frame', view);
|
||||||
$swipeWrap = $('.swipe-wrap', view);
|
$swipeWrap = $('.swipe-wrap', view);
|
||||||
$swipeBar = $('.swipe-bar', view);
|
$swipeBar = $('.swipe-bar', view);
|
||||||
|
|
||||||
$swipeFrame.css({
|
$swipeFrame.css({
|
||||||
width: maxWidth + 16,
|
width: maxWidth + 16,
|
||||||
height: maxHeight + 28
|
height: maxHeight + 28,
|
||||||
});
|
});
|
||||||
$swipeWrap.css({
|
$swipeWrap.css({
|
||||||
width: maxWidth + 1,
|
width: maxWidth + 1,
|
||||||
height: maxHeight + 2
|
height: maxHeight + 2,
|
||||||
});
|
});
|
||||||
// Set swipeBar left position to match image frame
|
// Set swipeBar left position to match image frame
|
||||||
$swipeBar.css({
|
$swipeBar.css({
|
||||||
left: 1
|
left: 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
wrapPadding = parseInt($swipeWrap.css('right').replace('px', ''), 10);
|
wrapPadding = parseInt($swipeWrap.css('right').replace('px', ''), 10);
|
||||||
|
|
||||||
_this.initDraggable($swipeBar, wrapPadding, function(e, left) {
|
_this.initDraggable($swipeBar, wrapPadding, function(e, left) {
|
||||||
if (left > 0 && left < $swipeFrame.width() - (wrapPadding * 2)) {
|
if (left > 0 && left < $swipeFrame.width() - wrapPadding * 2) {
|
||||||
$swipeWrap.width((maxWidth + 1) - left);
|
$swipeWrap.width(maxWidth + 1 - left);
|
||||||
$swipeBar.css('left', left);
|
$swipeBar.css('left', left);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
})(this));
|
})(this),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
'onion-skin': function() {
|
'onion-skin': function() {
|
||||||
var dragTrackWidth, maxHeight, maxWidth;
|
var dragTrackWidth, maxHeight, maxWidth;
|
||||||
maxWidth = 0;
|
maxWidth = 0;
|
||||||
maxHeight = 0;
|
maxHeight = 0;
|
||||||
dragTrackWidth = $('.drag-track', this.file).width() - $('.dragger', this.file).width();
|
dragTrackWidth = $('.drag-track', this.file).width() - $('.dragger', this.file).width();
|
||||||
return $('.onion-skin.view', this.file).each((function(_this) {
|
return $('.onion-skin.view', this.file).each(
|
||||||
|
(function(_this) {
|
||||||
return function(index, view) {
|
return function(index, view) {
|
||||||
var $frame, $track, $dragger, $frameAdded, framePadding, ref, dragging = false;
|
var $frame,
|
||||||
ref = _this.prepareFrames(view), [maxWidth, maxHeight] = ref;
|
$track,
|
||||||
|
$dragger,
|
||||||
|
$frameAdded,
|
||||||
|
framePadding,
|
||||||
|
ref,
|
||||||
|
dragging = false;
|
||||||
|
(ref = _this.prepareFrames(view)), ([maxWidth, maxHeight] = ref);
|
||||||
$frame = $('.onion-skin-frame', view);
|
$frame = $('.onion-skin-frame', view);
|
||||||
$frameAdded = $('.frame.added', view);
|
$frameAdded = $('.frame.added', view);
|
||||||
$track = $('.drag-track', view);
|
$track = $('.drag-track', view);
|
||||||
|
@ -167,14 +201,14 @@ export default class ImageFile {
|
||||||
|
|
||||||
$frame.css({
|
$frame.css({
|
||||||
width: maxWidth + 16,
|
width: maxWidth + 16,
|
||||||
height: maxHeight + 28
|
height: maxHeight + 28,
|
||||||
});
|
});
|
||||||
$('.swipe-wrap', view).css({
|
$('.swipe-wrap', view).css({
|
||||||
width: maxWidth + 1,
|
width: maxWidth + 1,
|
||||||
height: maxHeight + 2
|
height: maxHeight + 2,
|
||||||
});
|
});
|
||||||
$dragger.css({
|
$dragger.css({
|
||||||
left: dragTrackWidth
|
left: dragTrackWidth,
|
||||||
});
|
});
|
||||||
|
|
||||||
$frameAdded.css('opacity', 1);
|
$frameAdded.css('opacity', 1);
|
||||||
|
@ -189,9 +223,10 @@ export default class ImageFile {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
})(this));
|
})(this),
|
||||||
}
|
);
|
||||||
}
|
},
|
||||||
|
};
|
||||||
|
|
||||||
requestImageInfo(img, callback) {
|
requestImageInfo(img, callback) {
|
||||||
const domImg = img.get(0);
|
const domImg = img.get(0);
|
||||||
|
@ -199,11 +234,14 @@ export default class ImageFile {
|
||||||
if (domImg.complete) {
|
if (domImg.complete) {
|
||||||
return callback.call(this, domImg.naturalWidth, domImg.naturalHeight);
|
return callback.call(this, domImg.naturalWidth, domImg.naturalHeight);
|
||||||
} else {
|
} else {
|
||||||
return img.on('load', (function(_this) {
|
return img.on(
|
||||||
|
'load',
|
||||||
|
(function(_this) {
|
||||||
return function() {
|
return function() {
|
||||||
return callback.call(_this, domImg.naturalWidth, domImg.naturalHeight);
|
return callback.call(_this, domImg.naturalWidth, domImg.naturalHeight);
|
||||||
};
|
};
|
||||||
})(this));
|
})(this),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,10 +20,12 @@ export default () => {
|
||||||
|
|
||||||
if (pipelineTableViewEl) {
|
if (pipelineTableViewEl) {
|
||||||
// Update MR and Commits tabs
|
// Update MR and Commits tabs
|
||||||
pipelineTableViewEl.addEventListener('update-pipelines-count', (event) => {
|
pipelineTableViewEl.addEventListener('update-pipelines-count', event => {
|
||||||
if (event.detail.pipelines &&
|
if (
|
||||||
|
event.detail.pipelines &&
|
||||||
event.detail.pipelines.count &&
|
event.detail.pipelines.count &&
|
||||||
event.detail.pipelines.count.all) {
|
event.detail.pipelines.count.all
|
||||||
|
) {
|
||||||
const badge = document.querySelector('.js-pipelines-mr-count');
|
const badge = document.querySelector('.js-pipelines-mr-count');
|
||||||
|
|
||||||
badge.textContent = event.detail.pipelines.count.all;
|
badge.textContent = event.detail.pipelines.count.all;
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue