diff --git a/.flayignore b/.flayignore index 47597025115c9cd540035b84fe5a859c12c92287..2ea12099ca90de9a1601f40ffe9e429c5544f85f 100644 --- a/.flayignore +++ b/.flayignore @@ -1,5 +1,10 @@ *.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 diff --git a/.gitignore b/.gitignore index 89da29fd7904f484d7b65c7d9e1fef05b9a0994e..7b6cae268db4088466fddb55677b8c1904eb25da 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,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 b442e48a3d0ecb2404c4028433d712a8a236a215..9eeebc92bca421b09d9de9a9bc05f67afae0443a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -8,6 +8,7 @@ cache: variables: MYSQL_ALLOW_EMPTY_PASSWORD: "1" + ELASTIC_URL: "http://elasticsearch:9200" RAILS_ENV: "test" NODE_ENV: "test" SIMPLECOV: "true" @@ -17,6 +18,8 @@ variables: GET_SOURCES_ATTEMPTS: "3" 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 + # This hack is needed to make ES not that memory hungry + ES_JAVA_OPTS: "-Xms256m -Xmx256m" before_script: - bundle --version @@ -54,11 +57,13 @@ stages: services: - postgres:9.2 - redis:alpine + - elasticsearch:5.3 .use-mysql: &use-mysql services: - mysql:latest - redis:alpine + - elasticsearch:5.3 .only-master-and-ee-or-mysql: &only-master-and-ee-or-mysql only: @@ -156,6 +161,7 @@ build-package: variables: SETUP_DB: "false" USE_BUNDLE_INSTALL: "false" + EE_PACKAGE: "true" stage: build when: manual script: @@ -315,26 +321,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 stage: test 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 8f611a9670221a5ea531ac43ca0d0c888e369ba5..85be90346768d9b4262055e519c2aea639851177 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -20,6 +20,7 @@ AllCops: - 'node_modules/**/*' - 'db/*' - 'db/fixtures/**/*' + - 'db/geo/*' - 'tmp/**/*' - 'bin/**/*' - 'generator_templates/**/*' @@ -349,6 +350,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: @@ -983,6 +985,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..0f015af047e26a23457d827c7802462e77ea6437 --- /dev/null +++ b/CHANGELOG-EE.md @@ -0,0 +1,1473 @@ +Please view this file on the master branch, on stable branches it's out of date. + +## 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.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.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.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 e5567dc3b398d6f6cb67e7c14f53c617f8732990..0a7c1fdd2cfb519d8047719902f48128fe34f18a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,10 +8,13 @@ entry. ## 9.2.4 (2017-06-02) +- No changes. - Fix visibility when referencing snippets. ## 9.2.3 (2017-05-31) +- No changes. +- No changes. - Move uploads from 'public/uploads' to 'public/uploads/system'. - Escapes html content before appending it to the DOM. - Restrict API X-Frame-Options to same origin. @@ -42,6 +45,7 @@ entry. - Display slash commands outcome when previewing Markdown. !10054 (Rares Sfirlogea) - Resolve "Add more tests for spec/controllers/projects/builds_controller_spec.rb". !10244 (dosuken123) - Add keyboard edit shotcut for wiki. !10245 (George Andrinopoulos) +- Update issue board cards design. !10353 - Redirect old links after renaming a user/group/project. !10370 - Add system note on description change of issue/merge request. !10392 (blackst0ne) - Improve validation of namespace & project paths. !10413 @@ -156,6 +160,7 @@ entry. - Remove `#` being added on commit sha in MR widget. - Remove spinner from loading comment. - Fixes an issue preventing screen readers from reading some icons. +- Add metrics events for incoming emails. - Load milestone tabs asynchronously to increase initial load performance. - [BB Importer] Save the error trace and the whole raw document to debug problems easier. - Fixed branches dropdown rendering branch names as HTML. @@ -428,6 +433,7 @@ entry. - Fix API group/issues default state filter. (Alexander Randa) - Prevent builds dropdown to close when the user clicks in a build. - Display all closed issues in “done” board list. +- added focus mode button to issue boards. - Remove no-new annotation from file_template_mediator.js. - Changed dropdown style slightly. - Change gfm textarea to use monospace font. @@ -964,6 +970,7 @@ entry. ## 8.17.0 (2017-02-22) - API: Fix file downloading. !0 (8267) +- Read true-up info from license and validate it. !1159 - Changed composer installer script in the CI PHP example doc. !4342 (Jeffrey Cafferata) - Display fullscreen button on small screens. !5302 (winniehell) - Add system hook for when a project is updated (other than rename/transfer). !5711 (Tommy Beadle) @@ -1156,8 +1163,6 @@ entry. ## 8.16.7 (2017-02-27) -- No changes. -- No changes. - Fix MR changes tab size count when there are over 100 files in the diff. ## 8.16.6 (2017-02-17) @@ -1357,6 +1362,7 @@ entry. ## 8.15.8 (2017-03-19) +- Read true-up info from license and validate it. !1159 - Only show public emails in atom feeds. - To protect against Server-side Request Forgery project import URLs are now prohibited against localhost or the server IP except for the assigned instance URL and port. Imports are also prohibited from ports below 1024 with the exception of ports 22, 80, and 443. @@ -1371,6 +1377,14 @@ entry. - Prevent the GitHub importer from assigning labels and comments to merge requests or issues belonging to other projects. - Patch XSS vulnerability in RDOC support. +## 8.15.5 (2017-01-20) + +- Ensure export files are removed after a namespace is deleted. +- Don't allow project guests to subscribe to merge requests through the API. (Robert Schilling) +- Prevent users from creating notes on resources they can't access. +- Prevent users from deleting system deploy keys via the project deploy key API. +- Upgrade omniauth gem to 1.3.2. + ## 8.15.4 (2017-01-09) - Make successful pipeline emails off for watchers. !8176 @@ -1653,6 +1667,14 @@ entry. - Speed up group milestone index by passing group_id to IssuesFinder. !8363 - Ensure issuable state changes only fire webhooks once. +## 8.14.7 (2017-01-21) + +- Ensure export files are removed after a namespace is deleted. +- Don't allow project guests to subscribe to merge requests through the API. (Robert Schilling) +- Prevent users from creating notes on resources they can't access. +- Prevent users from deleting system deploy keys via the project deploy key API. +- Upgrade omniauth gem to 1.3.2. + ## 8.14.6 (2017-01-10) - Update the gitlab-markup gem to the version 1.5.1. !8509 @@ -1935,6 +1957,14 @@ entry. - Fix "Without projects" filter. !6611 (Ben Bodenmiller) - Fix 404 when visit /projects page +## 8.13.12 (2017-01-21) + +- Ensure export files are removed after a namespace is deleted. +- Don't allow project guests to subscribe to merge requests through the API. (Robert Schilling) +- Prevent users from creating notes on resources they can't access. +- Prevent users from deleting system deploy keys via the project deploy key API. +- Upgrade omniauth gem to 1.3.2. + ## 8.13.11 (2017-01-10) - Update the gitlab-markup gem to the version 1.5.1. !8509 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8b6c87ae51870e253b0009a9ecee9ad8a96e32df..bd03c6b0c94d88fd383561b34c077065853e919b 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) @@ -612,6 +613,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/Gemfile b/Gemfile index e197f53d9b57485622341709acfd726ffac0c46c..03293eb5d37b81f95ae33839b15bf6a51dc00813 100644 --- a/Gemfile +++ b/Gemfile @@ -12,7 +12,7 @@ gem 'sprockets', '~> 3.7.0' gem 'default_value_for', '~> 3.0.0' # Supported DBs -gem 'mysql2', '~> 0.3.16', group: :mysql +gem 'mysql2', '~> 0.4.5', group: :mysql gem 'pg', '~> 0.18.2', group: :postgres gem 'rugged', '~> 0.25.1.1' @@ -33,13 +33,14 @@ gem 'omniauth-gitlab', '~> 1.0.2' gem 'omniauth-google-oauth2', '~> 0.4.1' gem 'omniauth-kerberos', '~> 0.3.0', group: :kerberos gem 'omniauth-oauth2-generic', '~> 0.2.2' -gem 'omniauth-saml', '~> 1.7.0' -gem 'omniauth-shibboleth', '~> 1.2.0' -gem 'omniauth-twitter', '~> 1.2.0' -gem 'omniauth_crowd', '~> 2.2.0' -gem 'omniauth-authentiq', '~> 0.3.0' -gem 'rack-oauth2', '~> 1.2.1' -gem 'jwt', '~> 1.5.6' +gem 'omniauth-saml', '~> 1.7.0' +gem 'omniauth-shibboleth', '~> 1.2.0' +gem 'omniauth-twitter', '~> 1.2.0' +gem 'omniauth_crowd', '~> 2.2.0' +gem 'gssapi', group: :kerberos +gem 'omniauth-authentiq', '~> 0.3.0' +gem 'rack-oauth2', '~> 1.2.1' +gem 'jwt', '~> 1.5.6' # Spam and anti-bot protection gem 'recaptcha', '~> 3.0', require: 'recaptcha/rails' @@ -61,6 +62,7 @@ gem 'browser', '~> 2.2' # GitLab fork with several improvements to original library. For full list of changes # see https://github.com/intridea/omniauth-ldap/compare/master...gitlabhq:master gem 'gitlab_omniauth-ldap', '~> 1.2.1', require: 'omniauth-ldap' +gem 'net-ldap' # Git Wiki # Required manually in config/initializers/gollum.rb to control load order @@ -108,6 +110,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' @@ -245,12 +254,13 @@ gem 'font-awesome-rails', '~> 4.7' gem 'gemojione', '~> 3.0' gem 'gon', '~> 6.1.0' gem 'jquery-atwho-rails', '~> 1.3.2' -gem 'jquery-rails', '~> 4.1.0' -gem 'request_store', '~> 1.3' -gem 'select2-rails', '~> 3.5.9' -gem 'virtus', '~> 1.0.1' -gem 'net-ssh', '~> 3.0.1' -gem 'base32', '~> 0.3.0' +gem 'jquery-rails', '~> 4.1.0' +gem 'request_store', '~> 1.3' +gem 'select2-rails', '~> 3.5.9' +gem 'virtus', '~> 1.0.1' +gem 'net-ssh', '~> 3.0.1' +gem 'base32', '~> 0.3.0' +gem "gitlab-license", "~> 1.0" # Sentry integration gem 'sentry-raven', '~> 2.4.0' diff --git a/Gemfile.lock b/Gemfile.lock index b5f9c3beca75f1f70307e05d1afc1be99621a6b8..52d90bc936455227ab7cd0582c528d4b9096167c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -67,6 +67,14 @@ GEM execjs json awesome_print (1.2.0) + aws-sdk (2.7.8) + aws-sdk-resources (= 2.7.8) + aws-sdk-core (2.7.8) + aws-sigv4 (~> 1.0) + jmespath (~> 1.0) + aws-sdk-resources (2.7.8) + aws-sdk-core (= 2.7.8) + aws-sigv4 (1.0.0) axiom-types (0.1.1) descendants_tracker (~> 0.0.4) ice_nine (~> 0.11.0) @@ -171,6 +179,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) @@ -195,6 +216,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.5) + aws-sdk (~> 2.1) + faraday (~> 0.9) faraday_middleware-multi_json (0.0.6) faraday_middleware multi_json @@ -290,6 +314,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 (1.2.1) net-ldap (~> 0.9) @@ -351,6 +376,8 @@ GEM grpc (1.2.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.21.0) @@ -399,6 +426,7 @@ GEM jira-ruby (1.1.2) activesupport 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) @@ -466,7 +494,7 @@ GEM tool (~> 0.2) mustermann-grape (0.4.0) mustermann (= 0.4.0) - mysql2 (0.3.20) + mysql2 (0.4.5) net-ldap (0.12.1) net-ssh (3.0.1) netrc (0.11.0) @@ -883,6 +911,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) benchmark-ips (~> 2.3.0) @@ -913,10 +942,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.11.0) + faraday_middleware-aws-signers-v4 ffaker (~> 2.4) flay (~> 2.8.0) flipper (~> 0.10.2) @@ -939,6 +972,7 @@ DEPENDENCIES gitaly (~> 0.8.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 (~> 1.2.1) gollum-lib (~> 4.2) @@ -947,6 +981,7 @@ DEPENDENCIES google-api-client (~> 0.8.6) grape (~> 0.19.0) grape-entity (~> 0.6.0) + gssapi haml_lint (~> 0.21.0) hamlit (~> 2.6.1) hashie-forbidden_attributes @@ -972,7 +1007,8 @@ DEPENDENCIES method_source (~> 0.8) minitest (~> 5.7.0) mousetrap-rails (~> 1.4.6) - mysql2 (~> 0.3.16) + mysql2 (~> 0.4.5) + net-ldap net-ssh (~> 3.0.1) nokogiri (~> 1.6.7, >= 1.6.7.2) oauth2 (~> 1.3.0) 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 59de828e1ac6d5812da6e8eda500c170e789769c..279a3b6ed0d103307e6c37d2e62ba2757c926ac8 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,50 @@ # 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) [![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) [![Gitter](https://badges.gitter.im/gitlabhq/gitlabhq.svg)](https://gitter.im/gitlabhq/gitlabhq?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) ## 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=rake+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=rake+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/). @@ -106,7 +136,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/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 6680834a8d11dfde1119fdec04ee2b3be0cf8ceb..c12fb96056810e575730c2ef35e0121bdd2aa3fe 100644 --- a/app/assets/javascripts/api.js +++ b/app/assets/javascripts/api.js @@ -10,10 +10,10 @@ 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', - group(groupId, callback) { const url = Api.buildUrl(Api.groupPath) .replace(':id', groupId); @@ -25,7 +25,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, @@ -149,6 +149,33 @@ 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({ + private_token: gon.api_token, + search: query, + per_page: 20, + active: true, + }), + dataType: 'json', + }) + .done(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..f121da55e53b3829d51930f4dc8feb5e61b7f9d6 --- /dev/null +++ b/app/assets/javascripts/audit_logs.js @@ -0,0 +1,50 @@ +/* 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(); + }); + + $('form.filter-form').on('submit', function applyFilters(event) { + event.preventDefault(); + gl.utils.visitUrl(`${this.action}?${$(this).serialize()}`); + }); + } + + 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/boards/boards_bundle.js b/app/assets/javascripts/boards/boards_bundle.js index b94009ee76b7f18709ca683bece10dcf48e6b8c2..5210df31094cf41666461f870becaa3c994b590e 100644 --- a/app/assets/javascripts/boards/boards_bundle.js +++ b/app/assets/javascripts/boards/boards_bundle.js @@ -23,12 +23,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('.js-focus-mode-board'); window.gl = window.gl || {}; @@ -59,6 +64,7 @@ $(() => { rootPath: $boardApp.dataset.rootPath, bulkUpdatePath: $boardApp.dataset.bulkUpdatePath, detailIssue: Store.detail, + milestoneTitle: $boardApp.dataset.boardMilestoneTitle, defaultAvatar: $boardApp.dataset.defaultAvatar, }, computed: { @@ -67,9 +73,20 @@ $(() => { }, }, 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(this.endpoint, this.bulkUpdatePath, this.boardId); + Store.rootPath = this.endpoint; - 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 @@ -109,11 +126,12 @@ $(() => { gl.IssueBoardsSearch = new Vue({ el: document.getElementById('js-add-list'), data: { - filters: Store.state.filters + filters: Store.state.filters, + milestoneTitle: $boardApp.dataset.boardMilestoneTitle, }, mounted () { gl.issueBoards.newListDropdownInit(); - } + }, }); gl.IssueBoardsModalAddBtn = new Vue({ @@ -122,6 +140,7 @@ $(() => { data: { modal: ModalStore.store, store: Store.state, + isFullscreen: false, }, watch: { disabled() { @@ -142,7 +161,7 @@ $(() => { }, methods: { updateTooltip() { - const $tooltip = $(this.$el); + const $tooltip = $(this.$refs.addIssuesButton); this.$nextTick(() => { if (this.disabled) { @@ -157,21 +176,52 @@ $(() => { this.toggleModal(true); } }, + toggleFocusMode() { + $(this.$refs.toggleFocusModeButton).tooltip('hide'); + issueBoardsContent.classList.toggle('is-focused'); + + this.isFullscreen = !this.isFullscreen; + }, }, mounted() { this.updateTooltip(); }, 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_new_form.js b/app/assets/javascripts/boards/components/board_new_form.js new file mode 100644 index 0000000000000000000000000000000000000000..956f84a7fa93192da2112c81e1df1a6cebfedb71 --- /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) => { + 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') { + const data = resp.json(); + 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 b1c47b09c35e08b8226ae1eee1d884918d70784d..401cc87ff8835235e26d88c9ac7c2c11293bb76b 100644 --- a/app/assets/javascripts/boards/components/board_new_issue.js +++ b/app/assets/javascripts/boards/components/board_new_issue.js @@ -29,6 +29,10 @@ export default { assignees: [], }); + if (Store.state.currentBoard) { + issue.milestone_id = Store.state.currentBoard.milestone_id; + } + this.list.newIssue(issue) .then(() => { // Need this because our jQuery very kindly disables buttons on ALL form submissions 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..1761f14f430791599346d985055e0b6df47352b8 --- /dev/null +++ b/app/assets/javascripts/boards/components/boards_selector.js @@ -0,0 +1,99 @@ +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((resp) => { + this.loading = false; + this.boards = resp.json(); + }).catch(() => { + this.loading = false; + }); + } + }, + }, + created() { + this.state.currentBoard = this.currentBoard; + }, + }); +})(); 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 478a1335b2bb07ae6befdcfca1f7ad33e82fd177..9168e7b5ec7095c42df7abdb522dba2b81539f00 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/sidebar/remove_issue.js b/app/assets/javascripts/boards/components/sidebar/remove_issue.js index 5597f128b80db4aa078042afd11cf60820d8d4e9..5c354137a257f7e7eb9b62d0ea849a8105e60ecb 100644 --- a/app/assets/javascripts/boards/components/sidebar/remove_issue.js +++ b/app/assets/javascripts/boards/components/sidebar/remove_issue.js @@ -24,11 +24,16 @@ gl.issueBoards.RemoveIssueBtn = Vue.extend({ const issue = this.issue; const lists = issue.getLists(); const labelIds = lists.map(list => list.label.id); + const data = { + remove_label_ids: labelIds, + }; + + if (Store.state.currentBoard.milestone_id) { + data.milestone_id = -1; + } // Post the remove data - gl.boardService.bulkUpdate([issue.globalId], { - remove_label_ids: labelIds, - }).catch(() => { + gl.boardService.bulkUpdate([issue.globalId], data).catch(() => { new Flash('Failed to remove issue from board, please try again.', 'alert'); lists.forEach((list) => { diff --git a/app/assets/javascripts/boards/filtered_search_boards.js b/app/assets/javascripts/boards/filtered_search_boards.js index b37698fe9caa399bcbc3d402988424eb394f3720..3f083655f950819300ac2b93633d8587ad644f17 100644 --- a/app/assets/javascripts/boards/filtered_search_boards.js +++ b/app/assets/javascripts/boards/filtered_search_boards.js @@ -11,7 +11,6 @@ export default class FilteredSearchBoards extends gl.FilteredSearchManager { // Issue boards is slightly different, we handle all the requests async // instead or reloading the page, we just re-fire the list ajax requests this.isHandledAsync = true; - this.cantEdit = cantEdit; } 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 6c2d8a3781b38ab057444ae92d5091fc29e1fde9..c2f32bed9f6b8f57c11f6308648febc5198e8c48 100644 --- a/app/assets/javascripts/boards/models/issue.js +++ b/app/assets/javascripts/boards/models/issue.js @@ -17,6 +17,7 @@ class ListIssue { this.assignees = []; this.selected = false; this.position = obj.relative_position || Infinity; + this.milestone_id = obj.milestone_id; if (obj.milestone) { this.milestone = new ListMilestone(obj.milestone); diff --git a/app/assets/javascripts/boards/models/list.js b/app/assets/javascripts/boards/models/list.js index 548de1a4c522a856e508818638dd0793716762a0..bbb2a25805dda56f6c385431df8541b57b29bd89 100644 --- a/app/assets/javascripts/boards/models/list.js +++ b/app/assets/javascripts/boards/models/list.js @@ -112,6 +112,7 @@ class List { .then((resp) => { const data = resp.json(); issue.id = data.iid; + issue.milestone = data.milestone; }) .then(() => { if (this.issuesSize > 1) { diff --git a/app/assets/javascripts/boards/services/board_service.js b/app/assets/javascripts/boards/services/board_service.js index db9bced2f89c3a22035dc3315d9546b9fc4e3714..32ff516377634f64fef2a043ae7cf68e63bd026b 100644 --- a/app/assets/javascripts/boards/services/board_service.js +++ b/app/assets/javascripts/boards/services/board_service.js @@ -30,6 +30,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(); } @@ -93,6 +104,16 @@ class BoardService { return this.issues.bulkUpdate(data); } + + static loadMilestones(path) { + this.loading = true; + + return this.$http.get(this.milestonePath) + .then((res) => { + this.milestones = res.json(); + 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 1e12d4ca4154ed558970c78f9de0fc8f8ccfded7..9e73f54d1cb9399789534aced17b8ba3fdbcce7e 100644 --- a/app/assets/javascripts/boards/stores/boards_store.js +++ b/app/assets/javascripts/boards/stores/boards_store.js @@ -24,6 +24,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 +132,11 @@ 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}`); + } + }, }; 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 b53f6284afcf8177980d358a0327bd7f9115bd90..c178a69a90cf0bcbcddb482aae22047d6f6b4456 100644 --- a/app/assets/javascripts/commons/jquery.js +++ b/app/assets/javascripts/commons/jquery.js @@ -9,3 +9,6 @@ import 'vendor/jquery.scrollTo'; import 'vendor/jquery.nicescroll'; 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 ab9a8e43dd1291da3bf47d10f08c29c8f1048994..9c462e93781bb57775b80a9c490143553e685698 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/diff_notes_bundle.js b/app/assets/javascripts/diff_notes/diff_notes_bundle.js index a2d33b0936e323bef36ab9e9e484808b7aae25ef..aaeb84d5d0c6d899edcd320b433599e662e8140a 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'; @@ -62,7 +63,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 51cc8c085b279172908806d588cc52dcd9f79b5d..0694013ba14264edcfb08fb17db440ceed585645 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -31,6 +31,8 @@ /* global Labels */ /* global Shortcuts */ /* global Sidebar */ +/* global WeightSelect */ +/* global AdminEmailSelect */ /* global ShortcutsWiki */ import Issue from './issue'; @@ -50,6 +52,7 @@ import { ProtectedTagCreate, ProtectedTagEditList } from './protected_tags'; import ShortcutsWiki from './shortcuts_wiki'; import Pipelines from './pipelines'; import BlobViewer from './blob/viewer/index'; +import GeoNodes from './geo_nodes'; import AutoWidthDropdownSelect from './issuable/auto_width_dropdown_select'; import UsersSelect from './users_select'; import RefSelectDropdown from './ref_select_dropdown'; @@ -57,6 +60,10 @@ import GfmAutoComplete from './gfm_auto_complete'; import ShortcutsBlob from './shortcuts_blob'; import initSettingsPanels from './settings_panels'; +// EE-only +import ApproversSelect from './approvers_select'; +import AuditLogs from './audit_logs'; + (function() { var Dispatcher; @@ -196,6 +203,7 @@ import initSettingsPanels from './settings_panels'; new IssuableForm($('.issue-form')); new LabelsSelect(); new MilestoneSelect(); + new WeightSelect(); new gl.IssuableTemplateSelectors(); break; case 'projects:merge_requests:new': @@ -263,6 +271,9 @@ import initSettingsPanels from './settings_panels'; case 'projects:activity': shortcut_handler = new ShortcutsNavigation(); break; + case 'projects:edit': + new UsersSelect(); + break; case 'projects:show': shortcut_handler = new ShortcutsNavigation(); new NotificationsForm(); @@ -384,10 +395,21 @@ import initSettingsPanels from './settings_panels'; case 'search:show': new Search(); 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:repository:show': // Initialize Protected Branch Settings new gl.ProtectedBranchCreate(); new gl.ProtectedBranchEditList(); + new UsersSelect(); // Initialize Protected Tag Settings new ProtectedTagCreate(); new ProtectedTagEditList(); @@ -448,6 +470,9 @@ import initSettingsPanels from './settings_panels'; case 'abuse_reports': new gl.AbuseReports(); break; + case 'geo_nodes': + new GeoNodes($('.geo-nodes')); + break; } break; case 'dashboard': @@ -472,6 +497,7 @@ import initSettingsPanels from './settings_panels'; case 'edit': shortcut_handler = new ShortcutsNavigation(); new ProjectNew(); + new ApproversSelect(); break; case 'new': new ProjectNew(); @@ -507,7 +533,7 @@ import initSettingsPanels from './settings_panels'; case 'builds': case 'hooks': case 'services': - case 'protected_branches': + case 'repository': shortcut_handler = new ShortcutsNavigation(); } } 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..375aa29571a9562aedcff57265ac12f2af5e50ab --- /dev/null +++ b/app/assets/javascripts/environments/components/deploy_board_component.vue @@ -0,0 +1,213 @@ + +