Merge branch 'master' of https://gitlab.com/gitlab-org/gitlab-ce into update-template-name-via-sentence-case
# Conflicts: # .gitlab/issue_templates/Feature proposal.md
|
@ -69,9 +69,3 @@ rules:
|
|||
FunctionExpression:
|
||||
parameters: 1
|
||||
body: 1
|
||||
## Destructuring: https://eslint.org/docs/rules/prefer-destructuring
|
||||
prefer-destructuring: off
|
||||
## no-restricted-globals: https://eslint.org/docs/rules/no-restricted-globals
|
||||
no-restricted-globals: off
|
||||
## no-multi-assign: https://eslint.org/docs/rules/no-multi-assign
|
||||
no-multi-assign: off
|
||||
|
|
|
@ -14,3 +14,12 @@ lib/gitlab/gitaly_client/ref_service.rb
|
|||
lib/gitlab/gitaly_client/commit_service.rb
|
||||
lib/gitlab/git/commit.rb
|
||||
lib/gitlab/git/tag.rb
|
||||
|
||||
ee/db/**/*
|
||||
ee/app/serializers/ee/merge_request_widget_entity.rb
|
||||
ee/lib/api/epics.rb
|
||||
ee/lib/api/geo_nodes.rb
|
||||
ee/lib/ee/gitlab/ldap/sync/admin_users.rb
|
||||
ee/app/workers/geo/file_download_dispatch_worker/job_artifact_job_finder.rb
|
||||
ee/app/workers/geo/file_download_dispatch_worker/lfs_object_job_finder.rb
|
||||
ee/spec/**/*
|
||||
|
|
3
.gitignore
vendored
|
@ -29,7 +29,7 @@ eslint-report.html
|
|||
/app/assets/javascripts/locale/**/app.js
|
||||
/backups/*
|
||||
/config/aws.yml
|
||||
/config/database.yml
|
||||
/config/database*.yml
|
||||
/config/gitlab.yml
|
||||
/config/gitlab_ci.yml
|
||||
/config/initializers/rack_attack.rb
|
||||
|
@ -76,3 +76,4 @@ eslint-report.html
|
|||
/.rspec
|
||||
/plugins/*
|
||||
/.gitlab_pages_secret
|
||||
package-lock.json
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.4.4-golang-1.9-git-2.17-chrome-65.0-node-8.x-yarn-1.2-postgresql-9.6"
|
||||
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.4.4-golang-1.9-git-2.17-chrome-67.0-node-8.x-yarn-1.2-postgresql-9.6-graphicsmagick-1.3.29"
|
||||
|
||||
.dedicated-runner: &dedicated-runner
|
||||
retry: 1
|
||||
|
@ -220,18 +220,6 @@ stages:
|
|||
paths:
|
||||
- log/development.log
|
||||
|
||||
# Review docs base
|
||||
.review-docs: &review-docs
|
||||
<<: *dedicated-runner
|
||||
<<: *except-qa
|
||||
<<: *single-script-job
|
||||
variables:
|
||||
<<: *single-script-job-variables
|
||||
SCRIPT_NAME: trigger-build-docs
|
||||
when: manual
|
||||
only:
|
||||
- branches
|
||||
|
||||
# DB migration, rollback, and seed jobs
|
||||
.db-migrate-reset: &db-migrate-reset
|
||||
<<: *dedicated-no-docs-and-no-qa-pull-cache-job
|
||||
|
@ -264,29 +252,56 @@ package-and-qa:
|
|||
<<: *single-script-job
|
||||
variables:
|
||||
<<: *single-script-job-variables
|
||||
SCRIPT_NAME: trigger-build-omnibus
|
||||
SCRIPT_NAME: trigger-build
|
||||
retry: 0
|
||||
script:
|
||||
- ./$SCRIPT_NAME
|
||||
- ./$SCRIPT_NAME omnibus
|
||||
when: manual
|
||||
only:
|
||||
- //@gitlab-org/gitlab-ce
|
||||
- //@gitlab-org/gitlab-ee
|
||||
|
||||
# Trigger a docs build in gitlab-docs
|
||||
# Useful to preview the docs changes live
|
||||
review-docs-deploy:
|
||||
<<: *review-docs
|
||||
stage: build
|
||||
# Review docs base
|
||||
.review-docs: &review-docs
|
||||
<<: *dedicated-runner
|
||||
<<: *single-script-job
|
||||
variables:
|
||||
<<: *single-script-job-variables
|
||||
SCRIPT_NAME: trigger-build-docs
|
||||
environment:
|
||||
name: review-docs/$CI_COMMIT_REF_NAME
|
||||
# DOCS_REVIEW_APPS_DOMAIN and DOCS_GITLAB_REPO_SUFFIX are secret variables
|
||||
# Discussion: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/14236/diffs#note_40140693
|
||||
url: http://$DOCS_GITLAB_REPO_SUFFIX-$CI_COMMIT_REF_SLUG.$DOCS_REVIEW_APPS_DOMAIN/$DOCS_GITLAB_REPO_SUFFIX
|
||||
url: http://$DOCS_GITLAB_REPO_SUFFIX-$CI_ENVIRONMENT_SLUG.$DOCS_REVIEW_APPS_DOMAIN/$DOCS_GITLAB_REPO_SUFFIX
|
||||
on_stop: review-docs-cleanup
|
||||
|
||||
# Trigger a manual docs build in gitlab-docs only on non docs-only branches.
|
||||
# Useful to preview the docs changes live.
|
||||
review-docs-deploy-manual:
|
||||
<<: *review-docs
|
||||
stage: build
|
||||
script:
|
||||
- gem install gitlab --no-ri --no-rdoc
|
||||
- ./$SCRIPT_NAME deploy
|
||||
when: manual
|
||||
only:
|
||||
- branches@gitlab-org/gitlab-ce
|
||||
- branches@gitlab-org/gitlab-ee
|
||||
<<: *except-docs-and-qa
|
||||
|
||||
# Always trigger a docs build in gitlab-docs only on docs-only branches.
|
||||
# Useful to preview the docs changes live.
|
||||
review-docs-deploy:
|
||||
<<: *review-docs
|
||||
stage: post-test
|
||||
script:
|
||||
- gem install gitlab --no-ri --no-rdoc
|
||||
- ./$SCRIPT_NAME deploy
|
||||
only:
|
||||
- /(^docs[\/-].*|.*-docs$)/
|
||||
- branches@gitlab-org/gitlab-ce
|
||||
- branches@gitlab-org/gitlab-ee
|
||||
<<: *except-qa
|
||||
|
||||
# Cleanup remote environment of gitlab-docs
|
||||
review-docs-cleanup:
|
||||
|
@ -295,9 +310,10 @@ review-docs-cleanup:
|
|||
environment:
|
||||
name: review-docs/$CI_COMMIT_REF_NAME
|
||||
action: stop
|
||||
when: manual
|
||||
script:
|
||||
- gem install gitlab --no-ri --no-rdoc
|
||||
- ./SCRIPT_NAME cleanup
|
||||
- ./$SCRIPT_NAME cleanup
|
||||
|
||||
##
|
||||
# Trigger a docker image build in CNG (Cloud Native GitLab) repository
|
||||
|
@ -394,7 +410,11 @@ compile-assets:
|
|||
- date
|
||||
- yarn install --frozen-lockfile --cache-folder .yarn-cache
|
||||
- date
|
||||
- free -m
|
||||
- bundle exec rake gitlab:assets:compile
|
||||
variables:
|
||||
# we override the max_old_space_size to prevent OOM errors
|
||||
NODE_OPTIONS: --max_old_space_size=3584
|
||||
artifacts:
|
||||
expire_in: 7d
|
||||
paths:
|
||||
|
@ -411,6 +431,7 @@ setup-test-env:
|
|||
script:
|
||||
- bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init'
|
||||
- scripts/gitaly-test-build # Do not use 'bundle exec' here
|
||||
- BUNDLE_GEMFILE=Gemfile.rails5 bundle install $BUNDLE_INSTALL_FLAGS
|
||||
artifacts:
|
||||
expire_in: 7d
|
||||
paths:
|
||||
|
@ -586,6 +607,12 @@ downtime_check:
|
|||
- /(^docs[\/-].*|.*-docs$)/
|
||||
- /(^qa[\/-].*|.*-qa$)/
|
||||
|
||||
rails5_gemfile_lock_check:
|
||||
<<: *dedicated-no-docs-no-db-pull-cache-job
|
||||
<<: *except-docs-and-qa
|
||||
script:
|
||||
- scripts/rails5-gemfile-lock-check
|
||||
|
||||
ee_compat_check:
|
||||
<<: *rake-exec
|
||||
except:
|
||||
|
@ -658,10 +685,13 @@ gitlab:assets:compile:
|
|||
SKIP_STORAGE_VALIDATION: "true"
|
||||
WEBPACK_REPORT: "true"
|
||||
NO_COMPRESSION: "true"
|
||||
# we override the max_old_space_size to prevent OOM errors
|
||||
NODE_OPTIONS: --max_old_space_size=3584
|
||||
script:
|
||||
- date
|
||||
- yarn install --frozen-lockfile --production --cache-folder .yarn-cache
|
||||
- date
|
||||
- free -m
|
||||
- bundle exec rake gitlab:assets:compile
|
||||
artifacts:
|
||||
name: webpack-report
|
||||
|
@ -801,8 +831,6 @@ lint:javascript:report:
|
|||
- setup-test-env
|
||||
before_script: []
|
||||
script:
|
||||
- date
|
||||
- find app/ spec/ -name '*.js' -exec sed --in-place 's|/\* eslint-disable .*\*/||' {} \; # run report over all files
|
||||
- date
|
||||
- yarn run eslint-report || true # ignore exit code
|
||||
artifacts:
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
### Description
|
||||
### Problem to solve
|
||||
|
||||
(Include problem, use cases, benefits, and/or goals)
|
||||
### Further details
|
||||
|
||||
(Include use cases, benefits, and/or goals)
|
||||
|
||||
### Proposal
|
||||
|
||||
(Proposal)
|
||||
### What does success look like, and how can we measure that?
|
||||
|
||||
(If no way to measure success, link to an issue that will implement a way to measure this)
|
||||
|
||||
### Links / references
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@ Set the title to: `[Security] Description of the original issue`
|
|||
- [ ] Add the nickname of the external user who found the issue (and/or HackerOne profile) to the Thanks row in the [details section](#details)
|
||||
|
||||
### Summary
|
||||
|
||||
#### Links
|
||||
|
||||
| Description | Link |
|
|
@ -1,7 +1,7 @@
|
|||
Add a description of your merge request here. Merge requests without an adequate
|
||||
description will not be reviewed until one is added.
|
||||
|
||||
## Database Checklist
|
||||
## Database checklist
|
||||
|
||||
When adding migrations:
|
||||
|
||||
|
@ -31,7 +31,7 @@ When removing columns, tables, indexes or other structures:
|
|||
- [ ] Removed these in a post-deployment migration
|
||||
- [ ] Made sure the application no longer uses (or ignores) these structures
|
||||
|
||||
## General Checklist
|
||||
## General checklist
|
||||
|
||||
- [ ] [Changelog entry](https://docs.gitlab.com/ee/development/changelog.html) added, if necessary
|
||||
- [ ] [Documentation created/updated](https://docs.gitlab.com/ee/development/doc_styleguide.html)
|
2
.nvmrc
|
@ -1 +1 @@
|
|||
9.0.0
|
||||
8.11.3
|
||||
|
|
|
@ -487,7 +487,7 @@ Style/EmptyLiteral:
|
|||
- 'lib/gitlab/fogbugz_import/importer.rb'
|
||||
- 'lib/gitlab/git/diff_collection.rb'
|
||||
- 'lib/gitlab/gitaly_client.rb'
|
||||
- 'scripts/trigger-build-omnibus'
|
||||
- 'scripts/trigger-build'
|
||||
- 'spec/features/merge_requests/versions_spec.rb'
|
||||
- 'spec/helpers/merge_requests_helper_spec.rb'
|
||||
- 'spec/lib/gitlab/request_context_spec.rb'
|
||||
|
|
304
CHANGELOG.md
|
@ -2,6 +2,294 @@
|
|||
documentation](doc/development/changelog.md) for instructions on adding your own
|
||||
entry.
|
||||
|
||||
## 11.0.2 (2018-06-26)
|
||||
|
||||
### Fixed (8 changes, 1 of them is from the community)
|
||||
|
||||
- Serve favicon image always from the main GitLab domain to avoid issues with CORS. !19810 (Alexis Reigel)
|
||||
- Specify chart version when installing applications on Clusters. !20010
|
||||
- Fix invalid fuzzy translations being generated during installation. !20048
|
||||
- Fix incremental rollouts for Auto DevOps. !20061
|
||||
- Notify conflict for only open merge request. !20125
|
||||
- Only load Omniauth if enabled. !20132
|
||||
- Fix sorting by name on explore projects page. !20162
|
||||
- Fix alert button styling so that they don't show up white.
|
||||
|
||||
### Performance (1 change)
|
||||
|
||||
- Remove performance bottleneck preventing large wiki pages from displaying. !20174
|
||||
|
||||
### Added (1 change)
|
||||
|
||||
- Add support for verifying remote uploads, artifacts, and LFS objects in check rake tasks. !19501
|
||||
|
||||
|
||||
## 11.0.1 (2018-06-21)
|
||||
|
||||
### Security (5 changes)
|
||||
|
||||
- Fix XSS vulnerability for table of content generation.
|
||||
- Update sanitize gem to 4.6.5 to fix HTML injection vulnerability.
|
||||
- HTML escape branch name in project graphs page.
|
||||
- HTML escape the name of the user in ProjectsHelper#link_to_member.
|
||||
- Don't show events from internal projects for anonymous users in public feed.
|
||||
|
||||
|
||||
## 11.0.0 (2018-06-22)
|
||||
|
||||
### Security (3 changes)
|
||||
|
||||
- Fix API to remove deploy key from project instead of deleting it entirely.
|
||||
- Fixed bug that allowed importing arbitrary project attributes.
|
||||
- Prevent user passwords from being changed without providing the previous password.
|
||||
|
||||
### Removed (2 changes)
|
||||
|
||||
- Removed API v3 from the codebase. !18970
|
||||
- Removes outdated `g t` shortcut for TODO in favor of `Shift+T`. !19002
|
||||
|
||||
### Fixed (69 changes, 23 of them are from the community)
|
||||
|
||||
- Optimize the upload migration proces. !15947
|
||||
- Import bitbucket issues that are reported by an anonymous user. !18199 (bartl)
|
||||
- Fix an issue where the notification email address would be set to an unconfirmed email address. !18474
|
||||
- Stop logging email information when emails are disabled. !18521 (Marc Shaw)
|
||||
- Fix double-brackets being linkified in wiki markdown. !18524 (brewingcode)
|
||||
- Use case in-sensitive ordering by name for dashboard. !18553 (@vedharish)
|
||||
- Fix width of contributors graphs. !18639 (Paul Vorbach)
|
||||
- Fix modal width of shorcuts help page. !18766 (Lars Greiss)
|
||||
- Add missing tooltip to creation date on container registry overview. !18767 (Lars Greiss)
|
||||
- Add missing migration for minimal Project build_timeout. !18775
|
||||
- Update commit status from external CI services less aggressively. !18802
|
||||
- Fix Runner contacted at tooltip cache. !18810
|
||||
- Added support for LFS Download in the importing process. !18871
|
||||
- Fix issue board bug with long strings in titles. !18924
|
||||
- Does not log failed sign-in attempts when the database is in read-only mode. !18957
|
||||
- Fixes 500 error on /estimate BIG_VALUE. !18964 (Jacopo Beschi @jacopo-beschi)
|
||||
- Forbid to patch traces for finished jobs. !18969
|
||||
- Do not allow to trigger manual actions that were skipped. !18985
|
||||
- Renamed 'Overview' to 'Project' in collapsed contextual navigation at a project level. !18996 (Constance Okoghenun)
|
||||
- Fixed bug where generated api urls didn't add the base url if set. !19003
|
||||
- Fixed badge api endpoint route when relative url is set. !19004
|
||||
- Fixes: Runners search input placeholder is cut off. !19015 (Jacopo Beschi @jacopo-beschi)
|
||||
- Exclude CI_PIPELINE_ID from variables supported in dynamic environment name. !19032
|
||||
- Updates updated_at on label changes. !19065 (Jacopo Beschi @jacopo-beschi)
|
||||
- Disallow updating job status if the job is not running. !19101
|
||||
- Fix FreeBSD can not upload artifacts due to wrong tmp path. !19148
|
||||
- Check for nil AutoDevOps when saving project CI/CD settings. !19190
|
||||
- Missing timeout value in object storage pre-authorization. !19201
|
||||
- Use strings as properties key in kubernetes service spec. !19265 (Jasper Maes)
|
||||
- Fixed HTTP_PROXY environment not honored when reading remote traces. !19282 (NLR)
|
||||
- Updates ReactiveCaching clear_reactive_caching method to clear both data and alive caching. !19311
|
||||
- Fixes the styling on the modal headers. !19312 (samdbeckham)
|
||||
- Fixes a spelling error on the new label page. !19316 (samdbeckham)
|
||||
- Rails5 fix arel from. !19340 (Jasper Maes)
|
||||
- Support rails5 in postgres indexes function and fix some migrations. !19400 (Jasper Maes)
|
||||
- Fix repository archive generation when hashed storage is enabled. !19441
|
||||
- Rails 5 fix unknown keywords: changes, key_id, project, gl_repository, action, secret_token, protocol. !19466 (Jasper Maes)
|
||||
- Rails 5 fix glob spec. !19469 (Jasper Maes)
|
||||
- Showing project import_status in a humanized form no longer gives an error. !19470
|
||||
- Make avatars/icons hidden on mobile. !19585 (Takuya Noguchi)
|
||||
- Fix active tab highlight when creating new merge request. !19781 (Jan Beckmann)
|
||||
- Fixes Web IDE button on merge requests when GitLab is installed with relative URL.
|
||||
- Unverified hover state color changed to black.
|
||||
- Fix after sign-in with Google button.
|
||||
- Don't trim incoming emails that create new issues. (Cameron Crockett)
|
||||
- Wrapping problem on the issues page has been fixed.
|
||||
- Fix resolvable check if note's commit could not be found.
|
||||
- Fix filename matching when processing file or blob search results.
|
||||
- Allow maintainers to retry pipelines on forked projects (if allowed in merge request).
|
||||
- Fix deletion of Object Store uploads.
|
||||
- Fix overflowing Failed Jobs table in sm viewports on IE11.
|
||||
- Adjust insufficient diff hunks being persisted on NoteDiffFile.
|
||||
- Render calendar feed inline when accessed from GitLab.
|
||||
- Line height fixed. (Murat Dogan)
|
||||
- Use upload ID for creating lease key for file uploaders.
|
||||
- Use Github repo visibility during import while respecting restricted visibility levels.
|
||||
- Adjust permitted params filtering on merge scheduling.
|
||||
- Fix unscrollable Markdown preview of WebIDE on Firefox.
|
||||
- Enforce UTF-8 encoding on user input in LogrageWithTimestamp formatter and filter out file content from logs.
|
||||
- Fix project destruction failing due to idle in transaction timeouts.
|
||||
- Add a unique and not null constraint on the project_features.project_id column.
|
||||
- Expire Wiki content cache after importing a repository.
|
||||
- Fix admin counters not working when PostgreSQL has secondaries.
|
||||
- Fix backup creation and restore for specific Rake tasks.
|
||||
- Fix cross-origin errors when attempting to download JavaScript attachments.
|
||||
- Fix api_json.log not always reporting the right HTTP status code.
|
||||
- Fix attr_encryption key settings.
|
||||
- Remove gray button styles.
|
||||
- Fix print styles for markdown pages.
|
||||
|
||||
### Deprecated (4 changes)
|
||||
|
||||
- Deprecate Gemnasium project service. !18954
|
||||
- Rephrasing Merge Request's 'allow edits from maintainer' functionality. !19061
|
||||
- Rename issue scope created-by-me to created_by_me, and assigned-to-me to assigned_to_me. !44799
|
||||
- Migrate any remaining jobs from deprecated `object_storage_upload` queue.
|
||||
|
||||
### Changed (42 changes, 11 of them are from the community)
|
||||
|
||||
- Add support for smarter system notes. !17164
|
||||
- Automatically accepts project/group invite by email after user signup. !17634 (Jacopo Beschi @jacopo-beschi)
|
||||
- Dynamically fetch GCP cluster creation parameters. !17806
|
||||
- Label list page redesign. !18466
|
||||
- Move discussion actions to the right for small viewports. !18476 (George Tsiolis)
|
||||
- Add 2FA filter to the group members page. !18483
|
||||
- made listing and showing public issue apis available without authentication. !18638 (haseebeqx)
|
||||
- Refactoring UrlValidators to include url blocking. !18686
|
||||
- Removed "(Beta)" from "Auto DevOps" messages. !18759
|
||||
- Expose runner ip address to runners API. !18799 (Lars Greiss)
|
||||
- Moves MR widget external link icon to the right. !18828 (Jacopo Beschi @jacopo-beschi)
|
||||
- Add support for 'active' setting on Runner Registration API endpoint. !18848
|
||||
- Add dot to separate system notes content. !18864
|
||||
- Remove modalbox confirmation when retrying a pipeline. !18879
|
||||
- Remove docker pull prefix from registry clipboard feature. !18933 (Lars Greiss)
|
||||
- Move project sidebar sub-entries 'Environments' and 'Kubernetes' from 'CI/CD' to a new entry 'Operations'. !18941
|
||||
- Updated icons for branch and tag names in commit details. !18953 (Constance Okoghenun)
|
||||
- Expose readme url in Project API. !18960 (Imre Farkas)
|
||||
- Changes keyboard shortcut of Activity feed to `g v`. !19002
|
||||
- Updated Mattermost integration to use API v4 and only allow creation of Mattermost slash commands in the current user's teams. !19043 (Harrison Healey)
|
||||
- Add shortcuts to Web IDE docs and modal. !19044
|
||||
- Rename merge request widget author component. !19079 (George Tsiolis)
|
||||
- Rename the Master role to Maintainer. !19080
|
||||
- Use "right now" for short time periods. !19095
|
||||
- Update 404 and 403 pages with helpful actions. !19096
|
||||
- Add username to terms message in git and API calls. !19126
|
||||
- Change the IDE file buttons for an "Open in file view" button. !19129 (Sam Beckham)
|
||||
- Removes redundant script failure message from Job page. !19138
|
||||
- Add flash notice if user has already accepted terms and allow users to continue to root path. !19156
|
||||
- Redesign group settings page into expandable sections. !19184
|
||||
- Hashed Storage: migration rake task now can be executed to specific project. !19268
|
||||
- Make CI job update entrypoint to work as keep-alive endpoint. !19543
|
||||
- Avoid checking the user format in every url validation. !19575
|
||||
- Apply notification settings level of groups to all child objects.
|
||||
- Support restoring repositories into gitaly.
|
||||
- Bump omniauth-gitlab to 1.0.3.
|
||||
- Move API group deletion to Sidekiq.
|
||||
- Improve Failed Jobs tab in the Pipeline detail page.
|
||||
- Add additional theme color options.
|
||||
- Include milestones from parent groups when assigning a milestone to an issue or merge request.
|
||||
- Restore API v3 user endpoint.
|
||||
- Hide merge request option in IDE when disabled.
|
||||
|
||||
### Performance (28 changes, 1 of them is from the community)
|
||||
|
||||
- Add backgound migration for filling nullfied file_store columns. !18557
|
||||
- Add a cronworker to rescue stale live traces. !18680
|
||||
- Move SquashBeforeMerge vue component. !18813 (George Tsiolis)
|
||||
- Add index on runner_type for ci_runners. !18897
|
||||
- Fix CarrierWave reads local files into memoery when migrates to ObjectStorage. !19102
|
||||
- Remove double-checked internal id generation. !19181
|
||||
- Throttle updates to Project#last_repository_updated_at. !19183
|
||||
- Add background migrations for archiving legacy job traces. !19194
|
||||
- Use NPM provided version of SortableJS. !19274
|
||||
- Improve performance of group issues filtering on GitLab.com. !19429
|
||||
- Improve performance of LFS integrity check. !19494
|
||||
- Fix an N+1 when loading user avatars.
|
||||
- Only preload member records for the relevant projects/groups/user in projects API.
|
||||
- Fix some sources of excessive query counts when calculating notification recipients.
|
||||
- Optimise PagesWorker usage.
|
||||
- Optimise paused runners to reduce amount of used requests.
|
||||
- Update runner cached informations without performing validations.
|
||||
- Improve performance of project pipelines pages.
|
||||
- Persist truncated note diffs on a new table.
|
||||
- Remove unused running_or_pending_build_count.
|
||||
- Remove N+1 query for author in issues API.
|
||||
- Eliminate N+1 queries with authors and push_data_payload in Events API.
|
||||
- Eliminate cached N+1 queries for projects in Issue API.
|
||||
- Eliminate N+1 queries for CI job artifacts in /api/prjoects/:id/pipelines/:pipeline_id/jobs.
|
||||
- Fix N+1 with source_projects in merge requests API.
|
||||
- Replace grape-route-helpers with our own grape-path-helpers.
|
||||
- Move PR IO operations out of a transaction.
|
||||
- Improve performance of GroupsController#show.
|
||||
|
||||
### Added (25 changes, 10 of them are from the community)
|
||||
|
||||
- Closes MR check out branch modal with escape. (19050)
|
||||
- Allow changing the default favicon to a custom icon. !14497 (Alexis Reigel)
|
||||
- Export assigned issues in iCalendar feed. !17783 (Imre Farkas)
|
||||
- When MR becomes unmergeable, notify and create todo for author and merge user. !18042
|
||||
- Display help text below auto devops domain with nip.io domain name (#45561). !18496
|
||||
- Add per-project pipeline id. !18558
|
||||
- New design for wiki page deletion confirmation. !18712 (Constance Okoghenun)
|
||||
- Updates updated_at on issuable when setting time spent. !18757 (Jacopo Beschi @jacopo-beschi)
|
||||
- Expose artifacts_expire_at field for job entity in api. !18872 (Semyon Pupkov)
|
||||
- Add support for variables expression pattern matching syntax. !18902
|
||||
- Add API endpoint to render markdown text. !18926 (@blackst0ne)
|
||||
- Add `Squash and merge` to GitLab Core (CE). !18956 (@blackst0ne)
|
||||
- Adds keyboard shortcut `g k` for Kubernetes on Project pages. !19002
|
||||
- Adds keyboard shortcut `g e` for Environments on Project pages. !19002
|
||||
- Setup graphql with initial project & merge request query. !19008
|
||||
- Adds JupyterHub to cluster applications. !19019
|
||||
- Added ability to search by wiki titles. !19112
|
||||
- Add Avatar API. !19121 (Imre Farkas)
|
||||
- Add variables to POST api/v4/projects/:id/pipeline. !19124 (Jacopo Beschi @jacopo-beschi)
|
||||
- Add deploy strategies to the Auto DevOps settings. !19172
|
||||
- Automatize Deploy Token creation for Auto Devops. !19507
|
||||
- Add anchor for incoming email regex.
|
||||
- Support direct_upload with S3 Multipart uploads.
|
||||
- Add Open in Xcode link for xcode repositories.
|
||||
- Add pipeline status to the status bar of the Web IDE.
|
||||
|
||||
### Other (40 changes, 17 of them are from the community)
|
||||
|
||||
- Expand documentation for Runners API. !16484
|
||||
- Order UsersController#projects.json by updated_at. !18227 (Takuya Noguchi)
|
||||
- Replace the `project/issues/references.feature` spinach test with an rspec analog. !18769 (@blackst0ne)
|
||||
- Replace the `project/merge_requests/references.feature` spinach test with an rspec analog. !18794 (@blackst0ne)
|
||||
- Replace the `project/deploy_keys.feature` spinach test with an rspec analog. !18796 (@blackst0ne)
|
||||
- Replace the `project/ff_merge_requests.feature` spinach test with an rspec analog. !18800 (@blackst0ne)
|
||||
- Apply NestingDepth (level 5) (pages/pipelines.scss). !18830 (Takuya Noguchi)
|
||||
- Replace the `project/forked_merge_requests.feature` spinach test with an rspec analog. !18867 (@blackst0ne)
|
||||
- Remove Spinach. !18869 (@blackst0ne)
|
||||
- Add NOT NULL constraints to project_authorizations. !18980
|
||||
- Add helpful messages to empty wiki view. !19007
|
||||
- Increase text limit for GPG keys (mysql only). !19069
|
||||
- Take two for MR metrics population background migration. !19097
|
||||
- Remove Gemnasium badge from project README.md. !19136 (Takuya Noguchi)
|
||||
- Update awesome_print to 1.8.0. !19163 (Takuya Noguchi)
|
||||
- Update email_spec to 2.2.0. !19164 (Takuya Noguchi)
|
||||
- Update redis-namespace to 1.6.0. !19166 (Takuya Noguchi)
|
||||
- Update rdoc to 6.0.4. !19167 (Takuya Noguchi)
|
||||
- Updates the version of kubeclient from 3.0 to 3.1.0. !19199
|
||||
- Fix UI broken in line profiling modal due to Bootstrap 4. !19253 (Takuya Noguchi)
|
||||
- Add migration to disable the usage of DSA keys. !19299
|
||||
- Use the default strings of timeago.js for timeago. !19350 (Takuya Noguchi)
|
||||
- Update selenium-webdriver to 3.12.0. !19351 (Takuya Noguchi)
|
||||
- Include username in output when testing SSH to GitLab. !19358
|
||||
- Update screenshot in Gitlab.com integration documentation. !19433 (Tuğçe Nur Taş)
|
||||
- Users can accept terms during registration. !19583
|
||||
- Fix issue count on sidebar.
|
||||
- Add merge requests list endpoint for groups.
|
||||
- Upgrade GitLab from Bootstrap 3 to 4.
|
||||
- Make ActiveRecordSubscriber rails 5 compatible.
|
||||
- Show a more helpful error for import status.
|
||||
- Log response body to production_json.log when a controller responds with a 422 status.
|
||||
- Log Workhorse queue duration for Grape API calls.
|
||||
- Adjust SQL and transaction Prometheus buckets.
|
||||
- Adding branches through the WebUI is handled by Gitaly.
|
||||
- Remove shellout implementation for Repository checksums.
|
||||
- Refs containting sha checks are done by Gitaly.
|
||||
- Finding a wiki page is done by Gitaly by default.
|
||||
- Workhorse will use Gitaly to create archives.
|
||||
- Workhorse to send raw diff and patch for commits.
|
||||
|
||||
|
||||
## 10.8.5 (2018-06-21)
|
||||
|
||||
### Security (5 changes)
|
||||
|
||||
- Fix XSS vulnerability for table of content generation.
|
||||
- Update sanitize gem to 4.6.5 to fix HTML injection vulnerability.
|
||||
- HTML escape branch name in project graphs page.
|
||||
- HTML escape the name of the user in ProjectsHelper#link_to_member.
|
||||
- Don't show events from internal projects for anonymous users in public feed.
|
||||
|
||||
|
||||
## 10.8.4 (2018-06-06)
|
||||
|
||||
- No changes.
|
||||
|
||||
## 10.8.3 (2018-05-30)
|
||||
|
||||
### Fixed (4 changes)
|
||||
|
@ -216,6 +504,22 @@ entry.
|
|||
- Gitaly handles repository forks by default.
|
||||
|
||||
|
||||
## 10.7.6 (2018-06-21)
|
||||
|
||||
### Security (6 changes)
|
||||
|
||||
- Fix XSS vulnerability for table of content generation.
|
||||
- Update sanitize gem to 4.6.5 to fix HTML injection vulnerability.
|
||||
- HTML escape branch name in project graphs page.
|
||||
- HTML escape the name of the user in ProjectsHelper#link_to_member.
|
||||
- Don't show events from internal projects for anonymous users in public feed.
|
||||
- XSS fix to use safe_params instead of params in url_for helpers.
|
||||
|
||||
### Other (1 change)
|
||||
|
||||
- Replacing gollum libraries for gitlab custom libs. !18343
|
||||
|
||||
|
||||
## 10.7.5 (2018-05-28)
|
||||
|
||||
### Security (3 changes)
|
||||
|
|
117
CONTRIBUTING.md
|
@ -27,25 +27,26 @@ _This notice should stay as the first item in the CONTRIBUTING.md file._
|
|||
- [Helping others](#helping-others)
|
||||
- [I want to contribute!](#i-want-to-contribute)
|
||||
- [Workflow labels](#workflow-labels)
|
||||
- [Type labels (~"feature proposal", ~bug, ~customer, etc.)](#type-labels-feature-proposal-bug-customer-etc)
|
||||
- [Subject labels (~wiki, ~"container registry", ~ldap, ~api, etc.)](#subject-labels-wiki-container-registry-ldap-api-etc)
|
||||
- [Team labels (~"CI/CD", ~Discussion, ~Quality, ~Platform, etc.)](#team-labels-cicd-discussion-quality-platform-etc)
|
||||
- [Milestone labels (~Deliverable, ~Stretch, ~"Next Patch Release")](#milestone-labels-deliverable-stretch-next-patch-release)
|
||||
- [Priority labels (~P1, ~P2, ~P3 , ~P4)](#bug-priority-labels-p1-p2-p3-p4)
|
||||
- [Severity labels (~S1, ~S2, ~S3 , ~S4)](#bug-severity-labels-s1-s2-s3-s4)
|
||||
- [Label for community contributors (~"Accepting Merge Requests")](#label-for-community-contributors-accepting-merge-requests)
|
||||
- [Implement design & UI elements](#implement-design--ui-elements)
|
||||
- [Type labels](#type-labels)
|
||||
- [Subject labels](#subject-labels)
|
||||
- [Team labels](#team-labels)
|
||||
- [Release Scoping labels](#release-scoping-labels)
|
||||
- [Bug Priority labels](#bug-priority-labels)
|
||||
- [Bug Severity labels](#bug-severity-labels)
|
||||
- [Severity impact guidance](#severity-impact-guidance)
|
||||
- [Label for community contributors](#label-for-community-contributors)
|
||||
- [Implement design & UI elements](#implement-design-ui-elements)
|
||||
- [Issue tracker](#issue-tracker)
|
||||
- [Issue triaging](#issue-triaging)
|
||||
- [Feature proposals](#feature-proposals)
|
||||
- [Issue tracker guidelines](#issue-tracker-guidelines)
|
||||
- [Issue weight](#issue-weight)
|
||||
- [Regression issues](#regression-issues)
|
||||
- [Technical and UX debt](#technical-and-ux-debt)
|
||||
- [Stewardship](#stewardship)
|
||||
- [Issue triaging](#issue-triaging)
|
||||
- [Feature proposals](#feature-proposals)
|
||||
- [Issue tracker guidelines](#issue-tracker-guidelines)
|
||||
- [Issue weight](#issue-weight)
|
||||
- [Regression issues](#regression-issues)
|
||||
- [Technical and UX debt](#technical-and-ux-debt)
|
||||
- [Stewardship](#stewardship)
|
||||
- [Merge requests](#merge-requests)
|
||||
- [Merge request guidelines](#merge-request-guidelines)
|
||||
- [Contribution acceptance criteria](#contribution-acceptance-criteria)
|
||||
- [Merge request guidelines](#merge-request-guidelines)
|
||||
- [Contribution acceptance criteria](#contribution-acceptance-criteria)
|
||||
- [Definition of done](#definition-of-done)
|
||||
- [Style guides](#style-guides)
|
||||
- [Code of conduct](#code-of-conduct)
|
||||
|
@ -132,7 +133,7 @@ Most issues will have labels for at least one of the following:
|
|||
- Type: ~"feature proposal", ~bug, ~customer, etc.
|
||||
- Subject: ~wiki, ~"container registry", ~ldap, ~api, ~frontend, etc.
|
||||
- Team: ~"CI/CD", ~Discussion, ~Quality, ~Platform, etc.
|
||||
- Milestone: ~Deliverable, ~Stretch, ~"Next Patch Release"
|
||||
- Release Scoping: ~Deliverable, ~Stretch, ~"Next Patch Release"
|
||||
- Priority: ~P1, ~P2, ~P3, ~P4
|
||||
- Severity: ~S1, ~S2, ~S3, ~S4
|
||||
|
||||
|
@ -145,7 +146,7 @@ labels, you can _always_ add the team and type, and often also the subject.
|
|||
[milestones-page]: https://gitlab.com/gitlab-org/gitlab-ce/milestones
|
||||
[labels-page]: https://gitlab.com/gitlab-org/gitlab-ce/labels
|
||||
|
||||
### Type labels (~"feature proposal", ~bug, ~customer, etc.)
|
||||
### Type labels
|
||||
|
||||
Type labels are very important. They define what kind of issue this is. Every
|
||||
issue should have one or more.
|
||||
|
@ -161,28 +162,41 @@ already reserved for subject labels).
|
|||
|
||||
The descriptions on the [labels page][labels-page] explain what falls under each type label.
|
||||
|
||||
### Subject labels (~wiki, ~"container registry", ~ldap, ~api, etc.)
|
||||
### Subject labels
|
||||
|
||||
Subject labels are labels that define what area or feature of GitLab this issue
|
||||
hits. They are not always necessary, but very convenient.
|
||||
|
||||
Examples of subject labels are ~wiki, ~ldap, ~api,
|
||||
~issues, ~"merge requests", ~labels, and ~"container registry".
|
||||
|
||||
If you are an expert in a particular area, it makes it easier to find issues to
|
||||
work on. You can also subscribe to those labels to receive an email each time an
|
||||
issue is labeled with a subject label corresponding to your expertise.
|
||||
|
||||
Examples of subject labels are ~wiki, ~"container registry", ~ldap, ~api,
|
||||
~issues, ~"merge requests", ~labels, and ~"container registry".
|
||||
|
||||
Subject labels are always all-lowercase.
|
||||
|
||||
### Team labels (~"CI/CD", ~Discussion, ~Quality, ~Platform, etc.)
|
||||
### Team labels
|
||||
|
||||
Team labels specify what team is responsible for this issue.
|
||||
Assigning a team label makes sure issues get the attention of the appropriate
|
||||
people.
|
||||
|
||||
The current team labels are ~Distribution, ~"CI/CD", ~Discussion, ~Documentation, ~Quality,
|
||||
~Geo, ~Gitaly, ~Monitoring, ~Platform, ~Release, ~"Security Products" and ~"UX".
|
||||
The current team labels are:
|
||||
|
||||
- ~Configuration
|
||||
- ~"CI/CD"
|
||||
- ~Discussion
|
||||
- ~Distribution
|
||||
- ~Documentation
|
||||
- ~Geo
|
||||
- ~Gitaly
|
||||
- ~Monitoring
|
||||
- ~Platform
|
||||
- ~Quality
|
||||
- ~Release
|
||||
- ~"Security Products"
|
||||
- ~UX
|
||||
|
||||
The descriptions on the [labels page][labels-page] explain what falls under the
|
||||
responsibility of each team.
|
||||
|
@ -193,10 +207,10 @@ indicate if an issue needs backend work, frontend work, or both.
|
|||
Team labels are always capitalized so that they show up as the first label for
|
||||
any issue.
|
||||
|
||||
### Milestone labels (~Deliverable, ~Stretch, ~"Next Patch Release")
|
||||
### Release Scoping labels
|
||||
|
||||
Milestone labels help us clearly communicate expectations of the work for the
|
||||
release. There are three levels of Milestone labels:
|
||||
Release Scoping labels help us clearly communicate expectations of the work for the
|
||||
release. There are three levels of Release Scoping labels:
|
||||
|
||||
- ~Deliverable: Issues that are expected to be delivered in the current
|
||||
milestone.
|
||||
|
@ -211,9 +225,9 @@ Each issue scheduled for the current milestone should be labeled ~Deliverable
|
|||
or ~"Stretch". Any open issue for a previous milestone should be labeled
|
||||
~"Next Patch Release", or otherwise rescheduled to a different milestone.
|
||||
|
||||
### Bug Priority labels (~P1, ~P2, ~P3, ~P4)
|
||||
### Bug Priority labels
|
||||
|
||||
Bug Priority labels help us define the time a ~bug fix should be completed. Priority determines how quickly the defect turnaround time must be.
|
||||
Bug Priority labels help us define the time a ~bug fix should be completed. Priority determines how quickly the defect turnaround time must be.
|
||||
If there are multiple defects, the priority decides which defect has to be fixed immediately versus later.
|
||||
This label documents the planned timeline & urgency which is used to measure against our actual SLA on delivering ~bug fixes.
|
||||
|
||||
|
@ -224,7 +238,7 @@ This label documents the planned timeline & urgency which is used to measure aga
|
|||
| ~P3 | Medium Priority | Within the next 3 releases (approx one quarter) | |
|
||||
| ~P4 | Low Priority | Anything outside the next 3 releases (approx beyond one quarter) | The issue is prominent but does not impact user workflow and a workaround is documented |
|
||||
|
||||
### Bug Severity labels (~S1, ~S2, ~S3, ~S4)
|
||||
### Bug Severity labels
|
||||
|
||||
Severity labels help us clearly communicate the impact of a ~bug on users.
|
||||
|
||||
|
@ -240,11 +254,11 @@ Severity labels help us clearly communicate the impact of a ~bug on users.
|
|||
| Label | Security Impact | Availability / Performance Impact |
|
||||
|-------|---------------------------------------------------------------------|--------------------------------------------------------------|
|
||||
| ~S1 | >50% users impacted (possible company extinction level event) | |
|
||||
| ~S2 | Many users or multiple paid customers impacted (but not apocalyptic)| The issue is (almost) guaranteed to occur in the near future |
|
||||
| ~S2 | Many users or multiple paid customers impacted (but not apocalyptic)| The issue is (almost) guaranteed to occur in the near future |
|
||||
| ~S3 | A few users or a single paid customer impacted | The issue is likely to occur in the near future |
|
||||
| ~S4 | No paid users/customer impacted, or expected impact within 30 days | The issue _may_ occur but it's not likely |
|
||||
|
||||
### Label for community contributors (~"Accepting Merge Requests")
|
||||
### Label for community contributors
|
||||
|
||||
Issues that are beneficial to our users, 'nice to haves', that we currently do
|
||||
not have the capacity for or want to give the priority to, are labeled as
|
||||
|
@ -300,20 +314,29 @@ For guidance on UX implementation at GitLab, please refer to our [Design System]
|
|||
|
||||
The UX team uses labels to manage their workflow.
|
||||
|
||||
The ~"UX" label on an issue is a signal to the UX team that it will need UX attention.
|
||||
To better understand the priority by which UX tackles issues, see the [UX section](https://about.gitlab.com/handbook/ux/) of the handbook.
|
||||
The ~"UX" label on an issue is a signal to the UX team that it will need UX attention.
|
||||
To better understand the priority by which UX tackles issues, see the [UX section](https://about.gitlab.com/handbook/engineering/ux) of the handbook.
|
||||
|
||||
Once an issue has been worked on and is ready for development, a UXer applies the ~"UX ready" label to that issue.
|
||||
Once an issue has been worked on and is ready for development, a UXer removes the ~"UX" label and applies the ~"UX ready" label to that issue.
|
||||
|
||||
The UX team has a special type label called ~"design artifact". This label indicates that the final output
|
||||
for an issue is a UX solution/design. The solution will be developed by frontend and/or backend in a subsequent milestone.
|
||||
Any issue labeled ~"design artifact" should not also be labeled ~"frontend" or ~"backend" since no development is
|
||||
The UX team has a special type label called ~"design artifact". This label indicates that the final output
|
||||
for an issue is a UX solution/design. The solution will be developed by frontend and/or backend in a subsequent milestone.
|
||||
Any issue labeled ~"design artifact" should not also be labeled ~"frontend" or ~"backend" since no development is
|
||||
needed until the solution has been decided.
|
||||
|
||||
~"design artifact" issues are like any other issue and should contain a milestone label, ~"Deliverable" or ~"Stretch", when scheduled in the current milestone.
|
||||
|
||||
Once the ~"design artifact" issue has been completed, the UXer removes the ~"design artifact" label and applies the ~"UX ready" label. The Product Manager can use the
|
||||
existing issue or decide to create a whole new issue for the purpose of development.
|
||||
To prevent the misunderstanding that a feature will be be delivered in the
|
||||
assigned milestone, when only UX design is planned for that milestone, the
|
||||
Product Manager should create a separate issue for the ~"design artifact",
|
||||
assign the ~UX, ~"design artifact" and ~"Deliverable" labels, add a milestone
|
||||
and use a title that makes it clear that the scheduled issue is design only
|
||||
(e.g. `Design exploration for XYZ`).
|
||||
|
||||
When the ~"design artifact" issue has been completed, the UXer removes the ~UX
|
||||
label, adds the ~"UX ready" label and closes the issue. This indicates the
|
||||
design artifact is complete. The UXer will also copy the designs to related
|
||||
issues for implementation in an upcoming milestone.
|
||||
|
||||
## Issue tracker
|
||||
|
||||
|
@ -349,7 +372,7 @@ on those issues. Please select someone with relevant experience from the
|
|||
[GitLab team][team]. If there is nobody mentioned with that expertise look in
|
||||
the commit history for the affected files to find someone.
|
||||
|
||||
[described in our handbook]: https://about.gitlab.com/handbook/engineering/issues/issue-triage-policies/
|
||||
[described in our handbook]: https://about.gitlab.com/handbook/engineering/issue-triage/
|
||||
[issue bash events]: https://gitlab.com/gitlab-org/gitlab-ce/issues/17815
|
||||
|
||||
### Feature proposals
|
||||
|
@ -512,7 +535,7 @@ request is as follows:
|
|||
1. Write [tests](https://docs.gitlab.com/ee/development/rake_tasks.html#run-tests) and code
|
||||
1. [Generate a changelog entry with `bin/changelog`][changelog]
|
||||
1. If you are writing documentation, make sure to follow the
|
||||
[documentation styleguide][doc-styleguide]
|
||||
[documentation guidelines][doc-guidelines]
|
||||
1. If you have multiple commits please combine them into a few logically
|
||||
organized commits by [squashing them][git-squash]
|
||||
1. Push the commit(s) to your fork
|
||||
|
@ -627,7 +650,7 @@ the feature you contribute through all of these steps.
|
|||
1. Working and clean code that is commented where needed
|
||||
1. [Unit, integration, and system tests][testing] that pass on the CI server
|
||||
1. Performance/scalability implications have been considered, addressed, and tested
|
||||
1. [Documented][doc-styleguide] in the `/doc` directory
|
||||
1. [Documented][doc-guidelines] in the `/doc` directory
|
||||
1. [Changelog entry added][changelog], if necessary
|
||||
1. Reviewed and any concerns are addressed
|
||||
1. Merged by a project maintainer
|
||||
|
@ -664,7 +687,7 @@ merge request:
|
|||
contributors to enhance security
|
||||
1. [Database Migrations](doc/development/migration_style_guide.md)
|
||||
1. [Markdown](http://www.cirosantilli.com/markdown-styleguide)
|
||||
1. [Documentation styleguide][doc-styleguide]
|
||||
1. [Documentation styleguide](https://docs.gitlab.com/ee/development/documentation/styleguide.html)
|
||||
1. Interface text should be written subjectively instead of objectively. It
|
||||
should be the GitLab core team addressing a person. It should be written in
|
||||
present time and never use past tense (has been/was). For example instead
|
||||
|
@ -727,7 +750,7 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor
|
|||
[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-styleguide]: doc/development/doc_styleguide.md "Documentation styleguide"
|
||||
[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"
|
||||
|
|
|
@ -1 +1 @@
|
|||
0.104.0
|
||||
0.110.0
|
||||
|
|
|
@ -1 +1 @@
|
|||
7.1.2
|
||||
7.1.4
|
||||
|
|
|
@ -1 +1 @@
|
|||
4.2.1
|
||||
4.3.1
|
||||
|
|
27
Gemfile
|
@ -35,7 +35,7 @@ gem 'faraday', '~> 0.12'
|
|||
# Authentication libraries
|
||||
gem 'devise', '~> 4.4'
|
||||
gem 'doorkeeper', '~> 4.3'
|
||||
gem 'doorkeeper-openid_connect', '~> 1.3'
|
||||
gem 'doorkeeper-openid_connect', '~> 1.5'
|
||||
gem 'omniauth', '~> 1.8'
|
||||
gem 'omniauth-auth0', '~> 2.0.0'
|
||||
gem 'omniauth-azure-oauth2', '~> 0.0.9'
|
||||
|
@ -93,6 +93,10 @@ gem 'grape', '~> 1.0'
|
|||
gem 'grape-entity', '~> 0.7.1'
|
||||
gem 'rack-cors', '~> 1.0.0', require: 'rack/cors'
|
||||
|
||||
# GraphQL API
|
||||
gem 'graphql', '~> 1.8.0'
|
||||
gem 'graphiql-rails', '~> 1.4.10'
|
||||
|
||||
# Disable strong_params so that Mash does not respond to :permitted?
|
||||
gem 'hashie-forbidden_attributes'
|
||||
|
||||
|
@ -104,6 +108,7 @@ gem 'hamlit', '~> 2.6.1'
|
|||
|
||||
# Files attachments
|
||||
gem 'carrierwave', '~> 1.2'
|
||||
gem 'mini_magick'
|
||||
|
||||
# Drag and Drop UI
|
||||
gem 'dropzonejs-rails', '~> 0.7.1'
|
||||
|
@ -129,7 +134,7 @@ gem 'seed-fu', '~> 2.3.7'
|
|||
# Markdown and HTML processing
|
||||
gem 'html-pipeline', '~> 2.7.1'
|
||||
gem 'deckar01-task_list', '2.0.0'
|
||||
gem 'gitlab-markup', '~> 1.6.2'
|
||||
gem 'gitlab-markup', '~> 1.6.4'
|
||||
gem 'redcarpet', '~> 3.4'
|
||||
gem 'commonmarker', '~> 0.17'
|
||||
gem 'RedCloth', '~> 4.3.2'
|
||||
|
@ -225,7 +230,7 @@ gem 'ruby-fogbugz', '~> 0.2.1'
|
|||
gem 'kubeclient', '~> 3.1.0'
|
||||
|
||||
# Sanitize user input
|
||||
gem 'sanitize', '~> 2.0'
|
||||
gem 'sanitize', '~> 4.6.5'
|
||||
gem 'babosa', '~> 1.0.2'
|
||||
|
||||
# Sanitizes SVG input
|
||||
|
@ -294,7 +299,6 @@ gem 'peek-sidekiq', '~> 1.0.3'
|
|||
|
||||
# Metrics
|
||||
group :metrics do
|
||||
gem 'allocations', '~> 1.0', require: false, platform: :mri
|
||||
gem 'method_source', '~> 0.8', require: false
|
||||
gem 'influxdb', '~> 0.2', require: false
|
||||
|
||||
|
@ -328,7 +332,7 @@ group :development, :test do
|
|||
|
||||
gem 'database_cleaner', '~> 1.5.0'
|
||||
gem 'factory_bot_rails', '~> 4.8.2'
|
||||
gem 'rspec-rails', '~> 3.6.0'
|
||||
gem 'rspec-rails', '~> 3.7.0'
|
||||
gem 'rspec-retry', '~> 0.4.5'
|
||||
gem 'rspec_profiling', '~> 0.0.5'
|
||||
gem 'rspec-set', '~> 0.1.3'
|
||||
|
@ -342,7 +346,7 @@ group :development, :test do
|
|||
|
||||
gem 'capybara', '~> 2.15'
|
||||
gem 'capybara-screenshot', '~> 1.0.0'
|
||||
gem 'selenium-webdriver', '~> 3.5'
|
||||
gem 'selenium-webdriver', '~> 3.12'
|
||||
|
||||
gem 'spring', '~> 2.0.0'
|
||||
gem 'spring-commands-rspec', '~> 1.0.4'
|
||||
|
@ -374,7 +378,7 @@ end
|
|||
|
||||
group :test do
|
||||
gem 'shoulda-matchers', '~> 3.1.2', require: false
|
||||
gem 'email_spec', '~> 1.6.0'
|
||||
gem 'email_spec', '~> 2.2.0'
|
||||
gem 'json-schema', '~> 2.8.0'
|
||||
gem 'webmock', '~> 2.3.2'
|
||||
gem 'rails-controller-testing' if rails5? # Rails5 only gem.
|
||||
|
@ -384,7 +388,7 @@ group :test do
|
|||
gem 'test-prof', '~> 0.2.5'
|
||||
end
|
||||
|
||||
gem 'octokit', '~> 4.8'
|
||||
gem 'octokit', '~> 4.9'
|
||||
|
||||
gem 'mail_room', '~> 0.9.1'
|
||||
|
||||
|
@ -404,18 +408,17 @@ gem 'vmstat', '~> 2.3.0'
|
|||
gem 'sys-filesystem', '~> 1.1.6'
|
||||
|
||||
# SSH host key support
|
||||
gem 'net-ssh', '~> 4.2.0'
|
||||
gem 'net-ssh', '~> 5.0'
|
||||
gem 'sshkey', '~> 1.9.0'
|
||||
|
||||
# Required for ED25519 SSH host key support
|
||||
group :ed25519 do
|
||||
gem 'rbnacl-libsodium'
|
||||
gem 'rbnacl', '~> 4.0'
|
||||
gem 'ed25519', '~> 1.2'
|
||||
gem 'bcrypt_pbkdf', '~> 1.0'
|
||||
end
|
||||
|
||||
# Gitaly GRPC client
|
||||
gem 'gitaly-proto', '~> 0.100.0', require: 'gitaly'
|
||||
gem 'gitaly-proto', '~> 0.103.0', require: 'gitaly'
|
||||
gem 'grpc', '~> 1.11.0'
|
||||
|
||||
# Locked until https://github.com/google/protobuf/issues/4210 is closed
|
||||
|
|
120
Gemfile.lock
|
@ -49,7 +49,6 @@ GEM
|
|||
public_suffix (>= 2.0.2, < 4.0)
|
||||
aes_key_wrap (1.0.1)
|
||||
akismet (2.0.0)
|
||||
allocations (1.0.5)
|
||||
arel (6.0.4)
|
||||
asana (0.6.0)
|
||||
faraday (~> 0.9)
|
||||
|
@ -77,7 +76,7 @@ GEM
|
|||
babosa (1.0.2)
|
||||
base32 (0.3.2)
|
||||
batch-loader (1.2.1)
|
||||
bcrypt (3.1.11)
|
||||
bcrypt (3.1.12)
|
||||
bcrypt_pbkdf (1.0.0)
|
||||
benchmark-ips (2.3.0)
|
||||
better_errors (2.1.1)
|
||||
|
@ -115,7 +114,7 @@ GEM
|
|||
mime-types (>= 1.16)
|
||||
cause (0.1)
|
||||
charlock_holmes (0.7.6)
|
||||
childprocess (0.7.0)
|
||||
childprocess (0.9.0)
|
||||
ffi (~> 1.0, >= 1.0.11)
|
||||
chronic (0.10.2)
|
||||
chronic_duration (0.10.6)
|
||||
|
@ -172,15 +171,17 @@ GEM
|
|||
unf (>= 0.0.5, < 1.0.0)
|
||||
doorkeeper (4.3.2)
|
||||
railties (>= 4.2)
|
||||
doorkeeper-openid_connect (1.3.0)
|
||||
doorkeeper-openid_connect (1.5.0)
|
||||
doorkeeper (~> 4.3)
|
||||
json-jwt (~> 1.6)
|
||||
dropzonejs-rails (0.7.2)
|
||||
rails (> 3.1)
|
||||
ed25519 (1.2.4)
|
||||
email_reply_trimmer (0.1.6)
|
||||
email_spec (1.6.0)
|
||||
email_spec (2.2.0)
|
||||
htmlentities (~> 4.3.3)
|
||||
launchy (~> 2.1)
|
||||
mail (~> 2.2)
|
||||
mail (~> 2.7)
|
||||
encryptor (3.0.0)
|
||||
equalizer (0.0.11)
|
||||
erubis (2.7.0)
|
||||
|
@ -281,7 +282,7 @@ GEM
|
|||
gettext_i18n_rails (>= 0.7.1)
|
||||
po_to_json (>= 1.0.0)
|
||||
rails (>= 3.2.0)
|
||||
gitaly-proto (0.100.0)
|
||||
gitaly-proto (0.103.0)
|
||||
google-protobuf (~> 3.1)
|
||||
grpc (~> 1.10)
|
||||
github-linguist (5.3.3)
|
||||
|
@ -294,15 +295,15 @@ GEM
|
|||
flowdock (~> 0.7)
|
||||
gitlab-grit (>= 2.4.1)
|
||||
multi_json
|
||||
gitlab-gollum-lib (4.2.7.2)
|
||||
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 (~> 2.1)
|
||||
sanitize (~> 4.6.4)
|
||||
stringex (~> 2.6)
|
||||
gitlab-gollum-rugged_adapter (0.4.4)
|
||||
gitlab-gollum-rugged_adapter (0.4.4.1)
|
||||
mime-types (>= 1.15)
|
||||
rugged (~> 0.25)
|
||||
gitlab-grit (2.8.2)
|
||||
|
@ -310,7 +311,7 @@ GEM
|
|||
diff-lcs (~> 1.1)
|
||||
mime-types (>= 1.16)
|
||||
posix-spawn (~> 0.3)
|
||||
gitlab-markup (1.6.3)
|
||||
gitlab-markup (1.6.4)
|
||||
gitlab-styles (2.3.2)
|
||||
rubocop (~> 0.51)
|
||||
rubocop-gitlab-security (~> 0.1.0)
|
||||
|
@ -358,12 +359,16 @@ GEM
|
|||
grape-entity (0.7.1)
|
||||
activesupport (>= 4.0)
|
||||
multi_json (>= 1.3.2)
|
||||
grape-path-helpers (1.0.1)
|
||||
activesupport (~> 4)
|
||||
grape-path-helpers (1.0.5)
|
||||
activesupport (>= 4, < 5.1)
|
||||
grape (~> 1.0)
|
||||
rake (~> 12)
|
||||
grape_logging (1.7.0)
|
||||
grape
|
||||
graphiql-rails (1.4.10)
|
||||
railties
|
||||
sprockets-rails
|
||||
graphql (1.8.1)
|
||||
grpc (1.11.0)
|
||||
google-protobuf (~> 3.1)
|
||||
googleapis-common-protos-types (~> 1.0.0)
|
||||
|
@ -422,12 +427,10 @@ GEM
|
|||
oauth (~> 0.5, >= 0.5.0)
|
||||
jquery-atwho-rails (1.3.2)
|
||||
json (1.8.6)
|
||||
json-jwt (1.9.2)
|
||||
json-jwt (1.9.4)
|
||||
activesupport
|
||||
aes_key_wrap
|
||||
bindata
|
||||
securecompare
|
||||
url_safe_base64
|
||||
json-schema (2.8.0)
|
||||
addressable (>= 2.4)
|
||||
jwt (1.5.6)
|
||||
|
@ -446,7 +449,6 @@ GEM
|
|||
kgio (2.10.0)
|
||||
knapsack (1.16.0)
|
||||
rake
|
||||
timecop (>= 0.1.0)
|
||||
kubeclient (3.1.0)
|
||||
http (~> 2.2.2)
|
||||
recursive-open-struct (~> 1.0, >= 1.0.4)
|
||||
|
@ -493,6 +495,7 @@ GEM
|
|||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2016.0521)
|
||||
mimemagic (0.3.0)
|
||||
mini_magick (4.8.0)
|
||||
mini_mime (1.0.0)
|
||||
mini_portile2 (2.3.0)
|
||||
minitest (5.7.0)
|
||||
|
@ -505,10 +508,12 @@ GEM
|
|||
mustermann (~> 1.0.0)
|
||||
mysql2 (0.4.10)
|
||||
net-ldap (0.16.0)
|
||||
net-ssh (4.2.0)
|
||||
net-ssh (5.0.1)
|
||||
netrc (0.11.0)
|
||||
nokogiri (1.8.2)
|
||||
nokogiri (1.8.3)
|
||||
mini_portile2 (~> 2.3.0)
|
||||
nokogumbo (1.5.0)
|
||||
nokogiri
|
||||
numerizer (0.1.1)
|
||||
oauth (0.5.4)
|
||||
oauth2 (1.4.0)
|
||||
|
@ -517,7 +522,7 @@ GEM
|
|||
multi_json (~> 1.3)
|
||||
multi_xml (~> 0.5)
|
||||
rack (>= 1.2, < 3)
|
||||
octokit (4.8.0)
|
||||
octokit (4.9.0)
|
||||
sawyer (~> 0.8.0, >= 0.5.3)
|
||||
omniauth (1.8.1)
|
||||
hashie (>= 3.4.6, < 3.6.0)
|
||||
|
@ -691,10 +696,6 @@ GEM
|
|||
ffi (>= 0.5.0, < 2)
|
||||
rblineprof (0.3.6)
|
||||
debugger-ruby_core_source (~> 1.3)
|
||||
rbnacl (4.0.2)
|
||||
ffi
|
||||
rbnacl-libsodium (1.0.11)
|
||||
rbnacl (>= 3.0.1)
|
||||
rdoc (6.0.4)
|
||||
re2 (1.1.1)
|
||||
recaptcha (3.0.0)
|
||||
|
@ -740,36 +741,36 @@ GEM
|
|||
chunky_png
|
||||
rqrcode-rails3 (0.1.7)
|
||||
rqrcode (>= 0.4.2)
|
||||
rspec (3.6.0)
|
||||
rspec-core (~> 3.6.0)
|
||||
rspec-expectations (~> 3.6.0)
|
||||
rspec-mocks (~> 3.6.0)
|
||||
rspec-core (3.6.0)
|
||||
rspec-support (~> 3.6.0)
|
||||
rspec-expectations (3.6.0)
|
||||
rspec (3.7.0)
|
||||
rspec-core (~> 3.7.0)
|
||||
rspec-expectations (~> 3.7.0)
|
||||
rspec-mocks (~> 3.7.0)
|
||||
rspec-core (3.7.1)
|
||||
rspec-support (~> 3.7.0)
|
||||
rspec-expectations (3.7.0)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.6.0)
|
||||
rspec-mocks (3.6.0)
|
||||
rspec-support (~> 3.7.0)
|
||||
rspec-mocks (3.7.0)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.6.0)
|
||||
rspec-support (~> 3.7.0)
|
||||
rspec-parameterized (0.4.0)
|
||||
binding_of_caller
|
||||
parser
|
||||
proc_to_ast
|
||||
rspec (>= 2.13, < 4)
|
||||
unparser
|
||||
rspec-rails (3.6.0)
|
||||
rspec-rails (3.7.2)
|
||||
actionpack (>= 3.0)
|
||||
activesupport (>= 3.0)
|
||||
railties (>= 3.0)
|
||||
rspec-core (~> 3.6.0)
|
||||
rspec-expectations (~> 3.6.0)
|
||||
rspec-mocks (~> 3.6.0)
|
||||
rspec-support (~> 3.6.0)
|
||||
rspec-core (~> 3.7.0)
|
||||
rspec-expectations (~> 3.7.0)
|
||||
rspec-mocks (~> 3.7.0)
|
||||
rspec-support (~> 3.7.0)
|
||||
rspec-retry (0.4.5)
|
||||
rspec-core
|
||||
rspec-set (0.1.3)
|
||||
rspec-support (3.6.0)
|
||||
rspec-support (3.7.1)
|
||||
rspec_profiling (0.0.5)
|
||||
activerecord
|
||||
pg
|
||||
|
@ -801,10 +802,12 @@ GEM
|
|||
rubyzip (1.2.1)
|
||||
rufus-scheduler (3.4.0)
|
||||
et-orbi (~> 1.0)
|
||||
rugged (0.27.1)
|
||||
rugged (0.27.2)
|
||||
safe_yaml (1.0.4)
|
||||
sanitize (2.1.0)
|
||||
sanitize (4.6.5)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.4.4)
|
||||
nokogumbo (~> 1.4)
|
||||
sass (3.5.5)
|
||||
sass-listen (~> 4.0.0)
|
||||
sass-listen (4.0.0)
|
||||
|
@ -822,15 +825,14 @@ GEM
|
|||
scss_lint (0.56.0)
|
||||
rake (>= 0.9, < 13)
|
||||
sass (~> 3.5.3)
|
||||
securecompare (1.0.0)
|
||||
seed-fu (2.3.7)
|
||||
activerecord (>= 3.1)
|
||||
activesupport (>= 3.1)
|
||||
select2-rails (3.5.9.3)
|
||||
thor (~> 0.14)
|
||||
selenium-webdriver (3.5.0)
|
||||
selenium-webdriver (3.12.0)
|
||||
childprocess (~> 0.5)
|
||||
rubyzip (~> 1.0)
|
||||
rubyzip (~> 1.2)
|
||||
sentry-raven (2.7.2)
|
||||
faraday (>= 0.7.6, < 1.0)
|
||||
settingslogic (2.0.9)
|
||||
|
@ -866,7 +868,7 @@ GEM
|
|||
activesupport (>= 4.2)
|
||||
spring-commands-rspec (1.0.4)
|
||||
spring (>= 0.9.1)
|
||||
sprockets (3.7.1)
|
||||
sprockets (3.7.2)
|
||||
concurrent-ruby (~> 1.0)
|
||||
rack (> 1, < 3)
|
||||
sprockets-rails (3.2.1)
|
||||
|
@ -934,7 +936,6 @@ GEM
|
|||
equalizer (~> 0.0.9)
|
||||
parser (>= 2.3.1.2, < 2.6)
|
||||
procto (~> 0.0.2)
|
||||
url_safe_base64 (0.2.2)
|
||||
validates_hostname (1.0.6)
|
||||
activerecord (>= 3.0)
|
||||
activesupport (>= 3.0)
|
||||
|
@ -972,7 +973,6 @@ DEPENDENCIES
|
|||
acts-as-taggable-on (~> 5.0)
|
||||
addressable (~> 2.5.2)
|
||||
akismet (~> 2.0)
|
||||
allocations (~> 1.0)
|
||||
asana (~> 0.6.0)
|
||||
asciidoctor (~> 1.5.6)
|
||||
asciidoctor-plantuml (= 0.0.8)
|
||||
|
@ -1009,10 +1009,11 @@ DEPENDENCIES
|
|||
devise-two-factor (~> 3.0.0)
|
||||
diffy (~> 3.1.0)
|
||||
doorkeeper (~> 4.3)
|
||||
doorkeeper-openid_connect (~> 1.3)
|
||||
doorkeeper-openid_connect (~> 1.5)
|
||||
dropzonejs-rails (~> 0.7.1)
|
||||
ed25519 (~> 1.2)
|
||||
email_reply_trimmer (~> 0.1)
|
||||
email_spec (~> 1.6.0)
|
||||
email_spec (~> 2.2.0)
|
||||
factory_bot_rails (~> 4.8.2)
|
||||
faraday (~> 0.12)
|
||||
fast_blank
|
||||
|
@ -1036,12 +1037,12 @@ DEPENDENCIES
|
|||
gettext (~> 3.2.2)
|
||||
gettext_i18n_rails (~> 1.8.0)
|
||||
gettext_i18n_rails_js (~> 1.3)
|
||||
gitaly-proto (~> 0.100.0)
|
||||
gitaly-proto (~> 0.103.0)
|
||||
github-linguist (~> 5.3.3)
|
||||
gitlab-flowdock-git-hook (~> 1.0.1)
|
||||
gitlab-gollum-lib (~> 4.2)
|
||||
gitlab-gollum-rugged_adapter (~> 0.4.4)
|
||||
gitlab-markup (~> 1.6.2)
|
||||
gitlab-markup (~> 1.6.4)
|
||||
gitlab-styles (~> 2.3)
|
||||
gitlab_omniauth-ldap (~> 2.0.4)
|
||||
gon (~> 6.2)
|
||||
|
@ -1052,6 +1053,8 @@ DEPENDENCIES
|
|||
grape-entity (~> 0.7.1)
|
||||
grape-path-helpers (~> 1.0)
|
||||
grape_logging (~> 1.7)
|
||||
graphiql-rails (~> 1.4.10)
|
||||
graphql (~> 1.8.0)
|
||||
grpc (~> 1.11.0)
|
||||
haml_lint (~> 0.26.0)
|
||||
hamlit (~> 2.6.1)
|
||||
|
@ -1077,14 +1080,15 @@ DEPENDENCIES
|
|||
loofah (~> 2.2)
|
||||
mail_room (~> 0.9.1)
|
||||
method_source (~> 0.8)
|
||||
mini_magick
|
||||
minitest (~> 5.7.0)
|
||||
mousetrap-rails (~> 1.4.6)
|
||||
mysql2 (~> 0.4.10)
|
||||
net-ldap
|
||||
net-ssh (~> 4.2.0)
|
||||
net-ssh (~> 5.0)
|
||||
nokogiri (~> 1.8.2)
|
||||
oauth2 (~> 1.4)
|
||||
octokit (~> 4.8)
|
||||
octokit (~> 4.9)
|
||||
omniauth (~> 1.8)
|
||||
omniauth-auth0 (~> 2.0.0)
|
||||
omniauth-authentiq (~> 0.3.3)
|
||||
|
@ -1123,8 +1127,6 @@ DEPENDENCIES
|
|||
rainbow (~> 2.2)
|
||||
raindrops (~> 0.18)
|
||||
rblineprof (~> 0.3.6)
|
||||
rbnacl (~> 4.0)
|
||||
rbnacl-libsodium
|
||||
rdoc (~> 6.0)
|
||||
re2 (~> 1.1.1)
|
||||
recaptcha (~> 3.0)
|
||||
|
@ -1137,7 +1139,7 @@ DEPENDENCIES
|
|||
rouge (~> 3.1)
|
||||
rqrcode-rails3 (~> 0.1.7)
|
||||
rspec-parameterized
|
||||
rspec-rails (~> 3.6.0)
|
||||
rspec-rails (~> 3.7.0)
|
||||
rspec-retry (~> 0.4.5)
|
||||
rspec-set (~> 0.1.3)
|
||||
rspec_profiling (~> 0.0.5)
|
||||
|
@ -1149,12 +1151,12 @@ DEPENDENCIES
|
|||
ruby_parser (~> 3.8)
|
||||
rufus-scheduler (~> 3.4)
|
||||
rugged (~> 0.27)
|
||||
sanitize (~> 2.0)
|
||||
sanitize (~> 4.6.5)
|
||||
sass-rails (~> 5.0.6)
|
||||
scss_lint (~> 0.56.0)
|
||||
seed-fu (~> 2.3.7)
|
||||
select2-rails (~> 3.5.9)
|
||||
selenium-webdriver (~> 3.5)
|
||||
selenium-webdriver (~> 3.12)
|
||||
sentry-raven (~> 2.7)
|
||||
settingslogic (~> 2.0.9)
|
||||
sham_rack (~> 1.3.6)
|
||||
|
|
|
@ -3,7 +3,7 @@ GEM
|
|||
specs:
|
||||
RedCloth (4.3.2)
|
||||
abstract_type (0.0.7)
|
||||
ace-rails-ap (4.1.4)
|
||||
ace-rails-ap (4.1.2)
|
||||
actioncable (5.0.7)
|
||||
actionpack (= 5.0.7)
|
||||
nio4r (>= 1.2, < 3.0)
|
||||
|
@ -52,14 +52,13 @@ GEM
|
|||
public_suffix (>= 2.0.2, < 4.0)
|
||||
aes_key_wrap (1.0.1)
|
||||
akismet (2.0.0)
|
||||
allocations (1.0.5)
|
||||
arel (7.1.4)
|
||||
asana (0.6.3)
|
||||
asana (0.6.0)
|
||||
faraday (~> 0.9)
|
||||
faraday_middleware (~> 0.9)
|
||||
faraday_middleware-multi_json (~> 0.0)
|
||||
oauth2 (~> 1.0)
|
||||
asciidoctor (1.5.6.1)
|
||||
asciidoctor (1.5.6.2)
|
||||
asciidoctor-plantuml (0.0.8)
|
||||
asciidoctor (~> 1.5)
|
||||
asset_sync (2.4.0)
|
||||
|
@ -71,10 +70,8 @@ GEM
|
|||
atomic (1.1.99)
|
||||
attr_encrypted (3.1.0)
|
||||
encryptor (~> 3.0.0)
|
||||
attr_required (1.0.1)
|
||||
autoprefixer-rails (8.1.0.1)
|
||||
execjs
|
||||
awesome_print (1.2.0)
|
||||
attr_required (1.0.0)
|
||||
awesome_print (1.8.0)
|
||||
axiom-types (0.1.1)
|
||||
descendants_tracker (~> 0.0.4)
|
||||
ice_nine (~> 0.11.0)
|
||||
|
@ -90,15 +87,12 @@ GEM
|
|||
erubis (>= 2.6.6)
|
||||
rack (>= 0.9.0)
|
||||
bindata (2.4.3)
|
||||
binding_of_caller (0.7.3)
|
||||
binding_of_caller (0.7.2)
|
||||
debug_inspector (>= 0.0.1)
|
||||
blankslate (2.1.2.4)
|
||||
bootstrap-sass (3.3.7)
|
||||
autoprefixer-rails (>= 5.2.1)
|
||||
sass (>= 3.3.4)
|
||||
bootstrap_form (2.7.0)
|
||||
brakeman (4.2.1)
|
||||
browser (2.5.3)
|
||||
browser (2.2.0)
|
||||
builder (3.2.3)
|
||||
bullet (5.5.1)
|
||||
activesupport (>= 3.0.0)
|
||||
|
@ -107,32 +101,33 @@ GEM
|
|||
bundler (~> 1.2)
|
||||
thor (~> 0.18)
|
||||
byebug (9.0.6)
|
||||
capybara (2.18.0)
|
||||
capybara (2.15.1)
|
||||
addressable
|
||||
mini_mime (>= 0.1.3)
|
||||
nokogiri (>= 1.3.3)
|
||||
rack (>= 1.0.0)
|
||||
rack-test (>= 0.5.4)
|
||||
xpath (>= 2.0, < 4.0)
|
||||
capybara-screenshot (1.0.18)
|
||||
xpath (~> 2.0)
|
||||
capybara-screenshot (1.0.14)
|
||||
capybara (>= 1.0, < 3)
|
||||
launchy
|
||||
carrierwave (1.2.2)
|
||||
carrierwave (1.2.1)
|
||||
activemodel (>= 4.0.0)
|
||||
activesupport (>= 4.0.0)
|
||||
mime-types (>= 1.16)
|
||||
charlock_holmes (0.7.5)
|
||||
cause (0.1)
|
||||
charlock_holmes (0.7.6)
|
||||
childprocess (0.9.0)
|
||||
ffi (~> 1.0, >= 1.0.11)
|
||||
chronic (0.10.2)
|
||||
chronic_duration (0.10.6)
|
||||
numerizer (~> 0.1.1)
|
||||
chunky_png (1.3.10)
|
||||
chunky_png (1.3.5)
|
||||
citrus (3.0.2)
|
||||
coderay (1.1.2)
|
||||
coderay (1.1.1)
|
||||
coercible (1.0.0)
|
||||
descendants_tracker (~> 0.0.1)
|
||||
commonmarker (0.17.9)
|
||||
commonmarker (0.17.8)
|
||||
ruby-enum (~> 0.5)
|
||||
concord (0.1.5)
|
||||
adamantium (~> 0.2.0)
|
||||
|
@ -145,11 +140,11 @@ GEM
|
|||
safe_yaml (~> 1.0.0)
|
||||
crass (1.0.4)
|
||||
creole (0.5.0)
|
||||
css_parser (1.6.0)
|
||||
css_parser (1.5.0)
|
||||
addressable
|
||||
daemons (1.2.6)
|
||||
daemons (1.2.3)
|
||||
database_cleaner (1.5.3)
|
||||
debug_inspector (0.0.3)
|
||||
debug_inspector (0.0.2)
|
||||
debugger-ruby_core_source (1.3.8)
|
||||
deckar01-task_list (2.0.0)
|
||||
html-pipeline
|
||||
|
@ -159,44 +154,46 @@ GEM
|
|||
activerecord (>= 3.2.0, < 5.2)
|
||||
descendants_tracker (0.0.4)
|
||||
thread_safe (~> 0.3, >= 0.3.1)
|
||||
device_detector (1.0.1)
|
||||
devise (4.4.1)
|
||||
device_detector (1.0.0)
|
||||
devise (4.4.3)
|
||||
bcrypt (~> 3.0)
|
||||
orm_adapter (~> 0.1)
|
||||
railties (>= 4.1.0, < 5.2)
|
||||
railties (>= 4.1.0, < 6.0)
|
||||
responders
|
||||
warden (~> 1.2.3)
|
||||
devise-two-factor (3.0.2)
|
||||
activesupport (< 5.2)
|
||||
devise-two-factor (3.0.0)
|
||||
activesupport
|
||||
attr_encrypted (>= 1.3, < 4, != 2)
|
||||
devise (~> 4.0)
|
||||
railties (< 5.2)
|
||||
railties
|
||||
rotp (~> 2.0)
|
||||
diff-lcs (1.3)
|
||||
diffy (3.1.0)
|
||||
docile (1.1.5)
|
||||
domain_name (0.5.20170404)
|
||||
domain_name (0.5.20180417)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
doorkeeper (4.3.1)
|
||||
doorkeeper (4.3.2)
|
||||
railties (>= 4.2)
|
||||
doorkeeper-openid_connect (1.3.0)
|
||||
doorkeeper-openid_connect (1.5.0)
|
||||
doorkeeper (~> 4.3)
|
||||
json-jwt (~> 1.6)
|
||||
dropzonejs-rails (0.7.4)
|
||||
dropzonejs-rails (0.7.2)
|
||||
rails (> 3.1)
|
||||
email_reply_trimmer (0.1.10)
|
||||
email_spec (1.6.0)
|
||||
ed25519 (1.2.4)
|
||||
email_reply_trimmer (0.1.6)
|
||||
email_spec (2.2.0)
|
||||
htmlentities (~> 4.3.3)
|
||||
launchy (~> 2.1)
|
||||
mail (~> 2.2)
|
||||
mail (~> 2.7)
|
||||
encryptor (3.0.0)
|
||||
equalizer (0.0.11)
|
||||
erubis (2.7.0)
|
||||
escape_utils (1.1.1)
|
||||
et-orbi (1.0.9)
|
||||
et-orbi (1.0.3)
|
||||
tzinfo
|
||||
eventmachine (1.2.5)
|
||||
excon (0.60.0)
|
||||
execjs (2.7.0)
|
||||
eventmachine (1.0.8)
|
||||
excon (0.62.0)
|
||||
execjs (2.6.0)
|
||||
expression_parser (0.9.0)
|
||||
factory_bot (4.8.2)
|
||||
activesupport (>= 3.0.0)
|
||||
|
@ -212,8 +209,8 @@ GEM
|
|||
multi_json
|
||||
fast_blank (1.0.0)
|
||||
fast_gettext (1.6.0)
|
||||
ffaker (2.8.1)
|
||||
ffi (1.9.23)
|
||||
ffaker (2.4.0)
|
||||
ffi (1.9.18)
|
||||
flay (2.10.0)
|
||||
erubis (~> 2.7.0)
|
||||
path_expander (~> 1.0)
|
||||
|
@ -251,13 +248,13 @@ GEM
|
|||
fog-json (1.0.2)
|
||||
fog-core (~> 1.0)
|
||||
multi_json (~> 1.10)
|
||||
fog-local (0.5.0)
|
||||
fog-core (>= 1.27, < 3.0)
|
||||
fog-openstack (0.1.24)
|
||||
fog-core (~> 1.40)
|
||||
fog-local (0.3.1)
|
||||
fog-core (~> 1.27)
|
||||
fog-openstack (0.1.21)
|
||||
fog-core (>= 1.40)
|
||||
fog-json (>= 1.0)
|
||||
ipaddress (>= 0.8)
|
||||
fog-rackspace (0.1.5)
|
||||
fog-rackspace (0.1.1)
|
||||
fog-core (>= 1.35)
|
||||
fog-json (>= 1.0)
|
||||
fog-xml (>= 0.1)
|
||||
|
@ -265,8 +262,8 @@ GEM
|
|||
fog-xml (0.1.3)
|
||||
fog-core
|
||||
nokogiri (>= 1.5.11, < 2.0.0)
|
||||
font-awesome-rails (4.7.0.3)
|
||||
railties (>= 3.2, < 5.2)
|
||||
font-awesome-rails (4.7.0.1)
|
||||
railties (>= 3.2, < 5.1)
|
||||
foreman (0.84.0)
|
||||
thor (~> 0.19.1)
|
||||
formatador (0.2.5)
|
||||
|
@ -277,7 +274,7 @@ GEM
|
|||
rugged (~> 0.21)
|
||||
gemojione (3.3.0)
|
||||
json
|
||||
get_process_mem (0.2.1)
|
||||
get_process_mem (0.2.0)
|
||||
gettext (3.2.9)
|
||||
locale (>= 2.0.5)
|
||||
text (>= 1.3.0)
|
||||
|
@ -288,7 +285,7 @@ GEM
|
|||
gettext_i18n_rails (>= 0.7.1)
|
||||
po_to_json (>= 1.0.0)
|
||||
rails (>= 3.2.0)
|
||||
gitaly-proto (0.99.0)
|
||||
gitaly-proto (0.103.0)
|
||||
google-protobuf (~> 3.1)
|
||||
grpc (~> 1.10)
|
||||
github-linguist (5.3.3)
|
||||
|
@ -301,15 +298,15 @@ GEM
|
|||
flowdock (~> 0.7)
|
||||
gitlab-grit (>= 2.4.1)
|
||||
multi_json
|
||||
gitlab-gollum-lib (4.2.7.2)
|
||||
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 (~> 2.1)
|
||||
sanitize (~> 4.6.4)
|
||||
stringex (~> 2.6)
|
||||
gitlab-gollum-rugged_adapter (0.4.4)
|
||||
gitlab-gollum-rugged_adapter (0.4.4.1)
|
||||
mime-types (>= 1.15)
|
||||
rugged (~> 0.25)
|
||||
gitlab-grit (2.8.2)
|
||||
|
@ -317,7 +314,7 @@ GEM
|
|||
diff-lcs (~> 1.1)
|
||||
mime-types (>= 1.16)
|
||||
posix-spawn (~> 0.3)
|
||||
gitlab-markup (1.6.3)
|
||||
gitlab-markup (1.6.4)
|
||||
gitlab-styles (2.3.2)
|
||||
rubocop (~> 0.51)
|
||||
rubocop-gitlab-security (~> 0.1.0)
|
||||
|
@ -353,9 +350,9 @@ GEM
|
|||
multi_json (~> 1.11)
|
||||
os (~> 0.9)
|
||||
signet (~> 0.7)
|
||||
gpgme (2.0.16)
|
||||
mini_portile2 (~> 2.3)
|
||||
grape (1.0.2)
|
||||
gpgme (2.0.13)
|
||||
mini_portile2 (~> 2.1)
|
||||
grape (1.0.3)
|
||||
activesupport
|
||||
builder
|
||||
mustermann-grape (~> 1.0.0)
|
||||
|
@ -365,12 +362,16 @@ GEM
|
|||
grape-entity (0.7.1)
|
||||
activesupport (>= 4.0)
|
||||
multi_json (>= 1.3.2)
|
||||
grape-route-helpers (2.1.0)
|
||||
activesupport
|
||||
grape (>= 0.16.0)
|
||||
rake
|
||||
grape-path-helpers (1.0.5)
|
||||
activesupport (>= 4, < 5.1)
|
||||
grape (~> 1.0)
|
||||
rake (~> 12)
|
||||
grape_logging (1.7.0)
|
||||
grape
|
||||
graphiql-rails (1.4.10)
|
||||
railties
|
||||
sprockets-rails
|
||||
graphql (1.8.1)
|
||||
grpc (1.11.0)
|
||||
google-protobuf (~> 3.1)
|
||||
googleapis-common-protos-types (~> 1.0.0)
|
||||
|
@ -383,23 +384,23 @@ GEM
|
|||
rake (>= 10, < 13)
|
||||
rubocop (>= 0.49.0)
|
||||
sysexits (~> 1.1)
|
||||
hamlit (2.6.2)
|
||||
hamlit (2.6.1)
|
||||
temple (~> 0.7.6)
|
||||
thor
|
||||
tilt
|
||||
hashdiff (0.3.7)
|
||||
hashdiff (0.3.4)
|
||||
hashie (3.5.7)
|
||||
hashie-forbidden_attributes (0.1.1)
|
||||
hashie (>= 3.0)
|
||||
health_check (2.6.0)
|
||||
rails (>= 4.0)
|
||||
hipchat (1.5.4)
|
||||
hipchat (1.5.2)
|
||||
httparty
|
||||
mimemagic
|
||||
html-pipeline (2.7.1)
|
||||
activesupport (>= 2)
|
||||
nokogiri (>= 1.4)
|
||||
html2text (0.2.1)
|
||||
html2text (0.2.0)
|
||||
nokogiri (~> 1.6)
|
||||
htmlentities (4.3.4)
|
||||
http (2.2.2)
|
||||
|
@ -417,48 +418,49 @@ GEM
|
|||
httpclient (2.8.3)
|
||||
i18n (1.0.1)
|
||||
concurrent-ruby (~> 1.0)
|
||||
icalendar (2.4.1)
|
||||
ice_nine (0.11.2)
|
||||
influxdb (0.5.3)
|
||||
influxdb (0.2.3)
|
||||
cause
|
||||
json
|
||||
ipaddress (0.8.3)
|
||||
jira-ruby (1.5.0)
|
||||
jira-ruby (1.4.1)
|
||||
activesupport
|
||||
multipart-post
|
||||
oauth (~> 0.5, >= 0.5.0)
|
||||
jquery-atwho-rails (1.3.2)
|
||||
json (1.8.6)
|
||||
json-jwt (1.9.2)
|
||||
json-jwt (1.9.4)
|
||||
activesupport
|
||||
aes_key_wrap
|
||||
bindata
|
||||
securecompare
|
||||
url_safe_base64
|
||||
json-schema (2.8.0)
|
||||
addressable (>= 2.4)
|
||||
jwt (1.5.6)
|
||||
kaminari (1.1.1)
|
||||
kaminari (1.0.1)
|
||||
activesupport (>= 4.1.0)
|
||||
kaminari-actionview (= 1.1.1)
|
||||
kaminari-activerecord (= 1.1.1)
|
||||
kaminari-core (= 1.1.1)
|
||||
kaminari-actionview (1.1.1)
|
||||
kaminari-actionview (= 1.0.1)
|
||||
kaminari-activerecord (= 1.0.1)
|
||||
kaminari-core (= 1.0.1)
|
||||
kaminari-actionview (1.0.1)
|
||||
actionview
|
||||
kaminari-core (= 1.1.1)
|
||||
kaminari-activerecord (1.1.1)
|
||||
kaminari-core (= 1.0.1)
|
||||
kaminari-activerecord (1.0.1)
|
||||
activerecord
|
||||
kaminari-core (= 1.1.1)
|
||||
kaminari-core (1.1.1)
|
||||
kgio (2.11.2)
|
||||
kaminari-core (= 1.0.1)
|
||||
kaminari-core (1.0.1)
|
||||
kgio (2.10.0)
|
||||
knapsack (1.16.0)
|
||||
rake
|
||||
kubeclient (3.0.0)
|
||||
kubeclient (3.1.0)
|
||||
http (~> 2.2.2)
|
||||
recursive-open-struct (~> 1.0.4)
|
||||
recursive-open-struct (~> 1.0, >= 1.0.4)
|
||||
rest-client (~> 2.0)
|
||||
launchy (2.4.3)
|
||||
addressable (~> 2.3)
|
||||
letter_opener (1.6.0)
|
||||
letter_opener (1.4.1)
|
||||
launchy (~> 2.2)
|
||||
letter_opener_web (1.3.3)
|
||||
letter_opener_web (1.3.0)
|
||||
actionmailer (>= 3.2)
|
||||
letter_opener (~> 1.0)
|
||||
railties (>= 3.2)
|
||||
|
@ -477,7 +479,7 @@ GEM
|
|||
logging (2.2.2)
|
||||
little-plugger (~> 1.1)
|
||||
multi_json (~> 1.10)
|
||||
lograge (0.9.0)
|
||||
lograge (0.10.0)
|
||||
actionpack (>= 4)
|
||||
activesupport (>= 4)
|
||||
railties (>= 4)
|
||||
|
@ -491,11 +493,12 @@ GEM
|
|||
memoist (0.16.0)
|
||||
memoizable (0.4.2)
|
||||
thread_safe (~> 0.3, >= 0.3.1)
|
||||
method_source (0.9.0)
|
||||
method_source (0.8.2)
|
||||
mime-types (3.1)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2016.0521)
|
||||
mimemagic (0.3.2)
|
||||
mimemagic (0.3.0)
|
||||
mini_magick (4.8.0)
|
||||
mini_mime (1.0.0)
|
||||
mini_portile2 (2.3.0)
|
||||
minitest (5.7.0)
|
||||
|
@ -507,12 +510,14 @@ GEM
|
|||
mustermann-grape (1.0.0)
|
||||
mustermann (~> 1.0.0)
|
||||
mysql2 (0.4.10)
|
||||
net-ldap (0.16.1)
|
||||
net-ssh (4.2.0)
|
||||
net-ldap (0.16.0)
|
||||
net-ssh (5.0.1)
|
||||
netrc (0.11.0)
|
||||
nio4r (2.3.1)
|
||||
nokogiri (1.8.2)
|
||||
mini_portile2 (~> 2.3.0)
|
||||
nokogumbo (1.5.0)
|
||||
nokogiri
|
||||
numerizer (0.1.1)
|
||||
oauth (0.5.4)
|
||||
oauth2 (1.4.0)
|
||||
|
@ -521,15 +526,16 @@ GEM
|
|||
multi_json (~> 1.3)
|
||||
multi_xml (~> 0.5)
|
||||
rack (>= 1.2, < 3)
|
||||
octokit (4.8.0)
|
||||
octokit (4.9.0)
|
||||
sawyer (~> 0.8.0, >= 0.5.3)
|
||||
omniauth (1.8.1)
|
||||
hashie (>= 3.4.6, < 3.6.0)
|
||||
rack (>= 1.6.2, < 3)
|
||||
omniauth-auth0 (2.0.0)
|
||||
omniauth-oauth2 (~> 1.4)
|
||||
omniauth-authentiq (0.3.1)
|
||||
omniauth-oauth2 (~> 1.3, >= 1.3.1)
|
||||
omniauth-authentiq (0.3.3)
|
||||
jwt (>= 1.5)
|
||||
omniauth-oauth2 (>= 1.5)
|
||||
omniauth-azure-oauth2 (0.0.9)
|
||||
jwt (~> 1.0)
|
||||
omniauth (~> 1.0)
|
||||
|
@ -561,7 +567,7 @@ GEM
|
|||
omniauth-oauth2 (1.5.0)
|
||||
oauth2 (~> 1.1)
|
||||
omniauth (~> 1.2)
|
||||
omniauth-oauth2-generic (0.2.4)
|
||||
omniauth-oauth2-generic (0.2.2)
|
||||
omniauth-oauth2 (~> 1.0)
|
||||
omniauth-saml (1.10.0)
|
||||
omniauth (~> 1.3, >= 1.3.2)
|
||||
|
@ -580,7 +586,7 @@ GEM
|
|||
orm_adapter (0.5.0)
|
||||
os (0.9.6)
|
||||
parallel (1.12.1)
|
||||
parser (2.5.0.5)
|
||||
parser (2.5.1.0)
|
||||
ast (~> 2.4.0)
|
||||
parslet (1.5.0)
|
||||
blankslate (~> 2.0)
|
||||
|
@ -616,9 +622,9 @@ GEM
|
|||
json (>= 1.6.0)
|
||||
posix-spawn (0.3.13)
|
||||
powerpack (0.1.1)
|
||||
premailer (1.11.1)
|
||||
premailer (1.10.4)
|
||||
addressable
|
||||
css_parser (>= 1.6.0)
|
||||
css_parser (>= 1.4.10)
|
||||
htmlentities (>= 4.0.0)
|
||||
premailer-rails (1.9.7)
|
||||
actionmailer (>= 3, < 6)
|
||||
|
@ -628,15 +634,16 @@ GEM
|
|||
parser
|
||||
unparser
|
||||
procto (0.0.3)
|
||||
prometheus-client-mmap (0.9.2)
|
||||
pry (0.11.3)
|
||||
prometheus-client-mmap (0.9.3)
|
||||
pry (0.10.4)
|
||||
coderay (~> 1.1.0)
|
||||
method_source (~> 0.9.0)
|
||||
pry-byebug (3.4.3)
|
||||
byebug (>= 9.0, < 9.1)
|
||||
method_source (~> 0.8.1)
|
||||
slop (~> 3.4)
|
||||
pry-byebug (3.4.2)
|
||||
byebug (~> 9.0)
|
||||
pry (~> 0.10)
|
||||
pry-rails (0.3.6)
|
||||
pry (>= 0.10.4)
|
||||
pry-rails (0.3.5)
|
||||
pry (>= 0.9.10)
|
||||
public_suffix (3.0.2)
|
||||
pyu-ruby-sasl (0.0.3.3)
|
||||
rack (2.0.5)
|
||||
|
@ -653,7 +660,7 @@ GEM
|
|||
rack (>= 1.1)
|
||||
rack-protection (2.0.1)
|
||||
rack
|
||||
rack-proxy (0.6.4)
|
||||
rack-proxy (0.6.0)
|
||||
rack
|
||||
rack-test (0.6.3)
|
||||
rack (>= 1.0)
|
||||
|
@ -691,22 +698,18 @@ GEM
|
|||
thor (>= 0.18.1, < 2.0)
|
||||
rainbow (2.2.2)
|
||||
rake
|
||||
raindrops (0.19.0)
|
||||
raindrops (0.18.0)
|
||||
rake (12.3.1)
|
||||
rb-fsevent (0.10.3)
|
||||
rb-fsevent (0.10.2)
|
||||
rb-inotify (0.9.10)
|
||||
ffi (>= 0.5.0, < 2)
|
||||
rblineprof (0.3.7)
|
||||
rblineprof (0.3.6)
|
||||
debugger-ruby_core_source (~> 1.3)
|
||||
rbnacl (4.0.2)
|
||||
ffi
|
||||
rbnacl-libsodium (1.0.16)
|
||||
rbnacl (>= 3.0.1)
|
||||
rdoc (4.3.0)
|
||||
rdoc (6.0.4)
|
||||
re2 (1.1.1)
|
||||
recaptcha (3.4.0)
|
||||
recaptcha (3.0.0)
|
||||
json
|
||||
recursive-open-struct (1.0.5)
|
||||
recursive-open-struct (1.1.0)
|
||||
redcarpet (3.4.0)
|
||||
redis (3.3.5)
|
||||
redis-actionpack (5.0.2)
|
||||
|
@ -716,8 +719,8 @@ GEM
|
|||
redis-activesupport (5.0.4)
|
||||
activesupport (>= 3, < 6)
|
||||
redis-store (>= 1.3, < 2)
|
||||
redis-namespace (1.5.3)
|
||||
redis (~> 3.0, >= 3.0.4)
|
||||
redis-namespace (1.6.0)
|
||||
redis (>= 3.0.4)
|
||||
redis-rack (2.0.4)
|
||||
rack (>= 1.5, < 3)
|
||||
redis-store (>= 1.2, < 2)
|
||||
|
@ -731,8 +734,7 @@ GEM
|
|||
declarative (< 0.1.0)
|
||||
declarative-option (< 0.2.0)
|
||||
uber (< 0.2.0)
|
||||
request_store (1.4.0)
|
||||
rack (>= 1.4)
|
||||
request_store (1.3.1)
|
||||
responders (2.4.0)
|
||||
actionpack (>= 4.2.0, < 5.3)
|
||||
railties (>= 4.2.0, < 5.3)
|
||||
|
@ -741,43 +743,43 @@ GEM
|
|||
mime-types (>= 1.16, < 4.0)
|
||||
netrc (~> 0.8)
|
||||
retriable (3.1.1)
|
||||
rinku (2.0.4)
|
||||
rinku (2.0.0)
|
||||
rotp (2.1.2)
|
||||
rouge (3.1.1)
|
||||
rqrcode (0.10.1)
|
||||
chunky_png (~> 1.0)
|
||||
rqrcode (0.7.0)
|
||||
chunky_png
|
||||
rqrcode-rails3 (0.1.7)
|
||||
rqrcode (>= 0.4.2)
|
||||
rspec (3.6.0)
|
||||
rspec-core (~> 3.6.0)
|
||||
rspec-expectations (~> 3.6.0)
|
||||
rspec-mocks (~> 3.6.0)
|
||||
rspec-core (3.6.0)
|
||||
rspec-support (~> 3.6.0)
|
||||
rspec-expectations (3.6.0)
|
||||
rspec (3.7.0)
|
||||
rspec-core (~> 3.7.0)
|
||||
rspec-expectations (~> 3.7.0)
|
||||
rspec-mocks (~> 3.7.0)
|
||||
rspec-core (3.7.1)
|
||||
rspec-support (~> 3.7.0)
|
||||
rspec-expectations (3.7.0)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.6.0)
|
||||
rspec-mocks (3.6.0)
|
||||
rspec-support (~> 3.7.0)
|
||||
rspec-mocks (3.7.0)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.6.0)
|
||||
rspec-support (~> 3.7.0)
|
||||
rspec-parameterized (0.4.0)
|
||||
binding_of_caller
|
||||
parser
|
||||
proc_to_ast
|
||||
rspec (>= 2.13, < 4)
|
||||
unparser
|
||||
rspec-rails (3.6.1)
|
||||
rspec-rails (3.7.2)
|
||||
actionpack (>= 3.0)
|
||||
activesupport (>= 3.0)
|
||||
railties (>= 3.0)
|
||||
rspec-core (~> 3.6.0)
|
||||
rspec-expectations (~> 3.6.0)
|
||||
rspec-mocks (~> 3.6.0)
|
||||
rspec-support (~> 3.6.0)
|
||||
rspec-retry (0.4.6)
|
||||
rspec-core (~> 3.7.0)
|
||||
rspec-expectations (~> 3.7.0)
|
||||
rspec-mocks (~> 3.7.0)
|
||||
rspec-support (~> 3.7.0)
|
||||
rspec-retry (0.4.5)
|
||||
rspec-core
|
||||
rspec-set (0.1.3)
|
||||
rspec-support (3.6.0)
|
||||
rspec-support (3.7.1)
|
||||
rspec_profiling (0.0.5)
|
||||
activerecord
|
||||
pg
|
||||
|
@ -792,7 +794,7 @@ GEM
|
|||
unicode-display_width (~> 1.0, >= 1.0.1)
|
||||
rubocop-gitlab-security (0.1.1)
|
||||
rubocop (>= 0.51)
|
||||
rubocop-rspec (1.22.2)
|
||||
rubocop-rspec (1.22.1)
|
||||
rubocop (>= 0.52.1)
|
||||
ruby-enum (0.7.2)
|
||||
i18n
|
||||
|
@ -802,23 +804,25 @@ GEM
|
|||
ruby-progressbar (1.9.0)
|
||||
ruby-saml (1.7.2)
|
||||
nokogiri (>= 1.5.10)
|
||||
ruby_parser (3.11.0)
|
||||
sexp_processor (~> 4.9)
|
||||
ruby_parser (3.9.0)
|
||||
sexp_processor (~> 4.1)
|
||||
rubyntlm (0.6.2)
|
||||
rubypants (0.7.0)
|
||||
rubypants (0.2.0)
|
||||
rubyzip (1.2.1)
|
||||
rufus-scheduler (3.4.2)
|
||||
rufus-scheduler (3.4.0)
|
||||
et-orbi (~> 1.0)
|
||||
rugged (0.27.0)
|
||||
rugged (0.27.1)
|
||||
safe_yaml (1.0.4)
|
||||
sanitize (2.1.0)
|
||||
sanitize (4.6.5)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.4.4)
|
||||
nokogumbo (~> 1.4)
|
||||
sass (3.5.5)
|
||||
sass-listen (~> 4.0.0)
|
||||
sass-listen (4.0.0)
|
||||
rb-fsevent (~> 0.9, >= 0.9.4)
|
||||
rb-inotify (~> 0.9, >= 0.9.7)
|
||||
sass-rails (5.0.7)
|
||||
sass-rails (5.0.6)
|
||||
railties (>= 4.0.0, < 6)
|
||||
sass (~> 3.1)
|
||||
sprockets (>= 2.8, < 4.0)
|
||||
|
@ -830,29 +834,28 @@ GEM
|
|||
scss_lint (0.56.0)
|
||||
rake (>= 0.9, < 13)
|
||||
sass (~> 3.5.3)
|
||||
securecompare (1.0.0)
|
||||
seed-fu (2.3.7)
|
||||
activerecord (>= 3.1)
|
||||
activesupport (>= 3.1)
|
||||
select2-rails (3.5.10)
|
||||
select2-rails (3.5.9.3)
|
||||
thor (~> 0.14)
|
||||
selenium-webdriver (3.11.0)
|
||||
selenium-webdriver (3.12.0)
|
||||
childprocess (~> 0.5)
|
||||
rubyzip (~> 1.2)
|
||||
sentry-raven (2.7.2)
|
||||
faraday (>= 0.7.6, < 1.0)
|
||||
settingslogic (2.0.9)
|
||||
sexp_processor (4.10.1)
|
||||
sexp_processor (4.9.0)
|
||||
sham_rack (1.3.6)
|
||||
rack
|
||||
shoulda-matchers (3.1.2)
|
||||
activesupport (>= 4.0.0)
|
||||
sidekiq (5.1.1)
|
||||
sidekiq (5.1.3)
|
||||
concurrent-ruby (~> 1.0)
|
||||
connection_pool (~> 2.2, >= 2.2.0)
|
||||
rack-protection (>= 1.5.0)
|
||||
redis (>= 3.3.5, < 5)
|
||||
sidekiq-cron (0.6.3)
|
||||
sidekiq-cron (0.6.0)
|
||||
rufus-scheduler (>= 3.3.0)
|
||||
sidekiq (>= 4.2.1)
|
||||
sidekiq-limit_fetch (3.4.0)
|
||||
|
@ -862,14 +865,15 @@ GEM
|
|||
faraday (~> 0.9)
|
||||
jwt (>= 1.5, < 3.0)
|
||||
multi_json (~> 1.10)
|
||||
simple_po_parser (1.1.3)
|
||||
simple_po_parser (1.1.2)
|
||||
simplecov (0.14.1)
|
||||
docile (~> 1.1.0)
|
||||
json (>= 1.8, < 3)
|
||||
simplecov-html (~> 0.10.0)
|
||||
simplecov-html (0.10.2)
|
||||
simplecov-html (0.10.0)
|
||||
slack-notifier (1.5.1)
|
||||
spring (2.0.2)
|
||||
slop (3.6.0)
|
||||
spring (2.0.1)
|
||||
activesupport (>= 4.2)
|
||||
spring-commands-rspec (1.0.4)
|
||||
spring (>= 0.9.1)
|
||||
|
@ -882,7 +886,7 @@ GEM
|
|||
sprockets (>= 3.0.0)
|
||||
sqlite3 (1.3.13)
|
||||
sshkey (1.9.0)
|
||||
stackprof (0.2.11)
|
||||
stackprof (0.2.10)
|
||||
state_machines (0.5.0)
|
||||
state_machines-activemodel (0.5.1)
|
||||
activemodel (>= 4.1, < 6.0)
|
||||
|
@ -891,19 +895,19 @@ GEM
|
|||
activerecord (>= 4.1, < 6.0)
|
||||
state_machines-activemodel (>= 0.5.0)
|
||||
stringex (2.8.4)
|
||||
sys-filesystem (1.1.9)
|
||||
sys-filesystem (1.1.6)
|
||||
ffi
|
||||
sysexits (1.2.0)
|
||||
temple (0.7.7)
|
||||
test-prof (0.2.5)
|
||||
text (1.3.1)
|
||||
thin (1.7.2)
|
||||
thin (1.7.0)
|
||||
daemons (~> 1.0, >= 1.0.9)
|
||||
eventmachine (~> 1.0, >= 1.0.4)
|
||||
rack (>= 1, < 3)
|
||||
thor (0.19.4)
|
||||
thread_safe (0.3.6)
|
||||
tilt (2.0.8)
|
||||
tilt (2.0.6)
|
||||
timecop (0.8.1)
|
||||
timfel-krb5-auth (0.8.3)
|
||||
toml (0.1.2)
|
||||
|
@ -923,7 +927,7 @@ GEM
|
|||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.7.5)
|
||||
unicode-display_width (1.3.0)
|
||||
unicode-display_width (1.3.2)
|
||||
unicorn (5.1.0)
|
||||
kgio (~> 2.6)
|
||||
raindrops (~> 0.7)
|
||||
|
@ -939,8 +943,7 @@ GEM
|
|||
equalizer (~> 0.0.9)
|
||||
parser (>= 2.3.1.2, < 2.6)
|
||||
procto (~> 0.0.2)
|
||||
url_safe_base64 (0.2.2)
|
||||
validates_hostname (1.0.8)
|
||||
validates_hostname (1.0.6)
|
||||
activerecord (>= 3.0)
|
||||
activesupport (>= 3.0)
|
||||
version_sorter (2.1.0)
|
||||
|
@ -956,7 +959,7 @@ GEM
|
|||
addressable (>= 2.3.6)
|
||||
crack (>= 0.3.2)
|
||||
hashdiff
|
||||
webpack-rails (0.9.11)
|
||||
webpack-rails (0.9.10)
|
||||
railties (>= 3.2.0)
|
||||
websocket-driver (0.6.5)
|
||||
websocket-extensions (>= 0.1.0)
|
||||
|
@ -967,8 +970,8 @@ GEM
|
|||
rinku
|
||||
with_env (1.1.0)
|
||||
xml-simple (1.1.5)
|
||||
xpath (3.0.0)
|
||||
nokogiri (~> 1.8)
|
||||
xpath (2.1.0)
|
||||
nokogiri (~> 1.3)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
@ -980,13 +983,12 @@ DEPENDENCIES
|
|||
acts-as-taggable-on (~> 5.0)
|
||||
addressable (~> 2.5.2)
|
||||
akismet (~> 2.0)
|
||||
allocations (~> 1.0)
|
||||
asana (~> 0.6.0)
|
||||
asciidoctor (~> 1.5.6)
|
||||
asciidoctor-plantuml (= 0.0.8)
|
||||
asset_sync (~> 2.4)
|
||||
attr_encrypted (~> 3.1.0)
|
||||
awesome_print (~> 1.2.0)
|
||||
awesome_print
|
||||
babosa (~> 1.0.2)
|
||||
base32 (~> 0.3.0)
|
||||
batch-loader (~> 1.2.1)
|
||||
|
@ -994,7 +996,6 @@ DEPENDENCIES
|
|||
benchmark-ips (~> 2.3.0)
|
||||
better_errors (~> 2.1.0)
|
||||
binding_of_caller (~> 0.7.2)
|
||||
bootstrap-sass (~> 3.3.0)
|
||||
bootstrap_form (~> 2.7.0)
|
||||
brakeman (~> 4.2)
|
||||
browser (~> 2.2)
|
||||
|
@ -1018,10 +1019,11 @@ DEPENDENCIES
|
|||
devise-two-factor (~> 3.0.0)
|
||||
diffy (~> 3.1.0)
|
||||
doorkeeper (~> 4.3)
|
||||
doorkeeper-openid_connect (~> 1.3)
|
||||
doorkeeper-openid_connect (~> 1.5)
|
||||
dropzonejs-rails (~> 0.7.1)
|
||||
ed25519 (~> 1.2)
|
||||
email_reply_trimmer (~> 0.1)
|
||||
email_spec (~> 1.6.0)
|
||||
email_spec (~> 2.2.0)
|
||||
factory_bot_rails (~> 4.8.2)
|
||||
faraday (~> 0.12)
|
||||
fast_blank
|
||||
|
@ -1045,12 +1047,12 @@ DEPENDENCIES
|
|||
gettext (~> 3.2.2)
|
||||
gettext_i18n_rails (~> 1.8.0)
|
||||
gettext_i18n_rails_js (~> 1.3)
|
||||
gitaly-proto (~> 0.99.0)
|
||||
gitaly-proto (~> 0.103.0)
|
||||
github-linguist (~> 5.3.3)
|
||||
gitlab-flowdock-git-hook (~> 1.0.1)
|
||||
gitlab-gollum-lib (~> 4.2)
|
||||
gitlab-gollum-rugged_adapter (~> 0.4.4)
|
||||
gitlab-markup (~> 1.6.2)
|
||||
gitlab-markup (~> 1.6.4)
|
||||
gitlab-styles (~> 2.3)
|
||||
gitlab_omniauth-ldap (~> 2.0.4)
|
||||
gon (~> 6.2)
|
||||
|
@ -1059,8 +1061,10 @@ DEPENDENCIES
|
|||
gpgme
|
||||
grape (~> 1.0)
|
||||
grape-entity (~> 0.7.1)
|
||||
grape-route-helpers (~> 2.1.0)
|
||||
grape-path-helpers (~> 1.0)
|
||||
grape_logging (~> 1.7)
|
||||
graphiql-rails (~> 1.4.10)
|
||||
graphql (~> 1.8.0)
|
||||
grpc (~> 1.11.0)
|
||||
haml_lint (~> 0.26.0)
|
||||
hamlit (~> 2.6.1)
|
||||
|
@ -1070,6 +1074,7 @@ DEPENDENCIES
|
|||
html-pipeline (~> 2.7.1)
|
||||
html2text
|
||||
httparty (~> 0.13.3)
|
||||
icalendar
|
||||
influxdb (~> 0.2)
|
||||
jira-ruby (~> 1.4)
|
||||
jquery-atwho-rails (~> 1.3.2)
|
||||
|
@ -1077,7 +1082,7 @@ DEPENDENCIES
|
|||
jwt (~> 1.5.6)
|
||||
kaminari (~> 1.0)
|
||||
knapsack (~> 1.16)
|
||||
kubeclient (~> 3.0)
|
||||
kubeclient (~> 3.1.0)
|
||||
letter_opener_web (~> 1.3.0)
|
||||
license_finder (~> 3.1)
|
||||
licensee (~> 8.9)
|
||||
|
@ -1085,17 +1090,18 @@ DEPENDENCIES
|
|||
loofah (~> 2.2)
|
||||
mail_room (~> 0.9.1)
|
||||
method_source (~> 0.8)
|
||||
mini_magick
|
||||
minitest (~> 5.7.0)
|
||||
mousetrap-rails (~> 1.4.6)
|
||||
mysql2 (~> 0.4.10)
|
||||
net-ldap
|
||||
net-ssh (~> 4.2.0)
|
||||
net-ssh (~> 5.0)
|
||||
nokogiri (~> 1.8.2)
|
||||
oauth2 (~> 1.4)
|
||||
octokit (~> 4.8)
|
||||
octokit (~> 4.9)
|
||||
omniauth (~> 1.8)
|
||||
omniauth-auth0 (~> 2.0.0)
|
||||
omniauth-authentiq (~> 0.3.1)
|
||||
omniauth-authentiq (~> 0.3.3)
|
||||
omniauth-azure-oauth2 (~> 0.0.9)
|
||||
omniauth-cas3 (~> 1.1.4)
|
||||
omniauth-facebook (~> 4.0.0)
|
||||
|
@ -1118,7 +1124,7 @@ DEPENDENCIES
|
|||
peek-sidekiq (~> 1.0.3)
|
||||
pg (~> 0.18.2)
|
||||
premailer-rails (~> 1.9.7)
|
||||
prometheus-client-mmap (~> 0.9.2)
|
||||
prometheus-client-mmap (~> 0.9.3)
|
||||
pry-byebug (~> 3.4.1)
|
||||
pry-rails (~> 0.3.4)
|
||||
rack-attack (~> 4.4.1)
|
||||
|
@ -1132,21 +1138,19 @@ DEPENDENCIES
|
|||
rainbow (~> 2.2)
|
||||
raindrops (~> 0.18)
|
||||
rblineprof (~> 0.3.6)
|
||||
rbnacl (~> 4.0)
|
||||
rbnacl-libsodium
|
||||
rdoc (~> 4.2)
|
||||
rdoc (~> 6.0)
|
||||
re2 (~> 1.1.1)
|
||||
recaptcha (~> 3.0)
|
||||
redcarpet (~> 3.4)
|
||||
redis (~> 3.2)
|
||||
redis-namespace (~> 1.5.2)
|
||||
redis-namespace (~> 1.6.0)
|
||||
redis-rails (~> 5.0.2)
|
||||
request_store (~> 1.3)
|
||||
responders (~> 2.0)
|
||||
rouge (~> 3.1)
|
||||
rqrcode-rails3 (~> 0.1.7)
|
||||
rspec-parameterized
|
||||
rspec-rails (~> 3.6.0)
|
||||
rspec-rails (~> 3.7.0)
|
||||
rspec-retry (~> 0.4.5)
|
||||
rspec-set (~> 0.1.3)
|
||||
rspec_profiling (~> 0.0.5)
|
||||
|
@ -1154,20 +1158,21 @@ DEPENDENCIES
|
|||
rubocop-rspec (~> 1.22.1)
|
||||
ruby-fogbugz (~> 0.2.1)
|
||||
ruby-prof (~> 0.17.0)
|
||||
ruby-progressbar
|
||||
ruby_parser (~> 3.8)
|
||||
rufus-scheduler (~> 3.4)
|
||||
rugged (~> 0.27)
|
||||
sanitize (~> 2.0)
|
||||
sanitize (~> 4.6.5)
|
||||
sass-rails (~> 5.0.6)
|
||||
scss_lint (~> 0.56.0)
|
||||
seed-fu (~> 2.3.7)
|
||||
select2-rails (~> 3.5.9)
|
||||
selenium-webdriver (~> 3.5)
|
||||
selenium-webdriver (~> 3.12)
|
||||
sentry-raven (~> 2.7)
|
||||
settingslogic (~> 2.0.9)
|
||||
sham_rack (~> 1.3.6)
|
||||
shoulda-matchers (~> 3.1.2)
|
||||
sidekiq (~> 5.0)
|
||||
sidekiq (~> 5.1)
|
||||
sidekiq-cron (~> 0.6.0)
|
||||
sidekiq-limit_fetch (~> 3.4)
|
||||
simple_po_parser (~> 1.1.2)
|
||||
|
@ -1199,4 +1204,4 @@ DEPENDENCIES
|
|||
wikicloth (= 0.8.1)
|
||||
|
||||
BUNDLED WITH
|
||||
1.16.1
|
||||
1.16.2
|
||||
|
|
1
INSTALLATION_TYPE
Normal file
|
@ -0,0 +1 @@
|
|||
source
|
59
PROCESS.md
|
@ -15,6 +15,8 @@
|
|||
- [Between the 1st and the 7th](#between-the-1st-and-the-7th)
|
||||
- [On the 7th](#on-the-7th)
|
||||
- [After the 7th](#after-the-7th)
|
||||
- [Regressions](#regressions)
|
||||
- [How to manage a regression](#how-to-manage-a-regression)
|
||||
- [Release retrospective and kickoff](#release-retrospective-and-kickoff)
|
||||
- [Retrospective](#retrospective)
|
||||
- [Kickoff](#kickoff)
|
||||
|
@ -168,6 +170,8 @@ the stable branch are:
|
|||
|
||||
* Fixes for [regressions](#regressions)
|
||||
* Fixes for security issues
|
||||
* Fixes or improvements to automated QA scenarios
|
||||
* Documentation updates for changes in the same release
|
||||
* New or updated translations (as long as they do not touch application code)
|
||||
|
||||
During the feature freeze all merge requests that are meant to go into the
|
||||
|
@ -184,11 +188,7 @@ next patch release.
|
|||
|
||||
If a merge request is to be picked into more than one release it will need one
|
||||
`Pick into X.Y` label per release where the merge request should be back-ported
|
||||
to.
|
||||
|
||||
For example, if the current patch release is `10.1.1` and a regression fix needs
|
||||
to be backported down to the `9.5` release, you will need to assign it the
|
||||
`10.1` milestone and the following labels:
|
||||
to. For example:
|
||||
|
||||
- `Pick into 10.1`
|
||||
- `Pick into 10.0`
|
||||
|
@ -199,26 +199,9 @@ to be backported down to the `9.5` release, you will need to assign it the
|
|||
If you think a merge request should go into an RC or patch even though it does not meet these requirements,
|
||||
you can ask for an exception to be made.
|
||||
|
||||
Go to [Release tasks issue tracker](https://gitlab.com/gitlab-org/release/tasks/issues/new) and create an issue
|
||||
using the `Exception-request` issue template.
|
||||
Check [this guide](https://gitlab.com/gitlab-org/release/docs/blob/master/general/exception-request/process.md) about how to open an exception request before opening one.
|
||||
|
||||
**Do not** set the relevant `Pick into X.Y` label (see above) before request an
|
||||
exception; this should be done after the exception is approved.
|
||||
|
||||
You can find who is who on the [team page](https://about.gitlab.com/team/).
|
||||
|
||||
Whether an exception is made is determined by weighing the benefit and urgency of the change
|
||||
(how important it is to the company that this is released _right now_ instead of in a month)
|
||||
against the potential negative impact
|
||||
(things breaking without enough time to comfortably find and fix them before the release on the 22nd).
|
||||
When in doubt, we err on the side of _not_ cherry-picking.
|
||||
|
||||
For example, it is likely that an exception will be made for a trivial 1-5 line performance improvement
|
||||
(e.g. adding a database index or adding `includes` to a query), but not for a new feature, no matter how relatively small or thoroughly tested.
|
||||
|
||||
All MRs which have had exceptions granted must be merged by the 15th.
|
||||
|
||||
### Regressions
|
||||
## Regressions
|
||||
|
||||
A regression for a particular monthly release is a bug that exists in that
|
||||
release, but wasn't present in the release before. This includes bugs in
|
||||
|
@ -236,10 +219,30 @@ month. When we say 'the most recent monthly release', this can refer to either
|
|||
the version currently running on GitLab.com, or the most recent version
|
||||
available in the package repositories.
|
||||
|
||||
A regression issue should be labeled with the appropriate [subject label](../CONTRIBUTING.md#subject-labels-wiki-container-registry-ldap-api-etc)
|
||||
and [team label](../CONTRIBUTING.md#team-labels-ci-discussion-edge-platform-etc),
|
||||
just like any other issue, to help GitLab team members focus on issues that are
|
||||
relevant to [their area of responsibility](https://about.gitlab.com/handbook/engineering/workflow/#choosing-something-to-work-on).
|
||||
### How to manage a regression
|
||||
|
||||
Regressions are very important, and they should be considered high priority
|
||||
issues that should be solved as soon as possible, especially if they affect
|
||||
users. Despite that, ~regression label itself does not imply when the issue
|
||||
will be scheduled.
|
||||
|
||||
When a regression is found:
|
||||
1. Create an issue describing the problem in the most detailed way possible
|
||||
1. If possible, provide links to real examples and how to reproduce the problem
|
||||
1. Label the issue properly, using the [team label](../CONTRIBUTING.md#team-labels),
|
||||
the [subject label](../CONTRIBUTING.md#subject-labels)
|
||||
and any other label that may apply in the specific case
|
||||
1. Add the ~bug and ~regression labels
|
||||
1. Notify the respective Engineering Manager to evaluate the Severity of the regression and add a [Severity label](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#bug-severity-labels). The counterpart Product Manager is included to weigh-in on prioritization as needed to set the [Priority label](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#bug-priority-labels).
|
||||
1. If the regression is either an ~S1, ~S2 or ~S3 severity, label the regression with the current milestone as it should be fixed in the current milestone.
|
||||
1. If the regression was introduced in an RC of the current release, label with ~Deliverable
|
||||
1. If the regression was introduced in the previous release, label with ~"Next Patch Release"
|
||||
1. If the regression is an ~S4 severity, the regression may be scheduled for later milestones at the discretion of Engineering Manager and Product Manager.
|
||||
|
||||
When a new issue is found, the fix should start as soon as possible. You can
|
||||
ping the Engineering Manager or the Product Manager for the relative area to
|
||||
make them aware of the issue earlier. They will analyze the priority and change
|
||||
it if needed.
|
||||
|
||||
## Release retrospective and kickoff
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ We're hiring developers, support people, and production engineers all the time,
|
|||
There are two editions of GitLab:
|
||||
|
||||
- GitLab Community Edition (CE) is available freely under the MIT Expat license.
|
||||
- GitLab Enterprise Edition (EE) includes [extra features](https://about.gitlab.com/products/#compare-options) that are more useful for organizations with more than 100 users. To use EE and get official support please [become a subscriber](https://about.gitlab.com/products/).
|
||||
- GitLab Enterprise Edition (EE) includes [extra features](https://about.gitlab.com/pricing/#compare-options) that are more useful for organizations with more than 100 users. To use EE and get official support please [become a subscriber](https://about.gitlab.com/pricing/).
|
||||
|
||||
## Website
|
||||
|
||||
|
@ -126,5 +126,5 @@ Please see [Getting help for GitLab](https://about.gitlab.com/getting-help/) on
|
|||
|
||||
## Is it awesome?
|
||||
|
||||
Thanks for [asking this question](https://twitter.com/supersloth/status/489462789384056832) Joshua.
|
||||
[These people](https://twitter.com/gitlab/likes) seem to like it.
|
||||
|
||||
|
|
2
VERSION
|
@ -1 +1 @@
|
|||
11.0.0-pre
|
||||
11.1.0-pre
|
||||
|
|
Before Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 4.2 KiB |
BIN
app/assets/images/ci_favicons/favicon_status_canceled.png
Normal file
After Width: | Height: | Size: 864 B |
Before Width: | Height: | Size: 4.2 KiB |
BIN
app/assets/images/ci_favicons/favicon_status_created.png
Normal file
After Width: | Height: | Size: 889 B |
Before Width: | Height: | Size: 4.2 KiB |
BIN
app/assets/images/ci_favicons/favicon_status_failed.png
Normal file
After Width: | Height: | Size: 1,015 B |
Before Width: | Height: | Size: 4.2 KiB |
BIN
app/assets/images/ci_favicons/favicon_status_manual.png
Normal file
After Width: | Height: | Size: 1 KiB |
Before Width: | Height: | Size: 4.2 KiB |
BIN
app/assets/images/ci_favicons/favicon_status_not_found.png
Normal file
After Width: | Height: | Size: 945 B |
Before Width: | Height: | Size: 4.2 KiB |
BIN
app/assets/images/ci_favicons/favicon_status_pending.png
Normal file
After Width: | Height: | Size: 919 B |
Before Width: | Height: | Size: 4.2 KiB |
BIN
app/assets/images/ci_favicons/favicon_status_running.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 4.2 KiB |
BIN
app/assets/images/ci_favicons/favicon_status_skipped.png
Normal file
After Width: | Height: | Size: 923 B |
Before Width: | Height: | Size: 4.2 KiB |
BIN
app/assets/images/ci_favicons/favicon_status_success.png
Normal file
After Width: | Height: | Size: 1 KiB |
Before Width: | Height: | Size: 4.2 KiB |
BIN
app/assets/images/ci_favicons/favicon_status_warning.png
Normal file
After Width: | Height: | Size: 830 B |
Before Width: | Height: | Size: 5.3 KiB |
BIN
app/assets/images/favicon-blue.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 5.3 KiB |
BIN
app/assets/images/favicon-yellow.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 5.3 KiB |
BIN
app/assets/images/favicon.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
|
@ -1,4 +1,4 @@
|
|||
/* eslint-disable no-param-reassign, class-methods-use-this */
|
||||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import $ from 'jquery';
|
||||
import Cookies from 'js-cookie';
|
||||
|
|
|
@ -26,7 +26,7 @@ export default class AjaxLoadingSpinner {
|
|||
}
|
||||
|
||||
static toggleLoadingIcon(iconElement) {
|
||||
const classList = iconElement.classList;
|
||||
const { classList } = iconElement;
|
||||
classList.toggle(iconElement.dataset.icon);
|
||||
classList.toggle('fa-spinner');
|
||||
classList.toggle('fa-spin');
|
||||
|
|
|
@ -150,14 +150,15 @@ const Api = {
|
|||
},
|
||||
|
||||
// Return group projects list. Filtered by query
|
||||
groupProjects(groupId, query, callback) {
|
||||
groupProjects(groupId, query, options, callback) {
|
||||
const url = Api.buildUrl(Api.groupProjectsPath).replace(':id', groupId);
|
||||
const defaults = {
|
||||
search: query,
|
||||
per_page: 20,
|
||||
};
|
||||
return axios
|
||||
.get(url, {
|
||||
params: {
|
||||
search: query,
|
||||
per_page: 20,
|
||||
},
|
||||
params: Object.assign({}, defaults, options),
|
||||
})
|
||||
.then(({ data }) => callback(data));
|
||||
},
|
||||
|
@ -243,6 +244,15 @@ const Api = {
|
|||
});
|
||||
},
|
||||
|
||||
createBranch(id, { ref, branch }) {
|
||||
const url = Api.buildUrl(this.createBranchPath).replace(':id', encodeURIComponent(id));
|
||||
|
||||
return axios.post(url, {
|
||||
ref,
|
||||
branch,
|
||||
});
|
||||
},
|
||||
|
||||
buildUrl(url) {
|
||||
let urlRoot = '';
|
||||
if (gon.relative_url_root != null) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* eslint-disable no-param-reassign, prefer-template, no-var, no-void, consistent-return */
|
||||
/* eslint-disable no-param-reassign, prefer-template, no-void, consistent-return */
|
||||
|
||||
import AccessorUtilities from './lib/utils/accessor';
|
||||
|
||||
|
@ -31,7 +31,9 @@ export default class Autosave {
|
|||
// https://github.com/vuejs/vue/issues/2804#issuecomment-216968137
|
||||
const event = new Event('change', { bubbles: true, cancelable: false });
|
||||
const field = this.field.get(0);
|
||||
field.dispatchEvent(event);
|
||||
if (field) {
|
||||
field.dispatchEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
save() {
|
||||
|
|
|
@ -11,7 +11,8 @@ import axios from './lib/utils/axios_utils';
|
|||
|
||||
const animationEndEventString = 'animationend webkitAnimationEnd MSAnimationEnd oAnimationEnd';
|
||||
const transitionEndEventString = 'transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd';
|
||||
const requestAnimationFrame = window.requestAnimationFrame ||
|
||||
const requestAnimationFrame =
|
||||
window.requestAnimationFrame ||
|
||||
window.webkitRequestAnimationFrame ||
|
||||
window.mozRequestAnimationFrame ||
|
||||
window.setTimeout;
|
||||
|
@ -37,21 +38,28 @@ class AwardsHandler {
|
|||
this.emoji = emoji;
|
||||
this.eventListeners = [];
|
||||
// If the user shows intent let's pre-build the menu
|
||||
this.registerEventListener('one', $(document), 'mouseenter focus', '.js-add-award', 'mouseenter focus', () => {
|
||||
const $menu = $('.emoji-menu');
|
||||
if ($menu.length === 0) {
|
||||
requestAnimationFrame(() => {
|
||||
this.createEmojiMenu();
|
||||
});
|
||||
}
|
||||
});
|
||||
this.registerEventListener('on', $(document), 'click', '.js-add-award', (e) => {
|
||||
this.registerEventListener(
|
||||
'one',
|
||||
$(document),
|
||||
'mouseenter focus',
|
||||
'.js-add-award',
|
||||
'mouseenter focus',
|
||||
() => {
|
||||
const $menu = $('.emoji-menu');
|
||||
if ($menu.length === 0) {
|
||||
requestAnimationFrame(() => {
|
||||
this.createEmojiMenu();
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
this.registerEventListener('on', $(document), 'click', '.js-add-award', e => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
this.showEmojiMenu($(e.currentTarget));
|
||||
});
|
||||
|
||||
this.registerEventListener('on', $('html'), 'click', (e) => {
|
||||
this.registerEventListener('on', $('html'), 'click', e => {
|
||||
const $target = $(e.target);
|
||||
if (!$target.closest('.emoji-menu').length) {
|
||||
$('.js-awards-block.current').removeClass('current');
|
||||
|
@ -61,12 +69,14 @@ class AwardsHandler {
|
|||
}
|
||||
}
|
||||
});
|
||||
this.registerEventListener('on', $(document), 'click', '.js-emoji-btn', (e) => {
|
||||
this.registerEventListener('on', $(document), 'click', '.js-emoji-btn', e => {
|
||||
e.preventDefault();
|
||||
const $target = $(e.currentTarget);
|
||||
const $glEmojiElement = $target.find('gl-emoji');
|
||||
const $spriteIconElement = $target.find('.icon');
|
||||
const emojiName = ($glEmojiElement.length ? $glEmojiElement : $spriteIconElement).data('name');
|
||||
const emojiName = ($glEmojiElement.length ? $glEmojiElement : $spriteIconElement).data(
|
||||
'name',
|
||||
);
|
||||
|
||||
$target.closest('.js-awards-block').addClass('current');
|
||||
this.addAward(this.getVotesBlock(), this.getAwardUrl(), emojiName);
|
||||
|
@ -83,7 +93,10 @@ class AwardsHandler {
|
|||
|
||||
showEmojiMenu($addBtn) {
|
||||
if ($addBtn.hasClass('js-note-emoji')) {
|
||||
$addBtn.closest('.note').find('.js-awards-block').addClass('current');
|
||||
$addBtn
|
||||
.closest('.note')
|
||||
.find('.js-awards-block')
|
||||
.addClass('current');
|
||||
} else {
|
||||
$addBtn.closest('.js-awards-block').addClass('current');
|
||||
}
|
||||
|
@ -177,32 +190,38 @@ class AwardsHandler {
|
|||
const remainingCategories = Object.keys(categoryMap).slice(1);
|
||||
const allCategoriesAddedPromise = remainingCategories.reduce(
|
||||
(promiseChain, categoryNameKey) =>
|
||||
promiseChain.then(() =>
|
||||
new Promise((resolve) => {
|
||||
const emojisInCategory = categoryMap[categoryNameKey];
|
||||
const categoryMarkup = this.renderCategory(
|
||||
categoryLabelMap[categoryNameKey],
|
||||
emojisInCategory,
|
||||
);
|
||||
requestAnimationFrame(() => {
|
||||
emojiContentElement.insertAdjacentHTML('beforeend', categoryMarkup);
|
||||
resolve();
|
||||
});
|
||||
}),
|
||||
),
|
||||
promiseChain.then(
|
||||
() =>
|
||||
new Promise(resolve => {
|
||||
const emojisInCategory = categoryMap[categoryNameKey];
|
||||
const categoryMarkup = this.renderCategory(
|
||||
categoryLabelMap[categoryNameKey],
|
||||
emojisInCategory,
|
||||
);
|
||||
requestAnimationFrame(() => {
|
||||
emojiContentElement.insertAdjacentHTML('beforeend', categoryMarkup);
|
||||
resolve();
|
||||
});
|
||||
}),
|
||||
),
|
||||
Promise.resolve(),
|
||||
);
|
||||
|
||||
allCategoriesAddedPromise.then(() => {
|
||||
// Used for tests
|
||||
// We check for the menu in case it was destroyed in the meantime
|
||||
if (menu) {
|
||||
menu.dispatchEvent(new CustomEvent('build-emoji-menu-finish'));
|
||||
}
|
||||
}).catch((err) => {
|
||||
emojiContentElement.insertAdjacentHTML('beforeend', '<p>We encountered an error while adding the remaining categories</p>');
|
||||
throw new Error(`Error occurred in addRemainingEmojiMenuCategories: ${err.message}`);
|
||||
});
|
||||
allCategoriesAddedPromise
|
||||
.then(() => {
|
||||
// Used for tests
|
||||
// We check for the menu in case it was destroyed in the meantime
|
||||
if (menu) {
|
||||
menu.dispatchEvent(new CustomEvent('build-emoji-menu-finish'));
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
emojiContentElement.insertAdjacentHTML(
|
||||
'beforeend',
|
||||
'<p>We encountered an error while adding the remaining categories</p>',
|
||||
);
|
||||
throw new Error(`Error occurred in addRemainingEmojiMenuCategories: ${err.message}`);
|
||||
});
|
||||
}
|
||||
|
||||
renderCategory(name, emojiList, opts = {}) {
|
||||
|
@ -211,7 +230,9 @@ class AwardsHandler {
|
|||
${name}
|
||||
</h5>
|
||||
<ul class="clearfix emoji-menu-list ${opts.menuListClass || ''}">
|
||||
${emojiList.map(emojiName => `
|
||||
${emojiList
|
||||
.map(
|
||||
emojiName => `
|
||||
<li class="emoji-menu-list-item">
|
||||
<button class="emoji-menu-btn text-center js-emoji-btn" type="button">
|
||||
${this.emoji.glEmojiTag(emojiName, {
|
||||
|
@ -219,7 +240,9 @@ class AwardsHandler {
|
|||
})}
|
||||
</button>
|
||||
</li>
|
||||
`).join('\n')}
|
||||
`,
|
||||
)
|
||||
.join('\n')}
|
||||
</ul>
|
||||
`;
|
||||
}
|
||||
|
@ -232,7 +255,7 @@ class AwardsHandler {
|
|||
top: `${$addBtn.offset().top + $addBtn.outerHeight()}px`,
|
||||
};
|
||||
if (position === 'right') {
|
||||
css.left = `${($addBtn.offset().left - $menu.outerWidth()) + 20}px`;
|
||||
css.left = `${$addBtn.offset().left - $menu.outerWidth() + 20}px`;
|
||||
$menu.addClass('is-aligned-right');
|
||||
} else {
|
||||
css.left = `${$addBtn.offset().left}px`;
|
||||
|
@ -416,7 +439,10 @@ class AwardsHandler {
|
|||
</button>
|
||||
`;
|
||||
const $emojiButton = $(buttonHtml);
|
||||
$emojiButton.insertBefore(votesBlock.find('.js-award-holder')).find('.emoji-icon').data('name', emojiName);
|
||||
$emojiButton
|
||||
.insertBefore(votesBlock.find('.js-award-holder'))
|
||||
.find('.emoji-icon')
|
||||
.data('name', emojiName);
|
||||
this.animateEmoji($emojiButton);
|
||||
$('.award-control').tooltip();
|
||||
votesBlock.removeClass('current');
|
||||
|
@ -426,7 +452,7 @@ class AwardsHandler {
|
|||
const className = 'pulse animated once short';
|
||||
$emoji.addClass(className);
|
||||
|
||||
this.registerEventListener('on', $emoji, animationEndEventString, (e) => {
|
||||
this.registerEventListener('on', $emoji, animationEndEventString, e => {
|
||||
$(e.currentTarget).removeClass(className);
|
||||
});
|
||||
}
|
||||
|
@ -444,15 +470,16 @@ class AwardsHandler {
|
|||
if (this.isUserAuthored($emojiButton)) {
|
||||
this.userAuthored($emojiButton);
|
||||
} else {
|
||||
axios.post(awardUrl, {
|
||||
name: emoji,
|
||||
})
|
||||
.then(({ data }) => {
|
||||
if (data.ok) {
|
||||
callback();
|
||||
}
|
||||
})
|
||||
.catch(() => flash(__('Something went wrong on our end.')));
|
||||
axios
|
||||
.post(awardUrl, {
|
||||
name: emoji,
|
||||
})
|
||||
.then(({ data }) => {
|
||||
if (data.ok) {
|
||||
callback();
|
||||
}
|
||||
})
|
||||
.catch(() => flash(__('Something went wrong on our end.')));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -486,26 +513,33 @@ class AwardsHandler {
|
|||
}
|
||||
|
||||
getFrequentlyUsedEmojis() {
|
||||
return this.frequentlyUsedEmojis || (() => {
|
||||
const frequentlyUsedEmojis = _.uniq((Cookies.get('frequently_used_emojis') || '').split(','));
|
||||
this.frequentlyUsedEmojis = frequentlyUsedEmojis.filter(
|
||||
inputName => this.emoji.isEmojiNameValid(inputName),
|
||||
);
|
||||
return (
|
||||
this.frequentlyUsedEmojis ||
|
||||
(() => {
|
||||
const frequentlyUsedEmojis = _.uniq(
|
||||
(Cookies.get('frequently_used_emojis') || '').split(','),
|
||||
);
|
||||
this.frequentlyUsedEmojis = frequentlyUsedEmojis.filter(inputName =>
|
||||
this.emoji.isEmojiNameValid(inputName),
|
||||
);
|
||||
|
||||
return this.frequentlyUsedEmojis;
|
||||
})();
|
||||
return this.frequentlyUsedEmojis;
|
||||
})()
|
||||
);
|
||||
}
|
||||
|
||||
setupSearch() {
|
||||
const $search = $('.js-emoji-menu-search');
|
||||
|
||||
this.registerEventListener('on', $search, 'input', (e) => {
|
||||
const term = $(e.target).val().trim();
|
||||
this.registerEventListener('on', $search, 'input', e => {
|
||||
const term = $(e.target)
|
||||
.val()
|
||||
.trim();
|
||||
this.searchEmojis(term);
|
||||
});
|
||||
|
||||
const $menu = $('.emoji-menu');
|
||||
this.registerEventListener('on', $menu, transitionEndEventString, (e) => {
|
||||
this.registerEventListener('on', $menu, transitionEndEventString, e => {
|
||||
if (e.target === e.currentTarget) {
|
||||
// Clear the search
|
||||
this.searchEmojis('');
|
||||
|
@ -523,19 +557,26 @@ class AwardsHandler {
|
|||
// Generate a search result block
|
||||
const h5 = $('<h5 class="emoji-search-title"/>').text('Search results');
|
||||
const foundEmojis = this.findMatchingEmojiElements(term).show();
|
||||
const ul = $('<ul>').addClass('emoji-menu-list emoji-menu-search').append(foundEmojis);
|
||||
const ul = $('<ul>')
|
||||
.addClass('emoji-menu-list emoji-menu-search')
|
||||
.append(foundEmojis);
|
||||
$('.emoji-menu-content ul, .emoji-menu-content h5').hide();
|
||||
$('.emoji-menu-content').append(h5).append(ul);
|
||||
$('.emoji-menu-content')
|
||||
.append(h5)
|
||||
.append(ul);
|
||||
} else {
|
||||
$('.emoji-menu-content').children().show();
|
||||
$('.emoji-menu-content')
|
||||
.children()
|
||||
.show();
|
||||
}
|
||||
}
|
||||
|
||||
findMatchingEmojiElements(query) {
|
||||
const emojiMatches = this.emoji.filterEmojiNamesByAlias(query);
|
||||
const $emojiElements = $('.emoji-menu-list:not(.frequent-emojis) [data-name]');
|
||||
const $matchingElements = $emojiElements
|
||||
.filter((i, elm) => emojiMatches.indexOf(elm.dataset.name) >= 0);
|
||||
const $matchingElements = $emojiElements.filter(
|
||||
(i, elm) => emojiMatches.indexOf(elm.dataset.name) >= 0,
|
||||
);
|
||||
return $matchingElements.closest('li').clone();
|
||||
}
|
||||
|
||||
|
@ -550,16 +591,13 @@ class AwardsHandler {
|
|||
$emojiMenu.addClass(IS_RENDERED);
|
||||
|
||||
// enqueues animation as a microtask, so it begins ASAP once IS_RENDERED added
|
||||
return Promise.resolve()
|
||||
.then(() => $emojiMenu.addClass(IS_VISIBLE));
|
||||
return Promise.resolve().then(() => $emojiMenu.addClass(IS_VISIBLE));
|
||||
}
|
||||
|
||||
hideMenuElement($emojiMenu) {
|
||||
$emojiMenu.on(transitionEndEventString, (e) => {
|
||||
$emojiMenu.on(transitionEndEventString, e => {
|
||||
if (e.currentTarget === e.target) {
|
||||
$emojiMenu
|
||||
.removeClass(IS_RENDERED)
|
||||
.off(transitionEndEventString);
|
||||
$emojiMenu.removeClass(IS_RENDERED).off(transitionEndEventString);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -567,7 +605,7 @@ class AwardsHandler {
|
|||
}
|
||||
|
||||
destroy() {
|
||||
this.eventListeners.forEach((entry) => {
|
||||
this.eventListeners.forEach(entry => {
|
||||
entry.element.off.call(entry.element, ...entry.args);
|
||||
});
|
||||
$('.emoji-menu').remove();
|
||||
|
@ -577,8 +615,9 @@ class AwardsHandler {
|
|||
let awardsHandlerPromise = null;
|
||||
export default function loadAwardsHandler(reload = false) {
|
||||
if (!awardsHandlerPromise || reload) {
|
||||
awardsHandlerPromise = import(/* webpackChunkName: 'emoji' */ './emoji')
|
||||
.then(Emoji => new AwardsHandler(Emoji));
|
||||
awardsHandlerPromise = import(/* webpackChunkName: 'emoji' */ './emoji').then(
|
||||
Emoji => new AwardsHandler(Emoji),
|
||||
);
|
||||
}
|
||||
return awardsHandlerPromise;
|
||||
}
|
||||
|
|
|
@ -72,11 +72,11 @@ export default {
|
|||
rel="noopener noreferrer"
|
||||
>
|
||||
<img
|
||||
class="project-badge"
|
||||
:src="imageUrlWithRetries"
|
||||
class="project-badge"
|
||||
aria-hidden="true"
|
||||
@load="onLoad"
|
||||
@error="onError"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</a>
|
||||
|
||||
|
@ -91,9 +91,9 @@ export default {
|
|||
>
|
||||
<div class="btn btn-default btn-sm disabled">
|
||||
<icon
|
||||
:size="16"
|
||||
class="prepend-left-8 append-right-8"
|
||||
name="doc_image"
|
||||
:size="16"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
|
@ -105,16 +105,16 @@ export default {
|
|||
</div>
|
||||
|
||||
<button
|
||||
v-tooltip
|
||||
v-show="hasError"
|
||||
:title="s__('Badges|Reload badge image')"
|
||||
class="btn btn-transparent btn-sm text-primary"
|
||||
type="button"
|
||||
v-tooltip
|
||||
:title="s__('Badges|Reload badge image')"
|
||||
@click="reloadImage"
|
||||
>
|
||||
<icon
|
||||
name="retry"
|
||||
:size="16"
|
||||
name="retry"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -153,10 +153,10 @@ export default {
|
|||
<label for="badge-link-url">{{ s__('Badges|Link') }}</label>
|
||||
<input
|
||||
id="badge-link-url"
|
||||
type="text"
|
||||
class="form-control"
|
||||
v-model="linkUrl"
|
||||
:placeholder="$options.badgeLinkUrlPlaceholder"
|
||||
type="text"
|
||||
class="form-control"
|
||||
@input="debouncedPreview"
|
||||
/>
|
||||
<span
|
||||
|
@ -169,10 +169,10 @@ export default {
|
|||
<label for="badge-image-url">{{ s__('Badges|Badge image URL') }}</label>
|
||||
<input
|
||||
id="badge-image-url"
|
||||
type="text"
|
||||
class="form-control"
|
||||
v-model="imageUrl"
|
||||
:placeholder="$options.badgeImageUrlPlaceholder"
|
||||
type="text"
|
||||
class="form-control"
|
||||
@input="debouncedPreview"
|
||||
/>
|
||||
<span
|
||||
|
@ -184,8 +184,8 @@ export default {
|
|||
<div class="form-group">
|
||||
<label for="badge-preview">{{ s__('Badges|Badge image preview') }}</label>
|
||||
<badge
|
||||
id="badge-preview"
|
||||
v-show="renderedBadge && !isRendering"
|
||||
id="badge-preview"
|
||||
:image-url="renderedImageUrl"
|
||||
:link-url="renderedLinkUrl"
|
||||
/>
|
||||
|
@ -202,16 +202,16 @@ export default {
|
|||
|
||||
<div class="row-content-block">
|
||||
<loading-button
|
||||
type="submit"
|
||||
container-class="btn btn-success"
|
||||
:disabled="!canSubmit"
|
||||
:loading="isSaving"
|
||||
:label="submitButtonLabel"
|
||||
type="submit"
|
||||
container-class="btn btn-success"
|
||||
/>
|
||||
<button
|
||||
v-if="isEditing"
|
||||
class="btn btn-cancel"
|
||||
type="button"
|
||||
v-if="isEditing"
|
||||
@click="onCancel"
|
||||
>{{ __('Cancel') }}</button>
|
||||
</div>
|
||||
|
|
|
@ -41,9 +41,9 @@ export default {
|
|||
<template>
|
||||
<div class="gl-responsive-table-row-layout gl-responsive-table-row">
|
||||
<badge
|
||||
class="table-section section-30"
|
||||
:image-url="badge.renderedImageUrl"
|
||||
:link-url="badge.renderedLinkUrl"
|
||||
class="table-section section-30"
|
||||
/>
|
||||
<span class="table-section section-50 str-truncated">{{ badge.linkUrl }}</span>
|
||||
<div class="table-section section-10">
|
||||
|
@ -54,29 +54,29 @@ export default {
|
|||
v-if="canEditBadge"
|
||||
class="table-action-buttons">
|
||||
<button
|
||||
:disabled="badge.isDeleting"
|
||||
class="btn btn-default append-right-8"
|
||||
type="button"
|
||||
:disabled="badge.isDeleting"
|
||||
@click="editBadge(badge)"
|
||||
>
|
||||
<icon
|
||||
name="pencil"
|
||||
:size="16"
|
||||
:aria-label="__('Edit')"
|
||||
name="pencil"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
:disabled="badge.isDeleting"
|
||||
class="btn btn-danger"
|
||||
type="button"
|
||||
data-toggle="modal"
|
||||
data-target="#delete-badge-modal"
|
||||
:disabled="badge.isDeleting"
|
||||
@click="updateBadgeInModal(badge)"
|
||||
>
|
||||
<icon
|
||||
name="remove"
|
||||
:size="16"
|
||||
:aria-label="__('Delete')"
|
||||
name="remove"
|
||||
/>
|
||||
</button>
|
||||
<loading-icon
|
||||
|
|
|
@ -44,8 +44,8 @@ export default {
|
|||
<gl-modal
|
||||
id="delete-badge-modal"
|
||||
:header-title-text="s__('Badges|Delete badge?')"
|
||||
footer-primary-button-variant="danger"
|
||||
:footer-primary-button-text="s__('Badges|Delete badge')"
|
||||
footer-primary-button-variant="danger"
|
||||
@submit="onSubmitModal">
|
||||
<div class="well">
|
||||
<badge
|
||||
|
|
|
@ -52,7 +52,7 @@ export default function initCopyToClipboard() {
|
|||
* data types to the intended values.
|
||||
*/
|
||||
$(document).on('copy', 'body > textarea[readonly]', (e) => {
|
||||
const clipboardData = e.originalEvent.clipboardData;
|
||||
const { clipboardData } = e.originalEvent;
|
||||
if (!clipboardData) return;
|
||||
|
||||
const text = e.target.value;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* eslint-disable class-methods-use-this, object-shorthand, no-unused-vars, no-use-before-define, no-new, max-len, no-restricted-syntax, guard-for-in, no-continue */
|
||||
/* eslint-disable object-shorthand, no-unused-vars, no-use-before-define, max-len, no-restricted-syntax, guard-for-in, no-continue */
|
||||
|
||||
import $ from 'jquery';
|
||||
import _ from 'underscore';
|
||||
|
@ -119,7 +119,7 @@ const gfmRules = {
|
|||
return el.outerHTML;
|
||||
},
|
||||
'dl'(el, text) {
|
||||
let lines = text.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,
|
||||
// or leave the line entirely blank.
|
||||
lines = lines.map((l) => {
|
||||
|
@ -129,9 +129,13 @@ const gfmRules = {
|
|||
return ` ${line}`;
|
||||
});
|
||||
|
||||
return `<dl>\n${lines.join('\n')}\n</dl>`;
|
||||
return `<dl>\n${lines.join('\n')}\n</dl>\n`;
|
||||
},
|
||||
'sub, dt, dd, kbd, q, samp, var, ruby, rt, rp, abbr, summary, details'(el, text) {
|
||||
'dt, dd, summary, details'(el, text) {
|
||||
const tag = el.nodeName.toLowerCase();
|
||||
return `<${tag}>${text}</${tag}>\n`;
|
||||
},
|
||||
'sup, sub, kbd, q, samp, var, ruby, rt, rp, abbr'(el, text) {
|
||||
const tag = el.nodeName.toLowerCase();
|
||||
return `<${tag}>${text}</${tag}>`;
|
||||
},
|
||||
|
@ -215,22 +219,22 @@ const gfmRules = {
|
|||
return text.replace(/^- /mg, '1. ');
|
||||
},
|
||||
'h1'(el, text) {
|
||||
return `# ${text.trim()}`;
|
||||
return `# ${text.trim()}\n`;
|
||||
},
|
||||
'h2'(el, text) {
|
||||
return `## ${text.trim()}`;
|
||||
return `## ${text.trim()}\n`;
|
||||
},
|
||||
'h3'(el, text) {
|
||||
return `### ${text.trim()}`;
|
||||
return `### ${text.trim()}\n`;
|
||||
},
|
||||
'h4'(el, text) {
|
||||
return `#### ${text.trim()}`;
|
||||
return `#### ${text.trim()}\n`;
|
||||
},
|
||||
'h5'(el, text) {
|
||||
return `##### ${text.trim()}`;
|
||||
return `##### ${text.trim()}\n`;
|
||||
},
|
||||
'h6'(el, text) {
|
||||
return `###### ${text.trim()}`;
|
||||
return `###### ${text.trim()}\n`;
|
||||
},
|
||||
'strong'(el, text) {
|
||||
return `**${text}**`;
|
||||
|
@ -241,11 +245,13 @@ const gfmRules = {
|
|||
'del'(el, text) {
|
||||
return `~~${text}~~`;
|
||||
},
|
||||
'sup'(el, text) {
|
||||
return `^${text}`;
|
||||
},
|
||||
'hr'(el) {
|
||||
return '-----';
|
||||
// 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
|
||||
return '\n-----\n';
|
||||
},
|
||||
'p'(el, text) {
|
||||
return `${text.trim()}\n`;
|
||||
},
|
||||
'table'(el) {
|
||||
const theadEl = el.querySelector('thead');
|
||||
|
@ -263,7 +269,9 @@ const gfmRules = {
|
|||
|
||||
let before = '';
|
||||
let after = '';
|
||||
switch (cell.style.textAlign) {
|
||||
const alignment = cell.align || cell.style.textAlign;
|
||||
|
||||
switch (alignment) {
|
||||
case 'center':
|
||||
before = ':';
|
||||
after = ':';
|
||||
|
@ -313,7 +321,7 @@ export class CopyAsGFM {
|
|||
}
|
||||
|
||||
static copyAsGFM(e, transformer) {
|
||||
const clipboardData = e.originalEvent.clipboardData;
|
||||
const { clipboardData } = e.originalEvent;
|
||||
if (!clipboardData) return;
|
||||
|
||||
const documentFragment = getSelectedFragment();
|
||||
|
@ -330,7 +338,7 @@ export class CopyAsGFM {
|
|||
}
|
||||
|
||||
static pasteGFM(e) {
|
||||
const clipboardData = e.originalEvent.clipboardData;
|
||||
const { clipboardData } = e.originalEvent;
|
||||
if (!clipboardData) return;
|
||||
|
||||
const text = clipboardData.getData('text/plain');
|
||||
|
|
|
@ -84,7 +84,7 @@ class BalsamiqViewer {
|
|||
renderTemplate(preview) {
|
||||
const resource = this.getResource(preview.resourceID);
|
||||
const name = BalsamiqViewer.parseTitle(resource);
|
||||
const image = preview.image;
|
||||
const { image } = preview;
|
||||
|
||||
const template = PREVIEW_TEMPLATE({
|
||||
name,
|
||||
|
|
|
@ -12,7 +12,7 @@ export default function loadBalsamiqFile() {
|
|||
|
||||
if (!(viewer instanceof Element)) return;
|
||||
|
||||
const endpoint = viewer.dataset.endpoint;
|
||||
const { endpoint } = viewer.dataset;
|
||||
|
||||
const balsamiqViewer = new BalsamiqViewer(viewer);
|
||||
balsamiqViewer.loadFile(endpoint).catch(onError);
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable no-new */
|
||||
import Vue from 'vue';
|
||||
import pdfLab from '../../pdf/index.vue';
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ export default () => {
|
|||
|
||||
[].slice.call(document.querySelectorAll('.js-material-changer')).forEach((el) => {
|
||||
el.addEventListener('click', (e) => {
|
||||
const target = e.target;
|
||||
const { target } = e;
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ export default class BlobViewer {
|
|||
const initialViewer = this.$fileHolder[0].querySelector('.blob-viewer:not(.hidden)');
|
||||
let initialViewerName = initialViewer.getAttribute('data-type');
|
||||
|
||||
if (this.switcher && location.hash.indexOf('#L') === 0) {
|
||||
if (this.switcher && window.location.hash.indexOf('#L') === 0) {
|
||||
initialViewerName = 'simple';
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, no-var, quotes, vars-on-top, no-unused-vars, no-new, max-len */
|
||||
/* global EditBlob */
|
||||
/* eslint-disable no-new */
|
||||
|
||||
import $ from 'jquery';
|
||||
import NewCommitForm from '../new_commit_form';
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* eslint-disable comma-dangle, space-before-function-paren, one-var */
|
||||
/* eslint-disable comma-dangle */
|
||||
|
||||
import $ from 'jquery';
|
||||
import Sortable from 'sortablejs';
|
||||
import Vue from 'vue';
|
||||
import AccessorUtilities from '../../lib/utils/accessor';
|
||||
|
@ -14,17 +13,28 @@ window.gl = window.gl || {};
|
|||
window.gl.issueBoards = window.gl.issueBoards || {};
|
||||
|
||||
gl.issueBoards.Board = Vue.extend({
|
||||
template: '#js-board-template',
|
||||
components: {
|
||||
boardList,
|
||||
'board-delete': gl.issueBoards.BoardDelete,
|
||||
BoardBlankState,
|
||||
},
|
||||
props: {
|
||||
list: Object,
|
||||
disabled: Boolean,
|
||||
issueLinkBase: String,
|
||||
rootPath: String,
|
||||
list: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
issueLinkBase: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
rootPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
boardId: {
|
||||
type: String,
|
||||
required: true,
|
||||
|
@ -46,56 +56,8 @@ gl.issueBoards.Board = Vue.extend({
|
|||
});
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
detailIssue: {
|
||||
handler () {
|
||||
if (!Object.keys(this.detailIssue.issue).length) return;
|
||||
|
||||
const issue = this.list.findIssue(this.detailIssue.issue.id);
|
||||
|
||||
if (issue) {
|
||||
const offsetLeft = this.$el.offsetLeft;
|
||||
const boardsList = document.querySelectorAll('.boards-list')[0];
|
||||
const left = boardsList.scrollLeft - offsetLeft;
|
||||
let right = (offsetLeft + this.$el.offsetWidth);
|
||||
|
||||
if (window.innerWidth > 768 && boardsList.classList.contains('is-compact')) {
|
||||
// -290 here because width of boardsList is animating so therefore
|
||||
// getting the width here is incorrect
|
||||
// 290 is the width of the sidebar
|
||||
right -= (boardsList.offsetWidth - 290);
|
||||
} else {
|
||||
right -= boardsList.offsetWidth;
|
||||
}
|
||||
|
||||
if (right - boardsList.scrollLeft > 0) {
|
||||
$(boardsList).animate({
|
||||
scrollLeft: right
|
||||
}, this.sortableOptions.animation);
|
||||
} else if (left > 0) {
|
||||
$(boardsList).animate({
|
||||
scrollLeft: offsetLeft
|
||||
}, this.sortableOptions.animation);
|
||||
}
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showNewIssueForm() {
|
||||
this.$refs['board-list'].showIssueForm = !this.$refs['board-list'].showIssueForm;
|
||||
},
|
||||
toggleExpanded(e) {
|
||||
if (this.list.isExpandable && !e.target.classList.contains('js-no-trigger-collapse')) {
|
||||
this.list.isExpanded = !this.list.isExpanded;
|
||||
|
||||
if (AccessorUtilities.isLocalStorageAccessSafe()) {
|
||||
localStorage.setItem(`boards.${this.boardId}.${this.list.type}.expanded`, this.list.isExpanded);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
this.sortableOptions = gl.issueBoards.getBoardSortableDefaultOptions({
|
||||
disabled: this.disabled,
|
||||
|
@ -125,4 +87,19 @@ gl.issueBoards.Board = Vue.extend({
|
|||
this.list.isExpanded = !isCollapsed;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showNewIssueForm() {
|
||||
this.$refs['board-list'].showIssueForm = !this.$refs['board-list'].showIssueForm;
|
||||
},
|
||||
toggleExpanded(e) {
|
||||
if (this.list.isExpandable && !e.target.classList.contains('js-no-trigger-collapse')) {
|
||||
this.list.isExpanded = !this.list.isExpanded;
|
||||
|
||||
if (AccessorUtilities.isLocalStorageAccessSafe()) {
|
||||
localStorage.setItem(`boards.${this.boardId}.${this.list.type}.expanded`, this.list.isExpanded);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
template: '#js-board-template',
|
||||
});
|
||||
|
|
|
@ -72,8 +72,8 @@ export default {
|
|||
:key="index"
|
||||
>
|
||||
<span
|
||||
class="label-color"
|
||||
:style="{ backgroundColor: label.color }">
|
||||
:style="{ backgroundColor: label.color }"
|
||||
class="label-color">
|
||||
</span>
|
||||
{{ label.title }}
|
||||
</li>
|
||||
|
|
|
@ -1,83 +1,82 @@
|
|||
<script>
|
||||
/* eslint-disable vue/require-default-prop */
|
||||
import './issue_card_inner';
|
||||
import eventHub from '../eventhub';
|
||||
/* eslint-disable vue/require-default-prop */
|
||||
import IssueCardInner from './issue_card_inner.vue';
|
||||
import eventHub from '../eventhub';
|
||||
|
||||
const Store = gl.issueBoards.BoardsStore;
|
||||
const Store = gl.issueBoards.BoardsStore;
|
||||
|
||||
export default {
|
||||
name: 'BoardsIssueCard',
|
||||
components: {
|
||||
'issue-card-inner': gl.issueBoards.IssueCardInner,
|
||||
},
|
||||
props: {
|
||||
list: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
export default {
|
||||
name: 'BoardsIssueCard',
|
||||
components: {
|
||||
IssueCardInner,
|
||||
},
|
||||
issue: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
props: {
|
||||
list: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
issue: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
issueLinkBase: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
index: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
rootPath: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
groupId: {
|
||||
type: Number,
|
||||
},
|
||||
},
|
||||
issueLinkBase: {
|
||||
type: String,
|
||||
default: '',
|
||||
data() {
|
||||
return {
|
||||
showDetail: false,
|
||||
detailIssue: Store.detail,
|
||||
};
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
computed: {
|
||||
issueDetailVisible() {
|
||||
return this.detailIssue.issue && this.detailIssue.issue.id === this.issue.id;
|
||||
},
|
||||
},
|
||||
index: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
rootPath: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
groupId: {
|
||||
type: Number,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showDetail: false,
|
||||
detailIssue: Store.detail,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
issueDetailVisible() {
|
||||
return this.detailIssue.issue && this.detailIssue.issue.id === this.issue.id;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
mouseDown() {
|
||||
this.showDetail = true;
|
||||
},
|
||||
mouseMove() {
|
||||
this.showDetail = false;
|
||||
},
|
||||
showIssue(e) {
|
||||
if (e.target.classList.contains('js-no-trigger')) return;
|
||||
|
||||
if (this.showDetail) {
|
||||
methods: {
|
||||
mouseDown() {
|
||||
this.showDetail = true;
|
||||
},
|
||||
mouseMove() {
|
||||
this.showDetail = false;
|
||||
},
|
||||
showIssue(e) {
|
||||
if (e.target.classList.contains('js-no-trigger')) return;
|
||||
|
||||
if (Store.detail.issue && Store.detail.issue.id === this.issue.id) {
|
||||
eventHub.$emit('clearDetailIssue');
|
||||
} else {
|
||||
eventHub.$emit('newDetailIssue', this.issue);
|
||||
Store.detail.list = this.list;
|
||||
if (this.showDetail) {
|
||||
this.showDetail = false;
|
||||
|
||||
if (Store.detail.issue && Store.detail.issue.id === this.issue.id) {
|
||||
eventHub.$emit('clearDetailIssue');
|
||||
} else {
|
||||
eventHub.$emit('newDetailIssue', this.issue);
|
||||
Store.detail.list = this.list;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<li
|
||||
class="board-card"
|
||||
:class="{
|
||||
'user-can-drag': !disabled && issue.id,
|
||||
'is-disabled': disabled || !issue.id,
|
||||
|
@ -85,6 +84,7 @@ export default {
|
|||
}"
|
||||
:index="index"
|
||||
:data-issue-id="issue.id"
|
||||
class="board-card"
|
||||
@mousedown="mouseDown"
|
||||
@mousemove="mouseMove"
|
||||
@mouseup="showIssue($event)">
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* eslint-disable comma-dangle, space-before-function-paren, no-alert */
|
||||
/* eslint-disable comma-dangle, no-alert */
|
||||
|
||||
import $ from 'jquery';
|
||||
import Vue from 'vue';
|
||||
|
@ -8,13 +8,16 @@ window.gl.issueBoards = window.gl.issueBoards || {};
|
|||
|
||||
gl.issueBoards.BoardDelete = Vue.extend({
|
||||
props: {
|
||||
list: Object
|
||||
list: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
deleteBoard () {
|
||||
$(this.$el).tooltip('hide');
|
||||
|
||||
if (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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,10 +87,46 @@ export default {
|
|||
mounted() {
|
||||
const options = gl.issueBoards.getBoardSortableDefaultOptions({
|
||||
scroll: true,
|
||||
group: 'issues',
|
||||
disabled: this.disabled,
|
||||
filter: '.board-list-count, .is-disabled',
|
||||
dataIdAttr: 'data-issue-id',
|
||||
group: {
|
||||
name: 'issues',
|
||||
/**
|
||||
* Dynamically determine between which containers
|
||||
* items can be moved or copied as
|
||||
* Assignee lists (EE feature) require this behavior
|
||||
*/
|
||||
pull: (to, from, dragEl, e) => {
|
||||
// As per Sortable's docs, `to` should provide
|
||||
// reference to exact sortable container on which
|
||||
// we're trying to drag element, but either it is
|
||||
// a library's bug or our markup structure is too complex
|
||||
// that `to` never points to correct container
|
||||
// See https://github.com/RubaXa/Sortable/issues/1037
|
||||
//
|
||||
// So we use `e.target` which is always accurate about
|
||||
// which element we're currently dragging our card upon
|
||||
// So from there, we can get reference to actual container
|
||||
// and thus the container type to enable Copy or Move
|
||||
if (e.target) {
|
||||
const containerEl = e.target.closest('.js-board-list') || e.target.querySelector('.js-board-list');
|
||||
const toBoardType = containerEl.dataset.boardType;
|
||||
|
||||
if (toBoardType) {
|
||||
const fromBoardType = this.list.type;
|
||||
|
||||
if ((fromBoardType === 'assignee' && toBoardType === 'label') ||
|
||||
(fromBoardType === 'label' && toBoardType === 'assignee')) {
|
||||
return 'clone';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
revertClone: true,
|
||||
},
|
||||
onStart: (e) => {
|
||||
const card = this.$refs.issue[e.oldIndex];
|
||||
|
||||
|
@ -169,21 +205,22 @@ export default {
|
|||
<template>
|
||||
<div class="board-list-component">
|
||||
<div
|
||||
v-if="loading"
|
||||
class="board-list-loading text-center"
|
||||
aria-label="Loading issues"
|
||||
v-if="loading">
|
||||
aria-label="Loading issues">
|
||||
<loading-icon />
|
||||
</div>
|
||||
<board-new-issue
|
||||
v-if="list.type !== 'closed' && showIssueForm"
|
||||
:group-id="groupId"
|
||||
:list="list"
|
||||
v-if="list.type !== 'closed' && showIssueForm"/>
|
||||
:list="list"/>
|
||||
<ul
|
||||
class="board-list"
|
||||
v-show="!loading"
|
||||
ref="list"
|
||||
:data-board="list.id"
|
||||
:class="{ 'is-smaller': showIssueForm }">
|
||||
:data-board-type="list.type"
|
||||
:class="{ 'is-smaller': showIssueForm }"
|
||||
class="board-list js-board-list">
|
||||
<board-card
|
||||
v-for="(issue, index) in issues"
|
||||
ref="issue"
|
||||
|
@ -196,8 +233,8 @@ export default {
|
|||
:disabled="disabled"
|
||||
:key="issue.id" />
|
||||
<li
|
||||
class="board-list-count text-center"
|
||||
v-if="showCount"
|
||||
class="board-list-count text-center"
|
||||
data-issue-id="-1">
|
||||
<loading-icon
|
||||
v-show="list.loadingMore"
|
||||
|
|
|
@ -49,11 +49,12 @@ export default {
|
|||
this.error = false;
|
||||
|
||||
const labels = this.list.label ? [this.list.label] : [];
|
||||
const assignees = this.list.assignee ? [this.list.assignee] : [];
|
||||
const issue = new ListIssue({
|
||||
title: this.title,
|
||||
labels,
|
||||
subscribed: true,
|
||||
assignees: [],
|
||||
assignees,
|
||||
project_id: this.selectedProject.id,
|
||||
});
|
||||
|
||||
|
@ -95,26 +96,26 @@ export default {
|
|||
<div class="board-card">
|
||||
<form @submit="submit($event)">
|
||||
<div
|
||||
class="flash-container"
|
||||
v-if="error"
|
||||
class="flash-container"
|
||||
>
|
||||
<div class="flash-alert">
|
||||
An error occurred. Please try again.
|
||||
</div>
|
||||
</div>
|
||||
<label
|
||||
class="label-light"
|
||||
:for="list.id + '-title'"
|
||||
class="label-light"
|
||||
>
|
||||
Title
|
||||
</label>
|
||||
<input
|
||||
ref="input"
|
||||
v-model="title"
|
||||
:id="list.id + '-title'"
|
||||
class="form-control"
|
||||
type="text"
|
||||
v-model="title"
|
||||
ref="input"
|
||||
autocomplete="off"
|
||||
:id="list.id + '-title'"
|
||||
/>
|
||||
<project-select
|
||||
v-if="groupId"
|
||||
|
@ -122,10 +123,10 @@ export default {
|
|||
/>
|
||||
<div class="clearfix prepend-top-10">
|
||||
<button
|
||||
ref="submit-button"
|
||||
:disabled="disabled"
|
||||
class="btn btn-success float-left"
|
||||
type="submit"
|
||||
:disabled="disabled"
|
||||
ref="submit-button"
|
||||
>
|
||||
Submit issue
|
||||
</button>
|
||||
|
@ -141,4 +142,3 @@ export default {
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* eslint-disable comma-dangle, space-before-function-paren, no-new */
|
||||
/* eslint-disable comma-dangle, no-new */
|
||||
|
||||
import $ from 'jquery';
|
||||
import Vue from 'vue';
|
||||
|
@ -6,13 +6,13 @@ import Flash from '../../flash';
|
|||
import { __ } from '../../locale';
|
||||
import Sidebar from '../../right_sidebar';
|
||||
import eventHub from '../../sidebar/event_hub';
|
||||
import assigneeTitle from '../../sidebar/components/assignees/assignee_title.vue';
|
||||
import assignees from '../../sidebar/components/assignees/assignees.vue';
|
||||
import AssigneeTitle from '../../sidebar/components/assignees/assignee_title.vue';
|
||||
import Assignees from '../../sidebar/components/assignees/assignees.vue';
|
||||
import DueDateSelectors from '../../due_date_select';
|
||||
import './sidebar/remove_issue';
|
||||
import RemoveBtn from './sidebar/remove_issue.vue';
|
||||
import IssuableContext from '../../issuable_context';
|
||||
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';
|
||||
|
||||
const Store = gl.issueBoards.BoardsStore;
|
||||
|
@ -21,8 +21,17 @@ window.gl = window.gl || {};
|
|||
window.gl.issueBoards = window.gl.issueBoards || {};
|
||||
|
||||
gl.issueBoards.BoardSidebar = Vue.extend({
|
||||
components: {
|
||||
AssigneeTitle,
|
||||
Assignees,
|
||||
RemoveBtn,
|
||||
Subscriptions,
|
||||
},
|
||||
props: {
|
||||
currentUser: Object
|
||||
currentUser: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -64,6 +73,26 @@ gl.issueBoards.BoardSidebar = Vue.extend({
|
|||
deep: true
|
||||
},
|
||||
},
|
||||
created () {
|
||||
// Get events from glDropdown
|
||||
eventHub.$on('sidebar.removeAssignee', this.removeAssignee);
|
||||
eventHub.$on('sidebar.addAssignee', this.addAssignee);
|
||||
eventHub.$on('sidebar.removeAllAssignees', this.removeAllAssignees);
|
||||
eventHub.$on('sidebar.saveAssignees', this.saveAssignees);
|
||||
},
|
||||
beforeDestroy() {
|
||||
eventHub.$off('sidebar.removeAssignee', this.removeAssignee);
|
||||
eventHub.$off('sidebar.addAssignee', this.addAssignee);
|
||||
eventHub.$off('sidebar.removeAllAssignees', this.removeAllAssignees);
|
||||
eventHub.$off('sidebar.saveAssignees', this.saveAssignees);
|
||||
},
|
||||
mounted () {
|
||||
new IssuableContext(this.currentUser);
|
||||
new MilestoneSelect();
|
||||
new DueDateSelectors();
|
||||
new LabelsSelect();
|
||||
new Sidebar();
|
||||
},
|
||||
methods: {
|
||||
closeSidebar () {
|
||||
this.detail.issue = {};
|
||||
|
@ -97,30 +126,4 @@ gl.issueBoards.BoardSidebar = Vue.extend({
|
|||
});
|
||||
},
|
||||
},
|
||||
created () {
|
||||
// Get events from glDropdown
|
||||
eventHub.$on('sidebar.removeAssignee', this.removeAssignee);
|
||||
eventHub.$on('sidebar.addAssignee', this.addAssignee);
|
||||
eventHub.$on('sidebar.removeAllAssignees', this.removeAllAssignees);
|
||||
eventHub.$on('sidebar.saveAssignees', this.saveAssignees);
|
||||
},
|
||||
beforeDestroy() {
|
||||
eventHub.$off('sidebar.removeAssignee', this.removeAssignee);
|
||||
eventHub.$off('sidebar.addAssignee', this.addAssignee);
|
||||
eventHub.$off('sidebar.removeAllAssignees', this.removeAllAssignees);
|
||||
eventHub.$off('sidebar.saveAssignees', this.saveAssignees);
|
||||
},
|
||||
mounted () {
|
||||
new IssuableContext(this.currentUser);
|
||||
new MilestoneSelect();
|
||||
new DueDateSelectors();
|
||||
new LabelsSelect();
|
||||
new Sidebar();
|
||||
},
|
||||
components: {
|
||||
assigneeTitle,
|
||||
assignees,
|
||||
removeBtn: gl.issueBoards.RemoveIssueBtn,
|
||||
subscriptions,
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,195 +0,0 @@
|
|||
import $ from 'jquery';
|
||||
import Vue from 'vue';
|
||||
import UserAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
|
||||
import eventHub from '../eventhub';
|
||||
|
||||
const Store = gl.issueBoards.BoardsStore;
|
||||
|
||||
window.gl = window.gl || {};
|
||||
window.gl.issueBoards = window.gl.issueBoards || {};
|
||||
|
||||
gl.issueBoards.IssueCardInner = Vue.extend({
|
||||
props: {
|
||||
issue: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
issueLinkBase: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
list: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => ({}),
|
||||
},
|
||||
rootPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
updateFilters: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
groupId: {
|
||||
type: Number,
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
limitBeforeCounter: 3,
|
||||
maxRender: 4,
|
||||
maxCounter: 99,
|
||||
};
|
||||
},
|
||||
components: {
|
||||
UserAvatarLink,
|
||||
},
|
||||
computed: {
|
||||
numberOverLimit() {
|
||||
return this.issue.assignees.length - this.limitBeforeCounter;
|
||||
},
|
||||
assigneeCounterTooltip() {
|
||||
return `${this.assigneeCounterLabel} more`;
|
||||
},
|
||||
assigneeCounterLabel() {
|
||||
if (this.numberOverLimit > this.maxCounter) {
|
||||
return `${this.maxCounter}+`;
|
||||
}
|
||||
|
||||
return `+${this.numberOverLimit}`;
|
||||
},
|
||||
shouldRenderCounter() {
|
||||
if (this.issue.assignees.length <= this.maxRender) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.issue.assignees.length > this.numberOverLimit;
|
||||
},
|
||||
issueId() {
|
||||
if (this.issue.iid) {
|
||||
return `#${this.issue.iid}`;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
showLabelFooter() {
|
||||
return this.issue.labels.find(l => this.showLabel(l)) !== undefined;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
isIndexLessThanlimit(index) {
|
||||
return index < this.limitBeforeCounter;
|
||||
},
|
||||
shouldRenderAssignee(index) {
|
||||
// Eg. maxRender is 4,
|
||||
// Render up to all 4 assignees if there are only 4 assigness
|
||||
// Otherwise render up to the limitBeforeCounter
|
||||
if (this.issue.assignees.length <= this.maxRender) {
|
||||
return index < this.maxRender;
|
||||
}
|
||||
|
||||
return index < this.limitBeforeCounter;
|
||||
},
|
||||
assigneeUrl(assignee) {
|
||||
return `${this.rootPath}${assignee.username}`;
|
||||
},
|
||||
assigneeUrlTitle(assignee) {
|
||||
return `Assigned to ${assignee.name}`;
|
||||
},
|
||||
avatarUrlTitle(assignee) {
|
||||
return `Avatar for ${assignee.name}`;
|
||||
},
|
||||
showLabel(label) {
|
||||
if (!label.id) return false;
|
||||
return true;
|
||||
},
|
||||
filterByLabel(label, e) {
|
||||
if (!this.updateFilters) return;
|
||||
|
||||
const filterPath = gl.issueBoards.BoardsStore.filter.path.split('&');
|
||||
const labelTitle = encodeURIComponent(label.title);
|
||||
const param = `label_name[]=${labelTitle}`;
|
||||
const labelIndex = filterPath.indexOf(param);
|
||||
$(e.currentTarget).tooltip('hide');
|
||||
|
||||
if (labelIndex === -1) {
|
||||
filterPath.push(param);
|
||||
} else {
|
||||
filterPath.splice(labelIndex, 1);
|
||||
}
|
||||
|
||||
gl.issueBoards.BoardsStore.filter.path = filterPath.join('&');
|
||||
|
||||
Store.updateFiltersUrl();
|
||||
|
||||
eventHub.$emit('updateTokens');
|
||||
},
|
||||
labelStyle(label) {
|
||||
return {
|
||||
backgroundColor: label.color,
|
||||
color: label.textColor,
|
||||
};
|
||||
},
|
||||
},
|
||||
template: `
|
||||
<div>
|
||||
<div class="board-card-header">
|
||||
<h4 class="board-card-title">
|
||||
<i
|
||||
class="fa fa-eye-slash confidential-icon"
|
||||
v-if="issue.confidential"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<a
|
||||
class="js-no-trigger"
|
||||
:href="issue.path"
|
||||
:title="issue.title">{{ issue.title }}</a>
|
||||
<span
|
||||
class="board-card-number"
|
||||
v-if="issueId"
|
||||
>
|
||||
{{ issue.referencePath }}
|
||||
</span>
|
||||
</h4>
|
||||
<div class="board-card-assignee">
|
||||
<user-avatar-link
|
||||
v-for="(assignee, index) in issue.assignees"
|
||||
:key="assignee.id"
|
||||
v-if="shouldRenderAssignee(index)"
|
||||
class="js-no-trigger"
|
||||
:link-href="assigneeUrl(assignee)"
|
||||
:img-alt="avatarUrlTitle(assignee)"
|
||||
:img-src="assignee.avatar"
|
||||
:tooltip-text="assigneeUrlTitle(assignee)"
|
||||
tooltip-placement="bottom"
|
||||
/>
|
||||
<span
|
||||
class="avatar-counter has-tooltip"
|
||||
:title="assigneeCounterTooltip"
|
||||
v-if="shouldRenderCounter"
|
||||
>
|
||||
{{ assigneeCounterLabel }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="board-card-footer"
|
||||
v-if="showLabelFooter"
|
||||
>
|
||||
<button
|
||||
class="badge color-label has-tooltip"
|
||||
v-for="label in issue.labels"
|
||||
type="button"
|
||||
v-if="showLabel(label)"
|
||||
@click="filterByLabel(label, $event)"
|
||||
:style="labelStyle(label)"
|
||||
:title="label.description"
|
||||
data-container="body">
|
||||
{{ label.title }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
});
|
202
app/assets/javascripts/boards/components/issue_card_inner.vue
Normal file
|
@ -0,0 +1,202 @@
|
|||
<script>
|
||||
import $ from 'jquery';
|
||||
import UserAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
|
||||
import eventHub from '../eventhub';
|
||||
import tooltip from '../../vue_shared/directives/tooltip';
|
||||
|
||||
const Store = gl.issueBoards.BoardsStore;
|
||||
|
||||
export default {
|
||||
components: {
|
||||
UserAvatarLink,
|
||||
},
|
||||
directives: {
|
||||
tooltip,
|
||||
},
|
||||
props: {
|
||||
issue: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
issueLinkBase: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
list: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => ({}),
|
||||
},
|
||||
rootPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
updateFilters: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
groupId: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
limitBeforeCounter: 3,
|
||||
maxRender: 4,
|
||||
maxCounter: 99,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
numberOverLimit() {
|
||||
return this.issue.assignees.length - this.limitBeforeCounter;
|
||||
},
|
||||
assigneeCounterTooltip() {
|
||||
return `${this.assigneeCounterLabel} more`;
|
||||
},
|
||||
assigneeCounterLabel() {
|
||||
if (this.numberOverLimit > this.maxCounter) {
|
||||
return `${this.maxCounter}+`;
|
||||
}
|
||||
|
||||
return `+${this.numberOverLimit}`;
|
||||
},
|
||||
shouldRenderCounter() {
|
||||
if (this.issue.assignees.length <= this.maxRender) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.issue.assignees.length > this.numberOverLimit;
|
||||
},
|
||||
issueId() {
|
||||
if (this.issue.iid) {
|
||||
return `#${this.issue.iid}`;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
showLabelFooter() {
|
||||
return this.issue.labels.find(l => this.showLabel(l)) !== undefined;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
isIndexLessThanlimit(index) {
|
||||
return index < this.limitBeforeCounter;
|
||||
},
|
||||
shouldRenderAssignee(index) {
|
||||
// Eg. maxRender is 4,
|
||||
// Render up to all 4 assignees if there are only 4 assigness
|
||||
// Otherwise render up to the limitBeforeCounter
|
||||
if (this.issue.assignees.length <= this.maxRender) {
|
||||
return index < this.maxRender;
|
||||
}
|
||||
|
||||
return index < this.limitBeforeCounter;
|
||||
},
|
||||
assigneeUrl(assignee) {
|
||||
return `${this.rootPath}${assignee.username}`;
|
||||
},
|
||||
assigneeUrlTitle(assignee) {
|
||||
return `Assigned to ${assignee.name}`;
|
||||
},
|
||||
avatarUrlTitle(assignee) {
|
||||
return `Avatar for ${assignee.name}`;
|
||||
},
|
||||
showLabel(label) {
|
||||
if (!label.id) return false;
|
||||
return true;
|
||||
},
|
||||
filterByLabel(label, e) {
|
||||
if (!this.updateFilters) return;
|
||||
|
||||
const filterPath = gl.issueBoards.BoardsStore.filter.path.split('&');
|
||||
const labelTitle = encodeURIComponent(label.title);
|
||||
const param = `label_name[]=${labelTitle}`;
|
||||
const labelIndex = filterPath.indexOf(param);
|
||||
$(e.currentTarget).tooltip('hide');
|
||||
|
||||
if (labelIndex === -1) {
|
||||
filterPath.push(param);
|
||||
} else {
|
||||
filterPath.splice(labelIndex, 1);
|
||||
}
|
||||
|
||||
gl.issueBoards.BoardsStore.filter.path = filterPath.join('&');
|
||||
|
||||
Store.updateFiltersUrl();
|
||||
|
||||
eventHub.$emit('updateTokens');
|
||||
},
|
||||
labelStyle(label) {
|
||||
return {
|
||||
backgroundColor: label.color,
|
||||
color: label.textColor,
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<div class="board-card-header">
|
||||
<h4 class="board-card-title">
|
||||
<i
|
||||
v-if="issue.confidential"
|
||||
class="fa fa-eye-slash confidential-icon"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<a
|
||||
:href="issue.path"
|
||||
:title="issue.title"
|
||||
class="js-no-trigger">{{ issue.title }}</a>
|
||||
<span
|
||||
v-if="issueId"
|
||||
class="board-card-number"
|
||||
>
|
||||
{{ issue.referencePath }}
|
||||
</span>
|
||||
</h4>
|
||||
<div class="board-card-assignee">
|
||||
<user-avatar-link
|
||||
v-for="(assignee, index) in issue.assignees"
|
||||
v-if="shouldRenderAssignee(index)"
|
||||
:key="assignee.id"
|
||||
:link-href="assigneeUrl(assignee)"
|
||||
:img-alt="avatarUrlTitle(assignee)"
|
||||
:img-src="assignee.avatar"
|
||||
:tooltip-text="assigneeUrlTitle(assignee)"
|
||||
class="js-no-trigger"
|
||||
tooltip-placement="bottom"
|
||||
/>
|
||||
<span
|
||||
v-tooltip
|
||||
v-if="shouldRenderCounter"
|
||||
:title="assigneeCounterTooltip"
|
||||
class="avatar-counter"
|
||||
>
|
||||
{{ assigneeCounterLabel }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="showLabelFooter"
|
||||
class="board-card-footer"
|
||||
>
|
||||
<button
|
||||
v-tooltip
|
||||
v-for="label in issue.labels"
|
||||
v-if="showLabel(label)"
|
||||
:key="label.id"
|
||||
:style="labelStyle(label)"
|
||||
:title="label.description"
|
||||
class="badge color-label"
|
||||
type="button"
|
||||
data-container="body"
|
||||
@click="filterByLabel(label, $event)"
|
||||
>
|
||||
{{ label.title }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
|
@ -1,12 +1,9 @@
|
|||
import Vue from 'vue';
|
||||
<script>
|
||||
import ModalStore from '../../stores/modal_store';
|
||||
import modalMixin from '../../mixins/modal_mixins';
|
||||
|
||||
gl.issueBoards.ModalEmptyState = Vue.extend({
|
||||
export default {
|
||||
mixins: [modalMixin],
|
||||
data() {
|
||||
return ModalStore.store;
|
||||
},
|
||||
props: {
|
||||
newIssuePath: {
|
||||
type: String,
|
||||
|
@ -17,6 +14,9 @@ gl.issueBoards.ModalEmptyState = Vue.extend({
|
|||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return ModalStore.store;
|
||||
},
|
||||
computed: {
|
||||
contents() {
|
||||
const obj = {
|
||||
|
@ -38,32 +38,36 @@ gl.issueBoards.ModalEmptyState = Vue.extend({
|
|||
return obj;
|
||||
},
|
||||
},
|
||||
template: `
|
||||
<section class="empty-state">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-6 order-md-last">
|
||||
<aside class="svg-content"><img :src="emptyStateSvg"/></aside>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 order-md-first">
|
||||
<div class="text-content">
|
||||
<h4>{{ contents.title }}</h4>
|
||||
<p v-html="contents.content"></p>
|
||||
<a
|
||||
:href="newIssuePath"
|
||||
class="btn btn-success btn-inverted"
|
||||
v-if="activeTab === 'all'">
|
||||
New issue
|
||||
</a>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
@click="changeTab('all')"
|
||||
v-if="activeTab === 'selected'">
|
||||
Open issues
|
||||
</button>
|
||||
</div>
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="empty-state">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-6 order-md-last">
|
||||
<aside class="svg-content"><img :src="emptyStateSvg"/></aside>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 order-md-first">
|
||||
<div class="text-content">
|
||||
<h4>{{ contents.title }}</h4>
|
||||
<p v-html="contents.content"></p>
|
||||
<a
|
||||
v-if="activeTab === 'all'"
|
||||
:href="newIssuePath"
|
||||
class="btn btn-success btn-inverted"
|
||||
>
|
||||
New issue
|
||||
</a>
|
||||
<button
|
||||
v-if="activeTab === 'selected'"
|
||||
class="btn btn-default"
|
||||
type="button"
|
||||
@click="changeTab('all')"
|
||||
>
|
||||
Open issues
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
`,
|
||||
});
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
|
@ -1,12 +1,15 @@
|
|||
import Vue from 'vue';
|
||||
<script>
|
||||
import Flash from '../../../flash';
|
||||
import { __ } from '../../../locale';
|
||||
import './lists_dropdown';
|
||||
import ListsDropdown from './lists_dropdown.vue';
|
||||
import { pluralize } from '../../../lib/utils/text_utility';
|
||||
import ModalStore from '../../stores/modal_store';
|
||||
import modalMixin from '../../mixins/modal_mixins';
|
||||
|
||||
gl.issueBoards.ModalFooter = Vue.extend({
|
||||
export default {
|
||||
components: {
|
||||
ListsDropdown,
|
||||
},
|
||||
mixins: [modalMixin],
|
||||
data() {
|
||||
return {
|
||||
|
@ -52,31 +55,32 @@ gl.issueBoards.ModalFooter = Vue.extend({
|
|||
this.toggleModal(false);
|
||||
},
|
||||
},
|
||||
components: {
|
||||
'lists-dropdown': gl.issueBoards.ModalFooterListsDropdown,
|
||||
},
|
||||
template: `
|
||||
<footer
|
||||
class="form-actions add-issues-footer">
|
||||
<div class="float-left">
|
||||
<button
|
||||
class="btn btn-success"
|
||||
type="button"
|
||||
:disabled="submitDisabled"
|
||||
@click="addIssues">
|
||||
{{ submitText }}
|
||||
</button>
|
||||
<span class="inline add-issues-footer-to-list">
|
||||
to list
|
||||
</span>
|
||||
<lists-dropdown></lists-dropdown>
|
||||
</div>
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<footer
|
||||
class="form-actions add-issues-footer"
|
||||
>
|
||||
<div class="float-left">
|
||||
<button
|
||||
class="btn btn-default float-right"
|
||||
:disabled="submitDisabled"
|
||||
class="btn btn-success"
|
||||
type="button"
|
||||
@click="toggleModal(false)">
|
||||
Cancel
|
||||
@click="addIssues"
|
||||
>
|
||||
{{ submitText }}
|
||||
</button>
|
||||
</footer>
|
||||
`,
|
||||
});
|
||||
<span class="inline add-issues-footer-to-list">
|
||||
to list
|
||||
</span>
|
||||
<lists-dropdown/>
|
||||
</div>
|
||||
<button
|
||||
class="btn btn-default float-right"
|
||||
type="button"
|
||||
@click="toggleModal(false)"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</footer>
|
||||
</template>
|
|
@ -1,79 +0,0 @@
|
|||
import Vue from 'vue';
|
||||
import modalFilters from './filters';
|
||||
import './tabs';
|
||||
import ModalStore from '../../stores/modal_store';
|
||||
import modalMixin from '../../mixins/modal_mixins';
|
||||
|
||||
gl.issueBoards.ModalHeader = Vue.extend({
|
||||
mixins: [modalMixin],
|
||||
props: {
|
||||
projectId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
milestonePath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
labelPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return ModalStore.store;
|
||||
},
|
||||
computed: {
|
||||
selectAllText() {
|
||||
if (ModalStore.selectedCount() !== this.issues.length || this.issues.length === 0) {
|
||||
return 'Select all';
|
||||
}
|
||||
|
||||
return 'Deselect all';
|
||||
},
|
||||
showSearch() {
|
||||
return this.activeTab === 'all' && !this.loading && this.issuesCount > 0;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
toggleAll() {
|
||||
this.$refs.selectAllBtn.blur();
|
||||
|
||||
ModalStore.toggleAll();
|
||||
},
|
||||
},
|
||||
components: {
|
||||
'modal-tabs': gl.issueBoards.ModalTabs,
|
||||
modalFilters,
|
||||
},
|
||||
template: `
|
||||
<div>
|
||||
<header class="add-issues-header form-actions">
|
||||
<h2>
|
||||
Add issues
|
||||
<button
|
||||
type="button"
|
||||
class="close"
|
||||
data-dismiss="modal"
|
||||
aria-label="Close"
|
||||
@click="toggleModal(false)">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</h2>
|
||||
</header>
|
||||
<modal-tabs v-if="!loading && issuesCount > 0"></modal-tabs>
|
||||
<div
|
||||
class="add-issues-search append-bottom-10"
|
||||
v-if="showSearch">
|
||||
<modal-filters :store="filter" />
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-success btn-inverted prepend-left-10"
|
||||
ref="selectAllBtn"
|
||||
@click="toggleAll">
|
||||
{{ selectAllText }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
});
|
82
app/assets/javascripts/boards/components/modal/header.vue
Normal file
|
@ -0,0 +1,82 @@
|
|||
<script>
|
||||
import ModalFilters from './filters';
|
||||
import ModalTabs from './tabs.vue';
|
||||
import ModalStore from '../../stores/modal_store';
|
||||
import modalMixin from '../../mixins/modal_mixins';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ModalTabs,
|
||||
ModalFilters,
|
||||
},
|
||||
mixins: [modalMixin],
|
||||
props: {
|
||||
projectId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
milestonePath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
labelPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return ModalStore.store;
|
||||
},
|
||||
computed: {
|
||||
selectAllText() {
|
||||
if (ModalStore.selectedCount() !== this.issues.length || this.issues.length === 0) {
|
||||
return 'Select all';
|
||||
}
|
||||
|
||||
return 'Deselect all';
|
||||
},
|
||||
showSearch() {
|
||||
return this.activeTab === 'all' && !this.loading && this.issuesCount > 0;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
toggleAll() {
|
||||
this.$refs.selectAllBtn.blur();
|
||||
|
||||
ModalStore.toggleAll();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<header class="add-issues-header form-actions">
|
||||
<h2>
|
||||
Add issues
|
||||
<button
|
||||
type="button"
|
||||
class="close"
|
||||
data-dismiss="modal"
|
||||
aria-label="Close"
|
||||
@click="toggleModal(false)"
|
||||
>
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</h2>
|
||||
</header>
|
||||
<modal-tabs v-if="!loading && issuesCount > 0"/>
|
||||
<div
|
||||
v-if="showSearch"
|
||||
class="add-issues-search append-bottom-10">
|
||||
<modal-filters :store="filter" />
|
||||
<button
|
||||
ref="selectAllBtn"
|
||||
type="button"
|
||||
class="btn btn-success btn-inverted prepend-left-10"
|
||||
@click="toggleAll"
|
||||
>
|
||||
{{ selectAllText }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
|
@ -1,171 +0,0 @@
|
|||
/* global ListIssue */
|
||||
|
||||
import Vue from 'vue';
|
||||
import queryData from '~/boards/utils/query_data';
|
||||
import loadingIcon from '~/vue_shared/components/loading_icon.vue';
|
||||
import './header';
|
||||
import './list';
|
||||
import './footer';
|
||||
import './empty_state';
|
||||
import ModalStore from '../../stores/modal_store';
|
||||
|
||||
gl.issueBoards.IssuesModal = Vue.extend({
|
||||
props: {
|
||||
newIssuePath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
emptyStateSvg: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
issueLinkBase: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
rootPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
projectId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
milestonePath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
labelPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return ModalStore.store;
|
||||
},
|
||||
watch: {
|
||||
page() {
|
||||
this.loadIssues();
|
||||
},
|
||||
showAddIssuesModal() {
|
||||
if (this.showAddIssuesModal && !this.issues.length) {
|
||||
this.loading = true;
|
||||
const loadingDone = () => {
|
||||
this.loading = false;
|
||||
};
|
||||
|
||||
this.loadIssues()
|
||||
.then(loadingDone)
|
||||
.catch(loadingDone);
|
||||
} else if (!this.showAddIssuesModal) {
|
||||
this.issues = [];
|
||||
this.selectedIssues = [];
|
||||
this.issuesCount = false;
|
||||
}
|
||||
},
|
||||
filter: {
|
||||
handler() {
|
||||
if (this.$el.tagName) {
|
||||
this.page = 1;
|
||||
this.filterLoading = true;
|
||||
const loadingDone = () => {
|
||||
this.filterLoading = false;
|
||||
};
|
||||
|
||||
this.loadIssues(true)
|
||||
.then(loadingDone)
|
||||
.catch(loadingDone);
|
||||
}
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
loadIssues(clearIssues = false) {
|
||||
if (!this.showAddIssuesModal) return false;
|
||||
|
||||
return gl.boardService.getBacklog(queryData(this.filter.path, {
|
||||
page: this.page,
|
||||
per: this.perPage,
|
||||
}))
|
||||
.then(res => res.data)
|
||||
.then((data) => {
|
||||
if (clearIssues) {
|
||||
this.issues = [];
|
||||
}
|
||||
|
||||
data.issues.forEach((issueObj) => {
|
||||
const issue = new ListIssue(issueObj);
|
||||
const foundSelectedIssue = ModalStore.findSelectedIssue(issue);
|
||||
issue.selected = !!foundSelectedIssue;
|
||||
|
||||
this.issues.push(issue);
|
||||
});
|
||||
|
||||
this.loadingNewPage = false;
|
||||
|
||||
if (!this.issuesCount) {
|
||||
this.issuesCount = data.size;
|
||||
}
|
||||
}).catch(() => {
|
||||
// TODO: handle request error
|
||||
});
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
showList() {
|
||||
if (this.activeTab === 'selected') {
|
||||
return this.selectedIssues.length > 0;
|
||||
}
|
||||
|
||||
return this.issuesCount > 0;
|
||||
},
|
||||
showEmptyState() {
|
||||
if (!this.loading && this.issuesCount === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.activeTab === 'selected' && this.selectedIssues.length === 0;
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.page = 1;
|
||||
},
|
||||
components: {
|
||||
'modal-header': gl.issueBoards.ModalHeader,
|
||||
'modal-list': gl.issueBoards.ModalList,
|
||||
'modal-footer': gl.issueBoards.ModalFooter,
|
||||
'empty-state': gl.issueBoards.ModalEmptyState,
|
||||
loadingIcon,
|
||||
},
|
||||
template: `
|
||||
<div
|
||||
class="add-issues-modal"
|
||||
v-if="showAddIssuesModal">
|
||||
<div class="add-issues-container">
|
||||
<modal-header
|
||||
:project-id="projectId"
|
||||
:milestone-path="milestonePath"
|
||||
:label-path="labelPath">
|
||||
</modal-header>
|
||||
<modal-list
|
||||
:issue-link-base="issueLinkBase"
|
||||
:root-path="rootPath"
|
||||
:empty-state-svg="emptyStateSvg"
|
||||
v-if="!loading && showList && !filterLoading"></modal-list>
|
||||
<empty-state
|
||||
v-if="showEmptyState"
|
||||
:new-issue-path="newIssuePath"
|
||||
:empty-state-svg="emptyStateSvg"></empty-state>
|
||||
<section
|
||||
class="add-issues-list text-center"
|
||||
v-if="loading || filterLoading">
|
||||
<div class="add-issues-list-loading">
|
||||
<loading-icon />
|
||||
</div>
|
||||
</section>
|
||||
<modal-footer></modal-footer>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
});
|
178
app/assets/javascripts/boards/components/modal/index.vue
Normal file
|
@ -0,0 +1,178 @@
|
|||
<script>
|
||||
/* global ListIssue */
|
||||
import queryData from '~/boards/utils/query_data';
|
||||
import loadingIcon from '~/vue_shared/components/loading_icon.vue';
|
||||
import ModalHeader from './header.vue';
|
||||
import ModalList from './list.vue';
|
||||
import ModalFooter from './footer.vue';
|
||||
import EmptyState from './empty_state.vue';
|
||||
import ModalStore from '../../stores/modal_store';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
EmptyState,
|
||||
ModalHeader,
|
||||
ModalList,
|
||||
ModalFooter,
|
||||
loadingIcon,
|
||||
},
|
||||
props: {
|
||||
newIssuePath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
emptyStateSvg: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
issueLinkBase: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
rootPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
projectId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
milestonePath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
labelPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return ModalStore.store;
|
||||
},
|
||||
computed: {
|
||||
showList() {
|
||||
if (this.activeTab === 'selected') {
|
||||
return this.selectedIssues.length > 0;
|
||||
}
|
||||
|
||||
return this.issuesCount > 0;
|
||||
},
|
||||
showEmptyState() {
|
||||
if (!this.loading && this.issuesCount === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.activeTab === 'selected' && this.selectedIssues.length === 0;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
page() {
|
||||
this.loadIssues();
|
||||
},
|
||||
showAddIssuesModal() {
|
||||
if (this.showAddIssuesModal && !this.issues.length) {
|
||||
this.loading = true;
|
||||
const loadingDone = () => {
|
||||
this.loading = false;
|
||||
};
|
||||
|
||||
this.loadIssues()
|
||||
.then(loadingDone)
|
||||
.catch(loadingDone);
|
||||
} else if (!this.showAddIssuesModal) {
|
||||
this.issues = [];
|
||||
this.selectedIssues = [];
|
||||
this.issuesCount = false;
|
||||
}
|
||||
},
|
||||
filter: {
|
||||
handler() {
|
||||
if (this.$el.tagName) {
|
||||
this.page = 1;
|
||||
this.filterLoading = true;
|
||||
const loadingDone = () => {
|
||||
this.filterLoading = false;
|
||||
};
|
||||
|
||||
this.loadIssues(true)
|
||||
.then(loadingDone)
|
||||
.catch(loadingDone);
|
||||
}
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.page = 1;
|
||||
},
|
||||
methods: {
|
||||
loadIssues(clearIssues = false) {
|
||||
if (!this.showAddIssuesModal) return false;
|
||||
|
||||
return gl.boardService
|
||||
.getBacklog(
|
||||
queryData(this.filter.path, {
|
||||
page: this.page,
|
||||
per: this.perPage,
|
||||
}),
|
||||
)
|
||||
.then(res => res.data)
|
||||
.then(data => {
|
||||
if (clearIssues) {
|
||||
this.issues = [];
|
||||
}
|
||||
|
||||
data.issues.forEach(issueObj => {
|
||||
const issue = new ListIssue(issueObj);
|
||||
const foundSelectedIssue = ModalStore.findSelectedIssue(issue);
|
||||
issue.selected = !!foundSelectedIssue;
|
||||
|
||||
this.issues.push(issue);
|
||||
});
|
||||
|
||||
this.loadingNewPage = false;
|
||||
|
||||
if (!this.issuesCount) {
|
||||
this.issuesCount = data.size;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
// TODO: handle request error
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div
|
||||
v-if="showAddIssuesModal"
|
||||
class="add-issues-modal">
|
||||
<div class="add-issues-container">
|
||||
<modal-header
|
||||
:project-id="projectId"
|
||||
:milestone-path="milestonePath"
|
||||
:label-path="labelPath"
|
||||
/>
|
||||
<modal-list
|
||||
v-if="!loading && showList && !filterLoading"
|
||||
:issue-link-base="issueLinkBase"
|
||||
:root-path="rootPath"
|
||||
:empty-state-svg="emptyStateSvg"
|
||||
/>
|
||||
<empty-state
|
||||
v-if="showEmptyState"
|
||||
:new-issue-path="newIssuePath"
|
||||
:empty-state-svg="emptyStateSvg"
|
||||
/>
|
||||
<section
|
||||
v-if="loading || filterLoading"
|
||||
class="add-issues-list text-center"
|
||||
>
|
||||
<div class="add-issues-list-loading">
|
||||
<loading-icon />
|
||||
</div>
|
||||
</section>
|
||||
<modal-footer/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
|
@ -1,159 +0,0 @@
|
|||
import Vue from 'vue';
|
||||
import bp from '../../../breakpoints';
|
||||
import ModalStore from '../../stores/modal_store';
|
||||
|
||||
gl.issueBoards.ModalList = Vue.extend({
|
||||
props: {
|
||||
issueLinkBase: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
rootPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
emptyStateSvg: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return ModalStore.store;
|
||||
},
|
||||
watch: {
|
||||
activeTab() {
|
||||
if (this.activeTab === 'all') {
|
||||
ModalStore.purgeUnselectedIssues();
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
loopIssues() {
|
||||
if (this.activeTab === 'all') {
|
||||
return this.issues;
|
||||
}
|
||||
|
||||
return this.selectedIssues;
|
||||
},
|
||||
groupedIssues() {
|
||||
const groups = [];
|
||||
this.loopIssues.forEach((issue, i) => {
|
||||
const index = i % this.columns;
|
||||
|
||||
if (!groups[index]) {
|
||||
groups.push([]);
|
||||
}
|
||||
|
||||
groups[index].push(issue);
|
||||
});
|
||||
|
||||
return groups;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
scrollHandler() {
|
||||
const currentPage = Math.floor(this.issues.length / this.perPage);
|
||||
|
||||
if (
|
||||
this.scrollTop() > this.scrollHeight() - 100 &&
|
||||
!this.loadingNewPage &&
|
||||
currentPage === this.page
|
||||
) {
|
||||
this.loadingNewPage = true;
|
||||
this.page += 1;
|
||||
}
|
||||
},
|
||||
toggleIssue(e, issue) {
|
||||
if (e.target.tagName !== 'A') {
|
||||
ModalStore.toggleIssue(issue);
|
||||
}
|
||||
},
|
||||
listHeight() {
|
||||
return this.$refs.list.getBoundingClientRect().height;
|
||||
},
|
||||
scrollHeight() {
|
||||
return this.$refs.list.scrollHeight;
|
||||
},
|
||||
scrollTop() {
|
||||
return this.$refs.list.scrollTop + this.listHeight();
|
||||
},
|
||||
showIssue(issue) {
|
||||
if (this.activeTab === 'all') return true;
|
||||
|
||||
const index = ModalStore.selectedIssueIndex(issue);
|
||||
|
||||
return index !== -1;
|
||||
},
|
||||
setColumnCount() {
|
||||
const breakpoint = bp.getBreakpointSize();
|
||||
|
||||
if (breakpoint === 'lg' || breakpoint === 'md') {
|
||||
this.columns = 3;
|
||||
} else if (breakpoint === 'sm') {
|
||||
this.columns = 2;
|
||||
} else {
|
||||
this.columns = 1;
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.scrollHandlerWrapper = this.scrollHandler.bind(this);
|
||||
this.setColumnCountWrapper = this.setColumnCount.bind(this);
|
||||
this.setColumnCount();
|
||||
|
||||
this.$refs.list.addEventListener('scroll', this.scrollHandlerWrapper);
|
||||
window.addEventListener('resize', this.setColumnCountWrapper);
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.$refs.list.removeEventListener('scroll', this.scrollHandlerWrapper);
|
||||
window.removeEventListener('resize', this.setColumnCountWrapper);
|
||||
},
|
||||
components: {
|
||||
'issue-card-inner': gl.issueBoards.IssueCardInner,
|
||||
},
|
||||
template: `
|
||||
<section
|
||||
class="add-issues-list add-issues-list-columns"
|
||||
ref="list">
|
||||
<div
|
||||
class="empty-state add-issues-empty-state-filter text-center"
|
||||
v-if="issuesCount > 0 && issues.length === 0">
|
||||
<div
|
||||
class="svg-content">
|
||||
<img :src="emptyStateSvg"/>
|
||||
</div>
|
||||
<div class="text-content">
|
||||
<h4>
|
||||
There are no issues to show.
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-for="group in groupedIssues"
|
||||
class="add-issues-list-column">
|
||||
<div
|
||||
v-for="issue in group"
|
||||
v-if="showIssue(issue)"
|
||||
class="board-card-parent">
|
||||
<div
|
||||
class="board-card"
|
||||
:class="{ 'is-active': issue.selected }"
|
||||
@click="toggleIssue($event, issue)">
|
||||
<issue-card-inner
|
||||
:issue="issue"
|
||||
:issue-link-base="issueLinkBase"
|
||||
:root-path="rootPath">
|
||||
</issue-card-inner>
|
||||
<span
|
||||
:aria-label="'Issue #' + issue.id + ' selected'"
|
||||
aria-checked="true"
|
||||
v-if="issue.selected"
|
||||
class="issue-card-selected text-center">
|
||||
<i class="fa fa-check"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
`,
|
||||
});
|
162
app/assets/javascripts/boards/components/modal/list.vue
Normal file
|
@ -0,0 +1,162 @@
|
|||
<script>
|
||||
import bp from '../../../breakpoints';
|
||||
import ModalStore from '../../stores/modal_store';
|
||||
import IssueCardInner from '../issue_card_inner.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
IssueCardInner,
|
||||
},
|
||||
props: {
|
||||
issueLinkBase: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
rootPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
emptyStateSvg: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return ModalStore.store;
|
||||
},
|
||||
computed: {
|
||||
loopIssues() {
|
||||
if (this.activeTab === 'all') {
|
||||
return this.issues;
|
||||
}
|
||||
|
||||
return this.selectedIssues;
|
||||
},
|
||||
groupedIssues() {
|
||||
const groups = [];
|
||||
this.loopIssues.forEach((issue, i) => {
|
||||
const index = i % this.columns;
|
||||
|
||||
if (!groups[index]) {
|
||||
groups.push([]);
|
||||
}
|
||||
|
||||
groups[index].push(issue);
|
||||
});
|
||||
|
||||
return groups;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
activeTab() {
|
||||
if (this.activeTab === 'all') {
|
||||
ModalStore.purgeUnselectedIssues();
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.scrollHandlerWrapper = this.scrollHandler.bind(this);
|
||||
this.setColumnCountWrapper = this.setColumnCount.bind(this);
|
||||
this.setColumnCount();
|
||||
|
||||
this.$refs.list.addEventListener('scroll', this.scrollHandlerWrapper);
|
||||
window.addEventListener('resize', this.setColumnCountWrapper);
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.$refs.list.removeEventListener('scroll', this.scrollHandlerWrapper);
|
||||
window.removeEventListener('resize', this.setColumnCountWrapper);
|
||||
},
|
||||
methods: {
|
||||
scrollHandler() {
|
||||
const currentPage = Math.floor(this.issues.length / this.perPage);
|
||||
|
||||
if (
|
||||
this.scrollTop() > this.scrollHeight() - 100 &&
|
||||
!this.loadingNewPage &&
|
||||
currentPage === this.page
|
||||
) {
|
||||
this.loadingNewPage = true;
|
||||
this.page += 1;
|
||||
}
|
||||
},
|
||||
toggleIssue(e, issue) {
|
||||
if (e.target.tagName !== 'A') {
|
||||
ModalStore.toggleIssue(issue);
|
||||
}
|
||||
},
|
||||
listHeight() {
|
||||
return this.$refs.list.getBoundingClientRect().height;
|
||||
},
|
||||
scrollHeight() {
|
||||
return this.$refs.list.scrollHeight;
|
||||
},
|
||||
scrollTop() {
|
||||
return this.$refs.list.scrollTop + this.listHeight();
|
||||
},
|
||||
showIssue(issue) {
|
||||
if (this.activeTab === 'all') return true;
|
||||
|
||||
const index = ModalStore.selectedIssueIndex(issue);
|
||||
|
||||
return index !== -1;
|
||||
},
|
||||
setColumnCount() {
|
||||
const breakpoint = bp.getBreakpointSize();
|
||||
|
||||
if (breakpoint === 'lg' || breakpoint === 'md') {
|
||||
this.columns = 3;
|
||||
} else if (breakpoint === 'sm') {
|
||||
this.columns = 2;
|
||||
} else {
|
||||
this.columns = 1;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<section
|
||||
ref="list"
|
||||
class="add-issues-list add-issues-list-columns">
|
||||
<div
|
||||
v-if="issuesCount > 0 && issues.length === 0"
|
||||
class="empty-state add-issues-empty-state-filter text-center">
|
||||
<div
|
||||
class="svg-content">
|
||||
<img :src="emptyStateSvg" />
|
||||
</div>
|
||||
<div class="text-content">
|
||||
<h4>
|
||||
There are no issues to show.
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-for="(group, index) in groupedIssues"
|
||||
:key="index"
|
||||
class="add-issues-list-column">
|
||||
<div
|
||||
v-for="issue in group"
|
||||
v-if="showIssue(issue)"
|
||||
:key="issue.id"
|
||||
class="board-card-parent">
|
||||
<div
|
||||
:class="{ 'is-active': issue.selected }"
|
||||
class="board-card"
|
||||
@click="toggleIssue($event, issue)">
|
||||
<issue-card-inner
|
||||
:issue="issue"
|
||||
:issue-link-base="issueLinkBase"
|
||||
:root-path="rootPath"/>
|
||||
<span
|
||||
v-if="issue.selected"
|
||||
:aria-label="'Issue #' + issue.id + ' selected'"
|
||||
aria-checked="true"
|
||||
class="issue-card-selected text-center">
|
||||
<i class="fa fa-check"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
|
@ -1,54 +0,0 @@
|
|||
import Vue from 'vue';
|
||||
import ModalStore from '../../stores/modal_store';
|
||||
|
||||
gl.issueBoards.ModalFooterListsDropdown = Vue.extend({
|
||||
data() {
|
||||
return {
|
||||
modal: ModalStore.store,
|
||||
state: gl.issueBoards.BoardsStore.state,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
selected() {
|
||||
return this.modal.selectedList || this.state.lists[1];
|
||||
},
|
||||
},
|
||||
destroyed() {
|
||||
this.modal.selectedList = null;
|
||||
},
|
||||
template: `
|
||||
<div class="dropdown inline">
|
||||
<button
|
||||
class="dropdown-menu-toggle"
|
||||
type="button"
|
||||
data-toggle="dropdown"
|
||||
aria-expanded="false">
|
||||
<span
|
||||
class="dropdown-label-box"
|
||||
:style="{ backgroundColor: selected.label.color }">
|
||||
</span>
|
||||
{{ selected.title }}
|
||||
<i class="fa fa-chevron-down"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-selectable dropdown-menu-drop-up">
|
||||
<ul>
|
||||
<li
|
||||
v-for="list in state.lists"
|
||||
v-if="list.type == 'label'">
|
||||
<a
|
||||
href="#"
|
||||
role="button"
|
||||
:class="{ 'is-active': list.id == selected.id }"
|
||||
@click.prevent="modal.selectedList = list">
|
||||
<span
|
||||
class="dropdown-label-box"
|
||||
:style="{ backgroundColor: list.label.color }">
|
||||
</span>
|
||||
{{ list.title }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
});
|
|
@ -0,0 +1,56 @@
|
|||
<script>
|
||||
import ModalStore from '../../stores/modal_store';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
modal: ModalStore.store,
|
||||
state: gl.issueBoards.BoardsStore.state,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
selected() {
|
||||
return this.modal.selectedList || this.state.lists[1];
|
||||
},
|
||||
},
|
||||
destroyed() {
|
||||
this.modal.selectedList = null;
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div class="dropdown inline">
|
||||
<button
|
||||
class="dropdown-menu-toggle"
|
||||
type="button"
|
||||
data-toggle="dropdown"
|
||||
aria-expanded="false">
|
||||
<span
|
||||
:style="{ backgroundColor: selected.label.color }"
|
||||
class="dropdown-label-box">
|
||||
</span>
|
||||
{{ selected.title }}
|
||||
<i class="fa fa-chevron-down"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-selectable dropdown-menu-drop-up">
|
||||
<ul>
|
||||
<li
|
||||
v-for="(list, i) in state.lists"
|
||||
v-if="list.type == 'label'"
|
||||
:key="i">
|
||||
<a
|
||||
:class="{ 'is-active': list.id == selected.id }"
|
||||
href="#"
|
||||
role="button"
|
||||
@click.prevent="modal.selectedList = list">
|
||||
<span
|
||||
:style="{ backgroundColor: list.label.color }"
|
||||
class="dropdown-label-box">
|
||||
</span>
|
||||
{{ list.title }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
|
@ -1,46 +0,0 @@
|
|||
import Vue from 'vue';
|
||||
import ModalStore from '../../stores/modal_store';
|
||||
import modalMixin from '../../mixins/modal_mixins';
|
||||
|
||||
gl.issueBoards.ModalTabs = Vue.extend({
|
||||
mixins: [modalMixin],
|
||||
data() {
|
||||
return ModalStore.store;
|
||||
},
|
||||
computed: {
|
||||
selectedCount() {
|
||||
return ModalStore.selectedCount();
|
||||
},
|
||||
},
|
||||
destroyed() {
|
||||
this.activeTab = 'all';
|
||||
},
|
||||
template: `
|
||||
<div class="top-area prepend-top-10 append-bottom-10">
|
||||
<ul class="nav-links issues-state-filters">
|
||||
<li :class="{ 'active': activeTab == 'all' }">
|
||||
<a
|
||||
href="#"
|
||||
role="button"
|
||||
@click.prevent="changeTab('all')">
|
||||
Open issues
|
||||
<span class="badge badge-pill">
|
||||
{{ issuesCount }}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li :class="{ 'active': activeTab == 'selected' }">
|
||||
<a
|
||||
href="#"
|
||||
role="button"
|
||||
@click.prevent="changeTab('selected')">
|
||||
Selected issues
|
||||
<span class="badge badge-pill">
|
||||
{{ selectedCount }}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
`,
|
||||
});
|
49
app/assets/javascripts/boards/components/modal/tabs.vue
Normal file
|
@ -0,0 +1,49 @@
|
|||
<script>
|
||||
import ModalStore from '../../stores/modal_store';
|
||||
import modalMixin from '../../mixins/modal_mixins';
|
||||
|
||||
export default {
|
||||
mixins: [modalMixin],
|
||||
data() {
|
||||
return ModalStore.store;
|
||||
},
|
||||
computed: {
|
||||
selectedCount() {
|
||||
return ModalStore.selectedCount();
|
||||
},
|
||||
},
|
||||
destroyed() {
|
||||
this.activeTab = 'all';
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div class="top-area prepend-top-10 append-bottom-10">
|
||||
<ul class="nav-links issues-state-filters">
|
||||
<li :class="{ 'active': activeTab == 'all' }">
|
||||
<a
|
||||
href="#"
|
||||
role="button"
|
||||
@click.prevent="changeTab('all')"
|
||||
>
|
||||
Open issues
|
||||
<span class="badge badge-pill">
|
||||
{{ issuesCount }}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li :class="{ 'active': activeTab == 'selected' }">
|
||||
<a
|
||||
href="#"
|
||||
role="button"
|
||||
@click.prevent="changeTab('selected')"
|
||||
>
|
||||
Selected issues
|
||||
<span class="badge badge-pill">
|
||||
{{ selectedCount }}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
|
@ -1,4 +1,4 @@
|
|||
/* eslint-disable func-names, no-new, space-before-function-paren, one-var, promise/catch-or-return, max-len */
|
||||
/* eslint-disable func-names, no-new, promise/catch-or-return */
|
||||
|
||||
import $ from 'jquery';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
|
@ -56,6 +56,7 @@ gl.issueBoards.newListDropdownInit = () => {
|
|||
filterable: true,
|
||||
selectable: true,
|
||||
multiSelect: true,
|
||||
containerSelector: '.js-tab-container-labels .dropdown-page-one .dropdown-content',
|
||||
clicked (options) {
|
||||
const { e } = options;
|
||||
const label = options.selectedObj;
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
import Vue from 'vue';
|
||||
import Flash from '../../../flash';
|
||||
import { __ } from '../../../locale';
|
||||
|
||||
const Store = gl.issueBoards.BoardsStore;
|
||||
|
||||
window.gl = window.gl || {};
|
||||
window.gl.issueBoards = window.gl.issueBoards || {};
|
||||
|
||||
gl.issueBoards.RemoveIssueBtn = Vue.extend({
|
||||
props: {
|
||||
issue: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
list: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
updateUrl() {
|
||||
return this.issue.path;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
removeIssue() {
|
||||
const issue = this.issue;
|
||||
const lists = issue.getLists();
|
||||
const listLabelIds = lists.map(list => list.label.id);
|
||||
|
||||
let labelIds = issue.labels
|
||||
.map(label => label.id)
|
||||
.filter(id => !listLabelIds.includes(id));
|
||||
if (labelIds.length === 0) {
|
||||
labelIds = [''];
|
||||
}
|
||||
|
||||
const data = {
|
||||
issue: {
|
||||
label_ids: labelIds,
|
||||
},
|
||||
};
|
||||
|
||||
// Post the remove data
|
||||
Vue.http.patch(this.updateUrl, data).catch(() => {
|
||||
Flash(__('Failed to remove issue from board, please try again.'));
|
||||
|
||||
lists.forEach((list) => {
|
||||
list.addIssue(issue);
|
||||
});
|
||||
});
|
||||
|
||||
// Remove from the frontend store
|
||||
lists.forEach((list) => {
|
||||
list.removeIssue(issue);
|
||||
});
|
||||
|
||||
Store.detail.issue = {};
|
||||
},
|
||||
},
|
||||
template: `
|
||||
<div
|
||||
class="block list">
|
||||
<button
|
||||
class="btn btn-default btn-block"
|
||||
type="button"
|
||||
@click="removeIssue">
|
||||
Remove from board
|
||||
</button>
|
||||
</div>
|
||||
`,
|
||||
});
|
|
@ -0,0 +1,72 @@
|
|||
<script>
|
||||
import Vue from 'vue';
|
||||
import Flash from '../../../flash';
|
||||
import { __ } from '../../../locale';
|
||||
|
||||
const Store = gl.issueBoards.BoardsStore;
|
||||
|
||||
export default {
|
||||
props: {
|
||||
issue: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
list: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
updateUrl() {
|
||||
return this.issue.path;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
removeIssue() {
|
||||
const { issue } = this;
|
||||
const lists = issue.getLists();
|
||||
const listLabelIds = lists.map(list => list.label.id);
|
||||
|
||||
let labelIds = issue.labels.map(label => label.id).filter(id => !listLabelIds.includes(id));
|
||||
if (labelIds.length === 0) {
|
||||
labelIds = [''];
|
||||
}
|
||||
|
||||
const data = {
|
||||
issue: {
|
||||
label_ids: labelIds,
|
||||
},
|
||||
};
|
||||
|
||||
// Post the remove data
|
||||
Vue.http.patch(this.updateUrl, data).catch(() => {
|
||||
Flash(__('Failed to remove issue from board, please try again.'));
|
||||
|
||||
lists.forEach(list => {
|
||||
list.addIssue(issue);
|
||||
});
|
||||
});
|
||||
|
||||
// Remove from the frontend store
|
||||
lists.forEach(list => {
|
||||
list.removeIssue(issue);
|
||||
});
|
||||
|
||||
Store.detail.issue = {};
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div
|
||||
class="block list"
|
||||
>
|
||||
<button
|
||||
class="btn btn-default btn-block"
|
||||
type="button"
|
||||
@click="removeIssue"
|
||||
>
|
||||
Remove from board
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
import FilteredSearchContainer from '../filtered_search/container';
|
||||
import FilteredSearchManager from '../filtered_search/filtered_search_manager';
|
||||
|
||||
|
|