diff options
24 files changed, 158 insertions, 71 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f7528849c09..3c357c489f8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -20,7 +20,7 @@ before_script: - source ./scripts/prepare_build.sh - cp config/gitlab.yml.example config/gitlab.yml - bundle --version - - '[ "$USE_BUNDLE_INSTALL" != "true" ] || retry bundle install --without postgres production --jobs $(nproc) "${FLAGS[@]}"' + - '[ "$USE_BUNDLE_INSTALL" != "true" ] || retry bundle install --without postgres production --jobs $(nproc) $FLAGS' - retry gem install knapsack - '[ "$SETUP_DB" != "true" ] || bundle exec rake db:drop db:create db:schema:load db:migrate add_limits_mysql' @@ -328,7 +328,7 @@ migration paths: - git checkout -f FETCH_HEAD - cp config/resque.yml.example config/resque.yml - sed -i 's/localhost/redis/g' config/resque.yml - - bundle install --without postgres production --jobs $(nproc) ${FLAGS[@]} --retry=3 + - bundle install --without postgres production --jobs $(nproc) $FLAGS --retry=3 - rake db:drop db:create db:schema:load db:seed_fu - git checkout $CI_BUILD_REF - source scripts/prepare_build.sh diff --git a/app/assets/javascripts/boards/components/board_card.js.es6 b/app/assets/javascripts/boards/components/board_card.js.es6 index b1afbe7d97e..2299dafd217 100644 --- a/app/assets/javascripts/boards/components/board_card.js.es6 +++ b/app/assets/javascripts/boards/components/board_card.js.es6 @@ -54,6 +54,9 @@ mouseDown () { this.showDetail = true; }, + mouseMove() { + this.showDetail = false; + }, showIssue (e) { const targetTagName = e.target.tagName.toLowerCase(); diff --git a/app/assets/javascripts/boards/components/board_list.js.es6 b/app/assets/javascripts/boards/components/board_list.js.es6 index 379f4f0d72b..8e91cbfac75 100644 --- a/app/assets/javascripts/boards/components/board_list.js.es6 +++ b/app/assets/javascripts/boards/components/board_list.js.es6 @@ -94,12 +94,10 @@ gl.issueBoards.onStart(); }, onAdd: (e) => { - // Add the element back to original list to allow Vue to handle DOM updates - e.from.appendChild(e.item); + gl.issueBoards.BoardsStore.moveIssueToList(Store.moving.list, this.list, Store.moving.issue); this.$nextTick(() => { - // Update the issues once we know the element has been moved - gl.issueBoards.BoardsStore.moveIssueToList(Store.moving.list, this.list, Store.moving.issue); + e.item.remove(); }); }, }); diff --git a/app/assets/javascripts/environments/components/environment_item.js.es6 b/app/assets/javascripts/environments/components/environment_item.js.es6 index 36a0fec3cab..07f49cce3dc 100644 --- a/app/assets/javascripts/environments/components/environment_item.js.es6 +++ b/app/assets/javascripts/environments/components/environment_item.js.es6 @@ -1,4 +1,7 @@ -/*= require lib/utils/timeago */ +/* global Vue */ +/* global timeago */ + +/*= require timeago */ /*= require lib/utils/text_utility */ /*= require vue_common_component/commit */ /*= require ./environment_actions */ @@ -6,9 +9,6 @@ /*= require ./environment_stop */ /*= require ./environment_rollback */ -/* global Vue */ -/* global timeago */ - (() => { /** * Envrionment Item Component diff --git a/app/assets/javascripts/extensions/element.js.es6 b/app/assets/javascripts/extensions/element.js.es6 index afb2f0d6956..6d9b0c4bc3e 100644 --- a/app/assets/javascripts/extensions/element.js.es6 +++ b/app/assets/javascripts/extensions/element.js.es6 @@ -3,7 +3,7 @@ Element.prototype.matches = Element.prototype.matches || Element.prototype.msMatchesSelector; -Element.prototype.closest = function closest(selector, selectedElement = this) { +Element.prototype.closest = Element.prototype.closest || function closest(selector, selectedElement = this) { if (!selectedElement) return; return selectedElement.matches(selector) ? selectedElement : Element.prototype.closest(selector, selectedElement.parentElement); }; diff --git a/app/assets/javascripts/lib/utils/custom_event_polyfill.js.es6 b/app/assets/javascripts/lib/utils/custom_event_polyfill.js.es6 new file mode 100644 index 00000000000..5ae978010c9 --- /dev/null +++ b/app/assets/javascripts/lib/utils/custom_event_polyfill.js.es6 @@ -0,0 +1,12 @@ +/** + * CustomEvent support for IE + */ +if (typeof window.CustomEvent !== 'function') { + window.CustomEvent = function CustomEvent(e, params) { + const options = params || { bubbles: false, cancelable: false, detail: undefined }; + const evt = document.createEvent('CustomEvent'); + evt.initCustomEvent(e, options.bubbles, options.cancelable, options.detail); + return evt; + }; + window.CustomEvent.prototype = window.Event.prototype; +} diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js index d480fdc882b..963d2851e5f 100644 --- a/app/assets/javascripts/lib/utils/datetime_utility.js +++ b/app/assets/javascripts/lib/utils/datetime_utility.js @@ -1,4 +1,10 @@ -/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, no-undef, comma-dangle, no-unused-expressions, prefer-template, padded-blocks, max-len */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, comma-dangle, no-unused-expressions, prefer-template, padded-blocks, max-len */ +/* global timeago */ +/* global dateFormat */ + +/*= require timeago */ +/*= require date.format */ + (function() { (function(w) { var base; diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index 54c7caed8ed..e66c1f8d072 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -75,6 +75,8 @@ ul.notes { display: none; padding: 10px 0 0; cursor: pointer; + position: relative; + z-index: 2; &:hover { color: $gl-link-color; @@ -118,11 +120,11 @@ ul.notes { &::after { content: ''; width: 100%; - height: 20px; + height: 67px; position: absolute; left: 0; - bottom: 50px; - background: linear-gradient(rgba($gray-light, .3) 0, $white-light); + bottom: 0; + background: linear-gradient(rgba($gray-light, 0.1) -100px, $white-light 100%); } &.hide-shade { diff --git a/app/views/projects/boards/components/_board_list.html.haml b/app/views/projects/boards/components/_board_list.html.haml index d86e0ed8540..34fdb1f6a74 100644 --- a/app/views/projects/boards/components/_board_list.html.haml +++ b/app/views/projects/boards/components/_board_list.html.haml @@ -35,7 +35,7 @@ ":issue" => "issue", ":issue-link-base" => "issueLinkBase", ":disabled" => "disabled", - "key" => "id" } + ":key" => "issue.id" } %li.board-list-count.text-center{ "v-if" => "showCount" } = icon("spinner spin", "v-show" => "list.loadingMore" ) %span{ "v-if" => "list.issues.length === list.issuesSize" } diff --git a/app/views/projects/boards/components/_card.html.haml b/app/views/projects/boards/components/_card.html.haml index 72b31b8cdae..34effac17b2 100644 --- a/app/views/projects/boards/components/_card.html.haml +++ b/app/views/projects/boards/components/_card.html.haml @@ -1,6 +1,7 @@ %li.card{ ":class" => '{ "user-can-drag": !disabled && issue.id, "is-disabled": disabled || !issue.id, "is-active": issueDetailVisible }', ":index" => "index", "@mousedown" => "mouseDown", + "@mousemove" => "mouseMove", "@mouseup" => "showIssue($event)" } %h4.card-title = icon("eye-slash", class: "confidential-icon", "v-if" => "issue.confidential") diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index 0aa8801c2d8..3a5af2723c6 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -92,14 +92,15 @@ = project_feature_access_select(:wiki_access_level) - if Gitlab.config.lfs.enabled && current_user.admin? - .checkbox - = f.label :lfs_enabled do - = f.check_box :lfs_enabled - %strong LFS - %br - %span.descr + .row + .col-md-9 + = f.label :lfs_enabled, 'LFS', class: 'label-light' + %span.help-block Git Large File Storage = link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs') + .col-md-3 + = f.select :lfs_enabled, [%w(Enabled true), %w(Disabled false)], {}, selected: @project.lfs_enabled?, class: 'pull-right form-control', data: { field: 'lfs_enabled' } + - if Gitlab.config.registry.enabled .form-group.js-container-registry{ style: ("display: none;" if @project.project_feature.send(:repository_access_level) == 0) } diff --git a/changelogs/unreleased/api-delete-group-share.yml b/changelogs/unreleased/api-delete-group-share.yml new file mode 100644 index 00000000000..26cfb35bba3 --- /dev/null +++ b/changelogs/unreleased/api-delete-group-share.yml @@ -0,0 +1,4 @@ +--- +title: 'API: Add ability to unshare a project from a group' +merge_request: 7662 +author: Robert Schilling diff --git a/changelogs/unreleased/issue-boards-dragging-fix.yml b/changelogs/unreleased/issue-boards-dragging-fix.yml new file mode 100644 index 00000000000..565e09b930b --- /dev/null +++ b/changelogs/unreleased/issue-boards-dragging-fix.yml @@ -0,0 +1,4 @@ +--- +title: Fixed issue boards dragging card removing random issues +merge_request: +author: diff --git a/doc/api/projects.md b/doc/api/projects.md index 467a880ac13..de5d3b07c21 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -1074,6 +1074,25 @@ Parameters: | `group_access` | integer | yes | The permissions level to grant the group | | `expires_at` | string | no | Share expiration date in ISO 8601 format: 2016-09-26 | +### Delete a shared project link within a group + +Unshare the project from the group. Returns `204` and no content on success. + +``` +DELETE /projects/:id/share/:group_id +``` + +Parameters: + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME | +| `group_id` | integer | yes | The ID of the group | + +```bash +curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/share/17 +``` + ## Hooks Also called Project Hooks and Webhooks. diff --git a/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md b/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md index 28e6a21a229..6a7098e79d0 100644 --- a/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md +++ b/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md @@ -24,6 +24,7 @@ Documentation for GitLab instance administrators is under [LFS administration do ## Requirements * Git LFS is supported in GitLab starting with version 8.2 +* Git LFS must be enabled under project settings * [Git LFS client](https://git-lfs.github.com) version 1.0.1 and up ## Known limitations @@ -31,10 +32,10 @@ Documentation for GitLab instance administrators is under [LFS administration do * Git LFS v1 original API is not supported since it was deprecated early in LFS development * When SSH is set as a remote, Git LFS objects still go through HTTPS -* Any Git LFS request will ask for HTTPS credentials to be provided so good Git +* Any Git LFS request will ask for HTTPS credentials to be provided so a good Git credentials store is recommended * Git LFS always assumes HTTPS so if you have GitLab server on HTTP you will have - to add the URL to Git config manually (see #troubleshooting) + to add the URL to Git config manually (see [troubleshooting](#troubleshooting)) >**Note**: With 8.12 GitLab added LFS support to SSH. The Git LFS communication still goes over HTTP, but now the SSH client passes the correct credentials @@ -95,7 +96,7 @@ available to the project anymore. Probably the object was removed from the serve * Local git repository is using deprecated LFS API -### Invalid status for <url> : 501 +### Invalid status for `<url>` : 501 Git LFS will log the failures into a log file. To view this log file, while in project directory: @@ -106,6 +107,9 @@ git lfs logs last If the status `error 501` is shown, it is because: +* Git LFS is not enabled in project settings. Check your project settings and + enable Git LFS. + * Git LFS support is not enabled on the GitLab server. Check with your GitLab administrator why Git LFS is not enabled on the server. See [LFS administration documentation](lfs_administration.md) for instructions diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 6b856128c2e..ddfde178d30 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -438,6 +438,19 @@ module API end end + params do + requires :group_id, type: Integer, desc: 'The ID of the group' + end + delete ":id/share/:group_id" do + authorize! :admin_project, user_project + + link = user_project.project_group_links.find_by(group_id: params[:group_id]) + not_found!('Group Link') unless link + + link.destroy + no_content! + end + # Upload a file # # Parameters: diff --git a/lib/api/sidekiq_metrics.rb b/lib/api/sidekiq_metrics.rb index d3d6827dc54..11f2b40269a 100644 --- a/lib/api/sidekiq_metrics.rb +++ b/lib/api/sidekiq_metrics.rb @@ -39,50 +39,22 @@ module API end end - # Get Sidekiq Queue metrics - # - # Parameters: - # None - # - # Example: - # GET /sidekiq/queue_metrics - # + desc 'Get the Sidekiq queue metrics' get 'sidekiq/queue_metrics' do { queues: queue_metrics } end - # Get Sidekiq Process metrics - # - # Parameters: - # None - # - # Example: - # GET /sidekiq/process_metrics - # + desc 'Get the Sidekiq process metrics' get 'sidekiq/process_metrics' do { processes: process_metrics } end - # Get Sidekiq Job statistics - # - # Parameters: - # None - # - # Example: - # GET /sidekiq/job_stats - # + desc 'Get the Sidekiq job statistics' get 'sidekiq/job_stats' do { jobs: job_stats } end - # Get Sidekiq Compound metrics. Includes all previous metrics - # - # Parameters: - # None - # - # Example: - # GET /sidekiq/compound_metrics - # + desc 'Get the Sidekiq Compound metrics. Includes queue, process, and job statistics' get 'sidekiq/compound_metrics' do { queues: queue_metrics, processes: process_metrics, jobs: job_stats } end diff --git a/lib/gitlab/identifier.rb b/lib/gitlab/identifier.rb index c5acf18beb5..94678b6ec40 100644 --- a/lib/gitlab/identifier.rb +++ b/lib/gitlab/identifier.rb @@ -21,10 +21,8 @@ module Gitlab return if !commit || !commit.author_email - email = commit.author_email - - identify_with_cache(:email, email) do - User.find_by_any_email(email) + identify_with_cache(:email, commit.author_email) do + commit.author end end diff --git a/spec/javascripts/build_spec.js.es6 b/spec/javascripts/build_spec.js.es6 index ee192c4f18a..4208e076e96 100644 --- a/spec/javascripts/build_spec.js.es6 +++ b/spec/javascripts/build_spec.js.es6 @@ -2,7 +2,6 @@ /* global Build */ /* global Turbolinks */ -//= require lib/utils/timeago //= require lib/utils/datetime_utility //= require build //= require breakpoints diff --git a/spec/javascripts/merge_request_widget_spec.js b/spec/javascripts/merge_request_widget_spec.js index f38e9cb8ef5..62890f1ca96 100644 --- a/spec/javascripts/merge_request_widget_spec.js +++ b/spec/javascripts/merge_request_widget_spec.js @@ -1,6 +1,6 @@ /* eslint-disable space-before-function-paren, quotes, comma-dangle, dot-notation, indent, quote-props, no-var, padded-blocks, max-len */ + /*= require merge_request_widget */ -/*= require lib/utils/timeago */ /*= require lib/utils/datetime_utility */ (function() { diff --git a/spec/lib/gitlab/identifier_spec.rb b/spec/lib/gitlab/identifier_spec.rb index f42c4453dd1..bb758a8a202 100644 --- a/spec/lib/gitlab/identifier_spec.rb +++ b/spec/lib/gitlab/identifier_spec.rb @@ -40,7 +40,7 @@ describe Gitlab::Identifier do describe '#identify_using_commit' do it "returns the User for an existing commit author's Email address" do - commit = double(:commit, author_email: user.email) + commit = double(:commit, author: user, author_email: user.email) expect(project).to receive(:commit).with('123').and_return(commit) @@ -62,10 +62,9 @@ describe Gitlab::Identifier do end it 'caches the found users per Email' do - commit = double(:commit, author_email: user.email) + commit = double(:commit, author: user, author_email: user.email) expect(project).to receive(:commit).with('123').twice.and_return(commit) - expect(User).to receive(:find_by_any_email).once.and_call_original 2.times do expect(identifier.identify_using_commit(project, '123')).to eq(user) diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index f020d471422..e53ee2a4e76 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -908,6 +908,36 @@ describe API::API, api: true do end end + describe 'DELETE /projects/:id/share/:group_id' do + it 'returns 204 when deleting a group share' do + group = create(:group, :public) + create(:project_group_link, group: group, project: project) + + delete api("/projects/#{project.id}/share/#{group.id}", user) + + expect(response).to have_http_status(204) + expect(project.project_group_links).to be_empty + end + + it 'returns a 400 when group id is not an integer' do + delete api("/projects/#{project.id}/share/foo", user) + + expect(response).to have_http_status(400) + end + + it 'returns a 404 error when group link does not exist' do + delete api("/projects/#{project.id}/share/1234", user) + + expect(response).to have_http_status(404) + end + + it 'returns a 404 error when project does not exist' do + delete api("/projects/123/share/1234", user) + + expect(response).to have_http_status(404) + end + end + describe 'GET /projects/search/:query' do let!(:query) { 'query'} let!(:search) { create(:empty_project, name: query, creator_id: user.id, namespace: user.namespace) } diff --git a/spec/views/projects/edit.html.haml_spec.rb b/spec/views/projects/edit.html.haml_spec.rb new file mode 100644 index 00000000000..d2575702ecc --- /dev/null +++ b/spec/views/projects/edit.html.haml_spec.rb @@ -0,0 +1,24 @@ +require 'spec_helper' + +describe 'projects/edit' do + include Devise::Test::ControllerHelpers + + let(:project) { create(:empty_project) } + let(:user) { create(:admin) } + + before do + assign(:project, project) + + allow(controller).to receive(:current_user).and_return(user) + allow(view).to receive_messages(current_user: user, can?: true) + allow(Gitlab.config.lfs).to receive(:enabled).and_return(true) + end + + context 'LFS enabled setting' do + it 'displays the correct elements' do + render + expect(rendered).to have_select('project_lfs_enabled') + expect(rendered).to have_content('Git Large File Storage') + end + end +end diff --git a/app/assets/javascripts/lib/utils/timeago.js b/vendor/assets/javascripts/timeago.js index edf0a612374..0eb6f7967a5 100644 --- a/app/assets/javascripts/lib/utils/timeago.js +++ b/vendor/assets/javascripts/timeago.js @@ -1,5 +1,3 @@ -/* eslint-disable no-unused-expressions, wrap-iife, func-names, curly, no-param-reassign, no-trailing-spaces, prefer-arrow-callback, no-var, one-var, quote-props, space-before-function-paren, vars-on-top, radix, prefer-template, space-infix-ops, no-use-before-define, newline-per-chained-call, no-useless-escape, no-nested-ternary, indent, no-undef, no-plusplus, one-var-declaration-per-line, operator-assignment, consistent-return, keyword-spacing, max-len, space-unary-ops, no-shadow, no-restricted-syntax, guard-for-in, eol-last, max-len */ - /** * Copyright (c) 2016 hustcc * License: MIT @@ -14,7 +12,7 @@ module.exports = factory(root); else root.timeago = factory(root); -}(typeof window !== 'undefined' ? window : this, +}(typeof window !== 'undefined' ? window : this, function () { var cnt = 0, // the timer counter, for timer key indexMapEn = 'second_minute_hour_day_week_month_year'.split('_'), @@ -32,7 +30,7 @@ function () { SEC_ARRAY = [60, 60, 24, 7, 365/7/12, 12], SEC_ARRAY_LEN = 6, ATTR_DATETIME = 'datetime'; - + // format Date / string / timestamp to Date instance. function toDate(input) { if (input instanceof Date) return input; @@ -236,4 +234,4 @@ function () { }; return timeagoFactory; -});
\ No newline at end of file +}); |