Commit 7649497f authored by Stan Hu's avatar Stan Hu

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce

parents 341541d3 4d042afe
...@@ -21,10 +21,12 @@ v 8.12.0 (unreleased) ...@@ -21,10 +21,12 @@ v 8.12.0 (unreleased)
- Add Sentry logging to API calls - Add Sentry logging to API calls
- Automatically expand hidden discussions when accessed by a permalink !5585 (Mike Greiling) - Automatically expand hidden discussions when accessed by a permalink !5585 (Mike Greiling)
- Remove unused mixins (ClemMakesApps) - Remove unused mixins (ClemMakesApps)
- Add search to all issue board lists
- Fix groups sort dropdown alignment (ClemMakesApps) - Fix groups sort dropdown alignment (ClemMakesApps)
- Add horizontal scrolling to all sub-navs on mobile viewports (ClemMakesApps) - Add horizontal scrolling to all sub-navs on mobile viewports (ClemMakesApps)
- Fix markdown help references (ClemMakesApps) - Fix markdown help references (ClemMakesApps)
- Add last commit time to repo view (ClemMakesApps) - Add last commit time to repo view (ClemMakesApps)
- Added project specific enable/disable setting for LFS !5997
- Added tests for diff notes - Added tests for diff notes
- Add a button to download latest successful artifacts for branches and tags !5142 - Add a button to download latest successful artifacts for branches and tags !5142
- Add delimiter to project stars and forks count (ClemMakesApps) - Add delimiter to project stars and forks count (ClemMakesApps)
...@@ -78,6 +80,8 @@ v 8.11.2 ...@@ -78,6 +80,8 @@ v 8.11.2
v 8.11.1 v 8.11.1
- Pulled due to packaging error. - Pulled due to packaging error.
v 8.11.0 (unreleased)
- Fix pipelines tab layout regression (brycepj)
v 8.11.0 v 8.11.0
- Use test coverage value from the latest successful pipeline in badge. !5862 - Use test coverage value from the latest successful pipeline in badge. !5862
- Add test coverage report badge. !5708 - Add test coverage report badge. !5708
......
...@@ -54,4 +54,11 @@ $(() => { ...@@ -54,4 +54,11 @@ $(() => {
}); });
} }
}); });
gl.IssueBoardsSearch = new Vue({
el: '#js-boards-seach',
data: {
filters: Store.state.filters
}
});
}); });
...@@ -21,15 +21,10 @@ ...@@ -21,15 +21,10 @@
}, },
data () { data () {
return { return {
query: '',
filters: Store.state.filters filters: Store.state.filters
}; };
}, },
watch: { watch: {
query () {
this.list.filters = this.getFilterData();
this.list.getIssues(true);
},
filters: { filters: {
handler () { handler () {
this.list.page = 1; this.list.page = 1;
...@@ -38,16 +33,6 @@ ...@@ -38,16 +33,6 @@
deep: true deep: true
} }
}, },
methods: {
getFilterData () {
const filters = this.filters;
let queryData = { search: this.query };
Object.keys(filters).forEach((key) => { queryData[key] = filters[key]; });
return queryData;
}
},
ready () { ready () {
const options = gl.issueBoards.getBoardSortableDefaultOptions({ const options = gl.issueBoards.getBoardSortableDefaultOptions({
disabled: this.disabled, disabled: this.disabled,
......
...@@ -58,10 +58,6 @@ class List { ...@@ -58,10 +58,6 @@ class List {
} }
} }
canSearch () {
return this.type === 'backlog';
}
getIssues (emptyIssues = true) { getIssues (emptyIssues = true) {
const filters = this.filters; const filters = this.filters;
let data = { page: this.page }; let data = { page: this.page };
......
...@@ -15,7 +15,8 @@ ...@@ -15,7 +15,8 @@
author_id: gl.utils.getParameterValues('author_id')[0], author_id: gl.utils.getParameterValues('author_id')[0],
assignee_id: gl.utils.getParameterValues('assignee_id')[0], assignee_id: gl.utils.getParameterValues('assignee_id')[0],
milestone_title: gl.utils.getParameterValues('milestone_title')[0], milestone_title: gl.utils.getParameterValues('milestone_title')[0],
label_name: gl.utils.getParameterValues('label_name[]') label_name: gl.utils.getParameterValues('label_name[]'),
search: ''
}; };
}, },
addList (listObj) { addList (listObj) {
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
.is-dragging { .is-dragging {
// Important because plugin sets inline CSS // Important because plugin sets inline CSS
opacity: 1!important; opacity: 1!important;
* { * {
// !important to make sure no style can override this when dragging // !important to make sure no style can override this when dragging
cursor: -webkit-grabbing!important; cursor: -webkit-grabbing!important;
...@@ -160,40 +160,6 @@ ...@@ -160,40 +160,6 @@
border-bottom: 1px solid $border-color; border-bottom: 1px solid $border-color;
} }
.board-search-container {
position: relative;
background-color: #fff;
.form-control {
padding-right: 30px;
}
}
.board-search-icon,
.board-search-clear-btn {
position: absolute;
right: $gl-padding + 10px;
top: 50%;
margin-top: -7px;
font-size: 14px;
}
.board-search-icon {
color: $gl-placeholder-color;
}
.board-search-clear-btn {
padding: 0;
line-height: 1;
background: transparent;
border: 0;
outline: 0;
&:hover {
color: $gl-link-color;
}
}
.board-delete { .board-delete {
margin-right: 10px; margin-right: 10px;
padding: 0; padding: 0;
...@@ -304,3 +270,12 @@ ...@@ -304,3 +270,12 @@
margin-right: 8px; margin-right: 8px;
font-weight: 500; font-weight: 500;
} }
.issue-boards-search {
width: 335px;
.form-control {
display: inline-block;
width: 210px;
}
}
...@@ -477,3 +477,10 @@ ...@@ -477,3 +477,10 @@
width: 60px; width: 60px;
} }
} }
.ci-status-icon-created {
svg {
fill: $table-text-gray;
}
}
...@@ -311,6 +311,14 @@ a.deploy-project-label { ...@@ -311,6 +311,14 @@ a.deploy-project-label {
color: $gl-success; color: $gl-success;
} }
.lfs-enabled {
color: $gl-success;
}
.lfs-disabled {
color: $gl-warning;
}
.breadcrumb.repo-breadcrumb { .breadcrumb.repo-breadcrumb {
padding: 0; padding: 0;
background: transparent; background: transparent;
......
...@@ -8,10 +8,14 @@ module ToggleAwardEmoji ...@@ -8,10 +8,14 @@ module ToggleAwardEmoji
def toggle_award_emoji def toggle_award_emoji
name = params.require(:name) name = params.require(:name)
awardable.toggle_award_emoji(name, current_user) if awardable.user_can_award?(current_user, name)
TodoService.new.new_award_emoji(to_todoable(awardable), current_user) awardable.toggle_award_emoji(name, current_user)
TodoService.new.new_award_emoji(to_todoable(awardable), current_user)
render json: { ok: true } render json: { ok: true }
else
render json: { ok: false }
end
end end
private private
......
...@@ -309,7 +309,7 @@ class ProjectsController < Projects::ApplicationController ...@@ -309,7 +309,7 @@ class ProjectsController < Projects::ApplicationController
:issues_tracker_id, :default_branch, :issues_tracker_id, :default_branch,
:wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id, :avatar, :wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id, :avatar,
:builds_enabled, :build_allow_git_fetch, :build_timeout_in_minutes, :build_coverage_regex, :builds_enabled, :build_allow_git_fetch, :build_timeout_in_minutes, :build_coverage_regex,
:public_builds, :only_allow_merge_if_build_succeeds, :request_access_enabled :public_builds, :only_allow_merge_if_build_succeeds, :request_access_enabled, :lfs_enabled
) )
end end
......
...@@ -23,10 +23,14 @@ module LfsHelper ...@@ -23,10 +23,14 @@ module LfsHelper
end end
def lfs_download_access? def lfs_download_access?
return false unless project.lfs_enabled?
project.public? || ci? || (user && user.can?(:download_code, project)) project.public? || ci? || (user && user.can?(:download_code, project))
end end
def lfs_upload_access? def lfs_upload_access?
return false unless project.lfs_enabled?
user && user.can?(:push_code, project) user && user.can?(:push_code, project)
end end
......
...@@ -25,6 +25,8 @@ module NavHelper ...@@ -25,6 +25,8 @@ module NavHelper
current_path?('merge_requests#commits') || current_path?('merge_requests#commits') ||
current_path?('merge_requests#builds') || current_path?('merge_requests#builds') ||
current_path?('merge_requests#conflicts') || current_path?('merge_requests#conflicts') ||
current_path?('merge_requests#pipelines') ||
current_path?('issues#show') current_path?('issues#show')
if cookies[:collapsed_gutter] == 'true' if cookies[:collapsed_gutter] == 'true'
"page-gutter right-sidebar-collapsed" "page-gutter right-sidebar-collapsed"
......
...@@ -187,6 +187,18 @@ module ProjectsHelper ...@@ -187,6 +187,18 @@ module ProjectsHelper
nav_tabs.flatten nav_tabs.flatten
end end
def project_lfs_status(project)
if project.lfs_enabled?
content_tag(:span, class: 'lfs-enabled') do
'Enabled'
end
else
content_tag(:span, class: 'lfs-disabled') do
'Disabled'
end
end
end
def git_user_name def git_user_name
if current_user if current_user
current_user.name current_user.name
......
...@@ -59,6 +59,18 @@ module Awardable ...@@ -59,6 +59,18 @@ module Awardable
true true
end end
def awardable_votes?(name)
AwardEmoji::UPVOTE_NAME == name || AwardEmoji::DOWNVOTE_NAME == name
end
def user_can_award?(current_user, name)
if user_authored?(current_user)
!awardable_votes?(normalize_name(name))
else
true
end
end
def awarded_emoji?(emoji_name, current_user) def awarded_emoji?(emoji_name, current_user)
award_emoji.where(name: emoji_name, user: current_user).exists? award_emoji.where(name: emoji_name, user: current_user).exists?
end end
......
...@@ -196,6 +196,10 @@ module Issuable ...@@ -196,6 +196,10 @@ module Issuable
end end
end end
def user_authored?(user)
user == author
end
def subscribed_without_subscriptions?(user) def subscribed_without_subscriptions?(user)
participants(user).include?(user) participants(user).include?(user)
end end
......
...@@ -223,6 +223,10 @@ class Note < ActiveRecord::Base ...@@ -223,6 +223,10 @@ class Note < ActiveRecord::Base
end end
end end
def user_authored?(user)
user == author
end
def award_emoji? def award_emoji?
can_be_award_emoji? && contains_emoji_only? can_be_award_emoji? && contains_emoji_only?
end end
......
...@@ -390,6 +390,13 @@ class Project < ActiveRecord::Base ...@@ -390,6 +390,13 @@ class Project < ActiveRecord::Base
end end
end end
def lfs_enabled?
return false unless Gitlab.config.lfs.enabled
return Gitlab.config.lfs.enabled if self[:lfs_enabled].nil?
self[:lfs_enabled]
end
def repository_storage_path def repository_storage_path
Gitlab.config.repositories.storages[repository_storage] Gitlab.config.repositories.storages[repository_storage]
end end
......
...@@ -73,6 +73,12 @@ ...@@ -73,6 +73,12 @@
%span.light last commit: %span.light last commit:
%strong %strong
= last_commit(@project) = last_commit(@project)
%li
%span.light Git LFS status:
%strong
= project_lfs_status(@project)
= link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
- else - else
%li %li
%span.light repository: %span.light repository:
......
...@@ -21,11 +21,6 @@ ...@@ -21,11 +21,6 @@
%button.board-delete.has-tooltip.pull-right{ type: "button", title: "Delete list", "aria-label" => "Delete list", data: { placement: "bottom" }, "@click.stop" => "deleteBoard" } %button.board-delete.has-tooltip.pull-right{ type: "button", title: "Delete list", "aria-label" => "Delete list", data: { placement: "bottom" }, "@click.stop" => "deleteBoard" }
= icon("trash") = icon("trash")
= icon("spinner spin", class: "board-header-loading-spinner pull-right", "v-show" => "list.loadingMore") = icon("spinner spin", class: "board-header-loading-spinner pull-right", "v-show" => "list.loadingMore")
.board-inner-container.board-search-container{ "v-if" => "list.canSearch()" }
%input.form-control{ type: "text", placeholder: "Search issues", "v-model" => "query", "debounce" => "250" }
= icon("search", class: "board-search-icon", "v-show" => "!query")
%button.board-search-clear-btn{ type: "button", role: "button", "aria-label" => "Clear search", "@click" => "query = ''", "v-show" => "query" }
= icon("times", class: "board-search-clear")
%board-list{ "inline-template" => true, %board-list{ "inline-template" => true,
"v-if" => "list.type !== 'blank'", "v-if" => "list.type !== 'blank'",
":list" => "list", ":list" => "list",
......
...@@ -80,6 +80,16 @@ ...@@ -80,6 +80,16 @@
%strong Snippets %strong Snippets
%br %br
%span.descr Share code pastes with others out of git repository %span.descr Share code pastes with others out of git repository
- if Gitlab.config.lfs.enabled && current_user.admin?
.form-group
.checkbox
= f.label :lfs_enabled do
= f.check_box :lfs_enabled, checked: @project.lfs_enabled?
%strong LFS
%br
%span.descr
Git Large File Storage
= link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
- if Gitlab.config.registry.enabled - if Gitlab.config.registry.enabled
.form-group .form-group
.checkbox .checkbox
......
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 14" enable-background="new 0 0 14 14"><path d="M12.5,7 C12.5,4 10,1.5 7,1.5 C4,1.5 1.5,4 1.5,7 C1.5,10 4,12.5 7,12.5 C10,12.5 12.5,10 12.5,7 L12.5,7 Z M0,7 C0,3.1 3.1,0 7,0 C10.9,0 14,3.1 14,7 C14,10.9 10.9,14 7,14 C3.1,14 0,10.9 0,7 L0,7 Z" /><circle cx="7" cy="7" r="3.25"/></svg> <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14" enable-background="new 0 0 14 14"><path d="M12.5,7 C12.5,4 10,1.5 7,1.5 C4,1.5 1.5,4 1.5,7 C1.5,10 4,12.5 7,12.5 C10,12.5 12.5,10 12.5,7 L12.5,7 Z M0,7 C0,3.1 3.1,0 7,0 C10.9,0 14,3.1 14,7 C14,10.9 10.9,14 7,14 C3.1,14 0,10.9 0,7 L0,7 Z" /><circle cx="7" cy="7" r="3.25"/></svg>
...@@ -27,15 +27,18 @@ ...@@ -27,15 +27,18 @@
= render "shared/issuable/label_dropdown" = render "shared/issuable/label_dropdown"
.pull-right .pull-right
- if controller.controller_name == 'boards' && can?(current_user, :admin_list, @project) - if controller.controller_name == 'boards'
.dropdown #js-boards-seach.issue-boards-search
%button.btn.btn-create.js-new-board-list{ type: "button", data: { toggle: "dropdown", labels: labels_filter_path, project_id: @project.try(:id) } } %input.pull-left.form-control{ type: "search", placeholder: "Filter by name...", "v-model" => "filters.search", "debounce" => "250" }
Create new list - if can?(current_user, :admin_list, @project)
.dropdown-menu.dropdown-menu-paging.dropdown-menu-align-right.dropdown-menu-issues-board-new.dropdown-menu-selectable .dropdown.pull-right
= render partial: "shared/issuable/label_page_default", locals: { show_footer: true, show_create: true, show_boards_content: true, title: "Create a new list" } %button.btn.btn-create.js-new-board-list{ type: "button", data: { toggle: "dropdown", labels: labels_filter_path, project_id: @project.try(:id) } }
- if can?(current_user, :admin_label, @project) Create new list
= render partial: "shared/issuable/label_page_create" .dropdown-menu.dropdown-menu-paging.dropdown-menu-align-right.dropdown-menu-issues-board-new.dropdown-menu-selectable
= dropdown_loading = render partial: "shared/issuable/label_page_default", locals: { show_footer: true, show_create: true, show_boards_content: true, title: "Create a new list" }
- if can?(current_user, :admin_label, @project)
= render partial: "shared/issuable/label_page_create"
= dropdown_loading
- else - else
= render 'shared/sort_dropdown' = render 'shared/sort_dropdown'
......
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddLfsEnabledToProjects < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
# When a migration requires downtime you **must** uncomment the following
# constant and define a short and easy to understand explanation as to why the
# migration requires downtime.
# DOWNTIME_REASON = ''
# When using the methods "add_concurrent_index" or "add_column_with_default"
# you must disable the use of transactions as these methods can not run in an
# existing transaction. When using "add_concurrent_index" make sure that this
# method is the _only_ method called in the migration, any other changes
# should go in a separate migration. This ensures that upon failure _only_ the
# index creation fails and can be retried or reverted easily.
#
# To disable transactions uncomment the following line and remove these
# comments:
# disable_ddl_transaction!
def change
add_column :projects, :lfs_enabled, :boolean
end
end
class ChangeLockVersionNotNull < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def up
change_column_null :issues, :lock_version, true
change_column_null :merge_requests, :lock_version, true
end
def down
end
end
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20160827011312) do ActiveRecord::Schema.define(version: 20160830232601) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -825,6 +825,7 @@ ActiveRecord::Schema.define(version: 20160827011312) do ...@@ -825,6 +825,7 @@ ActiveRecord::Schema.define(version: 20160827011312) do
t.string "repository_storage", default: "default", null: false t.string "repository_storage", default: "default", null: false
t.boolean "request_access_enabled", default: true, null: false t.boolean "request_access_enabled", default: true, null: false
t.boolean "has_external_wiki" t.boolean "has_external_wiki"
t.boolean "lfs_enabled"
end end
add_index "projects", ["ci_id"], name: "index_projects_on_ci_id", using: :btree add_index "projects", ["ci_id"], name: "index_projects_on_ci_id", using: :btree
......
...@@ -452,6 +452,7 @@ Parameters: ...@@ -452,6 +452,7 @@ Parameters:
- `import_url` (optional) - `import_url` (optional)
- `public_builds` (optional) - `public_builds` (optional)
- `only_allow_merge_if_build_succeeds` (optional) - `only_allow_merge_if_build_succeeds` (optional)
- `lfs_enabled` (optional)
### Create project for user ### Create project for user
...@@ -478,6 +479,7 @@ Parameters: ...@@ -478,6 +479,7 @@ Parameters:
- `import_url` (optional) - `import_url` (optional)
- `public_builds` (optional) - `public_builds` (optional)
- `only_allow_merge_if_build_succeeds` (optional) - `only_allow_merge_if_build_succeeds` (optional)
- `lfs_enabled` (optional)
### Edit project ### Edit project
...@@ -505,6 +507,7 @@ Parameters: ...@@ -505,6 +507,7 @@ Parameters:
- `visibility_level` (optional) - `visibility_level` (optional)
- `public_builds` (optional) - `public_builds` (optional)
- `only_allow_merge_if_build_succeeds` (optional) - `only_allow_merge_if_build_succeeds` (optional)
- `lfs_enabled` (optional)
On success, method returns 200 with the updated project. If parameters are On success, method returns 200 with the updated project. If parameters are
invalid, 400 is returned. invalid, 400 is returned.
......
...@@ -32,4 +32,12 @@ Snippets are little bits of code or text. ...@@ -32,4 +32,12 @@ Snippets are little bits of code or text.
This is a nice place to put code or text that is used semi-regularly within the project, but does not belong in source control. This is a nice place to put code or text that is used semi-regularly within the project, but does not belong in source control.
For example, a specific config file that is used by > the team that is only valid for the people that work on the code. For example, a specific config file that is used by the team that is only valid for the people that work on the code.
## Git LFS
>**Note:** Project-specific LFS setting was added on 8.12 and is available only to admins.
Git Large File Storage allows you to easily manage large binary files with Git.
With this setting admins can better control which projects are allowed to use
LFS.
...@@ -54,7 +54,7 @@ module API ...@@ -54,7 +54,7 @@ module API
post endpoint do post endpoint do
required_attributes! [:name] required_attributes! [:name]
not_found!('Award Emoji') unless can_read_awardable? not_found!('Award Emoji') unless can_read_awardable? && can_award_awardable?
award = awardable.create_award_emoji(params[:name], current_user) award = awardable.create_award_emoji(params[:name], current_user)
...@@ -92,6 +92,10 @@ module API ...@@ -92,6 +92,10 @@ module API
can?(current_user, ability, awardable) can?(current_user, ability, awardable)
end end
def can_award_awardable?
awardable.user_can_award?(current_user, params[:name])
end
def awardable def awardable
@awardable ||= @awardable ||=
begin begin
......
...@@ -78,7 +78,7 @@ module API ...@@ -78,7 +78,7 @@ module API
expose :path, :path_with_namespace expose :path, :path_with_namespace
expose :issues_enabled, :merge_requests_enabled, :wiki_enabled, :builds_enabled, :snippets_enabled, :container_registry_enabled expose :issues_enabled, :merge_requests_enabled, :wiki_enabled, :builds_enabled, :snippets_enabled, :container_registry_enabled
expose :created_at, :last_activity_at expose :created_at, :last_activity_at
expose :shared_runners_enabled expose :shared_runners_enabled, :lfs_enabled
expose :creator_id expose :creator_id
expose :namespace expose :namespace
expose :forked_from_project, using: Entities::BasicProjectDetails, if: lambda{ |project, options| project.forked? } expose :forked_from_project, using: Entities::BasicProjectDetails, if: lambda{ |project, options| project.forked? }
......
...@@ -105,6 +105,7 @@ module API ...@@ -105,6 +105,7 @@ module API
# visibility_level (optional) - 0 by default # visibility_level (optional) - 0 by default
# import_url (optional) # import_url (optional)
# public_builds (optional) # public_builds (optional)
# lfs_enabled (optional)
# Example Request # Example Request
# POST /projects # POST /projects
post do post do
...@@ -124,7 +125,8 @@ module API ...@@ -124,7 +125,8 @@ module API
:visibility_level, :visibility_level,
:import_url, :import_url,
:public_builds, :public_builds,
:only_allow_merge_if_build_succeeds] :only_allow_merge_if_build_succeeds,
:lfs_enabled]
attrs = map_public_to_visibility_level(attrs) attrs = map_public_to_visibility_level(attrs)
@project = ::Projects::CreateService.new(current_user, attrs).execute @project = ::Projects::CreateService.new(current_user, attrs).execute
if @project.saved? if @project.saved?
...@@ -156,6 +158,7 @@ module API ...@@ -156,6 +158,7 @@ module API
# visibility_level (optional) # visibility_level (optional)
# import_url (optional) # import_url (optional)
# public_builds (optional) # public_builds (optional)
# lfs_enabled (optional)
# Example Request # Example Request
# POST /projects/user/:user_id # POST /projects/user/:user_id
post "user/:user_id" do post "user/:user_id" do
...@@ -174,7 +177,8 @@ module API ...@@ -174,7 +177,8 @@ module API
:visibility_level, :visibility_level,
:import_url, :import_url,
:public_builds, :public_builds,
:only_allow_merge_if_build_succeeds] :only_allow_merge_if_build_succeeds,
:lfs_enabled]
attrs = map_public_to_visibility_level(attrs) attrs = map_public_to_visibility_level(attrs)
@project = ::Projects::CreateService.new(user, attrs).execute @project = ::Projects::CreateService.new(user, attrs).execute
if @project.saved? if @project.saved?
...@@ -220,6 +224,7 @@ module API ...@@ -220,6 +224,7 @@ module API
# public (optional) - if true same as setting visibility_level = 20 # public (optional) - if true same as setting visibility_level = 20
# visibility_level (optional) - visibility level of a project # visibility_level (optional) - visibility level of a project
# public_builds (optional) # public_builds (optional)
# lfs_enabled (optional)
# Example Request # Example Request
# PUT /projects/:id # PUT /projects/:id
put ':id' do put ':id' do
...@@ -237,7 +242,8 @@ module API ...@@ -237,7 +242,8 @@ module API
:public, :public,
:visibility_level, :visibility_level,
:public_builds, :public_builds,
:only_allow_merge_if_build_succeeds] :only_allow_merge_if_build_succeeds,
:lfs_enabled]
attrs = map_public_to_visibility_level(attrs) attrs = map_public_to_visibility_level(attrs)
authorize_admin_project authorize_admin_project
authorize! :rename_project, user_project if attrs[:name].present? authorize! :rename_project, user_project if attrs[:name].present?
......
...@@ -110,6 +110,45 @@ describe 'Issue Boards', feature: true, js: true do ...@@ -110,6 +110,45 @@ describe 'Issue Boards', feature: true, js: true do
end end
end end
it 'search backlog list' do
page.within('#js-boards-seach') do
find('.form-control').set(issue1.title)
end
wait_for_vue_resource
expect(find('.board:nth-child(1)')).to have_selector('.card', count: 1)
expect(find('.board:nth-child(2)')).to have_selector('.card', count: 0)
expect(find('.board:nth-child(3)')).to have_selector('.card', count: 0)
expect(find('.board:nth-child(4)')).to have_selector('.card', count: 0)
end
it 'search done list' do
page.within('#js-boards-seach') do
find('.form-control').set(issue8.title)
end
wait_for_vue_resource
expect(find('.board:nth-child(1)')).to have_selector('.card', count: 0)
expect(find('.board:nth-child(2)')).to have_selector('.card', count: 0)
expect(find('.board:nth-child(3)')).to have_selector('.card', count: 0)
expect(find('.board:nth-child(4)')).to have_selector('.card', count: 1)
end
it 'search list' do
page.within('#js-boards-seach') do
find('.form-control').set(issue5.title)
end
wait_for_vue_resource
expect(find('.board:nth-child(1)')).to have_selector('.card', count: 0)
expect(find('.board:nth-child(2)')).to have_selector('.card', count: 1)
expect(find('.board:nth-child(3)')).to have_selector('.card', count: 0)
expect(find('.board:nth-child(4)')).to have_selector('.card', count: 0)
end
it 'allows user to delete board' do it 'allows user to delete board' do
page.within(find('.board:nth-child(2)')) do page.within(find('.board:nth-child(2)')) do
find('.board-delete').click find('.board-delete').click
...@@ -162,32 +201,6 @@ describe 'Issue Boards', feature: true, js: true do ...@@ -162,32 +201,6 @@ describe 'Issue Boards', feature: true, js: true do
end end
end end
it 'is searchable' do
page.within(find('.board', match: :first)) do
find('.form-control').set issue1.title
wait_for_vue_resource(spinner: false)
expect(page).to have_selector('.card', count: 1)
end
end
it 'clears search' do
page.within(find('.board', match: :first)) do
find('.form-control').set issue1.title
expect(page).to have_selector('.card', count: 1)
find('.board-search-clear-btn').click
end
wait_for_vue_resource
page.within(find('.board', match: :first)) do
expect(page).to have_selector('.card', count: 6)
end
end
it 'moves issue from backlog into list' do it 'moves issue from backlog into list' do
drag_to(list_to_index: 1) drag_to(list_to_index: 1)
......
...@@ -12,7 +12,6 @@ describe 'Awards Emoji', feature: true do ...@@ -12,7 +12,6 @@ describe 'Awards Emoji', feature: true do
describe 'Click award emoji from issue#show' do describe 'Click award emoji from issue#show' do
let!(:issue) do let!(:issue) do
create(:issue, create(:issue,
author: @user,
assignee: @user, assignee: @user,
project: project) project: project)
end end
......
...@@ -60,15 +60,6 @@ describe('List model', () => { ...@@ -60,15 +60,6 @@ describe('List model', () => {
}, 0); }, 0);
}); });
it('can\'t search when not backlog', () => {
expect(list.canSearch()).toBe(false);
});
it('can search when backlog', () => {
list.type = 'backlog';
expect(list.canSearch()).toBe(true);
});
it('gets issue from list', (done) => { it('gets issue from list', (done) => {
setTimeout(() => { setTimeout(() => {
const issue = list.findIssue(1); const issue = list.findIssue(1);
......
...@@ -4,7 +4,7 @@ describe API::API, api: true do ...@@ -4,7 +4,7 @@ describe API::API, api: true do
include ApiHelpers include ApiHelpers
let(:user) { create(:user) } let(:user) { create(:user) }
let!(:project) { create(:project) } let!(:project) { create(:project) }
let(:issue) { create(:issue, project: project, author: user) } let(:issue) { create(:issue, project: project) }
let!(:award_emoji) { create(:award_emoji, awardable: issue, user: user) } let!(:award_emoji) { create(:award_emoji, awardable: issue, user: user) }
let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) } let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
let!(:downvote) { create(:award_emoji, :downvote, awardable: merge_request, user: user) } let!(:downvote) { create(:award_emoji, :downvote, awardable: merge_request, user: user) }
...@@ -115,6 +115,8 @@ describe API::API, api: true do ...@@ -115,6 +115,8 @@ describe API::API, api: true do
end end
describe "POST /projects/:id/awardable/:awardable_id/award_emoji" do describe "POST /projects/:id/awardable/:awardable_id/award_emoji" do
let(:issue2) { create(:issue, project: project, author: user) }
context "on an issue" do context "on an issue" do
it "creates a new award emoji" do it "creates a new award emoji" do
post api("/projects/#{project.id}/issues/#{issue.id}/award_emoji", user), name: 'blowfish' post api("/projects/#{project.id}/issues/#{issue.id}/award_emoji", user), name: 'blowfish'
...@@ -136,6 +138,12 @@ describe API::API, api: true do ...@@ -136,6 +138,12 @@ describe API::API, api: true do
expect(response).to have_http_status(401) expect(response).to have_http_status(401)
end end
it "returns a 404 error if the user authored issue" do
post api("/projects/#{project.id}/issues/#{issue2.id}/award_emoji", user), name: 'thumbsup'
expect(response).to have_http_status(404)
end
it "normalizes +1 as thumbsup award" do it "normalizes +1 as thumbsup award" do
post api("/projects/#{project.id}/issues/#{issue.id}/award_emoji", user), name: '+1' post api("/projects/#{project.id}/issues/#{issue.id}/award_emoji", user), name: '+1'
...@@ -155,6 +163,8 @@ describe API::API, api: true do ...@@ -155,6 +163,8 @@ describe API::API, api: true do
end end
describe "POST /projects/:id/awardable/:awardable_id/notes/:note_id/award_emoji" do describe "POST /projects/:id/awardable/:awardable_id/notes/:note_id/award_emoji" do
let(:note2) { create(:note, project: project, noteable: issue, author: user) }
it 'creates a new award emoji' do it 'creates a new award emoji' do
expect do expect do
post api("/projects/#{project.id}/issues/#{issue.id}/notes/#{note.id}/award_emoji", user), name: 'rocket' post api("/projects/#{project.id}/issues/#{issue.id}/notes/#{note.id}/award_emoji", user), name: 'rocket'
...@@ -164,6 +174,12 @@ describe API::API, api: true do ...@@ -164,6 +174,12 @@ describe API::API, api: true do
expect(json_response['user']['username']).to eq(user.username) expect(json_response['user']['username']).to eq(user.username)
end end
it "it returns 404 error when user authored note" do
post api("/projects/#{project.id}/issues/#{issue.id}/notes/#{note2.id}/award_emoji", user), name: 'thumbsup'
expect(response).to have_http_status(404)
end
it "normalizes +1 as thumbsup award" do it "normalizes +1 as thumbsup award" do
post api("/projects/#{project.id}/issues/#{issue.id}/notes/#{note.id}/award_emoji", user), name: '+1' post api("/projects/#{project.id}/issues/#{issue.id}/notes/#{note.id}/award_emoji", user), name: '+1'
......
...@@ -44,6 +44,113 @@ describe 'Git LFS API and storage' do ...@@ -44,6 +44,113 @@ describe 'Git LFS API and storage' do
end end
end end
context 'project specific LFS settings' do
let(:project) { create(:empty_project) }
let(:body) do
{
'objects' => [
{ 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
'size' => 1575078
},
{ 'oid' => sample_oid,
'size' => sample_size
}
],
'operation' => 'upload'
}
end
let(:authorization) { authorize_user }
context 'with LFS disabled globally' do
before do
project.team << [user, :master]
allow(Gitlab.config.lfs).to receive(:enabled).and_return(false)
end
describe 'LFS disabled in project' do
before do
project.update_attribute(:lfs_enabled, false)
end
it 'responds with a 501 message on upload' do
post_lfs_json "#{project.http_url_to_repo}/info/lfs/objects/batch", body, headers
expect(response).to have_http_status(501)
end
it 'responds with a 501 message on download' do
get "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", nil, headers
expect(response).to have_http_status(501)
end
end
describe 'LFS enabled in project' do
before do
project.update_attribute(:lfs_enabled, true)
end
it 'responds with a 501 message on upload' do
post_lfs_json "#{project.http_url_to_repo}/info/lfs/objects/batch", body, headers
expect(response).to have_http_status(501)
end
it 'responds with a 501 message on download' do
get "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", nil, headers
expect(response).to have_http_status(501)
end
end
end
context 'with LFS enabled globally' do
before do
project.team << [user, :master]
enable_lfs
end
describe 'LFS disabled in project' do
before do
project.update_attribute(:lfs_enabled, false)
end
it 'responds with a 403 message on upload' do
post_lfs_json "#{project.http_url_to_repo}/info/lfs/objects/batch", body, headers
expect(response).to have_http_status(403)
expect(json_response).to include('message' => 'Access forbidden. Check your access level.')
end
it 'responds with a 403 message on download' do
get "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", nil, headers
expect(response).to have_http_status(403)
expect(json_response).to include('message' => 'Access forbidden. Check your access level.')
end
end
describe 'LFS enabled in project' do
before do
project.update_attribute(:lfs_enabled, true)
end
it 'responds with a 200 message on upload' do
post_lfs_json "#{project.http_url_to_repo}/info/lfs/objects/batch", body, headers
expect(response).to have_http_status(200)
expect(json_response['objects'].first['size']).to eq(1575078)
end
it 'responds with a 200 message on download' do
get "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", nil, headers
expect(response).to have_http_status(200)
end
end
end
end
describe 'deprecated API' do describe 'deprecated API' do
let(:project) { create(:empty_project) } let(:project) { create(:empty_project) }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment