diff --git a/.flayignore b/.flayignore index b63ce4c4df098840f37c14fce9addf006e7c470e..147a1344c30433ccf8c908a075c9d4ee463b62c7 100644 --- a/.flayignore +++ b/.flayignore @@ -1,6 +1,11 @@ *.erb lib/gitlab/sanitizers/svg/whitelist.rb lib/gitlab/diff/position_tracer.rb +app/controllers/projects/approver_groups_controller.rb +app/controllers/projects/approvers_controller.rb +app/controllers/projects/protected_branches/merge_access_levels_controller.rb +app/controllers/projects/protected_branches/push_access_levels_controller.rb +app/controllers/projects/protected_tags/create_access_levels_controller.rb app/policies/project_policy.rb app/models/concerns/relative_positioning.rb app/workers/stuck_merge_jobs_worker.rb diff --git a/.gitignore b/.gitignore index 3baf640a9c36d017cc22fdf0f59185c6de6db3a9..437fb53a5b7b16389457718aadb700733fda7ced 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ eslint-report.html /backups/* /config/aws.yml /config/database.yml +/config/database_geo.yml /config/gitlab.yml /config/gitlab_ci.yml /config/initializers/rack_attack.rb diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index dadce0733095242695b97482944ddf1e6fc01b98..b27004aba68208f84312c41763f5ee943cb2a99f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -18,6 +18,7 @@ image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-golang-1.8-git variables: MYSQL_ALLOW_EMPTY_PASSWORD: "1" + ELASTIC_URL: "http://elastic:changeme@docker.elastic.co-elasticsearch-elasticsearch:9200" RAILS_ENV: "test" NODE_ENV: "test" SIMPLECOV: "true" @@ -28,6 +29,8 @@ variables: KNAPSACK_RSPEC_SUITE_REPORT_PATH: knapsack/${CI_PROJECT_NAME}/rspec_report-master.json KNAPSACK_SPINACH_SUITE_REPORT_PATH: knapsack/${CI_PROJECT_NAME}/spinach_report-master.json FLAKY_RSPEC_SUITE_REPORT_PATH: rspec_flaky/${CI_PROJECT_NAME}/report-master.json + # This hack is needed to make ES not that memory hungry + ES_JAVA_OPTS: "-Xms256m -Xmx256m" before_script: - bundle --version @@ -64,11 +67,14 @@ stages: services: - postgres:9.2 - redis:alpine + - docker.elastic.co/elasticsearch/elasticsearch:5.5.2 .use-mysql: &use-mysql services: - mysql:latest - redis:alpine + - docker.elastic.co/elasticsearch/elasticsearch:5.5.2 + # Skip all jobs except the ones that begin with 'docs/'. # Used for commits including ONLY documentation changes. @@ -162,6 +168,7 @@ build-package: variables: SETUP_DB: "false" USE_BUNDLE_INSTALL: "false" + EE_PACKAGE: "true" stage: build cache: {} when: manual @@ -408,26 +415,6 @@ downtime_check: - /^[\d-]+-stable(-ee)?$/ - /(^docs[\/-].*|.*-docs$)/ -ee_compat_check: - <<: *rake-exec - only: - - branches@gitlab-org/gitlab-ce - except: - - master - - tags - - /^[\d-]+-stable(-ee)?/ - allow_failure: yes - cache: - key: "ee_compat_check_repo" - paths: - - ee_compat_check/ee-repo/ - artifacts: - name: "${CI_JOB_NAME}_${CI_COMIT_REF_NAME}_${CI_COMMIT_SHA}" - when: on_failure - expire_in: 10d - paths: - - ee_compat_check/patches/*.patch - # DB migration, rollback, and seed jobs .db-migrate-reset: &db-migrate-reset <<: *dedicated-runner @@ -452,7 +439,7 @@ db:migrate:reset-mysql: variables: SETUP_DB: "false" script: - - git fetch origin v8.14.10 + - git fetch origin v8.14.10-ee - git checkout -f FETCH_HEAD - bundle install $BUNDLE_INSTALL_FLAGS - cp config/gitlab.yml.example config/gitlab.yml diff --git a/.license_encryption_key.pub b/.license_encryption_key.pub new file mode 100644 index 0000000000000000000000000000000000000000..68f241b9741696e3c2e49ba5c9a1b0726fc2f727 --- /dev/null +++ b/.license_encryption_key.pub @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0Hxv3MkkZbMrKtIs6np9 +ccP4OwGBkNhIvhPjcQP48hbbascv5RqsOquQGrYSD2ZrE/kbkRdkIcoHEeTZLif+ +bDKFZFI7o5x0H92o9/GSvxHJhQ8mkmvwxD7lssGShwZEm8WG+U7BZqUV/gGmCDqe +9W8H8Fq2B0ck8IXjbQ4Zz+JlyV/NHZTZcs69plFiLKh4N6GYVftOVwSomh0bbypP +OB9WnLC7RC9a2LRrhtf8sqa2rRFmtyMMfgFFzLMzS+w+1K4+QLnWP1gKQVzaFnzk +pnwKPrqbGFYbRztIVEWbs8jPYlLkGb8ME4C84YVtQgbQcbyisU/VW3wUGkhT+J0k +xwIDAQAB +-----END PUBLIC KEY----- diff --git a/.rubocop.yml b/.rubocop.yml index 4640681379a88fcc59aab14b9c1dca933c318920..c90464ae3886becc4ea3b1a9a05bf19f57025c5f 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -22,6 +22,7 @@ AllCops: - 'node_modules/**/*' - 'db/*' - 'db/fixtures/**/*' + - 'db/geo/*' - 'tmp/**/*' - 'bin/**/*' - 'generator_templates/**/*' @@ -465,6 +466,7 @@ Style/MutableConstant: Exclude: - 'db/migrate/**/*' - 'db/post_migrate/**/*' + - 'db/geo/migrate/**/*' # Favor unless over if for negative conditions (or control flow or). Style/NegatedIf: @@ -1078,6 +1080,9 @@ RSpec/EmptyExampleGroup: Enabled: true CustomIncludeMethods: - run_permission_checks + - run_group_permission_checks + - it_should_email! + - it_should_not_email! # Checks for long example. RSpec/ExampleLength: diff --git a/CHANGELOG-EE.md b/CHANGELOG-EE.md new file mode 100644 index 0000000000000000000000000000000000000000..4fe9dc79cf7dc186d98ad2013e970ea38e90e825 --- /dev/null +++ b/CHANGELOG-EE.md @@ -0,0 +1,1829 @@ +Please view this file on the master branch, on stable branches it's out of date. + +## 10.0.7 (2017-12-07) + +- No changes. + +## 10.0.6 (2017-11-08) + +- [SECURITY] Geo JSON web tokens now expire after two minutes to reduce risk of compromise. + +## 10.0.5 (2017-11-03) + +- [FIXED] Find stuck scheduled import jobs and also mark them as failed. !3055 +- [FIXED] Fix removing the username from the git repository URL for pull mirroring. !3060 +- [FIXED] Fix base link for issues on group boards. +- [FIXED] Move group boards routes under - and remove "boards" from reserved paths. +- [FIXED] Geo: Fix attachments/avatars saving to the wrong directory. + +## 10.0.4 (2017-10-16) + +- [SECURITY] Prevent Related Issues from leaking confidential issues. !541 +- [SECURITY] Escape user name in filtered search bar. + +## 10.0.3 (2017-10-05) + +- [FIXED] Rewrite Geo database rake tasks so they operate on the correct database. !3052 +- [FIXED] Show group tab if member lock is enabled. +- [FIXED] File uploaders do not perform hard check, only soft check. +- [FIXED] Only show Turn on Service Desk button when user has permissions. +- [FIXED] Fix EE delta size check handling with annotated tags. + +## 10.0.2 (2017-09-27) + +- [FIXED] Send valid project path as name for Jira dev panel. +- [FIXED] Fix delta size check to handle commit or nil objects. + +## 10.0.1 (2017-09-23) + +- No changes. + +## 10.0.0 (2017-09-22) + +- [SECURITY] Check if LDAP users are in external groups on login. !2720 +- [FIXED] Fix typo for `required` attribute. !2659 +- [FIXED] Fix global code search when using negation queries. !2709 +- [FIXED] Fixes activation of project mirror when new project is created. !2756 +- [FIXED] Geo - Whitelist LFS requests to download objects on a secondary node. !2758 +- [FIXED] Fix Geo::RepositorySyncWorker so attempts to sync all projects if some are failing. !2796 +- [FIXED] Fix unsetting credentials data for pull mirrors. !2810 +- [FIXED] Geo: Gracefully catch incorrect db key on primary. !2819 +- [FIXED] Fix a regression breaking projects with an empty import URL. !2824 +- [FIXED] Fix a 500 error in the SSH host keys lookup action. !2827 +- [FIXED] Handle Geo DB replication lag as 24h/day & 7d/week. !2833 +- [FIXED] Geo - Add a unique index on project_id to the Geo project_registry table. !2850 +- [FIXED] Improve Geo repository sync performance for larger databases. !2887 +- [FIXED] Ensure #route_setting is available before calling it. !2908 +- [FIXED] Fix searching by assignee in the service desk. !2969 +- [FIXED] Fix approvals before merge error while importing projects. +- [FIXED] Fix the gap in approvals in merge request widget. +- [FIXED] Fix branch name regex not saving in /admin/push_rule config. +- [FIXED] Fix merges not working when project is not licensed for squash. +- [CHANGED] Add Time estimate and Time spend fields in csv export. !2627 (g3dinua, LockiStrike) +- [CHANGED] Improve copy so users will set up SSH from DB for Geo. !2644 +- [CHANGED] Support `codequality` job name for Code Quality feature. !2704 +- [CHANGED] Support Elasticsearch v5.1 - v5.5. !2751 +- [CHANGED] Geo primary nodes no longer require SSH keys. !2861 +- [CHANGED] Show Geo event log and cursor data in node status page. +- [CHANGED] Use a logger for the artifacts migration rake task. +- [ADDED] LFS files can be stored in remote object storage such as S3. !2760 +- [ADDED] Add LDAP sync endpoint to Groups API. !2785 +- [ADDED] Geo - Log a repository created event when a project is created. !2807 +- [ADDED] Show geo.log in the Admin area. !2845 +- [ADDED] Commits integration with Jira development panel. +- [OTHER] Add missing indexes to geo_event_log table. !2836 +- [OTHER] Geo - Ignore S3-backed LFS objects on secondary nodes. !2889 +- Fix a bug searching private projects with Elasticsearch as an admin or auditor. !2613 +- Don't put the password in the SSH remote if using public-key authentication. !2837 +- Support handling of rename events in Geo Log Cursor. +- Update delete board button text color to red and fix hover color. +- Search for issues with multiple assignees. +- Fix: When MR approvals are disabled, but approvers were previously assigned, all approvers receive a notification on every MR. +- Add group issue boards. +- Ports style changes fixed in a conflict in ce to ee upstream to master for new projects page. + +## 9.5.3 (2017-09-03) + +- [FIXED] Check if table exists before loading the current license. !2783 +- [FIXED] Extend early adopters feature set. + +## 9.5.2 (2017-08-28) + +- [FIXED] Fix LDAP backwards-compatibility when using "method" or when "verify_certificates" is not defined. !2690 +- [FIXED] Geo - Count projects where wiki sync failed in node status page. + +## 9.5.1 (2017-08-23) + +- [FIXED] Fix url for object store artifacts. +- [CHANGED] Ensure all database queries are routed through the database load balancer when load balancing is enabled +. !2707 + +## 9.5.0 (2017-08-22) + +- [FIXED] Fix Copy to Clipboard for SSH Public Key on Pull Repository settings. !2692 +- [FIXED] Enable mirror repository button. +- [FIXED] Create system notes only if issue was successfully related. +- [FIXED] Fix issue boards focus button not being visible to guest users. +- Namespace license checks Audit Events & Admin Audit Log. !2326 +- Namespace license checks for Repository Mirrors. !2328 +- Automatically link kerberos users to LDAP people. !2405 +- Implement SSH public-key support for repository mirroring. !2423 +- Shows project names for commits in elasticsearch global search. !2434 +- Add admin application setting to allow group owners to manage LDAP. !2529 +- Geo - Selectively choose which namespaces to replicate in DR. !2533 +- Support variables on Trigger API for Cross-project pipeline. !2557 +- Allow excluding sidekiq queues from execution in sidekiq-cluster. !2571 +- Ensure artifacts are moved locally within the filesystem to prevent timeouts. !2572 +- Audit failed login events. !2587 +- Spread load across all nodes in an elasticsearch cluster. !2625 +- Improves handling of stuck imports. !2628 +- Improves handling of the mirror threshold. !2671 +- Allow artifacts access with job_token parameter or CI_JOB_TOKEN header. +- Add initial Groups/Billing and Profile/Billing routing and template. +- Fix rebase from fork when upstream has protected branches. +- Present Related Issues add badge only when user can manage related issues (previously when user could edit issue). +- clean up merge request widget UI. +- Make contextual sidebar collapsible. +- Fix accessing individual files on Object Storage. +- Fix rebase button when merge request is created from a fork. +- Skip oAuth authorization for trusted applications. + +## 9.4.5 (2017-08-14) + +- Ensure artifacts are moved locally within the filesystem to prevent timeouts. !2572 +- Fix rebase from fork when upstream has protected branches. +- Present Related Issues add badge only when user can manage related issues (previously when user could edit issue). +- Fix accessing individual files on Object Storage. + +## 9.4.4 (2017-08-09) + +- No changes. + +## 9.4.3 (2017-07-31) + +- Present Related Issues widget for logged-out users when available. + +## 9.4.2 (2017-07-28) + +- Adds lower bound to pull mirror scheduling feature. !2366 +- Add warning and option toggle when rebuilding authorized_keys. !2508 +- Fix CSS for mini graph with downstream pipeline. +- Renamed board to boards in new project sidebar. +- Fix Rebasing not working with Merge Requests. +- Fixed issue boards focus mode when new navigation is turned on. + +## 9.4.1 (2017-07-25) + +- Cleans up mirror capacity in project destroy service if project is a scheduled mirror. !2445 +- Fixes unscoping of imposed capacity limit by find_each method on Mirror scheduler. !2460 +- Remove text underline from suggested approvers. + +## 9.4.0 (2017-07-22) + +- GeoLogCursor is part of a new experimental Geo replication system. !1988 +- Add explicit licensing for Elasticsearch. !2108 +- Add namespace license checks for Service Desk (EEP). !2109 +- Add environment scope to secret variables to specify environments. !2112 +- Namespace license checks for exporting issues (EES). !2164 +- Retry Elasticsearch queries on failure. !2181 +- Introduce namespace license checks for rebase before merge. !2200 +- Geo: fix removal of repositories from disk on secondary nodes. !2210 +- Add license checks for brundown charts. !2219 +- Add namespace license checks for squash before merge. !2249 +- Namespace license checks for fast-forward merge (EES). !2272 +- Empty repository mirror no longer creates master branch with README automatically. !2276 +- Introduce namespace licensing for issue weights (EES). !2291 +- Add namespace license checks for Contribution Analytics. !2302 +- Add license checks for focus mode on the issue board. !2303 +- Add license checks for issue boards with milestones. !2315 +- Add license checks for multiple issue boards. !2317 +- Geo: Fix clone instructions in a secondary node for SSH protocol. !2319 +- Namespace license checks Issue & MR template. !2321 +- Introduce namespace license checks for merge request approvers (EES). !2324 +- Introduce namespace license checks for Push Rules (EES). !2335 +- Geo: Implement alternative to geo_{primary|secondary}_role in gitlab.yml. !2352 +- Geo: Added extra SystemCheck checks. !2354 +- Implement progressive elasticsearch indexing for project mirrors. !2393 +- Fix undefined method quote when database load balancing is used. !2430 +- Improve the performance of the project list API. !12679 +- fix approver placeholder icon in ie11. +- Add public API for listing, creating and deleting Related Issues. +- All artifacts are now browsable. +- Escape symbols in exported CSV columns to prevent command execution in Microsoft Excel. +- Geo - Fix RepositorySyncService when cannot obtain a lease to sync a repository. +- Prevent mirror user to be assigned to users other than the current one. +- Geo - Makes the projects synchronization faster on secondaries nodes. +- Only show the LDAP sync banner on first login. +- Enable service desk be default. +- Fix creation of push rules via POST API. +- Fix Geo middleware to work properly with multiple requests. +- [GitLab.com only] Add Slack applicationq service. +- Speed up checking for approvers when approvers are specified on the MR. +- Allows manually adding bi-directional relationships between issues in the issue page (EES feature). +- Add Geo repository renamed event log. +- Merge states to allow realtime with deploy boards. +- Fix 500 error when approvals are enabled and editing an MR conflicts with another edit. +- add toggle for overriding approvers per MR. +- Add optional sha param when approving a merge request through the API. +- Allow updating shared_runners_minutes_limit on admin Namespace API. +- Allow to Store Artifacts on Object Storage. +- Adding support for AWS ec2 instance profile credentials with elasticsearch. (Matt Gresko) +- Fixed edit issue boards milestone action buttons not sticking to bottom of dropdown. +- Respect the external user setting in Elasticsearch. + +## 9.3.10 (2017-08-09) + +- No changes. + +## 9.3.9 (2017-07-20) + +- No changes. + +## 9.3.8 (2017-07-19) + +- Escape symbols in exported CSV columns to prevent command execution in Microsoft Excel. +- Prevent mirror user to be assigned to users other than the current one. + +## 9.3.7 (2017-07-18) + +- No changes. + +## 9.3.6 (2017-07-12) + +- Geo: Fix clone instructions in a secondary node for SSH protocol. !2319 +- Implement progressive elasticsearch indexing for project mirrors. !2393 + +## 9.3.5 (2017-07-05) + +- Make admin mirror application setting Gitlab.com exclusive. !2307 +- Make Geo::RepositorySyncService force create a repo. + +## 9.3.4 (2017-07-03) + +- Update gitlab-shell to 5.1.1 to fix Post Recieve errors + +## 9.3.3 (2017-06-30) + +- Add metrics to both remote and non remote mirroring. !2118 +- Forces import worker with mirror to insert mirror in front of queue. !2231 +- Fix locked and stale SSH keys file from 9.3.0 upgrade. !2240 +- Fix crash in LDAP sync when user was removed. !2289 +- allow rebase for unapproved merge requests. +- Geo - Fix path_with_namespace for instances of Geo::DeletedProject. + +## 9.3.2 (2017-06-27) + +- Fix GitLab check: Problem with Elastic Search. !2278 + +## 9.3.1 (2017-06-26) + +- Geo: fix removal of repositories from disk on secondary nodes. !2210 +- Fix Geo middleware to work properly with multiple requests. + +## 9.3.0 (2017-06-22) + +- Per user/group access levels for Protected Tags. !1629 +- Add a user's memberships when logging in through LDAP. !1819 +- Add server-wide Audit Log admin screen. !1852 +- Move pull mirroring to adaptive scheduling. !1853 +- Create a push rule to check the branch name. !1896 (Riccardo Padovani) +- Add shared_runners_minutes_limit to groups and users API. !1942 +- Compare codeclimate artifacts on the merge request page. !1984 +- Lookup users by email in LDAP if lookup by DN fails during sync. !2003 +- Update mirror_user for project when mirror_user is deleted. !2013 (Athar Hameed) +- Geo: persist clone url prefix in the database. !2015 +- Geo: prevent Gitlab::Git::Repository::NoRepository from stucking replication. !2115 +- Geo: fixed Dynamic Backoff strategy that was not being used by workers. !2128 +- [Elasticsearch] Improve code search for camel case. +- Fixed header being over issue boards when in focus mode. +- Fix: Approvals not reset if changing target branch. +- Fix bug where files over 2 GB would not be saved in Geo tracking DB. +- Add primary node clone URL to Geo secondary 'How to work faster with Geo' popover. +- Fix broken time sync leeway with Geo. +- Gracefully handle case when Geo secondary does not have the right db_key_base. +- Use the current node configuration to populate suggested new URL for Geo node. +- Check if a merge request is approved when merging from API or slash command. +- Add closed_at field to issue CSV export. +- Geo - Properly set tracking database connection and cron jobs on secondary nodes. +- Add push events to Geo event log. +- fix Rebase being disabled for unapproved MRs. +- Fix approvers dropdown when creating a merge request from a fork. +- Add relation between Pipelines. +- Allow to Trigger Pipeline using CI Job Token. +- Allow to view Personal pipelines quota. +- Geo - Use GeoNode#clone_url_prefix for the Geo::RepositorySyncService. +- Elasticsearch searches through the project description. +- Fix: /unassign by default unassigns everyone. Implement /reassign command. +- Speed up checking for approvers remaining. + +## 9.2.10 (2017-08-09) + +- No changes. + +## 9.2.9 (2017-07-20) + +- No changes. + +## 9.2.8 (2017-07-19) + +- Escape symbols in exported CSV columns to prevent command execution in Microsoft Excel. +- Prevent mirror user to be assigned to users other than the current one. + +## 9.2.7 (2017-06-21) + +- Geo: fixed Dynamic Backoff strategy that was not being used by workers. !2128 +- fix Rebase being disabled for unapproved MRs. + +## 9.2.6 (2017-06-16) + +- Geo: backported fix from 9.3 for big repository sync issues. !2000 +- Geo - Properly set tracking database connection and cron jobs on secondary nodes. +- Fix approvers dropdown when creating a merge request from a fork. +- Fixed header being over issue boards when in focus mode. +- Fix bug where files over 2 GB would not be saved in Geo tracking DB. + +## 9.2.5 (2017-06-07) + +- No changes. + +## 9.2.4 (2017-06-02) + +- No changes. +- No changes. + +## 9.2.3 (2017-05-31) + +- No changes. +- No changes. +- Respect the external user setting in Elasticsearch. + +## 9.2.2 (2017-05-25) + +- No changes. + +## 9.2.1 (2017-05-23) + +- No changes. + +## 9.2.0 (2017-05-22) + +- Stop using sidekiq cron for push mirrors. !1616 +- Inline RSS button with Export Issues button for mobile. !1637 +- Highlight Contribution Analytics tab under groups when active, remove sub-nav items. !1677 +- Uses etag polling for deployboards. !1713 +- Support more elasticsearch versions. !1716 +- Support advanced search queries using elasticsearch. !1770 +- Remove superfluous wording on push rules. !1811 +- Geo - Fix signing out from secondary node when "Remember me" option is checked. !1903 +- Add global wiki search using Elasticsearch. +- Remove warning about protecting Service Desk email from form. +- Geo: Resync repositories that have been updated recently. +- Respect project features when searching alternative branches with elasticsearch enabled. +- Backfill projects where the last attempt to backfill failed. +- Fix MR approvals sentence when all approvers need to approve the MR. +- Fix for XSS in project mirror errors caused by Hamlit filter usage. +- Feature availability check using feature list AND license addons. +- Disable mirror workers for Geo secondaries. + +## 9.1.10 (2017-08-09) + +- No changes. + +## 9.1.9 (2017-07-20) + +- No changes. + +## 9.1.8 (2017-07-19) + +- Escape symbols in exported CSV columns to prevent command execution in Microsoft Excel. +- Prevent mirror user to be assigned to users other than the current one. + +## 9.1.7 (2017-06-07) + +- No changes. + +## 9.1.6 (2017-06-02) + +- No changes. + +## 9.1.5 (2017-05-31) + +- Respect the external user setting in Elasticsearch. + +## 9.1.4 (2017-05-12) + +- Remove warning about protecting Service Desk email from form. +- Backfill projects where the last attempt to backfill failed. + +## 9.1.3 (2017-05-05) + +- No changes. +- No changes. +- No changes. +- Respect project features when searching alternative branches with elasticsearch enabled. +- Fix for XSS in project mirror errors caused by Hamlit filter usage. + +## 9.1.2 (2017-05-01) + +- No changes. +- No changes. +- No changes. +- Fix commit search on some elasticsearch indexes. !1745 +- Fix emailing issues to projects when Service Desk is enabled. +- Fix bug where Geo secondary Sidekiq cron jobs would not be activated if settings changed. + +## 9.1.1 (2017-04-26) + +- No changes. + +## 9.1.0 (2017-04-22) + +- Fix rake gitlab:env:info elasticsearch datum. !1422 +- Fix 500 errors caused by elasticsearch results referencing garbage-collected commits. !1430 +- Adds timeout option to push mirrors. !1439 +- elasticsearch: Add support for an experimental repository indexer. !1483 +- Update color palette to a more harmonious and consistent one. !1500 +- Cache Gitlab::Geo queries. !1507 +- Add Service Desk feature. !1508 +- Fix pre-receive hooks when using Git 2.11 or later. !1525 +- Geo: Add support to sync avatars and attachments. !1562 +- Fix Elasticsearch not working when URL ends with a forward slash. !1566 +- Allow admins to perform global searches with Elasticsearch. !1578 +- Periodically persists users activity to users.last_activity_on. !1597 +- Removes duplicate count of LFS objects from repository_and_lfs_size method. !1599 +- Fix searching notes and snippets as an auditor. !1674 +- Fix searching for notes with elasticsearch when a user is a member of many projects. !1675 +- Fix type declarations for spend/estimate values. +- Speed up suggested approvers on MR creation. +- Fix squashing MRs when the repository contains a ref named HEAD. +- Fix approver count reset when editing assignee or labels. +- Geo: handle git failures on GeoRepositoryFetchWorker. +- Give each elasticsearch worker its own sidekiq queue. +- Fixes broken link to pipeline quota. +- Prevent filtering issues by multiple Milestones or Authors. +- Fix 500 error when selecting a mirror user. +- Add index to approvals.merge_request_id. +- Added mock data for Deployboard. +- Add uuid to usage ping. +- Expose board project and milestone on boards API. +- Fix active user count to ignore internal users. +- Add warning when burndown data is not accurate. +- Check if incoming emails and email key are available for service desk. +- Add burndown chart to milestones. +- Make deployboard to be visible by default. +- Add a Rake task to make the current node the primary Geo node. +- Return 404 instead of a 500 error on API status endpoint if Geo tracking DB is not enabled. +- Remove N+1 queries for Groups::AnalyticsController. +- Show user cohorts data when usage ping is enabled. +- Visualise Canary Deployments. + +## 9.0.13 (2017-08-09) + +- No changes. + +## 9.0.12 (2017-07-20) + +- No changes. + +## 9.0.11 (2017-07-19) + +- Escape symbols in exported CSV columns to prevent command execution in Microsoft Excel. +- Prevent mirror user to be assigned to users other than the current one. + +## 9.0.10 (2017-06-07) + +- No changes. + +## 9.0.9 (2017-06-02) + +- No changes. + +## 9.0.8 (2017-05-31) + +- Respect the external user setting in Elasticsearch. + +## 9.0.7 (2017-05-05) + +- Respect project features when searching alternative branches with elasticsearch enabled. +- Fix for XSS in project mirror errors caused by Hamlit filter usage. + +## 9.0.6 (2017-04-21) + +- Cache Gitlab::Geo queries. !1507 +- Fix searching for notes with elasticsearch when a user is a member of many projects. !1675 +- Fix 500 error when selecting a mirror user. +- Fix active user count to ignore internal users. + +## 9.0.5 (2017-04-10) + +- Return 404 instead of a 500 error on API status endpoint if Geo tracking DB is not enabled. + +## 9.0.4 (2017-04-05) + +- No changes. + +## 9.0.3 (2017-04-05) + +- Allow to edit pipelines quota for user. +- Fixed label resetting when sorting by weight. (James Clark) +- Fixed issue boards milestone toggle text not updating when filtering. +- Fixed mirror user dropdown not displaying. + +## 9.0.2 (2017-03-29) + +- No changes. + +## 9.0.1 (2017-03-28) + +- No changes. + +## 9.0.0 (2017-03-22) + +- Geo: Replicate repository creation in Geo secondary node. !952 +- Make approval system notes lowercase. !1125 +- Issues can be exported as CSV, via email. !1126 +- Try to update mirrors again after 15 minutes if the previous update failed. !1183 +- Adds abitlity to render deploy boards in the frontend side. !1233 +- Add filtered search to MR page. !1243 +- Update project list API returns with approvals_before_merge attribute. !1245 (Geoff Webster) +- Catch Net::LDAP::DN exceptions in EE::Gitlab::LDAP::Group. !1260 +- API: Use `post ":id/#{type}/:subscribable_id/subscribe"` to subscribe and `post ":id/#{type}/:subscribable_id/unsubscribe"` to unsubscribe from a resource. !1274 (Robert Schilling) +- API: Remove deprecated fields Notes#upvotes and Notes#downvotes. !1275 (Robert Schilling) +- Deploy board backend. !1278 +- API: Remove the ProjectGitHook API. !1301 (Robert Schilling) +- Expose elasticsearch client params for AWS signing and HTTPS. !1305 (Matt Gresko) +- Fix LDAP DN case-mismatch bug in LDAP group sync. !1337 +- Remove es6 file extension from JavaScript files. !1344 (winniehell) +- Geo: Don't load dependent models when fetching an existing GeoNode from the database. !1348 +- Parallelise the gitlab:elastic:index_database Rake task. !1361 +- Robustify reading attributes for elasticsearch. !1365 +- Introduce one additional thread into bin/elastic_repo_indexer. !1372 +- Show hook errors for fast-forward merges. !1375 +- Allow all parameters of group webhooks to be set through the UI. !1376 +- Fix Elasticsearch queries when a group_id is specified. !1423 +- Check the right index mapping based on Rails environment for rake gitlab:elastic:add_feature_visiblity_levels_to_project. !1473 +- Fix issues with another milestone that has a matching list label could not be added to a board. +- Only admins or group owners can set LDAP overrides. +- Add support for load balancing database queries. +- Only replace non-approval mr-widget-footer on getMergeStatus. +- Remove repository_storage from V4 "/application/settings" settings API. +- Added headers to protected branches access dropdowns. +- Remove support for Git Annex. +- Repositioned multiple issue boards selector. +- Added back weight in issue rows on issue list. +- Add basic support for GitLab Geo file transfers over HTTP. +- Added weight slash command. +- Set deployment status invalid when the environments does not match a k8s label. +- Combined deploy keys, push rules, protect branches and mirror repository settings options into a single one called Repository. +- Rebase - fix commiter email & name. +- Adds a EE specific dev favicon. +- Elastic security fix: Respect feature visibility level. +- Update Elasticsearch to 5.1. +- [Elasticsearch] More efficient search. +- Get Geo secondaries nodes statuses over AJAX. + +## 8.17.8 (2017-08-09) + +- No changes. + +## 8.17.7 (2017-07-19) + +- Prevent mirror user to be assigned to users other than the current one. + +## 8.17.6 (2017-05-05) + +- Respect project features when searching alternative branches with elasticsearch enabled. + +## 8.17.5 (2017-04-05) + +- No changes. + +## 8.17.4 (2017-03-19) + +- Elastic security fix: Respect feature visibility level. + +## 8.17.3 (2017-03-07) + +- No changes. + +## 8.17.2 (2017-03-01) + +- No changes. + +## 8.17.1 (2017-02-28) + +- Fix admin email notification recipient group select list. +- Add repository_storage field back to projects API for admin users. +- Don't try to update a project's external service caches on a secondary Geo node. +- Fixed merge request state not updating when approvals feature is active. +- Improve error messages when squashing fails. + +## 8.17.0 (2017-02-22) + +- Read-only "auditor" user role. !998 +- Also reset approvals on push when merge request is closed. !1051 +- Copy commit SHA to clipboard. !1066 +- Pull EE specific Gitlab::Auth code in to its own module. !1112 +- Geo: Added `gitlab:geo:check` and improved `gitlab:envinfo` rake tasks. !1120 +- Geo: send the new event type with the backfill function. !1157 +- Re-add removed params from projects and issues V3 API. !1209 +- Add configurable minimum mirror sync time in admin section. !1217 +- Move RepositoryUpdateRemoteMirrorWorker jobs to project_mirror Sidekiq queue. !1234 +- Change Builds word to Pipelines in Mirror settings page. +- Fix bundle tag in anaytics page. +- Support v4 API for GitLab Geo endpoints. +- Fixed merge request environment link not displaying. +- Reduce queries needed to check if node is a primary or secondary Geo node. +- Allow squashing merge requests into a single commit. + +## 8.16.9 (2017-04-05) + +- No changes. + +## 8.16.8 (2017-03-19) + +- No changes. +- No changes. +- No changes. +- Elastic security fix: Respect feature visibility level. + +## 8.16.7 (2017-02-27) + +- Fixed merge request state not updating when approvals feature is active. + +## 8.16.6 (2017-02-17) + +- Geo: send the new event type with the backfill function. !1157 +- Move RepositoryUpdateRemoteMirrorWorker jobs to project_mirror Sidekiq queue. !1234 +- Fixed merge request environment link not displaying. +- Reduce queries needed to check if node is a primary or secondary Geo node. +- Read true-up info from license and validate it. !1159 + +## 8.16.5 (2017-02-14) + +- No changes. + +## 8.16.4 (2017-02-02) + +- Disable all merge acceptance buttons pending MR approval. + +## 8.16.3 (2017-01-27) + +- Fix sidekiq cluster mishandling of queue names. !1117 + +## 8.16.2 (2017-01-25) + +- Track Mattermost usage in usage ping. !1071 +- Fix count of required approvals displayed on MR edit form. !1082 +- Fix updating approvals count when editing an MR. !1106 +- Don't try to show assignee in approved_merge_request_email if there's no assignee. + +## 8.16.1 (2017-01-23) + +- No changes. + +## 8.16.0 (2017-01-22) + +- Allow to limit shared runners minutes quota for group. !965 +- About GitLab link in sidebar that links to help page. !1008 +- Prevent 500 error when uploading/entering a blank license. !1016 +- Add more push rules to the API. !1022 (Robert Schilling) +- Expose issue weight in the API. !1023 (Robert Schilling) +- Copy to clipboard. !1048 + +## 8.15.8 (2017-03-19) + +- No changes. +- No changes. +- Elastic security fix: Respect feature visibility level. + +## 8.15.7 (2017-02-15) + +- No changes. + +## 8.15.6 (2017-02-14) + +- No changes. + +## 8.15.5 (2017-01-20) + +- No changes. + +## 8.15.4 (2017-01-09) + +- No changes. + +## 8.15.3 (2017-01-06) + +- Disable LDAP permission override in project members edit list. +- Perform only one fetch per push on Geo secondary nodes. + +## 8.15.2 (2016-12-27) + +- No changes. +- Fix ES search for non-default branches. + +## 8.15.1 (2016-12-23) + +- Fix 404/500 error while navigating to the 'show/destroy' pages. !993 + +## 8.15.0 (2016-12-22) + +- Adds a check ensure only active, ie. non-blocked users can be emailed from the admin panel. +- Add user activities API. +- Add milestone total weight to the milestone summary. +- Allow master/owner to change permission levels when LDAP group sync is enabled. !822 +- Geo: Improve project view UI to teach users how to clone from a secondary Geo node and push to a primary. !905 +- Technical debt follow-up from restricting pushes / merges by group. !927 +- Geo: Enables nodes to be removed even without proper license. !978 +- Update validates_hostname to 1.0.6 to fix a bug in parsing hexadecimal-looking domain names. !982 + +## 8.14.10 (2017-02-15) + +- No changes. + +## 8.14.9 (2017-02-14) + +- No changes. + +## 8.14.8 (2017-01-25) + +- No changes. + +## 8.14.7 (2017-01-21) + +- No changes. + +## 8.14.6 (2017-01-10) + +- No changes. + +## 8.14.5 (2016-12-14) + +- Add milestone total weight to the milestone summary. + +## 8.14.4 (2016-12-08) + +- No changes. + +## 8.14.3 (2016-12-02) + +- No changes. + +## 8.14.2 (2016-12-01) + +- No changes. + +## 8.14.1 (2016-11-28) + +- Fix: MergeRequestSerializer breaks on MergeRequest#rebase_dir_path when source_project doesn't exist anymore. + +## 8.14.0 (2016-11-22) + +- Added Backfill service for Geo. !861 +- Fix for autosuggested approvers(https://gitlab.com/gitlab-org/gitlab-ee/issues/1273). +- Gracefully recover from previously failed rebase. +- Disable retries for remote mirror update worker. !848 +- Fix Approvals API documentation. +- Add ability to set approvals_before_merge for project through the API. +- gitlab:check rake task checks ES version according to requirements +- Convert ASCII-8BIT LDAP DNs to UTF-8 to avoid unnecessary user deletions +- [Fix] Only owner can see "Projects" button in group edit menu + +## 8.13.12 (2017-01-21) + +- No changes. + +## 8.13.11 (2017-01-10) + +- No changes. + +## 8.13.10 (2016-12-14) + +- No changes. + +## 8.13.9 (2016-12-08) + +- No changes. + +## 8.13.8 (2016-12-02) + +- No changes. + +## 8.13.7 (2016-11-28) + +- No changes. + +## 8.13.6 (2016-11-17) + +- Disable retries for remote mirror update worker. !848 +- Fixed cache clearing on secondary Geo nodes. !869 +- Geo: fix a problem that prevented git cloning from secondary node. !873 + +## 8.13.5 (2016-11-08) + +- No changes + +## 8.13.4 (2016-11-07) + +- Weight dropdown in issue filter form does not stay selected. !826 + +## 8.13.3 (2016-11-02) + +- No changes + +## 8.13.2 (2016-10-31) + +- Don't pass a current user to Member#add_user in LDAP group sync. !830 + +## 8.13.1 (2016-10-25) + +- Hide multiple board actions if user doesnt have permissions. !816 +- Fix Elasticsearch::Transport::Transport::Errors::BadRequest when ES is enabled. !818 + +## 8.13.0 (2016-10-22) + +- Cache the last usage data to avoid unicorn timeouts +- Add user activity table and service to query for active users +- Fix 500 error updating mirror URLs for projects +- Restrict protected branch access to specific groups !645 +- Fix validations related to mirroring settings form. !773 +- Add multiple issue boards. !782 +- Fix Git access panel for Wikis when Kerberos authentication is enabled (Borja Aparicio) +- Decrease maximum time that GitLab waits for a mirror to finish !791 (Borja Aparicio) +- User groups (that can be assigned as approvers) +- Fix a search for non-default branches when ES is enabled +- Re-organized the Sidekiq queues for EE specific workers + +## 8.12.12 (2016-12-08) + +- No changes. + +## 8.12.11 (2016-12-02) + +- No changes. + +## 8.12.10 (2016-11-28) + +- No changes. + +## 8.12.9 (2016-11-07) + +- No changes + +## 8.12.8 (2016-11-02) + +- No changes + +## 8.12.7 + + - No EE-specific changes + +## 8.12.6 + + - No EE-specific changes + +## 8.12.5 + + - No EE-specific changes + +## 8.12.4 + + - [ES] Indexer works with smaller batches of repositories to not exceed NOFILE limit. !774 + +## 8.12.3 + + - Fix prevent_secrets checkbox on admin view + +## 8.12.2 + + - Fix bug when protecting a branch due to missing url paramenter in request !760 + - Ignore unknown project ID in RepositoryUpdateMirrorWorker + +## 8.12.1 + + - Prevent secrets to be pushed to the repository + - Prevent secrets to be pushed to the repository + +## 8.12.0 (2016-09-22) + + - Include more data in EE usage ping + - Reduce UPDATE queries when moving between import states on projects + - [ES] Instrument Elasticsearch::Git::Repository + - Request only the LDAP attributes we need + - Add 'Sync now' to group members page !704 + - Add repository size limits and enforce them !740 + - [ES] Instrument other Gitlab::Elastic classes + - [ES] Fix: Elasticsearch does not find partial matches in project names + - Faster Active Directory group membership resolution !719 + - [ES] Global code search + - [ES] Improve logging + - Fix projects with remote mirrors asynchronously destruction + +## 8.11.11 (2016-11-07) + +- No changes + +## 8.11.10 (2016-11-02) + +- No changes + +## 8.11.9 + + - No EE-specific changes + +## 8.11.8 + + - No EE-specific changes + +## 8.11.7 + + - Refactor Protected Branches dropdown. !687 + - Fix mirrored projects allowing empty import urls. !700 + +## 8.11.6 + + - Exclude blocked users from potential MR approvers. + +## 8.11.5 + + - API: Restore backward-compatibility for POST /projects/:id/members when membership is locked + +## 8.11.4 + + - No EE-specific changes + +## 8.11.3 + + - [ES] Add logging to indexer + - Fix missing EE-specific service parameters for Jenkins CI + - Set the correct `GL_PROTOCOL` when rebasing !691 + - [ES] Elasticsearch workers checks ES settings before running + +## 8.11.2 + + - Additional documentation on protected branches for EE + - Change slash commands docs location + +## 8.11.1 + + - Pulled due to packaging error. + +## 8.11.0 (2016-08-22) + + - Allow projects to be moved between repository storages + - Add rake task to remove old repository copies from repositories moved to another storage + - Performance improvement of push rules + - Temporary fix for #825 - LDAP sync converts access requests to members. !655 + - Optimize commit and diff changes access check to reduce git operations + - Allow syncing a group against all providers at once + - Change LdapGroupSyncWorker to use new LDAP group sync classes + - Allow LDAP `sync_ssh_keys` setting to be set to `true` + - Removed unused GitLab GEO database index + - Restrict protected branch access to specific users !581 + - Enable monitoring for ES classes + - [Elastic] Improve code search + - [Elastic] Significant improvement of global search performance + - [Fix] Push rules check existing commits in some cases + - [ES] Limit amount of retries for sidekiq jobs + - Fix Projects::UpdateMirrorService to allow tags pointing to blob objects + +## 8.10.12 + + - No EE-specific changes + +## 8.10.11 + + - No EE-specific changes + +## 8.10.10 + + - No EE-specific changes + +## 8.10.9 + + - Exclude blocked users from potential MR approvers. + +## 8.10.8 + + - No EE-specific changes + +## 8.10.7 + + - No EE-specific changes + +## 8.10.6 + + - Fix race condition with UpdateMirrorWorker Lease. !641 + +## 8.10.5 + + - Used cached value of project count in `Elastic::RepositoriesSearch` to reduce DB load. !637 + +## 8.10.4 + + - Fix available users in userselect dropdown when there is more than one userselect on the page. !604 (Rik de Groot) + - Fix updating skipped approvers in search list on removal. !604 (Rik de Groot) + +## 8.10.3 + + - Fix regression in Git Annex permission check. !599 + - [Elastic] Fix commit search for some URLs. !605 + - [Elastic][Fix] Commit search breaks for some URLs on gitlab-ce project + +## 8.10.2 + + - Fix pagination on search result page when ES search is enabled. !592 + - Decouple an ES index update from `RepositoryUpdateMirrorWorker`. !593 + - Fix broken `user_allowed?` check in Git Annex push. !597 + +## 8.10.1 + + - No EE-specific changes + +## 8.10.0 (2016-07-22) + + - Add EE license usage ping !557 + - Rename Git Hooks to Push Rules + - Fix EE keys fingerprint add index migration if came from CE + - Add todos for MR approvers !547 + - Replace LDAP group sync exclusive lease with state machine + - Prevent the author of an MR from being on the approvers list + - Isolate EE LDAP library code in EE module (Part 1) !511 + - Make Elasticsearch indexer run as an async task + - Fix of removing wiki data from index when project is deleted + - Ticket-based Kerberos authentication (SPNEGO) + - [Elastic] Suppress ActiveRecord::RecordNotFound error in ElasticIndexWorker + +## 8.9.10 + + - No EE-specific changes + +## 8.9.9 + + - No EE-specific changes + +## 8.9.8 + + - No EE-specific changes + +## 8.9.7 + + - No EE-specific changes + +## 8.9.6 + + - Avoid adding index for key fingerprint if it already exists. !539 + +## 8.9.5 + + - Fix of quoted text in lock tooltip. !518 + +## 8.9.4 + + - Improve how File Lock feature works with nested items. !497 + +## 8.9.3 + + - Fix encrypted data backwards compatibility after upgrading attr_encrypted gem. !502 + - Fix creating MRs on forks of deleted projects. !503 + - Roll back Grack::Auth to fix Git HTTP SPNEGO. !504 + +## 8.9.2 + + - [Elastic] Fix visibility of snippets when searching. + +## 8.9.1 + + - Improve Geo documentation. !431 + - Fix remote mirror stuck on started issue. !491 + - Fix MR creation from forks where target project has approvals enabled. !496 + - Fix MR edit where target project has approvals enabled. !496 + - Fix vertical alignment of git-hooks page. !499 + +## 8.9.0 (2016-06-22) + + - Fix JenkinsService test button + - Fix nil user handling in UpdateMirrorService + - Allow overriding the number of approvers for a merge request + - Allow LDAP to mark users as external based on their group membership. !432 + - Instrument instance methods of Gitlab::InsecureKeyFingerprint class + - Add API endpoint for Merge Request Approvals !449 + - Send notification email when merge request is approved + - Distribute RepositoryUpdateMirror jobs in time and add exclusive lease on them by project_id + - [Elastic] Move ES settings to application settings + - Always allow merging a merge request whenever fast-forward is possible. !454 + - Disable mirror flag for projects without import_url + - UpdateMirror service return an error status when no mirror + - Don't reset approvals when rebasing an MR from the UI + - Show flash notice when Git Hooks are updated successfully + - Remove explicit Gitlab::Metrics.action assignments, are already automatic. + - [Elastic] Project members with guest role can't access confidential issues + - Ability to lock file or folder in the repository + - Fix: Git hooks don't fire when committing from the UI + +## 8.8.9 + + - No EE-specific changes + +## 8.8.8 + + - No EE-specific changes + +## 8.8.7 + + - No EE-specific changes + +## 8.8.6 + + - [Elastic] Fix visibility of snippets when searching. + +## 8.8.5 + + - Make sure OAuth routes that we generate for Geo matches with the ones in Rails routes !444 + +## 8.8.4 + + - Remove license overusage message + +## 8.8.3 + + - Add standard web hook headers to Jenkins CI post. !374 + - Gracefully handle malformed DNs in LDAP group sync. !392 + - Reduce load on DB for license upgrade check. !421 + - Make it clear the license overusage message is visible only to admins. !423 + - Fix Git hook validations for fast-forward merges. !427 + - [Elastic] In search results, only show notes on confidential issues that the user has access to. + +## 8.8.2 + + - Fix repository mirror updates for new imports stuck in started + - [Elastic] Search through the filenames. !409 + - Fix repository mirror updates for new imports stuck in "started" state. !416 + +## 8.8.1 + + - No EE-specific changes + +## 8.8.0 (2016-05-22) + + - [Elastic] Database indexer prints its status + - [Elastic][Fix] Database indexer skips projects with invalid HEAD reference + - Fix skipping pages when restoring backups + - Add EE license via API !400 + - [Elastic] More efficient snippets search + - [Elastic] Add rake task for removing all indexes + - [Elastic] Add rake task for clearing indexing status + - [Elastic] Improve code search + - [Elastic] Fix encoding issues during indexing + - Warn admin if current active count exceeds license + - [Elastic] Search through the filenames + - Set KRB5 as default clone protocol when Kerberos is enabled and user is logged in (Borja Aparicio) + - Add support for Admin Groups to SAML + - Reduce emails-on-push HTML size by using a simple monospace font + - API requests to /internal/authorized_keys are now tagged properly + - Geo: Single Sign Out support !380 + +## 8.7.9 + + - No EE-specific changes + +## 8.7.8 + + - [Elastic] Fix visibility of snippets when searching. + +## 8.7.7 + + - No EE-specific changes + +## 8.7.6 + + - Bump GitLab Pages to 0.2.4 to fix Content-Type for predefined 404 + +## 8.7.5 + + - No EE-specific changes + +## 8.7.4 + + - Delete ProjectImportData record only if Project is not a mirror !370 + - Fixed typo in GitLab GEO license check alert !379 + - Fix LDAP access level spillover bug !499 + +## 8.7.3 + + - No EE-specific changes + +## 8.7.2 + + - Fix MR notifications for slack and hipchat when approvals are fullfiled. !325 + - GitLab Geo: Merge requests on Secondary should not check mergeable status + +## 8.7.1 + + - No EE-specific changes + +## 8.7.0 (2016-04-22) + + - Update GitLab Pages to 0.2.1: support user-defined 404 pages + - Refactor group sync to pull access level logic to its own class. !306 + - [Elastic] Stabilize database indexer if database is inconsistent + - Add ability to sync to remote mirrors. !249 + - GitLab Geo: Many replication improvements and fixes !354 + +## 8.6.9 + + - No EE-specific changes + +## 8.6.8 + + - No EE-specific changes + +## 8.6.7 + + - No EE-specific changes + +## 8.6.6 + + - Concat AD group recursive member results with regular member results. !333 + - Fix LDAP group sync regression for groups with member value `uid=`. !335 + - Don't attempt to include too large diffs in e-mail-on-push messages (Stan Hu). !338 + +## 8.6.5 + + - No EE-specific changes + +## 8.6.4 + + - No EE-specific changes + +## 8.6.3 + + - Fix other cases where git hooks would fail due to old commits. !310 + - Exit ElasticIndexerWorker's job happily if record cannot be found. !311 + - Fix "Reload with full diff" button not working (Stan Hu). !313 + +## 8.6.2 + + - Fix old commits triggering git hooks on new branches branched off another branch. !281 + - Fix issue with deleted user in audit event (Stan Hu). !284 + - Mark pending todos as done when approving a merge request. !292 + - GitLab Geo: Display Attachments from Primary node. !302 + +## 8.6.1 + + - Only rename the `light_logo` column in the `appearances` table if its not there yet. !290 + - Fix diffs in text part of email-on-push messages (Stan Hu). !293 + - Fix an issue with methods not accessible in some controllers. !295 + - Ensure Projects::ApproversController inherits from Projects::ApplicationController. !296 + +## 8.6.0 (2016-03-22) + + - Handle duplicate appearances table creation issue with upgrade from CE to EE + - Add confidential issues + - Improve weight filter for issues + - Update settings and documentation for per-install LDAP sync time + - Fire merge request webhooks when a merge request is approved + - Add full diff highlighting to Email on push + - Clear "stuck" mirror updates before periodically updating all mirrors + - LDAP: Don't render Linked LDAP groups forms when LDAP is disabled + - [Elastic] Add elastic checker to gitlab:check + - [Elastic] Added UPDATE_INDEX option to rake task + - [Elastic] Removing repository and wiki index after removing project + - [Elastic] Update index on push to wiki + - [Elastic] Use subprocesses for ElasticSearch index jobs + - [Elastic] More accurate as_indexed_json (More stable database indexer) + - [Elastic] Fix: Don't index newly created system messages and awards + - [Elastic] Fixed exception on branch removing + - [Elastic] Fix bin/elastic_repo_indexer to follow config + - GitLab Geo: OAuth authentication + - GitLab Geo: Wiki synchronization + - GitLab Geo: ReadOnly Middleware improvements + - GitLab Geo: SSH Keys synchronization + - Allow SSL verification to be configurable when importing GitHub projects + - Disable git-hooks for git annex commits + +## 8.5.13 + + - No EE-specific changes + +## 8.5.12 + + - No EE-specific changes + +## 8.5.11 + + - Fix vulnerability that made it possible to enumerate private projects belonging to group + +## 8.5.10 + + - No EE-specific changes + +## 8.5.9 + + - No EE-specific changes + +## 8.5.8 + + - GitLab Geo: Documentation + +## 8.5.7 + + - No EE-specific changes + +## 8.5.6 + + - No EE-specific changes + +## 8.5.5 + + - GitLab Geo: Repository synchronization between primary and secondary nodes + - Add documentation for GitLab Pages + - Fix importing projects from GitHub Enterprise Edition + - Fix syntax error in init file + - Only show group member roles if explicitly requested + - GitLab Geo: Improve GeoNodes Admin screen + - GitLab Geo: Avoid locking yourself out when adding a GeoNode + +## 8.5.4 + + - [Elastic][Security] Notes exposure + +## 8.5.3 + + - Prevent LDAP from downgrading a group's last owner + - Update gitlab-elastic-search gem to 0.0.11 + +## 8.5.2 + + - Update LDAP groups asynchronously + - Fix an issue when weight text was displayed in Issuable collapsed sidebar +## 8.5.2 + + - Fix importing projects from GitHub Enterprise Edition. + +## 8.5.1 + + - Fix adding pages domain to projects in groups + +## 8.5.0 (2016-02-22) + + - Fix Elasticsearch blob results linking to the wrong reference ID (Stan Hu) + - Show warning when mirror repository default branch could not be updated because it has diverged from upstream. + - More reliable wiki indexer + - GitLab Pages gets support for custom domain and custom certificate + - Fix of Elastic indexer. It should not trigger record validation for projects + - Fix of Elastic indexer. Stabilze indexer when serialized data is corrupted + - [Elastic] Don't index unnecessary data into elastic + +## 8.4.11 + + - No EE-specific changes + +## 8.4.10 + + - No EE-specific changes + +## 8.4.9 + + - Fix vulnerability that made it possible to enumerate private projects belonging to group + +## 8.4.8 + + - No EE-specific changes + +## 8.4.7 + + - No EE-specific changes + +## 8.4.6 + + - No EE-specific changes + +## 8.4.5 + + - Update LDAP groups asynchronously + +## 8.4.4 + + - Re-introduce "Send email to users" link in Admin area + - Fix category values for Jenkins and JenkinsDeprecated services + - Fix Elasticsearch indexing for newly added snippets + - Make Elasticsearch indexer more stable + - Update gitlab-elasticsearch-git to 0.0.10 which contain a few important fixes + +## 8.4.3 + + - Elasticsearch: fix partial blob indexing on push + - Elasticsearch: added advanced indexer for repositories + - Fix Mirror User dropdown + +## 8.4.2 + + - Elasticsearch indexer performance improvements + - Don't redirect away from Mirror Repository settings when repo is empty + - Fix updating of branches in mirrored repository + - Fix a 500 error preventing LDAP users with 2FA enabled from logging in + - Rake task gitlab:elastic:index_repositories handles errors and shows progress + - Partial indexing of repo on push (indexing changes only) + +## 8.4.1 + + - No EE-specific changes + +## 8.4.0 (2016-01-22) + + - Add ability to create a note for user by admin + - Fix "Commit was rejected by git hook", when max_file_size was set null in project's Git hooks + - Fix "Approvals are not reset after a new push is made if the request is coming from a fork" + - Fix "User is not automatically removed from suggested approvers list if user is deleted" + - Add option to enforce a semi-linear history by only allowing merge requests to be merged that have been rebased + - Add option to trigger builds when branches or tags are updated from a mirrored upstream repository + - Ability to use Elasticsearch as a search engine + +## 8.3.10 + + - No EE-specific changes + +## 8.3.9 + + - No EE-specific changes + +## 8.3.8 + + - Fix vulnerability that made it possible to enumerate private projects belonging to group + +## 8.3.7 + + - No EE-specific changes + +## 8.3.6 + + - No EE-specific changes + +## 8.3.5 + + - No EE-specific changes + +## 8.3.4 + + - No EE-specific changes + +## 8.3.3 + + - Fix undefined method call in Jenkins integration service + +## 8.3.2 + + - No EE-specific changes + +## 8.3.1 + + - Rename "Group Statistics" to "Contribution Analytics" + +## 8.3.0 (2015-12-22) + + - License information can now be retrieved via the API + - Show Kerberos clone url when Kerberos enabled and url different than HTTP url (Borja Aparicio) + - Fix bug with negative approvals required + - Add group contribution analytics page + - Add GitLab Pages + - Add group contribution statistics page + - Automatically import Kerberos identities from Active Directory when Kerberos is enabled (Alex Lossent) + - Canonicalization of Kerberos identities to always include realm (Alex Lossent) + +## 8.2.6 + + - No EE-specific changes + +## 8.2.5 + + - No EE-specific changes + +## 8.2.4 + + - No EE-specific changes + +## 8.2.3 + + - No EE-specific changes + +## 8.2.2 + + - Fix 404 in redirection after removing a project (Stan Hu) + - Ensure cached application settings are refreshed at startup (Stan Hu) + - Fix Error 500 when viewing user's personal projects from admin page (Stan Hu) + - Fix: Raw private snippets access workflow + - Prevent "413 Request entity too large" errors when pushing large files with LFS + - Ensure GitLab fires custom update hooks after commit via UI + +## 8.2.1 + + - Forcefully update builds that didn't want to update with state machine + - Fix: saving GitLabCiService as Admin Template + +## 8.2.0 (2015-11-22) + + - Invalidate stored jira password if the endpoint URL is changed + - Fix: Page is not reloaded periodically to check if rebase is finished + - When someone as marked as a required approver for a merge request, an email should be sent + - Allow configuring the Jira API path (Alex Lossent) + - Fix "Rebase onto master" + - Ensure a comment is properly recorded in JIRA when a merge request is accepted + - Allow groups to appear in the `Share with group` share if the group owner allows it + - Add option to mirror an upstream repository. + +## 8.1.4 + + - Fix bug in JIRA integration which prevented merge requests from being accepted when using issue closing pattern + +## 8.1.3 + + - Fix "Rebase onto master" + +## 8.1.2 + + - Prevent a 500 error related to the JIRA external issue tracker service + +## 8.1.1 + + - Removed, see 8.1.2 + +## 8.1.0 (2015-10-22) + + - Add documentation for "Share project with group" API call + - Added an issues template (Hannes Rosenögger) + - Add documentation for "Share project with group" API call + - Ability to disable 'Share with Group' feature (via UI and API) + +## 8.0.6 + + - No EE-specific changes + +## 8.0.5 + + - "Multi-project" and "Treat unstable builds as passing" parameters for + the Jenkins CI service are now correctly persisted. + - Correct the build URL when "Multi-project" is enabled for the Jenkins CI + service. + +## 8.0.4 + + - Fix multi-project setup for Jenkins + +## 8.0.3 + + - No EE-specific changes + +## 8.0.2 + + - No EE-specific changes + +## 8.0.1 + + - Correct gem dependency versions + - Re-add the "Help Text" feature that was inadvertently removed + +## 8.0.0 (2015-09-22) + + - Fix navigation issue when viewing Group Settings pages + - Guests and Reporters can approve merge request as well + - Add fast-forward merge option in project settings + - Separate rebase & fast-forward merge features + +## 7.14.3 + + - No changes + +## 7.14.2 + + - Fix the rebase before merge feature + +## 7.14.1 + + - Fix sign in form when just Kerberos is enabled + +## 7.14.0 (2015-08-22) + + - Disable adding, updating and removing members from a group that is synced with LDAP + - Don't send "Added to group" notifications when group is LDAP synched + - Fix importing projects from GitHub Enterprise Edition. + - Automatic approver suggestions (based on an authority of the code) + - Add support for Jenkins unstable status + - Automatic approver suggestions (based on an authority of the code) + - Support Kerberos ticket-based authentication for Git HTTP access + +## 7.13.3 + + - Merge community edition changes for version 7.13.3 + - Improved validation for an approver + - Don't resend admin email to everyone if one delivery fails + - Added migration for removing of invalid approvers + +## 7.13.2 + + - Fix group web hook + - Don't resend admin email to everyone if one delivery fails + +## 7.13.1 + + - Merge community edition changes for version 7.13.1 + - Fix: "Rebase before merge" doesn't work when source branch is in the same project + +## 7.13.0 (2015-07-22) + + - Fix git hook validation on initial push to master branch. + - Reset approvals on push + - Fix 500 error when the source project of an MR is deleted + - Ability to define merge request approvers + +## 7.12.2 + + - Fixed the alignment of project settings icons + +## 7.12.1 + + - No changes specific to EE + +## 7.12.0 (2015-06-22) + + - Fix error when viewing merge request with a commit that includes "Closes #". + - Enhance LDAP group synchronization to check also for member attributes that only contain "uid=" + - Enhance LDAP group synchronization to check also for submember attributes + - Prevent LDAP group sync from removing a group's last owner + - Add Git hook to validate maximum file size. + - Project setting: approve merge request by N users before accept + - Support automatic branch jobs created by Jenkins in CI Status + - Add API support for adding and removing LDAP group links + +## 7.11.4 + + - no changes specific to EE + +## 7.11.3 + + - Fixed an issue with git annex + +## 7.11.2 + + - Fixed license upload and verification mechanism + +## 7.11.0 (2015-05-22) + + - Skip git hooks commit validation when pushing new tag. + - Add Two-factor authentication (2FA) for LDAP logins + +## 7.10.1 + + - Check if comment exists in Jira before sending a reference + +## 7.10.0 (2015-04-22) + + - Improve UI for next pages: Group LDAP sync, Project git hooks, Project share with groups, Admin -> Appearance settigns + - Default git hooks for new projects + - Fix LDAP group links page by using new group members route. + - Skip email confirmation when updated via LDAP. + +## 7.9.0 (2015-03-22) + + - Strip prefixes and suffixes from synced SSH keys: + `SSHKey:ssh-rsa keykeykey` and `ssh-rsa keykeykey (SSH key)` will now work + - Check if LDAP admin group exists before querying for user membership + - Use one custom header logo for all GitLab themes in appearance settings + - Escape wildcards when searching LDAP by group name. + - Group level Web Hooks + - Don't allow project to be shared with the group it is already in. + +## 7.8.0 (2015-02-22) + + - Improved Jira issue closing integration + - Improved message logging for Jira integration + - Added option of referencing JIRA issues from GitLab + - Update Sidetiq to 0.6.3 + - Added Github Enterprise importer + - When project has MR rebase enabled, MR will have rebase checkbox selected by default + - Minor UI fixes for sidebar navigation + - Manage large binaries with git annex + +## 7.7.0 (2015-01-22) + + - Added custom header logo support (Drew Blessing) + - Fixed preview appearance bug + - Improve performance for selectboxes: project share page, admin email users page + +## 7.6.2 + + - Fix failing migrations for MySQL, LDAP + +## 7.6.1 + + - No changes + +## 7.6.0 (2014-12-22) + + - Added Audit events related to membership changes for groups and projects + - Added option to attempt a rebase before merging merge request + - Dont show LDAP groups settings if LDAP disabled + - Added member lock for groups to disallow membership additions on project level + - Rebase on merge request. Introduced merge request option to rebase before merging + - Better message for failed pushes because of git hooks + - Kerberos support for web interface and git HTTP + +## 7.5.3 + + - Only set up Sidetiq from a Sidekiq server process (fixes Redis::InheritedError) + +## 7.5.0 (2014-11-22) + + - Added an ability to check each author commit's email by regex + - Added an ability to restrict commit authors to existing Gitlab users + - Add an option for automatic daily LDAP user sync + - Added git hook for preventing tag removal to API + - Added git hook for setting commit message regex to API + - Added an ability to block commits with certain filenames by regex expression + - Improved a jenkins parser + +## 7.4.4 + + - Fix broken ldap migration + +## 7.4.0 (2014-10-22) + + - Support for multiple LDAP servers + - Skip AD specific LDAP checks + - Do not show ldap users in dropdowns for groups with enabled ldap-sync + - Update the JIRA integration documentation + - Reset the homepage to show the GitLab logo by deleting the custom logo. + +## 7.3.0 (2014-09-22) + + - Add an option to change the LDAP sync time from default 1 hour + - User will receive an email when unsubscribed from admin notifications + - Show group sharing members on /my/project/team + - Improve explanation of the LDAP permission reset + - Fix some navigation issues + - Added support for multiple LDAP groups per Gitlab group + +## 7.2.0 (2014-08-22) + + - Improve Redmine integration + - Better logging for the JIRA issue closing service + - Administrators can now send email to all users through the admin interface + - JIRA issue transition ID is now customizable + - LDAP group settings are now visible in admin group show page and group members page + +## 7.1.0 (2014-07-22) + + - Synchronize LDAP-enabled GitLab administrators with an LDAP group (Marvin Frick, sponsored by SinnerSchrader) + - Synchronize SSH keys with LDAP (Oleg Girko (Jolla) and Marvin Frick (SinnerSchrader)) + - Support Jenkins jobs with multiple modules (Marvin Frick, sponsored by SinnerSchrader) + +## 7.0.0 (2014-06-22) + + - Fix: empty brand images are displayed as empty image_tag on login page (Marvin Frick, sponsored by SinnerSchrader) + +## 6.9.4 + + - Fix bug in JIRA Issue closing triggered by commit messages + - Fix JIRA issue reference bug + +## 6.9.3 + + - Fix check CI status only when CI service is enabled(Daniel Aquino) + +## 6.9.2 + + - Merge community edition changes for version 6.9.2 + +## 6.9.1 + + - Merge community edition changes for version 6.9.1 + +## 6.9.0 (2014-05-22) + + - Add support for closing Jira tickets with commits and MR + - Template for Merge Request description can be added in project settings + - Jenkins CI service + - Fix LDAP email upper case bug + +## 6.8.0 (2014-04-22) + + - Customise sign-in page with custom text and logo + +## 6.7.1 + + - Handle LDAP errors in Adapter#dn_matches_filter? + +## 6.7.0 (2014-03-22) + + - Improve LDAP sign-in speed by reusing connections + - Add support for Active Directory nested LDAP groups + - Git hooks: Commit message regex + - Git hooks: Deny git tag removal + - Fix group edit in admin area + +## 6.6.0 (2014-02-22) + + - Permission reset button for LDAP groups + - Better performance with large numbers of users with access to one project + +## 6.5.0 (2014-01-22) + + - Add reset permissions button to Group#members page + +## 6.4.0 (2013-12-22) + + - Respect existing group permissions during sync with LDAP group (d3844662ec7ce816b0a85c8b40f66ee6c5ae90a1) + +## 6.3.0 (2013-11-22) + + - When looking up a user by DN, use single scope (bc8a875df1609728f1c7674abef46c01168a0d20) + - Try sAMAccountName if omniauth nickname is nil (9b7174c333fa07c44cc53b80459a115ef1856e38) + +## 6.2.0 (2013-10-22) + + - API: expose ldap_cn and ldap_access group attributes + - Use omniauth-ldap nickname attribute as GitLab username + - Improve group sharing UI for installation with many groups + - Fix empty LDAP group raises exception + - Respect LDAP user filter for git access diff --git a/CHANGELOG.md b/CHANGELOG.md index 4cedfa60b3efd00c45727d33e5d8cea880a3fccc..4f93c13d72d5550f1d20fef841287b934cba978c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,254 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. +## 10.0.7 (2017-12-07) + +### Security (5 changes) + +- Fix e-mail address disclosure through member search fields +- Prevent creating issues through API when user does not have permissions +- Prevent an information disclosure in the Groups API +- Fix user without access to private Wiki being able to see it on the project page +- Fix Cross-Site Scripting (XSS) vulnerability while editing a comment + + +## 10.0.6 (2017-11-08) + +- [SECURITY] Add X-Content-Type-Options header in API responses to make it more difficult to find other vulnerabilities. +- [SECURITY] Properly translate IP addresses written in decimal, octal, or other formats in SSRF protections in project imports. + +## 10.0.5 (2017-11-03) + +- [FIXED] Fix incorrect X-axis labels in Prometheus graphs. !14258 +- [FIXED] Fix `rake gitlab:incoming_email:check` and make it report the actual error. !14423 +- [FIXED] Does not check if an invariant hashed storage path exists on disk when renaming projects. !14428 +- [FIXED] Fix bottom spacing for dropdowns that open upwards. !14535 +- [FIXED] Fix the project import with issues and milestones. !14657 +- [FIXED] Fix broken Y-axis scaling in some Prometheus graphs. !14693 +- [FIXED] Fixed duplicate notifications when added multiple labels on an issue. !14798 +- [FIXED] Don't rename paths that were freed up when upgrading. !15029 +- [FIXED] Fixed issue/merge request breadcrumb titles not having links. +- [FIXED] Fix application setting to cache nil object. +- [FIXED] Fix missing Import/Export issue assignees. +- [FIXED] Allow boards as top level route. +- [FIXED] Fixed milestone breadcrumb links. +- [FIXED] Fixed merge request widget merged & closed date tooltip text. +- [FIXED] fix merge request widget status icon for failed CI. + +## 10.0.4 (2017-10-16) + +- [SECURITY] Move project repositories between namespaces when renaming users. +- [SECURITY] Prevent an open redirect on project pages. +- [SECURITY] Prevent a persistent XSS in user-provided markup. + +## 10.0.3 (2017-10-05) + +- [FIXED] find_user Users helper method no longer overrides find_user API helper method. !14418 +- [FIXED] Fix CSRF validation issue when closing/opening merge requests from the UI. !14555 +- [FIXED] Kubernetes integration: ensure v1.8.0 compatibility. !14635 +- [FIXED] Fixes data parameter not being sent in ajax request for jobs log. +- [FIXED] Improves UX of autodevops popover to match gpg one. +- [FIXED] Fixed commenting on side-by-side commit diff. +- [FIXED] Make sure API responds with 401 when invalid authentication info is provided. +- [FIXED] Fix merge request counter updates after merge. +- [FIXED] Fix gitlab-rake gitlab:import:repos task failing. +- [FIXED] Fix pushes to an empty repository not invalidating has_visible_content? cache. +- [FIXED] Ensure all refs are restored on a restore from backup. +- [FIXED] Gitaly RepositoryExists remains opt-in for all method calls. +- [FIXED] Fix 500 error on merged merge requests when GitLab is restored from a backup. +- [FIXED] Adjust MRs being stuck on "process of being merged" for more than 2 hours. + +## 10.0.2 (2017-09-27) + +- [FIXED] Notes will not show an empty bubble when the author isn't a member. !14450 +- [FIXED] Some checks in `rake gitlab:check` were failling with 'undefined method `run_command`'. !14469 +- [FIXED] Make locked setting of Runner to not affect jobs scheduling. !14483 +- [FIXED] Re-allow `name` attribute on user-provided anchor HTML. + +## 10.0.1 (2017-09-23) + +- [FIXED] Fix duplicate key errors in PostDeployMigrateUserExternalMailData migration. + +## 10.0.0 (2017-09-22) + +- [SECURITY] Upgrade brace-expansion NPM package due to security issue. !13665 (Markus Koller) +- [REMOVED] Remove CI API v1. +- [FIXED] Ensure correct visibility level options shown on all Project, Group, and Snippets forms. !13442 +- [FIXED] Fix the /projects/:id/repository/files/:file_path/raw endpoint to handle dots in the file_path. !13512 (mahcsig) +- [FIXED] Merge request reference in merge commit changed to full reference. !13518 (haseebeqx) +- [FIXED] Removes Sortable default scope. !13558 +- [FIXED] Wiki table of contents are now properly nested to reflect header level. !13650 (Akihiro Nakashima) +- [FIXED] Improve bare project import: Allow subgroups, take default visibility level into account. !13670 +- [FIXED] Fix group and project search for anonymous users. !13745 +- [FIXED] Fix searching for files by path. !13798 +- [FIXED] Fix division by zero error in blame age mapping. !13803 (Jeff Stubler) +- [FIXED] Fix incorrect date/time formatting on prometheus graphs. !13865 +- [FIXED] Changes the password change workflow for admins. !13901 +- [FIXED] API: Respect default group visibility when creating a group. !13903 (Robert Schilling) +- [FIXED] Unescape HTML characters in Wiki title. !13942 (Jacopo Beschi @jacopo-beschi) +- [FIXED] Make blob viewer for rich contents wider for mobile. !14011 (Takuya Noguchi) +- [FIXED] Fix typo in the API Deploy Keys documentation page. !14014 (Vitaliy @blackst0ne Klachkov) +- [FIXED] Hide admin link from default search results for non-admins. !14015 +- [FIXED] Fix problems sanitizing URLs with empty passwords. !14083 +- [FIXED] Fix stray OR in New Project page. !14096 (Robin Bobbitt) +- [FIXED] Fix a wrong `X-Gitlab-Event` header when testing webhooks. !14108 +- [FIXED] Fix the diff file header from being html escaped for renamed files. !14121 +- [FIXED] Image attachments are properly displayed in notification emails again. !14161 +- [FIXED] Fixes the 500 errors caused by a race condition in GPG's tmp directory handling. !14194 (Alexis Reigel) +- [FIXED] Fix MR ready to merge buttons/controls at mobile breakpoint. !14242 +- [FIXED] Fix Pipeline Triggers to show triggered label and predefined variables (e.g. CI_PIPELINE_TRIGGERED). !14244 +- [FIXED] Allow using newlines in pipeline email service recipients. !14250 +- [FIXED] Fix errors when moving issue with reference to a group milestone. !14294 +- [FIXED] Fix the "resolve discussion in a new issue" button. !14357 +- [FIXED] File uploaders do not perform hard check, only soft check. +- [FIXED] Add to_project_id parameter to Move Issue via API example. +- [FIXED] Update x/x discussions resolved checkmark icon to be green when all discussions resolved. +- [FIXED] Fixed add diff note button not showing after deleting a comment. +- [FIXED] Fix broken svg in jobs dropdown for success status. +- [FIXED] Fix buttons with different height in merge request widget. +- [FIXED] Removes disabled state from dashboard project button. +- [FIXED] Better align fallback image emojis. +- [FIXED] Remove focus styles from dropdown empty links. +- [FIXED] Fix inconsistent spacing for edit buttons on issues and merge request page. +- [FIXED] Fix edit merge request and issues button inconsistent letter casing. +- [FIXED] Improve Import/Export memory usage. +- [FIXED] Fix Import/Export issue to do with fork merge requests. +- [FIXED] Fix invite by email address duplication. +- [FIXED] Adds tooltip to the branch name and improves performance. +- [FIXED] Disable GitLab Project Import Button if source disabled. +- [FIXED] Migrate issues authored by deleted user to the Ghost user. +- [FIXED] Fix new navigation wrapping and causing height to grow. +- [FIXED] Normalize styles for empty state combo button. +- [FIXED] Fix external link to Composer website. +- [FIXED] Prevents jobs dropdown from closing in pipeline graph. +- [FIXED] Include the `is_admin` field in the `GET /users/:id` API when current user is an admin. +- [FIXED] Fix breadcrumbs container in issue boards. +- [FIXED] Fix project feature being deleted when updating project with invalid visibility level. +- [FIXED] Truncate milestone title if sidebar is collapsed. +- [FIXED] Prevents rendering empty badges when request fails. +- [FIXED] Fixes margins on the top buttons of the pipeline table. +- [FIXED] Bump jira-ruby gem to 1.4.1 to fix issues with HTTP proxies. +- [FIXED] Eliminate N+1 queries in loading discussions.json endpoint. +- [FIXED] Eliminate N+1 queries referencing issues. +- [FIXED] Remove unnecessary loading of discussions in `IssuesController#show`. +- [FIXED] Fix errors thrown in merge request widget with external CI service/integration. +- [FIXED] Do not show the Auto DevOps banner when the project has a .gitlab-ci.yml on master. +- [FIXED] Reword job to pipeline to reflect what the graphs are really about. +- [FIXED] Sort templates in the dropdown. +- [FIXED] Fix Auto DevOps banner to be shown on empty projects. +- [FIXED] Resolve Image onion skin + swipe does not work anymore. +- [FIXED] Fix mini graph pipeline breakin in merge request view. +- [FIXED] Fixed merge request changes bar jumping. +- [FIXED] Improve migrations using triggers. +- [FIXED] Fix ConvDev Index nav item and Monitoring submenu regression. +- [DEPRECATED] Deprecate custom SSH client configuration for the git user. !13930 +- [CHANGED] allow all users to delete their account. !13636 (Jacopo Beschi @jacopo-beschi) +- [CHANGED] Use full path of project's avatar in webhooks. !13649 (Vitaliy @blackst0ne Klachkov) +- [CHANGED] Add filtered search to group merge requests dashboard. !13688 (Hiroyuki Sato) +- [CHANGED] Fire hooks asynchronously when creating a new job to improve performance. !13734 +- [CHANGED] Improve performance for AutocompleteController#users.json. !13754 (Hiroyuki Sato) +- [CHANGED] Update the GPG verification semantics: A GPG signature must additionally match the committer in order to be verified. !13771 (Alexis Reigel) +- [CHANGED] Support a multi-word fuzzy seach issues/merge requests on search bar. !13780 (Hiroyuki Sato) +- [CHANGED] Default LDAP config "verify_certificates" to true for security. !13915 +- [CHANGED] "Share with group lock" now applies to subgroups, but owner can override setting on subgroups. !13944 +- [CHANGED] Make Gitaly PostUploadPack mandatory. !13953 +- [CHANGED] Remove project select dropdown from breadcrumb. !14010 +- [CHANGED] Redesign project feature permissions settings. !14062 +- [CHANGED] Document version Group Milestones API introduced. +- [CHANGED] Finish migration to the new events setup. +- [CHANGED] restyling of OAuth authorization confirmation. (Jacopo Beschi @jacopo-beschi) +- [CHANGED] Added support for specific labels and colors. +- [CHANGED] Move "Move issue" controls to right-sidebar. +- [CHANGED] Remove pages settings when not available. +- [CHANGED] Allow all AutoDevOps banners to be turned off. +- [CHANGED] Update Rails project template to use Postgresql by default. +- [CHANGED] Added support the multiple time series for prometheus monitoring. +- [ADDED] API: Respect the "If-Unmodified-Since" header when delting a resource. !9621 (Robert Schilling) +- [ADDED] Protected runners. !13194 +- [ADDED] Add support for copying permalink to notes via more actions dropdown. !13299 +- [ADDED] Add API support for wiki pages. !13372 (Vitaliy @blackst0ne Klachkov) +- [ADDED] Add a `Last 7 days` option for Cycle Analytics view. !13443 (Mehdi Lahmam (@mehlah)) +- [ADDED] inherits milestone and labels when a merge request is created from issue. !13461 (haseebeqx) +- [ADDED] Add 'from commit' information to cherry-picked commits. !13475 (Saverio Miroddi) +- [ADDED] Add an option to list only archived projects. !13492 (Mehdi Lahmam (@mehlah)) +- [ADDED] Extend API: Pipeline Schedule Variable. !13653 +- [ADDED] Add settings for minimum SSH key strength and allowed key type. !13712 (Cory Hinshaw) +- [ADDED] Add div id to the readme in the project overview. !13735 (Riccardo Padovani @rpadovani) +- [ADDED] Add CI/CD job predefined variables with user name and login. !13824 +- [ADDED] API: Add GPG key management. !13828 (Robert Schilling) +- [ADDED] Add CI/CD active kubernetes job policy. !13849 +- [ADDED] Add dropdown to Projects nav item. !13866 +- [ADDED] Allow users and administrator to configure Auto-DevOps. !13923 +- [ADDED] Implement `failure_reason` on `ci_builds`. !13937 +- [ADDED] Add branch existence check to the APIv4 branches via HEAD request. !13979 (Vitaliy @blackst0ne Klachkov) +- [ADDED] Add quick submission on user settings page. !14007 (Vitaliy @blackst0ne Klachkov) +- [ADDED] Add my_reaction_emoji param to /issues and /merge_requests API. !14016 (Hiroyuki Sato) +- [ADDED] Make it possible to download a single job artifact file using the API. !14027 +- [ADDED] Add repository toggle for automatically resolving outdated diff discussions. !14053 (AshleyDumaine) +- [ADDED] Scripts to detect orphaned repositories. !14204 +- [ADDED] Created callout for auto devops. +- [ADDED] Add option in preferences to change navigation theme color. +- [ADDED] Add JSON logger in `log/api_json.log` for Grape API endpoints. +- [ADDED] Add CI_PIPELINE_SOURCE variable on CI Jobs. +- [ADDED] Changed message and title on the 404 page. (Branka Martinovic) +- [ADDED] Handle if Auto DevOps domain is not set in project settings. +- [ADDED] Add collapsable sections for Pipeline Settings. +- [OTHER] Add badge for dependency status. !13588 (Markus Koller) +- [OTHER] Migration to remove pending delete projects with non-existing namespace. !13598 +- [OTHER] Bump rouge to v2.2.0. !13633 +- [OTHER] Fix repository equality check and avoid fetching ref if the commit is already available. This affects merge request creation performance. !13685 +- [OTHER] Replace 'source/search_code.feature' spinach test with an rspec analog. !13697 (blackst0ne) +- [OTHER] Remove unwanted refs after importing a project. !13766 +- [OTHER] Never wait for sidekiq jobs when creating projects. !13775 +- [OTHER] Gitaly feature toggles are on by default in development. !13802 +- [OTHER] Remove `is_` prefix from predicate method names. !13810 (Maxim Rydkin) +- [OTHER] Update 'Using Docker images' documentation. !13848 +- [OTHER] Update gpg documentation with gpg2. !13851 (M M Arif) +- [OTHER] Replace 'project/star.feature' spinach test with an rspec analog. !13855 (Vitaliy @blackst0ne Klachkov) +- [OTHER] Replace 'project/user_lookup.feature' spinach test with an rspec analog. !13863 (Vitaliy @blackst0ne Klachkov) +- [OTHER] Bump rouge to v2.2.1. !13887 +- [OTHER] Add documentation for PlantUML in reStructuredText. !13900 (Markus Koller) +- [OTHER] Decrease ABC threshold to 55.25. !13904 (Maxim Rydkin) +- [OTHER] Decrease Cyclomatic Complexity threshold to 14. !13972 (Maxim Rydkin) +- [OTHER] Update documentation for confidential issue. !14117 +- [OTHER] Remove redundant WHERE from event queries. +- [OTHER] Memoize the latest builds of a pipeline on a project's homepage. +- [OTHER] Re-use issue/MR counts for the pagination system. +- [OTHER] Memoize pipelines for project download buttons. +- [OTHER] Reorganize indexes for the "deployments" table. +- [OTHER] Improves markdown rendering performance for commit lists. +- [OTHER] Only update the sidebar count caches when needed. +- [OTHER] Improves performance of vue code by using vue files and moving svg out of data function in pipeline schedule callout. +- [OTHER] Rework how recent push events are retrieved. +- [OTHER] Restyle dropdown menus to make them look consistent. +- [OTHER] Upgrade grape to 1.0. +- [OTHER] Add usage data for Auto DevOps. +- [OTHER] Cache the number of open issues and merge requests. +- [OTHER] Constrain environment deployments to project IDs. +- [OTHER] Eager load namespace owners for project dashboards. +- [OTHER] Add description template examples to documentation. +- [OTHER] Disallow NULL values for environments.project_id. +- Add my reaction filter to search bar. !12962 (Hiroyuki Sato) +- Generalize profile updates from providers. !12968 (Alexandros Keramidas) +- Validate PO-files in static analysis. !13000 +- First-time contributor badge. !13143 (Micaël Bergeron ) +- Add option to disable project export on instance. !13211 (Robin Bobbitt) +- Hashed Storage support for Repositories (EXPERIMENTAL). !13246 +- Added tests for commits API unauthenticated user and public/private project. !13287 (Jacopo Beschi @jacopo-beschi) +- Fix CI_PROJECT_PATH_SLUG slugify. !13350 (Ivan Chernov) +- Add checks for branch existence before changing HEAD. !13359 (Vitaliy @blackst0ne Klachkov) +- Fix the alignment of line numbers to lines of code in code viewer. !13403 (Trevor Flynn) +- Allow users to move issues to other projects using a / command. !13436 (Manolis Mavrofidis) +- Bumps omniauth-ldap gem version to 2.0.4. !13465 +- Implement the Gitaly RefService::RefExists endpoint. !13528 (Andrew Newdigate) +- Changed all font-weight values to 400 and 600 and introduced 2 variables to manage them. +- Simplify checking if objects exist code in new issaubles workers. +- Present enqueued merge jobs as Merging as well. +- Don't escape html entities in InlineDiffMarkdownMarker. +- Move ConvDev Index location to after Cohorts. +- Added type to CHANGELOG entries. (Jacopo Beschi @jacopo-beschi) +- [BUGIFX] Improves subgroup creation permissions. !13418 + ## 9.5.4 (2017-09-06) - [SECURITY] Upgrade mail and nokogiri gems due to security issues. !13662 (Markus Koller) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6fb2c6bd1dc109bf36d84ce14ac40b7a6e6a9f22..dbe64eb67a35568d10338c56811bb9fb3462038b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -39,6 +39,7 @@ _This notice should stay as the first item in the CONTRIBUTING.md file._ - [Definition of done](#definition-of-done) - [Style guides](#style-guides) - [Code of conduct](#code-of-conduct) +- [Contribution Flow](#contribution-flow) @@ -616,6 +617,24 @@ reported by emailing `contact@gitlab.com`. This Code of Conduct is adapted from the [Contributor Covenant][contributor-covenant], version 1.1.0, available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/). +## Contribution Flow + +When contributing to GitLab, your merge request is subject to review by merge request maintainers of a particular specialty. + +When you submit code to GitLab, we really want it to get merged, but there will be times when it will not be merged. + +When maintainers are reading through a merge request they may request guidance from other maintainers. If merge request maintainers conclude that the code should not be merged, our reasons will be fully disclosed. If it has been decided that the code quality is not up to GitLab’s standards, the merge request maintainer will refer the author to our docs and code style guides, and provide some guidance. + +Sometimes style guides will be followed but the code will lack structural integrity, or the maintainer will have reservations about the code’s overall quality. When there is a reservation the maintainer will inform the author and provide some guidance. The author may then choose to update the merge request. Once the merge request has been updated and reassigned to the maintainer, they will review the code again. Once the code has been resubmitted any number of times, the maintainer may choose to close the merge request with a summary of why it will not be merged, as well as some guidance. If the merge request is closed the maintainer will be open to discussion as to how to improve the code so it can be approved in the future. + +GitLab will do its best to review community contributions as quickly as possible. Specially appointed developers review community contributions daily. You may take a look at the [team page](https://about.gitlab.com/team/) for the merge request coach who specializes in the type of code you have written and mention them in the merge request. For example, if you have written some JavaScript in your code then you should mention the frontend merge request coach. If your code has multiple disciplines you may mention multiple merge request coaches. + +GitLab receives a lot of community contributions, so if your code has not been reviewed within 4 days of its initial submission feel free to re-mention the appropriate merge request coach. + +When submitting code to GitLab, you may feel that your contribution requires the aid of an external library. If your code includes an external library please provide a link to the library, as well as reasons for including it. + +When your code contains more than 500 changes, any major breaking changes, or an external library, `@mention` a maintainer in the merge request. If you are not sure who to mention, the reviewer will add one early in the merge request process. + [core team]: https://about.gitlab.com/core-team/ [team]: https://about.gitlab.com/team/ [getting-help]: https://about.gitlab.com/getting-help/ diff --git a/GITLAB_PAGES_VERSION b/GITLAB_PAGES_VERSION index 8f0916f768f0487bcf8d33827ce2c8dcecb645c1..4b9fcbec101a6ff8ec68e0f95131ccda4861407f 100644 --- a/GITLAB_PAGES_VERSION +++ b/GITLAB_PAGES_VERSION @@ -1 +1 @@ -0.5.0 +0.5.1 diff --git a/Gemfile b/Gemfile index a022319ae2c09443002781aaef47893b40f17daa..8ac41c6aaee34ae62e0865e2d1120b6f1e102348 100644 --- a/Gemfile +++ b/Gemfile @@ -42,6 +42,9 @@ gem 'omniauth-authentiq', '~> 0.3.1' gem 'rack-oauth2', '~> 1.2.1' gem 'jwt', '~> 1.5.6' +# Kerberos authentication. EE-only +gem 'gssapi', group: :kerberos + # Spam and anti-bot protection gem 'recaptcha', '~> 3.0', require: 'recaptcha/rails' gem 'akismet', '~> 2.0' @@ -113,6 +116,13 @@ gem 'unf', '~> 0.1.4' # Seed data gem 'seed-fu', '~> 2.3.5' +# Search +gem 'elasticsearch-model', '~> 0.1.9' +gem 'elasticsearch-rails', '~> 0.1.9' +gem 'elasticsearch-api', '5.0.3' +gem 'aws-sdk' +gem 'faraday_middleware-aws-signers-v4' + # Markdown and HTML processing gem 'html-pipeline', '~> 1.11.0' gem 'deckar01-task_list', '2.0.0' @@ -128,7 +138,7 @@ gem 'asciidoctor-plantuml', '0.0.7' gem 'rouge', '~> 2.0' gem 'truncato', '~> 0.7.9' gem 'bootstrap_form', '~> 2.7.0' -gem 'nokogiri', '~> 1.8.0' +gem 'nokogiri', '~> 1.8.1' # Diffs gem 'diffy', '~> 3.1.0' @@ -251,6 +261,8 @@ gem 'select2-rails', '~> 3.5.9' gem 'virtus', '~> 1.0.1' gem 'base32', '~> 0.3.0' +gem "gitlab-license", "~> 1.0" + # Sentry integration gem 'sentry-raven', '~> 2.5.3' @@ -281,7 +293,7 @@ group :metrics do gem 'influxdb', '~> 0.2', require: false # Prometheus - gem 'prometheus-client-mmap', '~>0.7.0.beta14' + gem 'prometheus-client-mmap', '~>0.7.0.beta18' gem 'raindrops', '~> 0.18' end @@ -386,8 +398,12 @@ gem 'health_check', '~> 2.6.0' gem 'vmstat', '~> 2.3.0' gem 'sys-filesystem', '~> 1.1.6' +# NTP client +gem 'net-ntp' + # SSH host key support gem 'net-ssh', '~> 4.1.0' +gem 'sshkey', '~> 1.9.0' # Required for ED25519 SSH host key support group :ed25519 do @@ -407,4 +423,4 @@ gem 'flipper-active_record', '~> 0.10.2' # Structured logging gem 'lograge', '~> 0.5' -gem 'grape_logging', '~> 1.6' +gem 'grape_logging', '~> 1.7' diff --git a/Gemfile.lock b/Gemfile.lock index d7e1c7581d56b9f0a9f7fe4d38a6bc6b3fdae7a9..d0f9d6b4639c2ac124a53106b1b0a10d567a1c12 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -70,6 +70,14 @@ GEM execjs json awesome_print (1.2.0) + aws-sdk (2.9.32) + aws-sdk-resources (= 2.9.32) + aws-sdk-core (2.9.32) + aws-sigv4 (~> 1.0) + jmespath (~> 1.0) + aws-sdk-resources (2.9.32) + aws-sdk-core (= 2.9.32) + aws-sigv4 (1.0.0) axiom-types (0.1.1) descendants_tracker (~> 0.0.4) ice_nine (~> 0.11.0) @@ -174,6 +182,19 @@ GEM json-jwt (~> 1.6) dropzonejs-rails (0.7.2) rails (> 3.1) + elasticsearch (5.0.3) + elasticsearch-api (= 5.0.3) + elasticsearch-transport (= 5.0.3) + elasticsearch-api (5.0.3) + multi_json + elasticsearch-model (0.1.9) + activesupport (> 3) + elasticsearch (> 0.4) + hashie + elasticsearch-rails (0.1.9) + elasticsearch-transport (5.0.3) + faraday + multi_json email_reply_trimmer (0.1.6) email_spec (1.6.0) launchy (~> 2.1) @@ -198,6 +219,9 @@ GEM multipart-post (>= 1.2, < 3) faraday_middleware (0.11.0.1) faraday (>= 0.7.4, < 1.0) + faraday_middleware-aws-signers-v4 (0.1.7) + aws-sdk-resources (~> 2) + faraday (~> 0.9) faraday_middleware-multi_json (0.0.6) faraday_middleware multi_json @@ -293,6 +317,7 @@ GEM diff-lcs (~> 1.1) mime-types (>= 1.16, < 3) posix-spawn (~> 0.3) + gitlab-license (1.0.0) gitlab-markup (1.5.1) gitlab_omniauth-ldap (2.0.4) net-ldap (~> 0.16) @@ -355,11 +380,13 @@ GEM activesupport grape (>= 0.16.0) rake - grape_logging (1.6.0) + grape_logging (1.7.0) grape grpc (1.4.5) google-protobuf (~> 3.1) googleauth (~> 0.5.1) + gssapi (1.2.0) + ffi (>= 1.0.1) haml (4.0.7) tilt haml_lint (0.26.0) @@ -410,6 +437,7 @@ GEM activesupport multipart-post oauth (~> 0.5, >= 0.5.0) + jmespath (1.3.1) jquery-atwho-rails (1.3.2) jquery-rails (4.1.1) rails-dom-testing (>= 1, < 3) @@ -482,7 +510,7 @@ GEM mime-types (2.99.3) mimemagic (0.3.0) mini_mime (0.1.4) - mini_portile2 (2.2.0) + mini_portile2 (2.3.0) minitest (5.7.0) mmap2 (2.2.7) mousetrap-rails (1.4.6) @@ -494,10 +522,11 @@ GEM mustermann (~> 1.0.0) mysql2 (0.4.5) net-ldap (0.16.0) + net-ntp (2.1.3) net-ssh (4.1.0) netrc (0.11.0) - nokogiri (1.8.0) - mini_portile2 (~> 2.2.0) + nokogiri (1.8.1) + mini_portile2 (~> 2.3.0) numerizer (0.1.1) oauth (0.5.1) oauth2 (1.4.0) @@ -624,7 +653,7 @@ GEM parser unparser procto (0.0.3) - prometheus-client-mmap (0.7.0.beta14) + prometheus-client-mmap (0.7.0.beta18) mmap2 (~> 2.2, >= 2.2.7) pry (0.10.4) coderay (~> 1.1.0) @@ -869,6 +898,7 @@ GEM activesupport (>= 4.0) sprockets (>= 3.0.0) sqlite3 (1.3.13) + sshkey (1.9.0) stackprof (0.2.10) state_machines (0.4.0) state_machines-activemodel (0.4.0) @@ -970,6 +1000,7 @@ DEPENDENCIES asciidoctor-plantuml (= 0.0.7) attr_encrypted (~> 3.0.0) awesome_print (~> 1.2.0) + aws-sdk babosa (~> 1.0.2) base32 (~> 0.3.0) bcrypt_pbkdf (~> 1.0) @@ -1001,10 +1032,14 @@ DEPENDENCIES doorkeeper (~> 4.2.0) doorkeeper-openid_connect (~> 1.1.0) dropzonejs-rails (~> 0.7.1) + elasticsearch-api (= 5.0.3) + elasticsearch-model (~> 0.1.9) + elasticsearch-rails (~> 0.1.9) email_reply_trimmer (~> 0.1) email_spec (~> 1.6.0) factory_girl_rails (~> 4.7.0) faraday (~> 0.12) + faraday_middleware-aws-signers-v4 ffaker (~> 2.4) flay (~> 2.8.0) flipper (~> 0.10.2) @@ -1027,6 +1062,7 @@ DEPENDENCIES gitaly-proto (~> 0.33.0) github-linguist (~> 4.7.0) gitlab-flowdock-git-hook (~> 1.0.1) + gitlab-license (~> 1.0) gitlab-markup (~> 1.5.1) gitlab_omniauth-ldap (~> 2.0.4) gollum-lib (~> 4.2) @@ -1037,7 +1073,8 @@ DEPENDENCIES grape (~> 1.0) grape-entity (~> 0.6.0) grape-route-helpers (~> 2.1.0) - grape_logging (~> 1.6) + grape_logging (~> 1.7) + gssapi haml_lint (~> 0.26.0) hamlit (~> 2.6.1) hashie-forbidden_attributes @@ -1066,8 +1103,9 @@ DEPENDENCIES mousetrap-rails (~> 1.4.6) mysql2 (~> 0.4.5) net-ldap + net-ntp net-ssh (~> 4.1.0) - nokogiri (~> 1.8.0) + nokogiri (~> 1.8.1) oauth2 (~> 1.4) octokit (~> 4.6.2) oj (~> 2.17.4) @@ -1100,7 +1138,7 @@ DEPENDENCIES pg (~> 0.18.2) poltergeist (~> 1.9.0) premailer-rails (~> 1.9.7) - prometheus-client-mmap (~> 0.7.0.beta14) + prometheus-client-mmap (~> 0.7.0.beta18) pry-byebug (~> 3.4.1) pry-rails (~> 0.3.4) rack-attack (~> 4.4.1) @@ -1160,6 +1198,7 @@ DEPENDENCIES spring-commands-rspec (~> 1.0.4) spring-commands-spinach (~> 1.1.0) sprockets (~> 3.7.0) + sshkey (~> 1.9.0) stackprof (~> 0.2.10) state_machines-activerecord (~> 0.4.0) sys-filesystem (~> 1.1.6) diff --git a/LICENSE b/LICENSE index ad4f2872db58eb4303cb2aa8f4d4bdf2425eb47e..a562c2f8e44c5154114d34a2350a65e63f6f66a8 100644 --- a/LICENSE +++ b/LICENSE @@ -1,19 +1,31 @@ +The GitLab Enterprise Edition (EE) license (the “EE License”) Copyright (c) 2011-2017 GitLab B.V. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +This software and associated documentation files (the "Software") may only be +used if you (and any entity that you represent) have agreed to, and are in +compliance with, the GitLab Subscription Terms of Service, available at +https://about.gitlab.com/terms/#subscription (the “EE Terms”), and otherwise +have a valid GitLab Enterprise Edition subscription for the correct number of +user seats. Subject to the foregoing sentence, you are free to modify this +Software and publish patches to the Software. You agree that GitLab and/or its +licensors (as applicable) retain all right, title and interest in and to all +Software incorporated in such modifications and/or patches, and all such +Software may only be used, copied, modified, displayed, distributed, or +otherwise exploited with a valid GitLab Enterprise Edition subscription for the +correct number of user seats. Subject to the foregoing, it is forbidden to +copy, merge, publish, distribute, sublicense, and/or sell the Software. -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. +This EE License applies only to the part of this Software that is not +distributed as part of GitLab Community Edition (CE), and that is not a file +that produces client-side JavaScript, in whole or in part. Any part of this +Software distributed as part of GitLab CE or that is a file that produces +client-side JavaScript, in whole or in part, is copyrighted under the MIT Expat +license. The full text of this EE License shall be included in all copies or +substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md index 9ead6d51c5df950d9a076d924fd27670d3fa3886..48fecb4352775b1fe0dcf43d7a0f2399cda818f1 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # GitLab -[![Build status](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/build.svg)](https://gitlab.com/gitlab-org/gitlab-ce/commits/master) -[![Overall test coverage](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg)](https://gitlab.com/gitlab-org/gitlab-ce/pipelines) +[![Build status](https://gitlab.com/gitlab-org/gitlab-ee/badges/master/build.svg)](https://gitlab.com/gitlab-org/gitlab-ee/commits/master) +[![Overall test coverage](https://gitlab.com/gitlab-org/gitlab-ee/badges/master/coverage.svg)](https://gitlab.com/gitlab-org/gitlab-ee/pipelines) [![Dependency Status](https://gemnasium.com/gitlabhq/gitlabhq.svg)](https://gemnasium.com/gitlabhq/gitlabhq) [![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.svg)](https://codeclimate.com/github/gitlabhq/gitlabhq) [![Core Infrastructure Initiative Best Practices](https://bestpractices.coreinfrastructure.org/projects/42/badge)](https://bestpractices.coreinfrastructure.org/projects/42) @@ -9,13 +9,43 @@ ## Test coverage -- [![Ruby coverage](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=coverage)](https://gitlab-org.gitlab.io/gitlab-ce/coverage-ruby) Ruby -- [![JavaScript coverage](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=karma)](https://gitlab-org.gitlab.io/gitlab-ce/coverage-javascript) JavaScript +- [![Ruby coverage](https://gitlab.com/gitlab-org/gitlab-ee/badges/master/coverage.svg?job=coverage)](https://gitlab-org.gitlab.io/gitlab-ee/coverage-ruby) Ruby +- [![JavaScript coverage](https://gitlab.com/gitlab-org/gitlab-ee/badges/master/coverage.svg?job=karma)](https://gitlab-org.gitlab.io/gitlab-ee/coverage-javascript) JavaScript ## Canonical source The canonical source of GitLab Community Edition is [hosted on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/). +The source of GitLab Enterprise Edition is [hosted on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ee). + +# ![logo](https://about.gitlab.com/images/gitlab_logo.png) GitLab + +## Free trial + +You can request a free trial of GitLab Enterprise Edition [on our website](https://about.gitlab.com/free-trial/). + +## Subscriber onboarding information + +Thank you for purchasing a GitLab subscription! + +For standard subscribers, please see **emergency contact info and other useful information** in [the Standard subscribers README](https://gitlab.com/standard/standard-subscriber-information/tree/master#README). + +GitLab Enterprise Edition repository: +https://gitlab.com/gitlab-com/gitlab-ee + +Download GitLab Enterprise Edition: +https://about.gitlab.com/downloads-ee + +Documentation: +http://doc.gitlab.com/ee/ + +To upgrade from CE, just perform a normal upgrade, but use an EE package: +https://about.gitlab.com/update/#ee + +If you need help with your GitLab installation and for any technical questions please contact us at subscribers@gitlab.com + +For all other questions, contact us at sales@gitlab.com + ## Open source software to collaborate on code To see how GitLab looks please see the [features page on our website](https://about.gitlab.com/features/). @@ -107,7 +137,7 @@ For upgrading information please see our [update page](https://about.gitlab.com/ ## Documentation -All documentation can be found on [docs.gitlab.com/ce/](https://docs.gitlab.com/ce/). +All documentation can be found on [doc.gitlab.com/ee/](http://doc.gitlab.com/ee/). ## Getting help diff --git a/VERSION b/VERSION index ddadd9f9c5a0c08efd4c7fca0ce09293ed9e537e..b1cb92bb4cbcc0914bc9776c47902ba92505c717 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -9.6.0-pre +10.0.7-ee diff --git a/app/assets/images/authbuttons/kerberos_32.png b/app/assets/images/authbuttons/kerberos_32.png new file mode 100644 index 0000000000000000000000000000000000000000..66b6f91d863449ecae27c29a5af58d3fb2d7c971 Binary files /dev/null and b/app/assets/images/authbuttons/kerberos_32.png differ diff --git a/app/assets/images/authbuttons/kerberos_64.png b/app/assets/images/authbuttons/kerberos_64.png new file mode 100644 index 0000000000000000000000000000000000000000..f22fbc57da3ea7414fcbcbe1935d92b4000288aa Binary files /dev/null and b/app/assets/images/authbuttons/kerberos_64.png differ diff --git a/app/assets/images/favicon-blue.ico b/app/assets/images/favicon-blue.ico deleted file mode 100755 index 156fcf075881c121ea364095cef181e5cc7fd285..0000000000000000000000000000000000000000 Binary files a/app/assets/images/favicon-blue.ico and /dev/null differ diff --git a/app/assets/images/favicon-green.ico b/app/assets/images/favicon-green.ico new file mode 100755 index 0000000000000000000000000000000000000000..906898589c96ef2b265a2d2cb9529bdcde8a521b Binary files /dev/null and b/app/assets/images/favicon-green.ico differ diff --git a/app/assets/images/mailers/approval/icon-merge-request-gray.gif b/app/assets/images/mailers/approval/icon-merge-request-gray.gif new file mode 100644 index 0000000000000000000000000000000000000000..6eef39d3b1e5e11d3da60e556e4a39adeadc8269 Binary files /dev/null and b/app/assets/images/mailers/approval/icon-merge-request-gray.gif differ diff --git a/app/assets/images/mailers/approval/icon-x-orange-inverted.gif b/app/assets/images/mailers/approval/icon-x-orange-inverted.gif new file mode 100644 index 0000000000000000000000000000000000000000..7fbf1c413845c1befcd3314a8ea6b74c4e594ff5 Binary files /dev/null and b/app/assets/images/mailers/approval/icon-x-orange-inverted.gif differ diff --git a/app/assets/javascripts/admin_email_select.js b/app/assets/javascripts/admin_email_select.js new file mode 100644 index 0000000000000000000000000000000000000000..3f2477341ce47255183b5f59dd1fdbc645280ef4 --- /dev/null +++ b/app/assets/javascripts/admin_email_select.js @@ -0,0 +1,87 @@ +/* eslint-disable no-var, wrap-iife, func-names, space-before-function-paren, camelcase, no-unused-vars, quotes, object-shorthand, one-var, one-var-declaration-per-line, prefer-arrow-callback, comma-dangle, prefer-template, no-else-return, yoda, prefer-rest-params, prefer-spread, max-len */ +import Api from './api'; + +var slice = [].slice; + +window.AdminEmailSelect = (function() { + function AdminEmailSelect() { + $('.ajax-admin-email-select').each((function(_this) { + return function(i, select) { + var skip_ldap; + skip_ldap = $(select).hasClass('skip_ldap'); + return $(select).select2({ + placeholder: "Select group or project", + multiple: $(select).hasClass('multiselect'), + minimumInputLength: 0, + query: function(query) { + var group_result, project_result; + group_result = Api.groups(query.term, {}, function(groups) { + return groups; + }); + project_result = Api.projects(query.term, { + order_by: 'id', + membership: false + }, function(projects) { + return projects; + }); + return $.when(project_result, group_result).done(function(projects, groups) { + var all, data; + all = { + id: "all" + }; + data = [all].concat(groups[0], projects[0]); + return query.callback({ + results: data + }); + }); + }, + id: function(object) { + if (object.path_with_namespace) { + return "project-" + object.id; + } else if (object.path) { + return "group-" + object.id; + } else { + return "all"; + } + }, + formatResult: function() { + var args; + args = 1 <= arguments.length ? slice.call(arguments, 0) : []; + return _this.formatResult.apply(_this, args); + }, + formatSelection: function() { + var args; + args = 1 <= arguments.length ? slice.call(arguments, 0) : []; + return _this.formatSelection.apply(_this, args); + }, + dropdownCssClass: "ajax-admin-email-dropdown", + escapeMarkup: function(m) { + return m; + } + }); + }; + })(this)); + } + + AdminEmailSelect.prototype.formatResult = function(object) { + if (object.path_with_namespace) { + return "
" + object.name + "
" + object.path_with_namespace + "
"; + } else if (object.path) { + return "
" + object.name + "
" + object.path + "
"; + } else { + return "
All
All groups and projects
"; + } + }; + + AdminEmailSelect.prototype.formatSelection = function(object) { + if (object.path_with_namespace) { + return "Project: " + object.name; + } else if (object.path) { + return "Group: " + object.name; + } else { + return "All groups and projects"; + } + }; + + return AdminEmailSelect; +})(); diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js index 38d1effc77c5dda645e7095dea29101d812a9dc1..1939e7e31e2325585dfb4f4f4053ca7d92f868f6 100644 --- a/app/assets/javascripts/api.js +++ b/app/assets/javascripts/api.js @@ -11,6 +11,7 @@ const Api = { licensePath: '/api/:version/templates/licenses/:key', gitignorePath: '/api/:version/templates/gitignores/:key', gitlabCiYmlPath: '/api/:version/templates/gitlab_ci_ymls/:key', + ldapGroupsPath: '/api/:version/ldap/:provider/groups.json', dockerfilePath: '/api/:version/templates/dockerfiles/:key', issuableTemplatePath: '/:namespace_path/:project_path/templates/:type/:key', usersPath: '/api/:version/users.json', @@ -27,7 +28,7 @@ const Api = { }, // Return groups list. Filtered by query - groups(query, options, callback) { + groups(query, options, callback = $.noop) { const url = Api.buildUrl(Api.groupsPath); return $.ajax({ url, @@ -177,6 +178,31 @@ const Api = { }); }, + approverUsers(search, options, callback = $.noop) { + const url = Api.buildUrl('/autocomplete/users.json'); + return $.ajax({ + url, + data: $.extend({ + search, + per_page: 20, + }, options), + dataType: 'json', + }).done(callback); + }, + + ldap_groups(query, provider, callback) { + const url = Api.buildUrl(this.ldapGroupsPath).replace(':provider', provider); + return Api.wrapAjaxCall({ + url, + data: Object.assign({ + search: query, + per_page: 20, + active: true, + }), + dataType: 'json', + }).then(groups => callback(groups)); + }, + buildUrl(url) { let urlRoot = ''; if (gon.relative_url_root != null) { diff --git a/app/assets/javascripts/application_settings.js b/app/assets/javascripts/application_settings.js new file mode 100644 index 0000000000000000000000000000000000000000..efa962cc3d417413ff04641dea2f371eef83a145 --- /dev/null +++ b/app/assets/javascripts/application_settings.js @@ -0,0 +1,16 @@ + +function ApplicationSettings() { + const usageDataUrl = $('.usage-data').data('endpoint'); + + $.ajax({ + type: 'GET', + url: usageDataUrl, + dataType: 'html', + success(html) { + $('.usage-data').html(html); + }, + }); +} + +window.gl = window.gl || {}; +window.gl.ApplicationSettings = ApplicationSettings; diff --git a/app/assets/javascripts/approvals.js b/app/assets/javascripts/approvals.js new file mode 100644 index 0000000000000000000000000000000000000000..f050fb3632f79249f0e758383844919af01c5d55 --- /dev/null +++ b/app/assets/javascripts/approvals.js @@ -0,0 +1,70 @@ +/* eslint-disable func-names, prefer-arrow-callback, space-before-function-paren, quotes, no-var, comma-spacing, keyword-spacing, one-var, one-var-declaration-per-line, no-unused-vars, camelcase, prefer-template, max-len */ + +$(function() { + $(".approver-list").on("click", ".unsaved-approvers.approver .btn-remove", function(ev) { + var removeElement = $(this).closest("li"); + var approverId = parseInt(removeElement.attr("id").replace("user_",""), 10); + var approverIds = $("input#merge_request_approver_ids"); + var skipUsers = approverIds.data("skip-users") || []; + var approverIndex = skipUsers.indexOf(approverId); + + removeElement.remove(); + + if(approverIndex > -1) { + approverIds.data("skip-users", skipUsers.splice(approverIndex, 1)); + } + + ev.preventDefault(); + }); + + $(".approver-list").on("click", ".unsaved-approvers.approver-group .btn-remove", function(ev) { + var removeElement = $(this).closest("li"); + var approverGroupId = parseInt(removeElement.attr("id").replace("group_",""), 10); + var approverGroupIds = $("input#merge_request_approver_group_ids"); + var skipGroups = approverGroupIds.data("skip-groups") || []; + var approverGroupIndex = skipGroups.indexOf(approverGroupId); + + removeElement.remove(); + + if(approverGroupIndex > -1) { + approverGroupIds.data("skip-groups", skipGroups.splice(approverGroupIndex, 1)); + } + + ev.preventDefault(); + }); + + $("form.merge-request-form").submit(function() { + var approverIds, approversInput, approverGroupIds, approverGroupsInput; + + if ($("input#merge_request_approver_ids").length) { + approverIds = $.map($("li.unsaved-approvers.approver").not(".approver-template"), function(li, i) { + return li.id.replace("user_", ""); + }); + approversInput = $(this).find("input#merge_request_approver_ids"); + approverIds = approverIds.concat(approversInput.val().split(",")); + approversInput.val(_.compact(approverIds).join(",")); + } + + if ($("input#merge_request_approver_group_ids").length) { + approverGroupIds = $.map($("li.unsaved-approvers.approver-group"), function(li, i) { + return li.id.replace("group_", ""); + }); + approverGroupsInput = $(this).find("input#merge_request_approver_group_ids"); + approverGroupIds = approverGroupIds.concat(approverGroupsInput.val().split(",")); + approverGroupsInput.val(_.compact(approverGroupIds).join(",")); + } + }); + + return $(".suggested-approvers a").click(function() { + var approver_item_html, user_id, user_name; + user_id = this.id.replace("user_", ""); + user_name = this.text; + if ($(".approver-list #user_" + user_id).length) { + return false; + } + approver_item_html = $(".unsaved-approvers.approver-template").clone().removeClass("hide approver-template")[0].outerHTML.replace(/\{approver_name\}/g, user_name).replace(/\{user_id\}/g, user_id); + $(".no-approvers").remove(); + $(".approver-list").append(approver_item_html); + return false; + }); +}); diff --git a/app/assets/javascripts/approvers_select.js b/app/assets/javascripts/approvers_select.js new file mode 100644 index 0000000000000000000000000000000000000000..656ed95494e5cbc8e3bccfca0f1e3b7feedfc875 --- /dev/null +++ b/app/assets/javascripts/approvers_select.js @@ -0,0 +1,202 @@ +import Api from './api'; + +export default class ApproversSelect { + constructor() { + this.$approverSelect = $('.js-select-user-and-group'); + const name = this.$approverSelect.data('name'); + this.fieldNames = [`${name}[approver_ids]`, `${name}[approver_group_ids]`]; + this.$loadWrapper = $('.load-wrapper'); + + this.bindEvents(); + this.addEvents(); + this.initSelect2(); + } + + bindEvents() { + this.handleSelectChange = this.handleSelectChange.bind(this); + this.fetchGroups = this.fetchGroups.bind(this); + this.fetchUsers = this.fetchUsers.bind(this); + } + + addEvents() { + $(document).on('click', '.js-add-approvers', () => this.addApprover()); + $(document).on('click', '.js-approver-remove', e => ApproversSelect.removeApprover(e)); + } + + static getApprovers(fieldName, approverList) { + const input = $(`[name="${fieldName}"]`); + const existingApprovers = $(approverList).map((i, el) => + parseInt($(el).data('id'), 10), + ); + const selectedApprovers = input.val() + .split(',') + .filter(val => val !== ''); + return [...existingApprovers, ...selectedApprovers]; + } + + fetchGroups(term) { + const options = { + skip_groups: ApproversSelect.getApprovers(this.fieldNames[1], '.js-approver-group'), + }; + return Api.groups(term, options); + } + + fetchUsers(term) { + const options = { + skip_users: ApproversSelect.getApprovers(this.fieldNames[0], '.js-approver'), + project_id: $('#project_id').val(), + }; + return Api.approverUsers(term, options); + } + + handleSelectChange(e) { + const { added, removed } = e; + const userInput = $(`[name="${this.fieldNames[0]}"]`); + const groupInput = $(`[name="${this.fieldNames[1]}"]`); + + if (added) { + if (added.full_name) { + groupInput.val(`${groupInput.val()},${added.id}`.replace(/^,/, '')); + } else { + userInput.val(`${userInput.val()},${added.id}`.replace(/^,/, '')); + } + } + + if (removed) { + if (removed.full_name) { + groupInput.val(groupInput.val().replace(new RegExp(`,?${removed.id}`), '')); + } else { + userInput.val(userInput.val().replace(new RegExp(`,?${removed.id}`), '')); + } + } + } + + initSelect2() { + this.$approverSelect.select2({ + placeholder: 'Search for users or groups', + multiple: true, + minimumInputLength: 0, + query: (query) => { + const fetchGroups = this.fetchGroups(query.term); + const fetchUsers = this.fetchUsers(query.term); + return $.when(fetchGroups, fetchUsers).then((groups, users) => { + const data = { + results: groups[0].concat(users[0]), + }; + return query.callback(data); + }); + }, + formatResult: ApproversSelect.formatResult, + formatSelection: ApproversSelect.formatSelection, + dropdownCss() { + const $input = $('.js-select-user-and-group .select2-input'); + const offset = $input.offset(); + const inputRightPosition = offset.left + $input.outerWidth(); + const $dropdown = $('.select2-drop-active'); + + let left = offset.left; + if ($dropdown.outerWidth() > $input.outerWidth()) { + left = `${inputRightPosition - $dropdown.width()}px`; + } + return { + left, + right: 'auto', + width: 'auto', + }; + }, + }) + .on('change', this.handleSelectChange); + } + + static formatSelection(group) { + return group.full_name || group.name; + } + + static formatResult({ + name, + username, + avatar_url: avatarUrl, + full_name: fullName, + full_path: fullPath, + }) { + if (username) { + const avatar = avatarUrl || gon.default_avatar_url; + return ` +
+
+ +
+ +
+ `; + } + + return ` +
+
${fullName}
+
${fullPath}
+
+ `; + } + + addApprover() { + this.fieldNames.forEach(ApproversSelect.saveApprovers); + } + + static saveApprovers(fieldName) { + const $input = window.$(`[name="${fieldName}"]`); + const newValue = $input.val(); + const $loadWrapper = $('.load-wrapper'); + const $approverSelect = $('.js-select-user-and-group'); + + if (!newValue) { + return; + } + + const $form = $('.js-add-approvers').closest('form'); + $loadWrapper.removeClass('hidden'); + window.$.ajax({ + url: $form.attr('action'), + type: 'POST', + data: { + _method: 'PATCH', + [fieldName]: newValue, + }, + success: ApproversSelect.updateApproverList, + complete() { + $input.val(''); + $approverSelect.select2('val', ''); + $loadWrapper.addClass('hidden'); + }, + error() { + window.Flash('Failed to add Approver', 'alert'); + }, + }); + } + + static removeApprover(e) { + e.preventDefault(); + const target = e.currentTarget; + const $loadWrapper = $('.load-wrapper'); + $loadWrapper.removeClass('hidden'); + $.ajax({ + url: target.getAttribute('href'), + type: 'POST', + data: { + _method: 'DELETE', + }, + success: ApproversSelect.updateApproverList, + complete: () => $loadWrapper.addClass('hidden'), + error() { + window.Flash('Failed to remove Approver', 'alert'); + }, + }); + } + + static updateApproverList(html) { + $('.js-current-approvers').html($(html).find('.js-current-approvers').html()); + } +} diff --git a/app/assets/javascripts/audit_logs.js b/app/assets/javascripts/audit_logs.js new file mode 100644 index 0000000000000000000000000000000000000000..84fba1fb304bcbad4a1174170ae25ef8fca4cffc --- /dev/null +++ b/app/assets/javascripts/audit_logs.js @@ -0,0 +1,45 @@ +/* eslint-disable class-methods-use-this, no-unneeded-ternary, quote-props, no-new */ +/* global GroupsSelect */ +/* global ProjectSelect */ + +import UsersSelect from './users_select'; +import './groups_select'; +import './project_select'; + +class AuditLogs { + constructor() { + this.initFilters(); + } + + initFilters() { + new ProjectSelect(); + new GroupsSelect(); + new UsersSelect(); + + this.initFilterDropdown($('.js-type-filter'), 'event_type', null, () => { + $('.hidden-filter-value').val(''); + $('form.filter-form').submit(); + }); + + $('.project-item-select').on('click', () => { + $('form.filter-form').submit(); + }); + } + + initFilterDropdown($dropdown, fieldName, searchFields, cb) { + const dropdownOptions = { + fieldName, + selectable: true, + filterable: searchFields ? true : false, + search: { fields: searchFields }, + data: $dropdown.data('data'), + clicked: () => $dropdown.closest('form.filter-form').submit(), + }; + if (cb) { + dropdownOptions.clicked = cb; + } + $dropdown.glDropdown(dropdownOptions); + } +} + +export default AuditLogs; diff --git a/app/assets/javascripts/behaviors/toggler_behavior.js b/app/assets/javascripts/behaviors/toggler_behavior.js index b70b0a9bbf8465112d67b52652909227b9b609a8..8ef2c3954ea9933b4bf3b11915c5b633250985bd 100644 --- a/app/assets/javascripts/behaviors/toggler_behavior.js +++ b/app/assets/javascripts/behaviors/toggler_behavior.js @@ -5,6 +5,7 @@ // %button.js-toggle-button // %div.js-toggle-content // +import '~/lib/utils/url_utility'; $(() => { function toggleContainer(container, toggleState) { diff --git a/app/assets/javascripts/boards/boards_bundle.js b/app/assets/javascripts/boards/boards_bundle.js index ea00efe4b46e03de9e00700e1797565fe1ded81d..7df2e76ff14eea73449ecc89364020b77499d525 100644 --- a/app/assets/javascripts/boards/boards_bundle.js +++ b/app/assets/javascripts/boards/boards_bundle.js @@ -11,6 +11,7 @@ import './models/issue'; import './models/label'; import './models/list'; import './models/milestone'; +import './models/project'; import './models/assignee'; import './stores/boards_store'; import './stores/modal_store'; @@ -24,12 +25,17 @@ import './components/new_list_dropdown'; import './components/modal/index'; import '../vue_shared/vue_resource_interceptor'; +import './components/boards_selector'; +import collapseIcon from './icons/fullscreen_collapse.svg'; +import expandIcon from './icons/fullscreen_expand.svg'; + Vue.use(VueResource); $(() => { const $boardApp = document.getElementById('board-app'); const Store = gl.issueBoards.BoardsStore; const ModalStore = gl.issueBoards.ModalStore; + const issueBoardsContent = document.querySelector('.content-wrapper > .js-focus-mode-board'); window.gl = window.gl || {}; @@ -61,6 +67,7 @@ $(() => { rootPath: $boardApp.dataset.rootPath, bulkUpdatePath: $boardApp.dataset.bulkUpdatePath, detailIssue: Store.detail, + milestoneTitle: $boardApp.dataset.boardMilestoneTitle, defaultAvatar: $boardApp.dataset.defaultAvatar, }, computed: { @@ -69,6 +76,16 @@ $(() => { }, }, created () { + if (this.milestoneTitle) { + const milestoneTitleParam = `milestone_title=${this.milestoneTitle}`; + + Store.filter.path = [milestoneTitleParam].concat( + Store.filter.path.split('&').filter(param => param.match(/^milestone_title=(.*)$/g) === null) + ).join('&'); + + Store.updateFiltersUrl(true); + } + gl.boardService = new BoardService({ boardsEndpoint: this.boardsEndpoint, listsEndpoint: this.listsEndpoint, @@ -77,7 +94,7 @@ $(() => { }); Store.rootPath = this.boardsEndpoint; - this.filterManager = new FilteredSearchBoards(Store.filter, true); + this.filterManager = new FilteredSearchBoards(Store.filter, true, [(this.milestoneTitle ? 'milestone' : null)]); this.filterManager.setup(); // Listen for updateTokens event @@ -105,6 +122,7 @@ $(() => { this.state.lists = _.sortBy(this.state.lists, 'position'); Store.addBlankState(); + Store.addPromotionState(); this.loading = false; }) .catch(() => new Flash('An error occurred. Please try again.')); @@ -120,6 +138,7 @@ $(() => { el: document.getElementById('js-add-list'), data: { filters: Store.state.filters, + milestoneTitle: $boardApp.dataset.boardMilestoneTitle, }, mounted () { gl.issueBoards.newListDropdownInit(); @@ -133,6 +152,13 @@ $(() => { return { modal: ModalStore.store, store: Store.state, + isFullscreen: false, + focusModeAvailable: gl.utils.convertPermissionToBoolean( + $boardApp.dataset.focusModeAvailable, + ), + canAdminList: this.$options.el && gl.utils.convertPermissionToBoolean( + this.$options.el.dataset.canAdminList, + ), }; }, watch: { @@ -186,10 +212,58 @@ $(() => { :class="{ 'disabled': disabled }" :title="tooltipTitle" :aria-disabled="disabled" + v-if="canAdminList" @click="openModal"> Add issues `, }); + + gl.IssueBoardsToggleFocusBtn = new Vue({ + el: document.getElementById('js-toggle-focus-btn'), + data: { + modal: ModalStore.store, + store: Store.state, + isFullscreen: false, + focusModeAvailable: gl.utils.convertPermissionToBoolean($boardApp.dataset.focusModeAvailable), + }, + methods: { + toggleFocusMode() { + if (!this.focusModeAvailable) { return; } + + $(this.$refs.toggleFocusModeButton).tooltip('hide'); + issueBoardsContent.classList.toggle('is-focused'); + + this.isFullscreen = !this.isFullscreen; + }, + }, + template: ` + + `, + }); + + gl.IssueboardsSwitcher = new Vue({ + el: '#js-multiple-boards-switcher', + components: { + 'boards-selector': gl.issueBoards.BoardsSelector, + } + }); }); diff --git a/app/assets/javascripts/boards/components/board.js b/app/assets/javascripts/boards/components/board.js index adb7360327c6cd82a8b26d2e2e6decbfbd85e11e..86a4cbe89d046e56b53673f36fc29357c1223691 100644 --- a/app/assets/javascripts/boards/components/board.js +++ b/app/assets/javascripts/boards/components/board.js @@ -1,6 +1,7 @@ /* eslint-disable comma-dangle, space-before-function-paren, one-var */ /* global Sortable */ import Vue from 'vue'; +import boardPromotionState from 'ee/boards/components/board_promotion_state'; import AccessorUtilities from '../../lib/utils/accessor'; import boardList from './board_list'; import boardBlankState from './board_blank_state'; @@ -17,6 +18,7 @@ gl.issueBoards.Board = Vue.extend({ boardList, 'board-delete': gl.issueBoards.BoardDelete, boardBlankState, + boardPromotionState, }, props: { list: Object, diff --git a/app/assets/javascripts/boards/components/board_card.js b/app/assets/javascripts/boards/components/board_card.js index 079fb6438b95d4498fa539a207607feedc1032cf..12329ee5daf9d3266b6ab71900c413266f07cbe5 100644 --- a/app/assets/javascripts/boards/components/board_card.js +++ b/app/assets/javascripts/boards/components/board_card.js @@ -16,6 +16,7 @@ export default { :list="list" :issue="issue" :issue-link-base="issueLinkBase" + :group-id="groupId" :root-path="rootPath" :update-filters="true" /> @@ -30,6 +31,7 @@ export default { disabled: Boolean, index: Number, rootPath: String, + groupId: Number, }, data() { return { diff --git a/app/assets/javascripts/boards/components/board_list.js b/app/assets/javascripts/boards/components/board_list.js index 6159680f1e6f664ebe947d95c2a83bb7f9293d29..34feac5a18f6dab0bbf193ed778db687ed18fb07 100644 --- a/app/assets/javascripts/boards/components/board_list.js +++ b/app/assets/javascripts/boards/components/board_list.js @@ -9,6 +9,11 @@ const Store = gl.issueBoards.BoardsStore; export default { name: 'BoardList', props: { + groupId: { + type: Number, + required: false, + default: 0, + }, disabled: { type: Boolean, required: true, @@ -77,7 +82,7 @@ export default { this.showIssueForm = !this.showIssueForm; }, onScroll() { - if (!this.loadingMore && (this.scrollTop() > this.scrollHeight() - this.scrollOffset)) { + if (!this.list.loadingMore && (this.scrollTop() > this.scrollHeight() - this.scrollOffset)) { this.loadNextPage(); } }, @@ -160,15 +165,19 @@ export default { template: `
    diff --git a/app/assets/javascripts/boards/components/board_new_form.js b/app/assets/javascripts/boards/components/board_new_form.js new file mode 100644 index 0000000000000000000000000000000000000000..62b4d5548ddc3f2fb68a2164542ba0b39c8fcac9 --- /dev/null +++ b/app/assets/javascripts/boards/components/board_new_form.js @@ -0,0 +1,117 @@ +/* global BoardService */ + +import Vue from 'vue'; +import boardMilestoneSelect from './milestone_select'; +import extraMilestones from '../mixins/extra_milestones'; + +(() => { + window.gl = window.gl || {}; + window.gl.issueBoards = window.gl.issueBoards || {}; + + const Store = gl.issueBoards.BoardsStore; + + gl.issueBoards.BoardSelectorForm = Vue.extend({ + props: { + milestonePath: { + type: String, + required: true, + }, + }, + data() { + return { + board: { + id: false, + name: '', + milestone: extraMilestones[0], + milestone_id: extraMilestones[0].id, + }, + currentBoard: Store.state.currentBoard, + currentPage: Store.state.currentPage, + milestones: [], + milestoneDropdownOpen: false, + extraMilestones, + }; + }, + components: { + boardMilestoneSelect, + }, + mounted() { + if (this.currentBoard && Object.keys(this.currentBoard).length && this.currentPage !== 'new') { + this.board = Vue.util.extend({}, this.currentBoard); + } + }, + computed: { + buttonText() { + if (this.currentPage === 'new') { + return 'Create'; + } + + return 'Save'; + }, + milestoneToggleText() { + return this.board.milestone.title || 'Milestone'; + }, + submitDisabled() { + if (this.currentPage !== 'milestone') { + return this.board.name === ''; + } + + return false; + }, + }, + methods: { + refreshPage() { + location.href = location.pathname; + }, + loadMilestones(e) { + this.milestoneDropdownOpen = !this.milestoneDropdownOpen; + BoardService.loadMilestones.call(this); + + if (this.milestoneDropdownOpen) { + this.$nextTick(() => { + const milestoneDropdown = this.$refs.milestoneDropdown; + const rect = e.target.getBoundingClientRect(); + + milestoneDropdown.style.width = `${rect.width}px`; + }); + } + }, + submit() { + gl.boardService.createBoard(this.board) + .then(resp => resp.json()) + .then((data) => { + if (this.currentBoard && this.currentPage !== 'new') { + this.currentBoard.name = this.board.name; + + if (this.currentPage === 'milestone') { + // We reload the page to make sure the store & state of the app are correct + this.refreshPage(); + } + + // Enable the button thanks to our jQuery disabling it + $(this.$refs.submitBtn).enable(); + + // Reset the selectors current page + Store.state.currentPage = ''; + Store.state.reload = true; + } else if (this.currentPage === 'new') { + gl.utils.visitUrl(`${Store.rootPath}/${data.id}`); + } + }) + .catch(() => { + // https://gitlab.com/gitlab-org/gitlab-ce/issues/30821 + }); + }, + cancel() { + Store.state.currentPage = ''; + }, + selectMilestone(milestone) { + this.milestoneDropdownOpen = false; + this.board.milestone_id = milestone.id; + this.board.milestone = { + title: milestone.title, + }; + }, + }, + }); +})(); diff --git a/app/assets/javascripts/boards/components/board_new_issue.js b/app/assets/javascripts/boards/components/board_new_issue.js index 541b8049855f3e8f7413979293733c050f6ce8aa..9c07e66255de47c5092159d88bd62ebddd0192af 100644 --- a/app/assets/javascripts/boards/components/board_new_issue.js +++ b/app/assets/javascripts/boards/components/board_new_issue.js @@ -1,11 +1,17 @@ /* global ListIssue */ import eventHub from '../eventhub'; +import ProjectSelect from './project_select.vue'; const Store = gl.issueBoards.BoardsStore; export default { name: 'BoardNewIssue', props: { + groupId: { + type: Number, + required: false, + default: 0, + }, list: { type: Object, required: true, @@ -15,8 +21,20 @@ export default { return { title: '', error: false, + selectedProject: {}, }; }, + components: { + 'project-select': ProjectSelect, + }, + computed: { + disabled() { + if (this.groupId) { + return this.title === '' || !this.selectedProject.name; + } + return this.title === ''; + }, + }, methods: { submit(e) { e.preventDefault(); @@ -30,8 +48,13 @@ export default { labels, subscribed: true, assignees: [], + project_id: this.selectedProject.id, }); + if (Store.state.currentBoard) { + issue.milestone_id = Store.state.currentBoard.milestone_id; + } + eventHub.$emit(`scroll-board-list-${this.list.id}`); this.cancel(); @@ -58,43 +81,53 @@ export default { this.title = ''; eventHub.$emit(`hide-issue-form-${this.list.id}`); }, + setSelectedProject(selectedProject) { + this.selectedProject = selectedProject; + }, }, mounted() { this.$refs.input.focus(); + eventHub.$on('setSelectedProject', this.setSelectedProject); }, template: ` -
    -
    -
    -
    - An error occured. Please try again. +
    +
    + +
    +
    + An error occured. Please try again. +
    +
    + + + +
    + +
    -
    - - -
    - - -
    - + +
    `, }; diff --git a/app/assets/javascripts/boards/components/boards_selector.js b/app/assets/javascripts/boards/components/boards_selector.js new file mode 100644 index 0000000000000000000000000000000000000000..8d86acd0370ebddfa3a1236eaf5398ed836f6602 --- /dev/null +++ b/app/assets/javascripts/boards/components/boards_selector.js @@ -0,0 +1,102 @@ +import Vue from 'vue'; +import './board_new_form'; + +(() => { + window.gl = window.gl || {}; + window.gl.issueBoards = window.gl.issueBoards || {}; + + const Store = gl.issueBoards.BoardsStore; + + Store.createNewListDropdownData(); + + gl.issueBoards.BoardsSelector = Vue.extend({ + components: { + 'board-selector-form': gl.issueBoards.BoardSelectorForm, + }, + props: { + currentBoard: { + type: Object, + required: true, + }, + milestonePath: { + type: String, + required: true, + }, + }, + data() { + return { + open: false, + loading: true, + boards: [], + state: Store.state, + }; + }, + watch: { + reload() { + if (this.reload) { + this.boards = []; + this.loading = true; + this.reload = false; + + this.loadBoards(false); + } + }, + }, + computed: { + currentPage() { + return this.state.currentPage; + }, + reload() { + return this.state.reload; + }, + board() { + return this.state.currentBoard; + }, + showDelete() { + return this.boards.length > 1; + }, + title() { + if (this.currentPage === 'edit') { + return 'Edit board name'; + } else if (this.currentPage === 'milestone') { + return 'Edit board milestone'; + } else if (this.currentPage === 'new') { + return 'Create new board'; + } else if (this.currentPage === 'delete') { + return 'Delete board'; + } + + return 'Go to a board'; + }, + }, + methods: { + showPage(page) { + this.state.reload = false; + this.state.currentPage = page; + }, + toggleDropdown() { + this.open = !this.open; + }, + loadBoards(toggleDropdown = true) { + if (toggleDropdown) { + this.toggleDropdown(); + } + + if (this.open && !this.boards.length) { + gl.boardService.allBoards() + .then(res => res.json()) + .then((json) => { + this.loading = false; + this.boards = json; + }) + .catch(() => { + this.loading = false; + }); + } + }, + }, + created() { + this.state.currentBoard = this.currentBoard; + }, + }); +})(); diff --git a/app/assets/javascripts/boards/components/issue_card_inner.js b/app/assets/javascripts/boards/components/issue_card_inner.js index bf474879024498902a644584fb5b72bfa56a3672..cf1e340490a7a7579db8a4454486792a87fa18f4 100644 --- a/app/assets/javascripts/boards/components/issue_card_inner.js +++ b/app/assets/javascripts/boards/components/issue_card_inner.js @@ -31,6 +31,10 @@ gl.issueBoards.IssueCardInner = Vue.extend({ required: false, default: false, }, + groupId: { + type: Number, + required: false, + }, }, data() { return { @@ -64,7 +68,13 @@ gl.issueBoards.IssueCardInner = Vue.extend({ return this.issue.assignees.length > this.numberOverLimit; }, cardUrl() { - return `${this.issueLinkBase}/${this.issue.iid}`; + let baseUrl = this.issueLinkBase; + + if (this.groupId && this.issue.project) { + baseUrl = this.issueLinkBase.replace(':project_path', this.issue.project.path); + } + + return `${baseUrl}/${this.issue.iid}`; }, issueId() { if (this.issue.iid) { diff --git a/app/assets/javascripts/boards/components/milestone_select.js b/app/assets/javascripts/boards/components/milestone_select.js new file mode 100644 index 0000000000000000000000000000000000000000..f9c9ac7018747425a96691ad035b37d24a68171e --- /dev/null +++ b/app/assets/javascripts/boards/components/milestone_select.js @@ -0,0 +1,64 @@ +/* global BoardService */ + +import extraMilestones from '../mixins/extra_milestones'; + +export default { + props: { + board: { + type: Object, + required: true, + }, + milestonePath: { + type: String, + required: true, + }, + selectMilestone: { + type: Function, + required: true, + }, + }, + data() { + return { + loading: false, + milestones: [], + extraMilestones, + }; + }, + mounted() { + BoardService.loadMilestones.call(this); + }, + template: ` + + `, +}; diff --git a/app/assets/javascripts/boards/components/modal/footer.js b/app/assets/javascripts/boards/components/modal/footer.js index a656f0546c0e937e32ff68b2c9eef00a60b88d13..d61ddc9ea1381dac33efec1dc9298c0ec8baa16e 100644 --- a/app/assets/javascripts/boards/components/modal/footer.js +++ b/app/assets/javascripts/boards/components/modal/footer.js @@ -34,6 +34,7 @@ gl.issueBoards.ModalFooter = Vue.extend({ // Post the data to the backend gl.boardService.bulkUpdate(issueIds, { add_label_ids: [list.label.id], + milestone_id: this.state.currentBoard.milestone_id, }).catch(() => { new Flash('Failed to update issues, please try again.', 'alert'); diff --git a/app/assets/javascripts/boards/components/project_select.vue b/app/assets/javascripts/boards/components/project_select.vue new file mode 100644 index 0000000000000000000000000000000000000000..a3bbed842a409294f0d565c734b1939193197b07 --- /dev/null +++ b/app/assets/javascripts/boards/components/project_select.vue @@ -0,0 +1,103 @@ + + + diff --git a/app/assets/javascripts/boards/components/sidebar/remove_issue.js b/app/assets/javascripts/boards/components/sidebar/remove_issue.js index 1e623cf58b70124138d7dd808d82add1c2921295..c3ecda718df6285dd32880b57b3f8538baeb4808 100644 --- a/app/assets/javascripts/boards/components/sidebar/remove_issue.js +++ b/app/assets/javascripts/boards/components/sidebar/remove_issue.js @@ -25,7 +25,7 @@ gl.issueBoards.RemoveIssueBtn = Vue.extend({ }, computed: { updateUrl() { - return this.issueUpdate; + return this.issueUpdate.replace(':project_path', this.issue.project.path); }, }, methods: { @@ -44,6 +44,11 @@ gl.issueBoards.RemoveIssueBtn = Vue.extend({ label_ids: labelIds, }, }; + if (Store.state.currentBoard.milestone_id) { + data.issue.milestone_id = -1; + } + + // Post the remove data Vue.http.patch(this.updateUrl, data).catch(() => { new Flash('Failed to remove issue from board, please try again.', 'alert'); diff --git a/app/assets/javascripts/boards/icons/fullscreen_collapse.svg b/app/assets/javascripts/boards/icons/fullscreen_collapse.svg new file mode 100644 index 0000000000000000000000000000000000000000..6bd773dc4c5a8a306e823015e596b0f2a2d17c69 --- /dev/null +++ b/app/assets/javascripts/boards/icons/fullscreen_collapse.svg @@ -0,0 +1 @@ + diff --git a/app/assets/javascripts/boards/icons/fullscreen_expand.svg b/app/assets/javascripts/boards/icons/fullscreen_expand.svg new file mode 100644 index 0000000000000000000000000000000000000000..306073b8af2ba32f1f590f600bd0f28cb1c5fa53 --- /dev/null +++ b/app/assets/javascripts/boards/icons/fullscreen_expand.svg @@ -0,0 +1 @@ + diff --git a/app/assets/javascripts/boards/mixins/extra_milestones.js b/app/assets/javascripts/boards/mixins/extra_milestones.js new file mode 100644 index 0000000000000000000000000000000000000000..a2de065af8a5ab6f088dfd680a70fc4c6c24cff6 --- /dev/null +++ b/app/assets/javascripts/boards/mixins/extra_milestones.js @@ -0,0 +1,14 @@ +export default [ + { + id: null, + title: 'Any Milestone', + }, + { + id: -2, + title: 'Upcoming', + }, + { + id: -3, + title: 'Started', + }, +]; diff --git a/app/assets/javascripts/boards/models/issue.js b/app/assets/javascripts/boards/models/issue.js index 407db176446006af214734f82da7f5421b8e0a4f..a701a5804f3ac433cb3fa467799942ada38cddd1 100644 --- a/app/assets/javascripts/boards/models/issue.js +++ b/app/assets/javascripts/boards/models/issue.js @@ -4,6 +4,7 @@ /* global ListAssignee */ import Vue from 'vue'; +import IssueProject from './project'; class ListIssue { constructor (obj, defaultAvatar) { @@ -17,6 +18,12 @@ class ListIssue { this.assignees = []; this.selected = false; this.position = obj.relative_position || Infinity; + this.milestone_id = obj.milestone_id; + this.project_id = obj.project_id; + + if (obj.project) { + this.project = new IssueProject(obj.project); + } if (obj.milestone) { this.milestone = new ListMilestone(obj.milestone); @@ -87,7 +94,8 @@ class ListIssue { data.issue.label_ids = ['']; } - return Vue.http.patch(url, data); + const projectPath = this.project ? this.project.path : ''; + return Vue.http.patch(url.replace(':project_path', projectPath), data); } } diff --git a/app/assets/javascripts/boards/models/list.js b/app/assets/javascripts/boards/models/list.js index df2809e18052ae9980c2be14dbdb131e64dfd61c..db70d4e64791e16577af394123fe5770fd4bf98a 100644 --- a/app/assets/javascripts/boards/models/list.js +++ b/app/assets/javascripts/boards/models/list.js @@ -12,7 +12,7 @@ class List { this.position = obj.position; this.title = obj.title; this.type = obj.list_type; - this.preset = ['backlog', 'closed', 'blank'].indexOf(this.type) > -1; + this.preset = ['backlog', 'closed', 'blank', 'promotion'].indexOf(this.type) > -1; this.isExpandable = ['backlog', 'closed'].indexOf(this.type) > -1; this.isExpanded = true; this.page = 1; @@ -26,7 +26,7 @@ class List { this.label = new ListLabel(obj.label); } - if (this.type !== 'blank' && this.id) { + if (this.type !== 'blank' && this.type !== 'promotion' && this.id) { this.getIssues().catch(() => { // TODO: handle request error }); @@ -112,6 +112,7 @@ class List { .then((data) => { issue.id = data.id; issue.iid = data.iid; + issue.milestone = data.milestone; issue.project = data.project; if (this.issuesSize > 1) { diff --git a/app/assets/javascripts/boards/models/project.js b/app/assets/javascripts/boards/models/project.js new file mode 100644 index 0000000000000000000000000000000000000000..a3d5c7af7acee96301b8abb037df7824ed8a47d7 --- /dev/null +++ b/app/assets/javascripts/boards/models/project.js @@ -0,0 +1,6 @@ +export default class IssueProject { + constructor(obj) { + this.id = obj.id; + this.path = obj.path; + } +} diff --git a/app/assets/javascripts/boards/services/board_service.js b/app/assets/javascripts/boards/services/board_service.js index 38eea38f949987165fd98aa070ba168f4d8301a6..aad1cf571123295ab321859731f30f6094072f98 100644 --- a/app/assets/javascripts/boards/services/board_service.js +++ b/app/assets/javascripts/boards/services/board_service.js @@ -7,7 +7,7 @@ class BoardService { this.boards = Vue.resource(`${boardsEndpoint}{/id}.json`, {}, { issues: { method: 'GET', - url: `${gon.relative_url_root}/boards/${boardId}/issues.json`, + url: `${gon.relative_url_root}/-/boards/${boardId}/issues.json`, } }); this.lists = Vue.resource(`${listsEndpoint}{/id}`, {}, { @@ -16,7 +16,7 @@ class BoardService { url: `${listsEndpoint}/generate.json` } }); - this.issue = Vue.resource(`${gon.relative_url_root}/boards/${boardId}/issues{/id}`, {}); + this.issue = Vue.resource(`${gon.relative_url_root}/-/boards/${boardId}/issues{/id}`, {}); this.issues = Vue.resource(`${listsEndpoint}{/id}/issues`, {}, { bulkUpdate: { method: 'POST', @@ -25,6 +25,17 @@ class BoardService { }); } + allBoards () { + return this.boards.get(); + } + + createBoard (board) { + if (board.id) { + return this.boards.update({ id: board.id }, board); + } + return this.boards.save({}, board); + } + all () { return this.lists.get(); } @@ -88,6 +99,17 @@ class BoardService { return this.issues.bulkUpdate(data); } + + static loadMilestones(path) { + this.loading = true; + + return this.$http.get(this.milestonePath) + .then(resp => resp.json()) + .then((data) => { + this.milestones = data; + this.loading = false; + }); + } } window.BoardService = BoardService; diff --git a/app/assets/javascripts/boards/stores/boards_store.js b/app/assets/javascripts/boards/stores/boards_store.js index 43928e602d66f90f33ec43fc573104ae037743e7..fa93f6f2f6b8b271214c7f0ef475955ef888fe37 100644 --- a/app/assets/javascripts/boards/stores/boards_store.js +++ b/app/assets/javascripts/boards/stores/boards_store.js @@ -2,6 +2,7 @@ /* global List */ import _ from 'underscore'; import Cookies from 'js-cookie'; +import boardsStoreEE from 'ee/boards/stores/boards_store_ee'; window.gl = window.gl || {}; window.gl.issueBoards = window.gl.issueBoards || {}; @@ -24,6 +25,11 @@ gl.issueBoards.BoardsStore = { this.filter.path = gl.utils.getUrlParamsArray().join('&'); this.detail = { issue: {} }; }, + createNewListDropdownData() { + this.state.currentBoard = {}; + this.state.currentPage = ''; + this.state.reload = false; + }, addList (listObj, defaultAvatar) { const list = new List(listObj, defaultAvatar); this.state.lists.push(list); @@ -127,7 +133,13 @@ gl.issueBoards.BoardsStore = { return list[key] === val && byType; })[0]; }, - updateFiltersUrl () { - history.pushState(null, null, `?${this.filter.path}`); - } + updateFiltersUrl (replaceState = false) { + if (replaceState) { + history.replaceState(null, null, `?${this.filter.path}`); + } else { + history.pushState(null, null, `?${this.filter.path}`); + } + }, }; + +boardsStoreEE.initEESpecific(gl.issueBoards.BoardsStore); diff --git a/app/assets/javascripts/build.js b/app/assets/javascripts/build.js index ae1a23132a73bbcd5b1a29c4202dbcc5e21e2f54..e85a7bc1772897c85d11b30aac3ca164f375d4b1 100644 --- a/app/assets/javascripts/build.js +++ b/app/assets/javascripts/build.js @@ -166,7 +166,7 @@ window.Build = (function () { Build.prototype.getBuildTrace = function () { return $.ajax({ url: `${this.pageUrl}/trace.json`, - data: this.state, + data: { state: this.state }, }) .done((log) => { gl.utils.setCiStatusFavicon(`${this.pageUrl}/status.json`); diff --git a/app/assets/javascripts/burndown_chart/burndown_chart.js b/app/assets/javascripts/burndown_chart/burndown_chart.js new file mode 100644 index 0000000000000000000000000000000000000000..e37e0fe17dce62e55597c4cdc18883d7e51e6656 --- /dev/null +++ b/app/assets/javascripts/burndown_chart/burndown_chart.js @@ -0,0 +1,367 @@ +import d3 from 'd3'; + +const margin = { top: 5, right: 65, bottom: 30, left: 50 }; +const parseDate = d3.time.format('%Y-%m-%d').parse; +const bisectDate = d3.bisector(d => d.date).left; +const tooltipPadding = { x: 8, y: 3 }; +const tooltipDistance = 15; + +export default class BurndownChart { + constructor({ container, startDate, dueDate }) { + this.canvas = d3.select(container).append('svg') + .attr('height', '100%') + .attr('width', '100%'); + + // create svg nodes + this.chartGroup = this.canvas.append('g').attr('class', 'chart'); + this.xAxisGroup = this.chartGroup.append('g').attr('class', 'x axis'); + this.yAxisGroup = this.chartGroup.append('g').attr('class', 'y axis'); + this.idealLinePath = this.chartGroup.append('path').attr('class', 'ideal line'); + this.actualLinePath = this.chartGroup.append('path').attr('class', 'actual line'); + + this.xAxisGroup.append('line').attr('class', 'domain-line'); + + // create y-axis label + this.label = 'Remaining'; + const yAxisLabel = this.yAxisGroup.append('g').attr('class', 'axis-label'); + this.yAxisLabelText = yAxisLabel.append('text').text(this.label); + this.yAxisLabelBBox = this.yAxisLabelText.node().getBBox(); + this.yAxisLabelLineA = yAxisLabel.append('line'); + this.yAxisLabelLineB = yAxisLabel.append('line'); + + // create chart legend + this.chartLegendGroup = this.chartGroup.append('g').attr('class', 'legend'); + this.chartLegendGroup.append('rect'); + + this.chartLegendIdealKey = this.chartLegendGroup.append('g'); + this.chartLegendIdealKey.append('line').attr('class', 'ideal line'); + this.chartLegendIdealKey.append('text').text('Guideline'); + this.chartLegendIdealKeyBBox = this.chartLegendIdealKey.select('text').node().getBBox(); + + this.chartLegendActualKey = this.chartLegendGroup.append('g'); + this.chartLegendActualKey.append('line').attr('class', 'actual line'); + this.chartLegendActualKey.append('text').text('Progress'); + this.chartLegendActualKeyBBox = this.chartLegendActualKey.select('text').node().getBBox(); + + // create tooltips + this.chartFocus = this.chartGroup.append('g').attr('class', 'focus').style('display', 'none'); + this.chartFocus.append('circle').attr('r', 4); + this.tooltipGroup = this.chartFocus.append('g').attr('class', 'chart-tooltip'); + this.tooltipGroup.append('rect').attr('rx', 3).attr('ry', 3); + this.tooltipGroup.append('text'); + + this.chartOverlay = this.chartGroup.append('rect').attr('class', 'overlay') + .on('mouseover', () => this.chartFocus.style('display', null)) + .on('mouseout', () => this.chartFocus.style('display', 'none')) + .on('mousemove', () => this.handleMousemove()); + + // parse start and due dates + this.startDate = parseDate(startDate); + this.dueDate = parseDate(dueDate); + + // get width and height + const dimensions = this.canvas.node().getBoundingClientRect(); + this.width = dimensions.width; + this.height = dimensions.height; + this.chartWidth = this.width - (margin.left + margin.right); + this.chartHeight = this.height - (margin.top + margin.bottom); + + // set default scale domains + this.xMax = this.dueDate; + this.yMax = 1; + + // create scales + this.xScale = d3.time.scale() + .range([0, this.chartWidth]) + .domain([this.startDate, this.xMax]); + + this.yScale = d3.scale.linear() + .range([this.chartHeight, 0]) + .domain([0, this.yMax]); + + // create axes + this.xAxis = d3.svg.axis() + .scale(this.xScale) + .orient('bottom') + .tickFormat(d3.time.format('%b %-d')) + .tickPadding(6) + .tickSize(4, 0); + + this.yAxis = d3.svg.axis() + .scale(this.yScale) + .orient('left') + .tickPadding(6) + .tickSize(4, 0); + + // create lines + this.line = d3.svg.line() + .x(d => this.xScale(d.date)) + .y(d => this.yScale(d.value)); + + // render the chart + this.scheduleRender(); + } + + // set data and force re-render + setData(data, { label = 'Remaining', animate } = {}) { + this.data = data.map(datum => ({ + date: parseDate(datum[0]), + value: parseInt(datum[1], 10), + })).sort((a, b) => (a.date - b.date)); + + // adjust axis domain to correspond with data + this.xMax = Math.max(d3.max(this.data, d => d.date) || 0, this.dueDate); + this.yMax = d3.max(this.data, d => d.value) || 1; + + this.xScale.domain([this.startDate, this.xMax]); + this.yScale.domain([0, this.yMax]); + + // calculate the bounding box for the axis label if updated + // (this must be done here to prevent layout thrashing) + if (this.label !== label) { + this.label = label; + this.yAxisLabelBBox = this.yAxisLabelText.text(label).node().getBBox(); + } + + // set ideal line data + if (this.data.length > 1) { + const idealStart = this.data[0] || { date: this.startDate, value: 0 }; + const idealEnd = { date: this.dueDate, value: 0 }; + this.idealData = [idealStart, idealEnd]; + } + + this.scheduleLineAnimation = !!animate; + this.scheduleRender(); + } + + handleMousemove() { + if (!this.data) return; + + const mouseOffsetX = d3.mouse(this.chartOverlay.node())[0]; + const dateOffset = this.xScale.invert(mouseOffsetX); + const i = bisectDate(this.data, dateOffset, 1); + const d0 = this.data[i - 1]; + const d1 = this.data[i]; + if (d1 == null || dateOffset - d0.date < d1.date - dateOffset) { + this.renderTooltip(d0); + } else { + this.renderTooltip(d1); + } + } + + // reset width and height to match the svg element, then re-render if necessary + handleResize() { + const dimensions = this.canvas.node().getBoundingClientRect(); + if (this.width !== dimensions.width || this.height !== dimensions.height) { + this.width = dimensions.width; + this.height = dimensions.height; + + // adjust axis range to correspond with chart size + this.chartWidth = this.width - (margin.left + margin.right); + this.chartHeight = this.height - (margin.top + margin.bottom); + + this.xScale.range([0, this.chartWidth]); + this.yScale.range([this.chartHeight, 0]); + + this.scheduleRender(); + } + } + + scheduleRender() { + if (this.queuedRender == null) { + this.queuedRender = requestAnimationFrame(() => this.render()); + } + } + + render() { + this.queuedRender = null; + this.renderedTooltipPoint = null; // force tooltip re-render + + this.xAxis.ticks(Math.floor(this.chartWidth / 120)); + this.yAxis.ticks(Math.min(Math.floor(this.chartHeight / 60), this.yMax)); + + this.chartGroup.attr('transform', `translate(${margin.left}, ${margin.top})`); + this.xAxisGroup.attr('transform', `translate(0, ${this.chartHeight})`); + + this.xAxisGroup.call(this.xAxis); + this.yAxisGroup.call(this.yAxis); + + // replace x-axis line with one which continues into the right margin + this.xAxisGroup.select('.domain').remove(); + this.xAxisGroup.select('.domain-line').attr('x1', 0).attr('x2', this.chartWidth + margin.right); + + // update y-axis label + const axisLabelOffset = (this.yAxisLabelBBox.height / 2) - margin.left; + const axisLabelPadding = (this.chartHeight - this.yAxisLabelBBox.width - 10) / 2; + + this.yAxisLabelText + .attr('y', 0 - margin.left) + .attr('x', 0 - (this.chartHeight / 2)) + .attr('dy', '1em') + .style('text-anchor', 'middle') + .attr('transform', 'rotate(-90)'); + this.yAxisLabelLineA + .attr('x1', axisLabelOffset) + .attr('x2', axisLabelOffset) + .attr('y1', 0) + .attr('y2', axisLabelPadding); + this.yAxisLabelLineB + .attr('x1', axisLabelOffset) + .attr('x2', axisLabelOffset) + .attr('y1', this.chartHeight - axisLabelPadding) + .attr('y2', this.chartHeight); + + // update legend + const legendPadding = 10; + const legendSpacing = 5; + + const idealBBox = this.chartLegendIdealKeyBBox; + const actualBBox = this.chartLegendActualKeyBBox; + const keyWidth = Math.ceil(Math.max(idealBBox.width, actualBBox.width)); + const keyHeight = Math.ceil(Math.max(idealBBox.height, actualBBox.height)); + + const idealKeyOffset = legendPadding; + const actualKeyOffset = legendPadding + keyHeight + legendSpacing; + const legendWidth = (legendPadding * 2) + 24 + keyWidth; + const legendHeight = (legendPadding * 2) + (keyHeight * 2) + legendSpacing; + const legendOffset = (this.chartWidth + margin.right) - legendWidth - 1; + + this.chartLegendGroup.select('rect') + .attr('width', legendWidth) + .attr('height', legendHeight); + + this.chartLegendGroup.selectAll('text') + .attr('x', 24) + .attr('dy', '1em'); + this.chartLegendGroup.selectAll('line') + .attr('y1', keyHeight / 2) + .attr('y2', keyHeight / 2) + .attr('x1', 0) + .attr('x2', 18); + + this.chartLegendGroup.attr('transform', `translate(${legendOffset}, 0)`); + this.chartLegendIdealKey.attr('transform', `translate(${legendPadding}, ${idealKeyOffset})`); + this.chartLegendActualKey.attr('transform', `translate(${legendPadding}, ${actualKeyOffset})`); + + // update overlay + this.chartOverlay + .attr('fill', 'none') + .attr('pointer-events', 'all') + .attr('width', this.chartWidth) + .attr('height', this.chartHeight); + + // render lines if data available + if (this.data != null && this.data.length > 1) { + this.actualLinePath.datum(this.data).attr('d', this.line); + this.idealLinePath.datum(this.idealData).attr('d', this.line); + + if (this.scheduleLineAnimation === true) { + this.scheduleLineAnimation = false; + + // hide tooltips until animation is finished + this.chartFocus.attr('opacity', 0); + + this.constructor.animateLinePath(this.actualLinePath, 800, () => { + this.chartFocus.attr('opacity', null); + }); + } + } + } + + renderTooltip(datum) { + if (this.renderedTooltipPoint === datum) return; + this.renderedTooltipPoint = datum; + + // generate tooltip content + const format = d3.time.format('%b %-d, %Y'); + const tooltip = `${datum.value} ${this.label} / ${format(datum.date)}`; + + // move the tooltip point of origin to the point on the graph + const x = this.xScale(datum.date); + const y = this.yScale(datum.value); + + const textSize = this.tooltipGroup.select('text').text(tooltip).node().getBBox(); + const width = textSize.width + (tooltipPadding.x * 2); + const height = textSize.height + (tooltipPadding.y * 2); + + // calculate bounraries + const xMin = 0 - x - margin.left; + const yMin = 0 - y - margin.top; + const xMax = (this.chartWidth + margin.right) - x - width; + const yMax = (this.chartHeight + margin.bottom) - y - height; + + // try to fit tooltip above point + let xOffset = 0 - Math.floor(width / 2); + let yOffset = 0 - tooltipDistance - height; + + if (yOffset <= yMin) { + // else try to fit tooltip to the right + xOffset = tooltipDistance; + yOffset = 0 - Math.floor(height / 2); + + if (xOffset >= xMax) { + // else place tooltip on the left + xOffset = 0 - tooltipDistance - width; + } + } + + // ensure coordinates keep the entire tooltip in-bounds + xOffset = Math.max(xMin, Math.min(xMax, xOffset)); + yOffset = Math.max(yMin, Math.min(yMax, yOffset)); + + // move everything into place + this.chartFocus.attr('transform', `translate(${x}, ${y})`); + this.tooltipGroup.attr('transform', `translate(${xOffset}, ${yOffset})`); + + this.tooltipGroup.select('text') + .attr('dy', '1em') + .attr('x', tooltipPadding.x) + .attr('y', tooltipPadding.y); + + this.tooltipGroup.select('rect') + .attr('width', width) + .attr('height', height); + } + + animateResize(seconds = 5) { + this.ticksLeft = this.ticksLeft || 0; + if (this.ticksLeft <= 0) { + const interval = setInterval(() => { + this.ticksLeft -= 1; + if (this.ticksLeft <= 0) { + clearInterval(interval); + } + this.handleResize(); + }, 20); + } + this.ticksLeft = seconds * 50; + } + + static animateLinePath(path, duration = 1000, cb) { + // hack to run a callback at transition end + function after(transition, callback) { + let i = 0; + transition + .each(() => (i += 1)) + .each('end', function end(...args) { + i -= 1; + if (i === 0) { + callback.apply(this, args); + } + }); + } + + const lineLength = path.node().getTotalLength(); + path + .attr('stroke-dasharray', `${lineLength} ${lineLength}`) + .attr('stroke-dashoffset', lineLength) + .transition() + .duration(duration) + .ease('linear') + .attr('stroke-dashoffset', 0) + .call(after, () => { + path.attr('stroke-dasharray', null); + if (cb) cb(); + }); + } +} diff --git a/app/assets/javascripts/burndown_chart/index.js b/app/assets/javascripts/burndown_chart/index.js new file mode 100644 index 0000000000000000000000000000000000000000..88547663a4deaadccb47c3b41be2b8e09e841587 --- /dev/null +++ b/app/assets/javascripts/burndown_chart/index.js @@ -0,0 +1,50 @@ +import Cookies from 'js-cookie'; +import BurndownChart from './burndown_chart'; + +$(() => { + // handle hint dismissal + const hint = $('.burndown-hint'); + hint.on('click', '.dismiss-icon', () => { + hint.hide(); + Cookies.set('hide_burndown_message', 'true'); + }); + + // generate burndown chart (if data available) + const container = '.burndown-chart'; + const $chartElm = $(container); + + if ($chartElm.length) { + const startDate = $chartElm.data('startDate'); + const dueDate = $chartElm.data('dueDate'); + const chartData = $chartElm.data('chartData'); + const openIssuesCount = chartData.map(d => [d[0], d[1]]); + const openIssuesWeight = chartData.map(d => [d[0], d[2]]); + + const chart = new BurndownChart({ container, startDate, dueDate }); + + let currentView = 'count'; + chart.setData(openIssuesCount, { label: 'Open issues', animate: true }); + + $('.js-burndown-data-selector').on('click', 'button', function switchData() { + const $this = $(this); + const show = $this.data('show'); + if (currentView !== show) { + currentView = show; + $this.addClass('active').siblings().removeClass('active'); + switch (show) { + case 'count': + chart.setData(openIssuesCount, { label: 'Open issues', animate: true }); + break; + case 'weight': + chart.setData(openIssuesWeight, { label: 'Open issue weight', animate: true }); + break; + default: + break; + } + } + }); + + window.addEventListener('resize', () => chart.animateResize(1)); + $(document).on('click', '.js-sidebar-toggle', () => chart.animateResize(2)); + } +}); diff --git a/app/assets/javascripts/commons/jquery.js b/app/assets/javascripts/commons/jquery.js index b93e94a3c977dc819cbde1887510ab783d69dac9..358935d8488df00c6a8db02f60a2a1969bbdff5c 100644 --- a/app/assets/javascripts/commons/jquery.js +++ b/app/assets/javascripts/commons/jquery.js @@ -8,3 +8,6 @@ import 'vendor/jquery.atwho'; import 'vendor/jquery.scrollTo'; import 'vendor/jquery.waitforimages'; import 'select2/select2'; + +// EE-only +import 'vendor/jquery.tablesorter'; diff --git a/app/assets/javascripts/confirm_danger_modal.js b/app/assets/javascripts/confirm_danger_modal.js index b375b61202eea1b311fa3fd80440a76dcdc2f5cb..ce79c31d6ba576638d705addacbe037ce593f3c0 100644 --- a/app/assets/javascripts/confirm_danger_modal.js +++ b/app/assets/javascripts/confirm_danger_modal.js @@ -1,10 +1,14 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife, one-var, no-var, camelcase, one-var-declaration-per-line, no-else-return, max-len */ window.ConfirmDangerModal = (function() { - function ConfirmDangerModal(form, text) { - var project_path, submit; + function ConfirmDangerModal(form, text, arg) { + var project_path, submit, warningMessage; + warningMessage = (arg != null ? arg : {}).warningMessage; this.form = form; - $('.js-confirm-text').text(text || ''); + $('.js-confirm-text').html(text || ''); + if (warningMessage) { + $('.js-warning-text').html(warningMessage); + } $('.js-confirm-danger-input').val(''); $('#modal-confirm-danger').modal('show'); project_path = $('.js-confirm-danger-match').text(); diff --git a/app/assets/javascripts/copy_to_clipboard.js b/app/assets/javascripts/copy_to_clipboard.js index 1f3c7e1772d22208a348b095b437597e871b927f..e0d724ab544d39feebaed563d240007e6f82036c 100644 --- a/app/assets/javascripts/copy_to_clipboard.js +++ b/app/assets/javascripts/copy_to_clipboard.js @@ -1,6 +1,6 @@ /* eslint-disable func-names, space-before-function-paren, one-var, no-var, one-var-declaration-per-line, prefer-template, quotes, no-unused-vars, prefer-arrow-callback, max-len */ -import Clipboard from 'vendor/clipboard'; +import Clipboard from 'clipboard'; var genericError, genericSuccess, showTooltip; diff --git a/app/assets/javascripts/diff_notes/components/jump_to_discussion.js b/app/assets/javascripts/diff_notes/components/jump_to_discussion.js index 298f737a2bc4c37958d2186b43d9f95380e75a82..497c23f014f791bf8bfbd4bf5a3d919cbddce100 100644 --- a/app/assets/javascripts/diff_notes/components/jump_to_discussion.js +++ b/app/assets/javascripts/diff_notes/components/jump_to_discussion.js @@ -4,6 +4,8 @@ import Vue from 'vue'; +import '../mixins/discussion'; + const JumpToDiscussion = Vue.extend({ mixins: [DiscussionMixins], props: { diff --git a/app/assets/javascripts/diff_notes/components/resolve_count.js b/app/assets/javascripts/diff_notes/components/resolve_count.js index 96e5a4403578b8426fb40910256636ee84716a0e..fe7cf8f5fc1db28eb5dabdcf7d2d8032ad71c09e 100644 --- a/app/assets/javascripts/diff_notes/components/resolve_count.js +++ b/app/assets/javascripts/diff_notes/components/resolve_count.js @@ -4,6 +4,8 @@ import Vue from 'vue'; +import '../mixins/discussion'; + window.ResolveCount = Vue.extend({ mixins: [DiscussionMixins], props: { diff --git a/app/assets/javascripts/diff_notes/diff_notes_bundle.js b/app/assets/javascripts/diff_notes/diff_notes_bundle.js index 0863c3406bda06838b41f946ea8c81d81bc4e60e..a955467cec466622dadc8549ce1f3bf425090aa5 100644 --- a/app/assets/javascripts/diff_notes/diff_notes_bundle.js +++ b/app/assets/javascripts/diff_notes/diff_notes_bundle.js @@ -1,5 +1,6 @@ /* eslint-disable func-names, comma-dangle, new-cap, no-new, max-len */ /* global ResolveCount */ +/* global ResolveServiceClass */ import Vue from 'vue'; import './models/discussion'; @@ -70,7 +71,7 @@ $(() => { el: '#resolve-count-app', components: { 'resolve-count': ResolveCount - } + }, }); $(window).trigger('resize.nav'); diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index f3b537c83e213d84802ca1a8b183959c73127061..da5ebe1754b42fde4891a85a11098d8d3e23202c 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -9,6 +9,7 @@ /* global MilestoneSelect */ /* global Commit */ /* global CommitsList */ +/* global NewCommitForm */ /* global NewBranchForm */ /* global NotificationsForm */ /* global NotificationsDropdown */ @@ -27,6 +28,7 @@ /* global MergeRequest */ /* global Compare */ /* global CompareAutocomplete */ +/* global PathLocks */ /* global ProjectFindFile */ /* global ProjectNew */ /* global ProjectShow */ @@ -35,6 +37,8 @@ /* global Shortcuts */ /* global ShortcutsFindFile */ /* global Sidebar */ +/* global WeightSelect */ +/* global AdminEmailSelect */ /* global ShortcutsWiki */ import Issue from './issue'; @@ -52,6 +56,8 @@ import UserCallout from './user_callout'; import ShortcutsWiki from './shortcuts_wiki'; import Pipelines from './pipelines'; import BlobViewer from './blob/viewer/index'; +import GeoNodeForm from './geo/geo_node_form'; +import GeoNodes from './geo_nodes'; import AutoWidthDropdownSelect from './issuable/auto_width_dropdown_select'; import UsersSelect from './users_select'; import RefSelectDropdown from './ref_select_dropdown'; @@ -78,6 +84,12 @@ import GpgBadges from './gpg_badges'; import UserFeatureHelper from './helpers/user_feature_helper'; import initChangesDropdown from './init_changes_dropdown'; +// EE-only +import ApproversSelect from './approvers_select'; +import AuditLogs from './audit_logs'; +import initGeoInfoModal from './init_geo_info_modal'; +import initGroupAnalytics from './init_group_analytics'; + (function() { var Dispatcher; @@ -111,6 +123,19 @@ import initChangesDropdown from './init_changes_dropdown'; }); }); + function initBlobEE() { + const dataEl = document.getElementById('js-file-lock'); + + if (dataEl) { + const { + toggle_path, + path, + } = JSON.parse(dataEl.innerHTML); + + PathLocks.init(toggle_path, path); + } + } + function initBlob() { new LineHighlighter(); @@ -136,6 +161,8 @@ import initChangesDropdown from './init_changes_dropdown'; actionTextPieces: document.querySelectorAll('.js-file-fork-suggestion-section-action'), }) .init(); + + initBlobEE(); } const filteredSearchEnabled = gl.FilteredSearchManager && document.querySelector('.filtered-search'); @@ -179,6 +206,7 @@ import initChangesDropdown from './init_changes_dropdown'; new ProjectSelect(); break; case 'projects:milestones:show': + new UserCallout(); case 'groups:milestones:show': case 'dashboard:milestones:show': new Milestone(); @@ -248,6 +276,7 @@ import initChangesDropdown from './init_changes_dropdown'; new IssuableForm($('.issue-form')); new LabelsSelect(); new MilestoneSelect(); + new WeightSelect(); new gl.IssuableTemplateSelectors(); break; case 'projects:merge_requests:creations:new': @@ -264,6 +293,7 @@ import initChangesDropdown from './init_changes_dropdown'; action: mrNewSubmitNode.dataset.mrSubmitAction, }); } + new UserCallout(); case 'projects:merge_requests:creations:diffs': case 'projects:merge_requests:edit': new gl.Diff(); @@ -342,6 +372,9 @@ import initChangesDropdown from './init_changes_dropdown'; shortcut_handler = new ShortcutsNavigation(); GpgBadges.fetch(); break; + case 'projects:imports:show': + new ProjectImport(); + break; case 'projects:show': shortcut_handler = new ShortcutsNavigation(); new NotificationsForm(); @@ -350,14 +383,21 @@ import initChangesDropdown from './init_changes_dropdown'; if ($('#tree-slider').length) new TreeView(); if ($('.blob-viewer').length) new BlobViewer(); if ($('.project-show-activity').length) new gl.Activities(); + $('#tree-slider').waitForImages(function() { gl.utils.ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath); }); + + initGeoInfoModal(); break; case 'projects:edit': + new UsersSelect(); + new GroupsSelect(); setupProjectEdit(); // Initialize expandable settings panels initSettingsPanels(); + new UserCallout({ className: 'js-service-desk-callout' }); + new UserCallout({ className: 'js-mr-approval-callout' }); break; case 'projects:imports:show': new ProjectImport(); @@ -425,6 +465,14 @@ import initChangesDropdown from './init_changes_dropdown'; new TreeView(); new BlobViewer(); new NewCommitForm($('.js-create-dir-form')); + + if (document.querySelector('.js-tree-content').dataset.pathLocksAvailable === 'true') { + PathLocks.init( + document.querySelector('.js-tree-content').dataset.pathLocksToggle, + document.querySelector('.js-tree-content').dataset.pathLocksPath, + ); + } + new UserCallout({ setCalloutPerProject: true }); $('#tree-slider').waitForImages(function() { gl.utils.ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath); @@ -490,8 +538,21 @@ import initChangesDropdown from './init_changes_dropdown'; break; case 'search:show': new Search(); + new UserCallout(); + break; + case 'projects:mirrors:show': + case 'projects:mirrors:update': + new UsersSelect(); + break; + case 'admin:emails:show': + new AdminEmailSelect(); + break; + case 'admin:audit_logs:index': + new AuditLogs(); break; case 'projects:settings:repository:show': + new UsersSelect(); + new UserCallout(); // Initialize expandable settings panels initSettingsPanels(); break; @@ -523,6 +584,23 @@ import initChangesDropdown from './init_changes_dropdown'; case 'admin:impersonation_tokens:index': new gl.DueDateSelectors(); break; + case 'admin:licenses:new': + const $licenseFile = $('.license-file'); + const $licenseKey = $('.license-key'); + + const showLicenseType = () => { + const $checkedFile = $('input[name="license_type"]:checked').val() === 'file'; + + $licenseFile.toggle($checkedFile); + $licenseKey.toggle(!$checkedFile); + }; + + $('input[name="license_type"]').on('change', showLicenseType); + showLicenseType(); + break; + case 'groups:analytics:show': + initGroupAnalytics(); + break; } switch (path[0]) { case 'sessions': @@ -557,6 +635,10 @@ import initChangesDropdown from './init_changes_dropdown'; case 'abuse_reports': new gl.AbuseReports(); break; + case 'geo_nodes': + new GeoNodes($('.geo-nodes')); + new GeoNodeForm($('.js-geo-node-form')); + break; } break; case 'dashboard': @@ -577,6 +659,7 @@ import initChangesDropdown from './init_changes_dropdown'; case 'edit': shortcut_handler = new ShortcutsNavigation(); new ProjectNew(); + new ApproversSelect(); import(/* webpackChunkName: 'project_permissions' */ './projects/permissions') .then(permissions => permissions.default()) .catch(() => {}); @@ -616,7 +699,7 @@ import initChangesDropdown from './init_changes_dropdown'; case 'builds': case 'hooks': case 'services': - case 'protected_branches': + case 'repository': shortcut_handler = new ShortcutsNavigation(); } break; diff --git a/app/assets/javascripts/dropzone_input.js b/app/assets/javascripts/dropzone_input.js index 975903159be805b310ebd4ce656df8b8ceaf413f..591dfe58d95e384f2ffac0eae931eb947be9aa34 100644 --- a/app/assets/javascripts/dropzone_input.js +++ b/app/assets/javascripts/dropzone_input.js @@ -238,9 +238,12 @@ window.DropzoneInput = (function() { }; const insertToTextArea = function(filename, url) { - return $(child).val(function(index, val) { + const $child = $(child); + $child.val(function(index, val) { return val.replace(`{{${filename}}}`, url); }); + + $child.trigger('change'); }; const appendToTextArea = function(url) { diff --git a/app/assets/javascripts/ee_trial_banner/ee_trial_banner.js b/app/assets/javascripts/ee_trial_banner/ee_trial_banner.js new file mode 100644 index 0000000000000000000000000000000000000000..654006761b4ce6d6df8a9bf32cb0287b9393dbce --- /dev/null +++ b/app/assets/javascripts/ee_trial_banner/ee_trial_banner.js @@ -0,0 +1,102 @@ +import Cookies from 'js-cookie'; + +export default class EETrialBanner { + constructor($trialBanner) { + this.COOKIE_KEY = 'show_ee_trial_banner'; + this.$trialBanner = $trialBanner; + this.$mainNavbar = this.$trialBanner.siblings('.js-navbar-gitlab'); + this.$secondaryNavbar = this.$mainNavbar.siblings('.js-page-with-sidebar'); + + this.licenseExpiresOn = new Date(this.$trialBanner.data('license-expiry')); + } + + init() { + // Wait for navbars to render before querying + this.setCookies(); + this.$trialBanner.on('close.bs.alert', e => this.handleTrialBannerDismiss(e)); + } + + /** + * Trial Expiring/Expired Banner has two stages; + * 1. Show banner when user enters last 7 days of trial + * 2. Show banner again when last 7 days are over and license has expired + * + * Stage 1: + * Banner is showed when `trial_license_message` is sent by backend + * for the first time (in `app/views/layouts/header/_default.html.haml`). + * Here, we perform following steps; + * + * 1. Set cookie `show_ee_trial_banner` with expiry same as license + * 2. Set cookie value to `true` + * 3. Show banner using `toggleBanner(true)` + * + * At this stage, if user dismisses banner, we set cookie value to `false` + * and everytime page is initialized, we check for cookie existence as + * well as its value, and decide show/hide status of banner + * + * Stage 2: + * At this point, Cookie we had set earlier will be expired and + * backend will now send updated message in `trial_license_message`. + * Here, we perform following steps; + * + * 1. Check if cookie is defined (it'll not be defined as it is expired now) + * 2. If cookie is gone, we re-set `show_ee_trial_banner` cookie but with + * expiry of 20 years + * 3. Set cookie value to `true` + * 4. Show banner using `toggleBanner(true)`, which now has updated message + * + * At this stage, if user dismisses banner, we set cookie value to `false` + * and our existing logic of show/hide banner based on cookie value continues + * to work. And since, cookie is set to expire after 20 years, user won't be + * seeing banner again. + */ + setCookies() { + const today = new Date(); + + // Check if Cookie is defined + if (!Cookies.get(this.COOKIE_KEY)) { + // Cookie was not defined, let's define with default value + + // Check if License is yet to expire + if (today < this.licenseExpiresOn) { + // License has not expired yet, we show initial banner of 7 days + // with cookie set to validity same as license expiry + Cookies.set(this.COOKIE_KEY, 'true', { expires: this.licenseExpiresOn }); + } else { + // License is already expired so we show final Banner with cookie set to 20 years validity. + Cookies.set(this.COOKIE_KEY, 'true', { expires: 7300 }); + } + + this.toggleBanner(true); + } else { + // Cookie was defined, let's read value and show/hide banner + this.toggleBanner(Cookies.get(this.COOKIE_KEY) === 'true'); + } + } + + toggleMainNavbarMargin(state) { + if (this.$mainNavbar.length) { + this.$mainNavbar.toggleClass('has-trial-banner', state); + } + } + + toggleSecondaryNavbarMargin(state) { + if (this.$secondaryNavbar.length) { + this.$secondaryNavbar.toggleClass('has-trial-banner', state); + } + } + + toggleBanner(state) { + this.$trialBanner.toggleClass('hidden', !state); + this.toggleMainNavbarMargin(state); + this.toggleSecondaryNavbarMargin(state); + } + + handleTrialBannerDismiss() { + this.toggleMainNavbarMargin(false); + this.toggleSecondaryNavbarMargin(false); + if (Cookies.get(this.COOKIE_KEY)) { + Cookies.set(this.COOKIE_KEY, 'false'); + } + } +} diff --git a/app/assets/javascripts/ee_trial_banner/index.js b/app/assets/javascripts/ee_trial_banner/index.js new file mode 100644 index 0000000000000000000000000000000000000000..406c1c2c9c1c468615829fc8727095a999937c98 --- /dev/null +++ b/app/assets/javascripts/ee_trial_banner/index.js @@ -0,0 +1,9 @@ +import EETrialBanner from './ee_trial_banner'; + +$(() => { + const $trialBanner = $('.js-gitlab-ee-license-banner'); + if ($trialBanner.length) { + const eeTrialBanner = new EETrialBanner($trialBanner); + eeTrialBanner.init(); + } +}); diff --git a/app/assets/javascripts/environments/components/deploy_board_component.vue b/app/assets/javascripts/environments/components/deploy_board_component.vue new file mode 100644 index 0000000000000000000000000000000000000000..29a4d03a35f5d991c6883e70854be91e00af09f3 --- /dev/null +++ b/app/assets/javascripts/environments/components/deploy_board_component.vue @@ -0,0 +1,138 @@ + +