summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/awards_handler.coffee3
-rw-r--r--app/assets/stylesheets/framework/gitlab-theme.scss16
-rw-r--r--app/assets/stylesheets/pages/detail_page.scss8
-rw-r--r--app/assets/stylesheets/pages/issuable.scss25
-rw-r--r--app/assets/stylesheets/pages/issues.scss2
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss4
-rw-r--r--app/controllers/application_controller.rb2
-rw-r--r--app/controllers/ci/projects_controller.rb6
-rw-r--r--app/controllers/projects/badges_controller.rb11
-rw-r--r--app/controllers/projects/builds_controller.rb15
-rw-r--r--app/controllers/projects/commit_controller.rb4
-rw-r--r--app/controllers/projects/imports_controller.rb12
-rw-r--r--app/controllers/projects/milestones_controller.rb11
-rw-r--r--app/controllers/projects/repositories_controller.rb4
-rw-r--r--app/helpers/application_helper.rb73
-rw-r--r--app/helpers/diff_helper.rb2
-rw-r--r--app/helpers/gitlab_markdown_helper.rb15
-rw-r--r--app/helpers/issuables_helper.rb37
-rw-r--r--app/helpers/issues_helper.rb6
-rw-r--r--app/helpers/nav_helper.rb18
-rw-r--r--app/helpers/projects_helper.rb4
-rw-r--r--app/helpers/sorting_helper.rb18
-rw-r--r--app/mailers/emails/builds.rb13
-rw-r--r--app/models/application_setting.rb4
-rw-r--r--app/models/concerns/issuable.rb36
-rw-r--r--app/models/email.rb2
-rw-r--r--app/models/member.rb1
-rw-r--r--app/models/merge_request.rb4
-rw-r--r--app/models/project.rb21
-rw-r--r--app/models/project_services/pushover_service.rb2
-rw-r--r--app/models/project_wiki.rb2
-rw-r--r--app/models/repository.rb21
-rw-r--r--app/models/user.rb7
-rw-r--r--app/services/archive_repository_service.rb23
-rw-r--r--app/services/ci/image_for_build_service.rb19
-rw-r--r--app/services/create_branch_service.rb6
-rw-r--r--app/services/delete_branch_service.rb6
-rw-r--r--app/services/projects/destroy_service.rb14
-rw-r--r--app/services/system_note_service.rb19
-rw-r--r--app/validators/email_validator.rb15
-rw-r--r--app/views/notify/build_fail_email.html.haml3
-rw-r--r--app/views/notify/build_success_email.html.haml2
-rw-r--r--app/views/projects/blob/_blob.html.haml2
-rw-r--r--app/views/projects/blob/_image.html.haml9
-rw-r--r--app/views/projects/diffs/_image.html.haml16
-rw-r--r--app/views/projects/forks/index.html.haml17
-rw-r--r--app/views/projects/issues/_issue.html.haml19
-rw-r--r--app/views/projects/issues/show.html.haml29
-rw-r--r--app/views/projects/issues/update.js.haml2
-rw-r--r--app/views/projects/merge_requests/_merge_request.html.haml19
-rw-r--r--app/views/projects/merge_requests/update.js.haml6
-rw-r--r--app/views/shared/_sort_dropdown.html.haml4
-rw-r--r--app/views/shared/issuable/_sidebar.html.haml19
-rw-r--r--app/workers/repository_fork_worker.rb1
-rw-r--r--app/workers/repository_import_worker.rb1
55 files changed, 395 insertions, 265 deletions
diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee
index 047df4786a9..360acb864f6 100644
--- a/app/assets/javascripts/awards_handler.coffee
+++ b/app/assets/javascripts/awards_handler.coffee
@@ -49,10 +49,11 @@ class @AwardsHandler
counter.text(parseInt(counter.text()) - 1)
emojiIcon.removeClass("active")
@removeMeFromAuthorList(emoji)
- else if emoji =="thumbsup" || emoji == "thumbsdown"
+ else if emoji == "thumbsup" || emoji == "thumbsdown"
emojiIcon.tooltip("destroy")
counter.text(0)
emojiIcon.removeClass("active")
+ @removeMeFromAuthorList(emoji)
else
emojiIcon.tooltip("destroy")
emojiIcon.remove()
diff --git a/app/assets/stylesheets/framework/gitlab-theme.scss b/app/assets/stylesheets/framework/gitlab-theme.scss
index 8d9a0aae568..0f68582e447 100644
--- a/app/assets/stylesheets/framework/gitlab-theme.scss
+++ b/app/assets/stylesheets/framework/gitlab-theme.scss
@@ -118,3 +118,19 @@ body {
@include gitlab-theme(#9988CC, $theme-violet, #443366, #332255);
}
}
+
+::-webkit-scrollbar{
+ width: 3px;
+}
+
+::-webkit-scrollbar-thumb{
+ background-color:$theme-charcoal; border-radius: 0;
+}
+
+::-webkit-scrollbar-thumb:hover{
+ background-color:$theme-charcoal;
+}
+
+::-webkit-scrollbar-track{
+ background-color:#FFF;
+} \ No newline at end of file
diff --git a/app/assets/stylesheets/pages/detail_page.scss b/app/assets/stylesheets/pages/detail_page.scss
index 529a43548c8..d93b6ee6733 100644
--- a/app/assets/stylesheets/pages/detail_page.scss
+++ b/app/assets/stylesheets/pages/detail_page.scss
@@ -12,6 +12,14 @@
.identifier {
color: #5c5d5e;
}
+
+ .issue_created_ago, .author_link {
+ white-space: nowrap;
+ }
+
+ .issue-meta {
+ margin-left: 65px
+ }
}
.detail-page-description {
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 9d5dc42b6cc..ef62f069dc2 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -80,6 +80,10 @@
display: inline-block;
}
+ .select2-container span {
+ margin-top: 0;
+ }
+
.issuable-count {
}
@@ -88,6 +92,10 @@
margin-left: 20px;
border-left: 1px solid $border-gray-light;
padding-left: 10px;
+
+ &:hover {
+ color: $gray-darkest;
+ }
}
}
@@ -192,6 +200,10 @@
.btn {
background: $gray-normal;
border: 1px solid $border-gray-normal;
+ &:hover {
+ background: $gray-dark;
+ border: 1px solid $border-gray-dark;
+ }
}
&.right-sidebar-collapsed {
@@ -223,6 +235,19 @@
display: block;
margin-top: 0;
}
+
+ .btn-clipboard {
+ border: none;
+
+ &:hover {
+ background: transparent;
+ }
+
+ i {
+ color: #999999;
+ }
+ }
+
}
}
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index 8694bd654a7..1cc853dd4f5 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -24,7 +24,7 @@
display: inline-block;
}
- .issue-no-comments {
+ .issue-no-comments, .issue-no-votes {
opacity: 0.5;
}
}
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index f033ff15f88..6b497cd56ed 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -163,7 +163,7 @@
display: inline-block;
}
- .merge-request-no-comments {
+ .merge-request-no-comments, .merge-request-no-votes {
opacity: 0.5;
}
}
@@ -236,4 +236,4 @@
}
}
}
-} \ No newline at end of file
+}
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 48b1f95acb9..2c329b60a19 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -164,7 +164,7 @@ class ApplicationController < ActionController::Base
end
def git_not_found!
- render html: "errors/git_not_found", layout: "errors", status: 404
+ render "errors/git_not_found.html", layout: "errors", status: 404
end
def method_missing(method_sym, *arguments, &block)
diff --git a/app/controllers/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb
index 711c2847d5e..d1824b481d7 100644
--- a/app/controllers/ci/projects_controller.rb
+++ b/app/controllers/ci/projects_controller.rb
@@ -12,9 +12,13 @@ module Ci
# Project status badge
# Image with build status for sha or ref
+ #
+ # This action in DEPRECATED, this is here only for backwards compatibility
+ # with projects migrated from GitLab CI.
+ #
def badge
+ return render_404 unless @project
image = Ci::ImageForBuildService.new.execute(@project, params)
-
send_file image.path, filename: image.name, disposition: 'inline', type:"image/svg+xml"
end
diff --git a/app/controllers/projects/badges_controller.rb b/app/controllers/projects/badges_controller.rb
new file mode 100644
index 00000000000..a4dd94b941c
--- /dev/null
+++ b/app/controllers/projects/badges_controller.rb
@@ -0,0 +1,11 @@
+class Projects::BadgesController < Projects::ApplicationController
+ def build
+ respond_to do |format|
+ format.html { render_404 }
+ format.svg do
+ image = Ci::ImageForBuildService.new.execute(project, ref: params[:ref])
+ send_file(image.path, filename: image.name, disposition: 'inline', type: 'image/svg+xml')
+ end
+ end
+ end
+end
diff --git a/app/controllers/projects/builds_controller.rb b/app/controllers/projects/builds_controller.rb
index 9e89296e71d..ec379c53b8f 100644
--- a/app/controllers/projects/builds_controller.rb
+++ b/app/controllers/projects/builds_controller.rb
@@ -1,10 +1,8 @@
class Projects::BuildsController < Projects::ApplicationController
before_action :build, except: [:index, :cancel_all]
-
before_action :authorize_read_build!, except: [:cancel, :cancel_all, :retry]
before_action :authorize_update_build!, except: [:index, :show, :status]
-
- layout "project"
+ layout 'project'
def index
@scope = params[:scope]
@@ -24,7 +22,6 @@ class Projects::BuildsController < Projects::ApplicationController
def cancel_all
@project.builds.running_or_pending.each(&:cancel)
-
redirect_to namespace_project_builds_path(project.namespace, project)
end
@@ -47,20 +44,18 @@ class Projects::BuildsController < Projects::ApplicationController
end
build = Ci::Build.retry(@build)
-
redirect_to build_path(build)
end
- def status
- render json: @build.to_json(only: [:status, :id, :sha, :coverage], methods: :sha)
- end
-
def cancel
@build.cancel
-
redirect_to build_path(@build)
end
+ def status
+ render json: @build.to_json(only: [:status, :id, :sha, :coverage], methods: :sha)
+ end
+
private
def build
diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb
index 21f4d9f44ec..36951b91372 100644
--- a/app/controllers/projects/commit_controller.rb
+++ b/app/controllers/projects/commit_controller.rb
@@ -11,8 +11,6 @@ class Projects::CommitController < Projects::ApplicationController
before_action :define_show_vars, only: [:show, :builds]
def show
- return git_not_found! unless @commit
-
apply_diff_view_cookie!
@line_notes = commit.notes.inline
@@ -68,6 +66,8 @@ class Projects::CommitController < Projects::ApplicationController
end
def define_show_vars
+ return git_not_found! unless commit
+
if params[:w].to_i == 1
@diffs = commit.diffs({ ignore_whitespace_change: true })
else
diff --git a/app/controllers/projects/imports_controller.rb b/app/controllers/projects/imports_controller.rb
index 07f355c35b1..196996f1752 100644
--- a/app/controllers/projects/imports_controller.rb
+++ b/app/controllers/projects/imports_controller.rb
@@ -3,6 +3,7 @@ class Projects::ImportsController < Projects::ApplicationController
before_action :authorize_admin_project!
before_action :require_no_repo, only: [:new, :create]
before_action :redirect_if_progress, only: [:new, :create]
+ before_action :redirect_if_no_import, only: :show
def new
end
@@ -63,14 +64,19 @@ class Projects::ImportsController < Projects::ApplicationController
def require_no_repo
if @project.repository_exists?
- redirect_to(namespace_project_path(@project.namespace, @project))
+ redirect_to namespace_project_path(@project.namespace, @project)
end
end
def redirect_if_progress
if @project.import_in_progress?
- redirect_to namespace_project_import_path(@project.namespace, @project) &&
- return
+ redirect_to namespace_project_import_path(@project.namespace, @project)
+ end
+ end
+
+ def redirect_if_no_import
+ if @project.repository_exists? && @project.no_import?
+ redirect_to namespace_project_path(@project.namespace, @project)
end
end
end
diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb
index 15506bd677a..a5c4ef1c7c7 100644
--- a/app/controllers/projects/milestones_controller.rb
+++ b/app/controllers/projects/milestones_controller.rb
@@ -11,11 +11,12 @@ class Projects::MilestonesController < Projects::ApplicationController
respond_to :html
def index
- @milestones = case params[:state]
- when 'all'; @project.milestones.order("state, due_date DESC")
- when 'closed'; @project.milestones.closed.order("due_date DESC")
- else @project.milestones.active.order("due_date ASC")
- end
+ @milestones =
+ case params[:state]
+ when 'all' then @project.milestones.reorder(due_date: :desc, title: :asc)
+ when 'closed' then @project.milestones.closed.reorder(due_date: :desc, title: :asc)
+ else @project.milestones.active.reorder(due_date: :asc, title: :asc)
+ end
@milestones = @milestones.includes(:project)
@milestones = @milestones.page(params[:page]).per(PER_PAGE)
diff --git a/app/controllers/projects/repositories_controller.rb b/app/controllers/projects/repositories_controller.rb
index ba9aea1c165..5c7614cfbaf 100644
--- a/app/controllers/projects/repositories_controller.rb
+++ b/app/controllers/projects/repositories_controller.rb
@@ -11,7 +11,9 @@ class Projects::RepositoriesController < Projects::ApplicationController
end
def archive
- render json: ArchiveRepositoryService.new(@project, params[:ref], params[:format]).execute
+ RepositoryArchiveCacheWorker.perform_async
+ headers.store(*Gitlab::Workhorse.send_git_archive(@project, params[:ref], params[:format]))
+ head :ok
rescue => ex
logger.error("#{self.class.name}: #{ex}")
return git_not_found!
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 622cbfe3cc4..ecefa9b006d 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -212,8 +212,7 @@ module ApplicationHelper
file_content
end
else
- GitHub::Markup.render(file_name, file_content).
- force_encoding(file_content.encoding).html_safe
+ other_markup(file_name, file_content)
end
rescue RuntimeError
simple_format(file_content)
@@ -281,76 +280,6 @@ module ApplicationHelper
end
end
- def issuable_link_next(project,issuable)
- if project.nil?
- nil
- elsif current_controller?(:issues)
- namespace_project_issue_path(project.namespace, project, next_issuable_for(project, issuable.id).try(:iid))
- elsif current_controller?(:merge_requests)
- namespace_project_merge_request_path(project.namespace, project, next_issuable_for(project, issuable.id).try(:iid))
- end
- end
-
- def issuable_link_prev(project,issuable)
- if project.nil?
- nil
- elsif current_controller?(:issues)
- namespace_project_issue_path(project.namespace, project, prev_issuable_for(project, issuable.id).try(:iid))
- elsif current_controller?(:merge_requests)
- namespace_project_merge_request_path(project.namespace, project, prev_issuable_for(project, issuable.id).try(:iid))
- end
- end
-
- def issuable_count(entity, project)
- if project.nil?
- 0
- elsif current_controller?(:issues)
- project.issues.send(entity).count
- elsif current_controller?(:merge_requests)
- project.merge_requests.send(entity).count
- end
- end
-
- def next_issuable_for(project, id)
- if project.nil?
- nil
- elsif current_controller?(:issues)
- project.issues.where("id > ?", id).last
- elsif current_controller?(:merge_requests)
- project.merge_requests.where("id > ?", id).last
- end
- end
-
- def has_next_issuable?(project, id)
- if project.nil?
- nil
- elsif current_controller?(:issues)
- project.issues.where("id > ?", id).last
- elsif current_controller?(:merge_requests)
- project.merge_requests.where("id > ?", id).last
- end
- end
-
- def prev_issuable_for(project, id)
- if project.nil?
- nil
- elsif current_controller?(:issues)
- project.issues.where("id < ?", id).first
- elsif current_controller?(:merge_requests)
- project.merge_requests.where("id < ?", id).first
- end
- end
-
- def has_prev_issuable?(project, id)
- if project.nil?
- nil
- elsif current_controller?(:issues)
- project.issues.where("id < ?", id).first
- elsif current_controller?(:merge_requests)
- project.merge_requests.where("id < ?", id).first
- end
- end
-
def state_filters_text_for(entity, project)
titles = {
opened: "Open"
diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb
index f9bacc8ba45..6a3ab3ea40a 100644
--- a/app/helpers/diff_helper.rb
+++ b/app/helpers/diff_helper.rb
@@ -69,7 +69,7 @@ module DiffHelper
end
def line_comments
- @line_comments ||= @line_notes.select(&:active?).group_by(&:line_code)
+ @line_comments ||= @line_notes.select(&:active?).sort_by(&:created_at).group_by(&:line_code)
end
def organize_comments(type_left, type_right, line_code_left, line_code_right)
diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb
index 1a226252251..89d2a648494 100644
--- a/app/helpers/gitlab_markdown_helper.rb
+++ b/app/helpers/gitlab_markdown_helper.rb
@@ -78,6 +78,21 @@ module GitlabMarkdownHelper
)
end
+ def other_markup(file_name, text)
+ Gitlab::OtherMarkup.render(
+ file_name,
+ text,
+ project: @project,
+ current_user: (current_user if defined?(current_user)),
+
+ # RelativeLinkFilter
+ project_wiki: @project_wiki,
+ requested_path: @path,
+ ref: @ref,
+ commit: @commit
+ )
+ end
+
# Return the first line of +text+, up to +max_chars+, after parsing the line
# as Markdown. HTML tags in the parsed output are not counted toward the
# +max_chars+ limit. If the length limit falls within a tag's contents, then
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
new file mode 100644
index 00000000000..91a3aa371ef
--- /dev/null
+++ b/app/helpers/issuables_helper.rb
@@ -0,0 +1,37 @@
+module IssuablesHelper
+
+ def sidebar_gutter_toggle_icon
+ sidebar_gutter_collapsed? ? icon('angle-double-left') : icon('angle-double-right')
+ end
+
+ def sidebar_gutter_collapsed_class
+ "right-sidebar-#{sidebar_gutter_collapsed? ? 'collapsed' : 'expanded'}"
+ end
+
+ def issuables_count(issuable)
+ base_issuable_scope(issuable).maximum(:iid)
+ end
+
+ def next_issuable_for(issuable)
+ base_issuable_scope(issuable).where('iid > ?', issuable.iid).last
+ end
+
+ def prev_issuable_for(issuable)
+ base_issuable_scope(issuable).where('iid < ?', issuable.iid).first
+ end
+
+ private
+
+ def sidebar_gutter_collapsed?
+ cookies[:collapsed_gutter] == 'true'
+ end
+
+ def base_issuable_scope(issuable)
+ issuable.project.send(issuable.class.table_name).send(issuable_state_scope(issuable))
+ end
+
+ def issuable_state_scope(issuable)
+ issuable.open? ? :opened : :closed
+ end
+
+end
diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb
index 43262d579e9..ae4ebc0854a 100644
--- a/app/helpers/issues_helper.rb
+++ b/app/helpers/issues_helper.rb
@@ -44,14 +44,14 @@ module IssuesHelper
end
def bulk_update_milestone_options
- milestones = project_active_milestones.to_a
+ milestones = @project.milestones.active.reorder(due_date: :asc, title: :asc).to_a
milestones.unshift(Milestone::None)
options_from_collection_for_select(milestones, 'id', 'title', params[:milestone_id])
end
def milestone_options(object)
- milestones = object.project.milestones.active.to_a
+ milestones = object.project.milestones.active.reorder(due_date: :asc, title: :asc).to_a
milestones.unshift(Milestone::None)
options_from_collection_for_select(milestones, 'id', 'title', object.milestone_id)
@@ -69,7 +69,7 @@ module IssuesHelper
end
end
- def issue_button_visibility(issue, closed)
+ def issue_button_visibility(issue, closed)
return 'hidden' if issue.closed? == closed
end
diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb
index 75f2ed5e054..29cb753e62c 100644
--- a/app/helpers/nav_helper.rb
+++ b/app/helpers/nav_helper.rb
@@ -3,18 +3,6 @@ module NavHelper
cookies[:collapsed_nav] == 'true'
end
- def sidebar_gutter_collapsed_class
- if cookies[:collapsed_gutter] == 'true'
- "right-sidebar-collapsed"
- else
- "right-sidebar-expanded"
- end
- end
-
- def sidebar_gutter_collapsed?
- cookies[:collapsed_gutter] == 'true'
- end
-
def nav_sidebar_class
if nav_menu_collapsed?
"sidebar-collapsed"
@@ -32,9 +20,9 @@ module NavHelper
end
def page_gutter_class
- if current_path?('merge_requests#show') ||
- current_path?('merge_requests#diffs') ||
- current_path?('merge_requests#commits') ||
+ if current_path?('merge_requests#show') ||
+ current_path?('merge_requests#diffs') ||
+ current_path?('merge_requests#commits') ||
current_path?('issues#show')
if cookies[:collapsed_gutter] == 'true'
"page-gutter right-sidebar-collapsed"
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index cc1c61e9ec0..d6fb629b0c2 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -98,10 +98,6 @@ module ProjectsHelper
project_nav_tabs.include? name
end
- def project_active_milestones
- @project.milestones.active.order("due_date, title ASC")
- end
-
def project_for_deploy_key(deploy_key)
if deploy_key.projects.include?(@project)
@project
diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb
index 241179b0212..f9026b887da 100644
--- a/app/helpers/sorting_helper.rb
+++ b/app/helpers/sorting_helper.rb
@@ -11,6 +11,8 @@ module SortingHelper
sort_value_largest_repo => sort_title_largest_repo,
sort_value_recently_signin => sort_title_recently_signin,
sort_value_oldest_signin => sort_title_oldest_signin,
+ sort_value_downvotes => sort_title_downvotes,
+ sort_value_upvotes => sort_title_upvotes
}
end
@@ -54,6 +56,14 @@ module SortingHelper
'Oldest sign in'
end
+ def sort_title_downvotes
+ 'Least popular'
+ end
+
+ def sort_title_upvotes
+ 'Most popular'
+ end
+
def sort_value_oldest_updated
'updated_asc'
end
@@ -93,4 +103,12 @@ module SortingHelper
def sort_value_oldest_signin
'oldest_sign_in'
end
+
+ def sort_value_downvotes
+ 'downvotes_desc'
+ end
+
+ def sort_value_upvotes
+ 'upvotes_desc'
+ end
end
diff --git a/app/mailers/emails/builds.rb b/app/mailers/emails/builds.rb
index 64c1ce8cfab..2f86d1be576 100644
--- a/app/mailers/emails/builds.rb
+++ b/app/mailers/emails/builds.rb
@@ -3,26 +3,27 @@ module Emails
def build_fail_email(build_id, to)
@build = Ci::Build.find(build_id)
@project = @build.project
+
add_project_headers
- add_build_headers
- headers['X-GitLab-Build-Status'] = "failed"
+ add_build_headers('failed')
mail(to: to, subject: subject("Build failed for #{@project.name}", @build.short_sha))
end
def build_success_email(build_id, to)
@build = Ci::Build.find(build_id)
@project = @build.project
+
add_project_headers
- add_build_headers
- headers['X-GitLab-Build-Status'] = "success"
+ add_build_headers('success')
mail(to: to, subject: subject("Build success for #{@project.name}", @build.short_sha))
end
private
- def add_build_headers
+
+ def add_build_headers(status)
headers['X-GitLab-Build-Id'] = @build.id
headers['X-GitLab-Build-Ref'] = @build.ref
+ headers['X-GitLab-Build-Status'] = status.to_s
end
-
end
end
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index fa48fe5b9e4..269056e0e77 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -71,8 +71,8 @@ class ApplicationSetting < ActiveRecord::Base
url: true
validates :admin_notification_email,
- allow_blank: true,
- email: true
+ email: true,
+ allow_blank: true
validates :two_factor_grace_period,
numericality: { greater_than_or_equal_to: 0 }
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index cf6aa592e2a..e5f089fb8a0 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -69,10 +69,35 @@ module Issuable
case method.to_s
when 'milestone_due_asc' then order_milestone_due_asc
when 'milestone_due_desc' then order_milestone_due_desc
+ when 'downvotes_desc' then order_downvotes_desc
+ when 'upvotes_desc' then order_upvotes_desc
else
order_by(method)
end
end
+
+ def order_downvotes_desc
+ order_votes_desc('thumbsdown')
+ end
+
+ def order_upvotes_desc
+ order_votes_desc('thumbsup')
+ end
+
+ def order_votes_desc(award_emoji_name)
+ issuable_table = self.arel_table
+ note_table = Note.arel_table
+
+ join_clause = issuable_table.join(note_table, Arel::Nodes::OuterJoin).on(
+ note_table[:noteable_id].eq(issuable_table[:id]).and(
+ note_table[:noteable_type].eq(self.name).and(
+ note_table[:is_award].eq(true).and(note_table[:note].eq(award_emoji_name))
+ )
+ )
+ ).join_sources
+
+ joins(join_clause).group(issuable_table[:id]).reorder("COUNT(notes.id) DESC")
+ end
end
def today?
@@ -129,13 +154,10 @@ module Issuable
hook_data = {
object_kind: self.class.name.underscore,
user: user.hook_attrs,
- repository: {
- name: project.name,
- url: project.url_to_repo,
- description: project.description,
- homepage: project.web_url
- },
- object_attributes: hook_attrs
+ project: project.hook_attrs,
+ object_attributes: hook_attrs,
+ # DEPRECATED
+ repository: project.hook_attrs.slice(:name, :url, :description, :homepage)
}
hook_data.merge!(assignee: assignee.hook_attrs) if assignee
diff --git a/app/models/email.rb b/app/models/email.rb
index 935705e2ed4..b323d1edd10 100644
--- a/app/models/email.rb
+++ b/app/models/email.rb
@@ -15,7 +15,7 @@ class Email < ActiveRecord::Base
belongs_to :user
validates :user_id, presence: true
- validates :email, presence: true, email: { strict_mode: true }, uniqueness: true
+ validates :email, presence: true, uniqueness: true, email: true
validate :unique_email, if: ->(email) { email.email_changed? }
before_validation :cleanup_email
diff --git a/app/models/member.rb b/app/models/member.rb
index 34efcd0088d..ca08007b7eb 100644
--- a/app/models/member.rb
+++ b/app/models/member.rb
@@ -39,7 +39,6 @@ class Member < ActiveRecord::Base
if: :invite?
},
email: {
- strict_mode: true,
allow_nil: true
},
uniqueness: {
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 345b9a26d4e..ea2b0e075f6 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -137,7 +137,7 @@ class MergeRequest < ActiveRecord::Base
scope :by_milestone, ->(milestone) { where(milestone_id: milestone) }
scope :in_projects, ->(project_ids) { where("source_project_id in (:project_ids) OR target_project_id in (:project_ids)", project_ids: project_ids) }
scope :of_projects, ->(ids) { where(target_project_id: ids) }
- scope :opened, -> { with_state(:opened) }
+ scope :opened, -> { with_states(:opened, :reopened) }
scope :merged, -> { with_state(:merged) }
scope :closed, -> { with_state(:closed) }
scope :closed_and_merged, -> { with_states(:closed, :merged) }
@@ -241,7 +241,7 @@ class MergeRequest < ActiveRecord::Base
return unless unchecked?
can_be_merged =
- project.repository.can_be_merged?(source_sha, target_branch)
+ !broken? && project.repository.can_be_merged?(source_sha, target_branch)
if can_be_merged
mark_as_mergeable
diff --git a/app/models/project.rb b/app/models/project.rb
index f11c6d7c6be..95ad88c76ae 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -342,7 +342,7 @@ class Project < ActiveRecord::Base
end
def repository
- @repository ||= Repository.new(path_with_namespace, nil, self)
+ @repository ||= Repository.new(path_with_namespace, self)
end
def commit(id = 'HEAD')
@@ -382,6 +382,10 @@ class Project < ActiveRecord::Base
external_import? || forked?
end
+ def no_import?
+ import_status == 'none'
+ end
+
def external_import?
import_url.present?
end
@@ -738,11 +742,20 @@ class Project < ActiveRecord::Base
def hook_attrs
{
name: name,
- ssh_url: ssh_url_to_repo,
- http_url: http_url_to_repo,
+ description: description,
web_url: web_url,
+ avatar_url: avatar_url,
+ git_ssh_url: ssh_url_to_repo,
+ git_http_url: http_url_to_repo,
namespace: namespace.name,
- visibility_level: visibility_level
+ visibility_level: visibility_level,
+ path_with_namespace: path_with_namespace,
+ default_branch: default_branch,
+ # Backward compatibility
+ homepage: web_url,
+ url: url_to_repo,
+ ssh_url: ssh_url_to_repo,
+ http_url: http_url_to_repo
}
end
diff --git a/app/models/project_services/pushover_service.rb b/app/models/project_services/pushover_service.rb
index 3d7e8bbee61..e76d9eca2ab 100644
--- a/app/models/project_services/pushover_service.rb
+++ b/app/models/project_services/pushover_service.rb
@@ -112,7 +112,7 @@ class PushoverService < Service
priority: priority,
title: "#{project.name_with_namespace}",
message: message,
- url: data[:repository][:homepage],
+ url: data[:project][:web_url],
url_title: "See project #{project.name_with_namespace}"
}
diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb
index c847eba8d1c..c96e6f0b8ea 100644
--- a/app/models/project_wiki.rb
+++ b/app/models/project_wiki.rb
@@ -123,7 +123,7 @@ class ProjectWiki
end
def repository
- Repository.new(path_with_namespace, default_branch, @project)
+ Repository.new(path_with_namespace, @project)
end
def default_branch
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 7f0047a002e..5a25ccb1dd6 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -15,7 +15,7 @@ class Repository
Gitlab::Popen.popen(%W(find #{repository_downloads_path} -not -path #{repository_downloads_path} -mmin +120 -delete))
end
- def initialize(path_with_namespace, default_branch = nil, project = nil)
+ def initialize(path_with_namespace, project)
@path_with_namespace = path_with_namespace
@project = project
end
@@ -238,6 +238,15 @@ class Repository
expire_branch_cache(branch_name)
end
+ # Expires _all_ caches, including those that would normally only be expired
+ # under specific conditions.
+ def expire_all_caches!
+ expire_cache
+ expire_root_ref_cache
+ expire_emptiness_caches
+ expire_has_visible_content_cache
+ end
+
def expire_branch_cache(branch_name = nil)
# When we push to the root branch we have to flush the cache for all other
# branches as their statistics are based on the commits relative to the
@@ -258,6 +267,14 @@ class Repository
@root_ref = nil
end
+ # Expires the cache(s) used to determine if a repository is empty or not.
+ def expire_emptiness_caches
+ cache.expire(:empty?)
+ @empty = nil
+
+ expire_has_visible_content_cache
+ end
+
def expire_has_visible_content_cache
cache.expire(:has_visible_content?)
@has_visible_content = nil
@@ -611,6 +628,8 @@ class Repository
end
def merge_base(first_commit_id, second_commit_id)
+ first_commit_id = commit(first_commit_id).try(:id) || first_commit_id
+ second_commit_id = commit(second_commit_id).try(:id) || second_commit_id
rugged.merge_base(first_commit_id, second_commit_id)
rescue Rugged::ReferenceError
nil
diff --git a/app/models/user.rb b/app/models/user.rb
index 234c1cd89f9..9fe94b13e52 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -146,11 +146,8 @@ class User < ActiveRecord::Base
# Validations
#
validates :name, presence: true
- # Note that a 'uniqueness' and presence check is provided by devise :validatable for email. We do not need to
- # duplicate that here as the validation framework will have duplicate errors in the event of a failure.
- validates :email, presence: true, email: { strict_mode: true }
- validates :notification_email, presence: true, email: { strict_mode: true }
- validates :public_email, presence: true, email: { strict_mode: true }, allow_blank: true, uniqueness: true
+ validates :notification_email, presence: true, email: true
+ validates :public_email, presence: true, uniqueness: true, email: true, allow_blank: true
validates :bio, length: { maximum: 255 }, allow_blank: true
validates :projects_limit, presence: true, numericality: { greater_than_or_equal_to: 0 }
validates :username,
diff --git a/app/services/archive_repository_service.rb b/app/services/archive_repository_service.rb
deleted file mode 100644
index 2160bf13e6d..00000000000
--- a/app/services/archive_repository_service.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-class ArchiveRepositoryService
- attr_reader :project, :ref, :format
-
- def initialize(project, ref, format)
- format ||= 'tar.gz'
- @project, @ref, @format = project, ref, format.downcase
- end
-
- def execute(options = {})
- RepositoryArchiveCacheWorker.perform_async
-
- metadata = project.repository.archive_metadata(ref, storage_path, format)
- raise "Repository or ref not found" if metadata.empty?
-
- metadata
- end
-
- private
-
- def storage_path
- Gitlab.config.gitlab.repository_downloads_path
- end
-end
diff --git a/app/services/ci/image_for_build_service.rb b/app/services/ci/image_for_build_service.rb
index f469b13e902..005a5c4661c 100644
--- a/app/services/ci/image_for_build_service.rb
+++ b/app/services/ci/image_for_build_service.rb
@@ -1,28 +1,23 @@
module Ci
class ImageForBuildService
- def execute(project, params)
- sha = params[:sha]
- sha ||=
- if params[:ref]
- project.commit(params[:ref]).try(:sha)
- end
+ def execute(project, opts)
+ sha = opts[:sha] || ref_sha(project, opts[:ref])
commit = project.ci_commits.ordered.find_by(sha: sha)
image_name = image_for_commit(commit)
image_path = Rails.root.join('public/ci', image_name)
-
- OpenStruct.new(
- path: image_path,
- name: image_name
- )
+ OpenStruct.new(path: image_path, name: image_name)
end
private
+ def ref_sha(project, ref)
+ project.commit(ref).try(:sha) if ref
+ end
+
def image_for_commit(commit)
return 'build-unknown.svg' unless commit
-
'build-' + commit.status + ".svg"
end
end
diff --git a/app/services/create_branch_service.rb b/app/services/create_branch_service.rb
index c0e08a151f2..707c2f7ff85 100644
--- a/app/services/create_branch_service.rb
+++ b/app/services/create_branch_service.rb
@@ -29,11 +29,7 @@ class CreateBranchService < BaseService
end
if new_branch
- push_data = build_push_data(project, current_user, new_branch)
-
- project.execute_hooks(push_data.dup, :push_hooks)
- project.execute_services(push_data.dup, :push_hooks)
-
+ # GitPushService handles execution of services and hooks for branch pushes
success(new_branch)
else
error('Invalid reference name')
diff --git a/app/services/delete_branch_service.rb b/app/services/delete_branch_service.rb
index 004b3ce7286..fae069ee4a5 100644
--- a/app/services/delete_branch_service.rb
+++ b/app/services/delete_branch_service.rb
@@ -25,11 +25,7 @@ class DeleteBranchService < BaseService
end
if repository.rm_branch(current_user, branch_name)
- push_data = build_push_data(branch)
-
- project.execute_hooks(push_data.dup, :push_hooks)
- project.execute_services(push_data.dup, :push_hooks)
-
+ # GitPushService handles execution of services and hooks for branch pushes
success('Branch was removed')
else
error('Failed to remove branch')
diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb
index 294157b4f0e..f4dcb142850 100644
--- a/app/services/projects/destroy_service.rb
+++ b/app/services/projects/destroy_service.rb
@@ -16,11 +16,15 @@ module Projects
return false unless can?(current_user, :remove_project, project)
project.team.truncate
- project.repository.expire_cache unless project.empty_repo?
repo_path = project.path_with_namespace
wiki_path = repo_path + '.wiki'
+ # Flush the cache for both repositories. This has to be done _before_
+ # removing the physical repositories as some expiration code depends on
+ # Git data (e.g. a list of branch names).
+ flush_caches(project, wiki_path)
+
Project.transaction do
project.destroy!
@@ -70,5 +74,13 @@ module Projects
def removal_path(path)
"#{path}+#{project.id}#{DELETED_FLAG}"
end
+
+ def flush_caches(project, wiki_path)
+ project.repository.expire_all_caches! if project.repository.exists?
+
+ wiki_repo = Repository.new(wiki_path, project)
+
+ wiki_repo.expire_all_caches! if wiki_repo.exists?
+ end
end
end
diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb
index 1083bcec054..edced010811 100644
--- a/app/services/system_note_service.rb
+++ b/app/services/system_note_service.rb
@@ -274,12 +274,15 @@ class SystemNoteService
# Check if a cross reference to a noteable from a mentioner already exists
#
# This method is used to prevent multiple notes being created for a mention
- # when a issue is updated, for example.
+ # when a issue is updated, for example. The method also calls notes_for_mentioner
+ # to check if the mentioner is a commit, and return matches only on commit hash
+ # instead of project + commit, to avoid repeated mentions from forks.
#
# noteable - Noteable object being referenced
# mentioner - Mentionable object
#
# Returns Boolean
+
def self.cross_reference_exists?(noteable, mentioner)
# Initial scope should be system notes of this noteable type
notes = Note.system.where(noteable_type: noteable.class)
@@ -291,14 +294,20 @@ class SystemNoteService
notes = notes.where(noteable_id: noteable.id)
end
- gfm_reference = mentioner.gfm_reference(noteable.project)
- notes = notes.where(note: cross_reference_note_content(gfm_reference))
-
- notes.count > 0
+ notes_for_mentioner(mentioner, noteable, notes).count > 0
end
private
+ def self.notes_for_mentioner(mentioner, noteable, notes)
+ if mentioner.is_a?(Commit)
+ notes.where('note LIKE ?', "#{cross_reference_note_prefix}%#{mentioner.to_reference(nil)}")
+ else
+ gfm_reference = mentioner.gfm_reference(noteable.project)
+ notes.where(note: cross_reference_note_content(gfm_reference))
+ end
+ end
+
def self.create_note(args = {})
Note.create(args.merge(system: true))
end
diff --git a/app/validators/email_validator.rb b/app/validators/email_validator.rb
index b35af100803..aab07a7ece4 100644
--- a/app/validators/email_validator.rb
+++ b/app/validators/email_validator.rb
@@ -1,18 +1,5 @@
-# EmailValidator
-#
-# Based on https://github.com/balexand/email_validator
-#
-# Extended to use only strict mode with following allowed characters:
-# ' - apostrophe
-#
-# See http://www.remote.org/jochen/mail/info/chars.html
-#
class EmailValidator < ActiveModel::EachValidator
- PATTERN = /\A\s*([-a-z0-9+._']{1,64})@((?:[-a-z0-9]+\.)+[a-z]{2,})\s*\z/i.freeze
-
def validate_each(record, attribute, value)
- unless value =~ PATTERN
- record.errors.add(attribute, options[:message] || :invalid)
- end
+ record.errors.add(attribute, :invalid) unless value =~ Devise.email_regexp
end
end
diff --git a/app/views/notify/build_fail_email.html.haml b/app/views/notify/build_fail_email.html.haml
index f4e9749e5c7..81d65037312 100644
--- a/app/views/notify/build_fail_email.html.haml
+++ b/app/views/notify/build_fail_email.html.haml
@@ -1,9 +1,10 @@
- content_for :header do
%h1{style: "background: #c40834; color: #FFF; font: normal 20px Helvetica, Arial, sans-serif; margin: 0; padding: 5px 10px; line-height: 32px; font-size: 16px;"}
GitLab (build failed)
+
%h3
Project:
- = link_to ci_project_url(@project) do
+ = link_to namespace_project_url(@project.namespace, @project) do
= @project.name
%p
diff --git a/app/views/notify/build_success_email.html.haml b/app/views/notify/build_success_email.html.haml
index 8b004d34cca..5d247eb4cf2 100644
--- a/app/views/notify/build_success_email.html.haml
+++ b/app/views/notify/build_success_email.html.haml
@@ -4,7 +4,7 @@
%h3
Project:
- = link_to ci_project_url(@project) do
+ = link_to namespace_project_url(@project.namespace, @project) do
= @project.name
%p
diff --git a/app/views/projects/blob/_blob.html.haml b/app/views/projects/blob/_blob.html.haml
index 2c5b8dc4356..f3bfe0a18b0 100644
--- a/app/views/projects/blob/_blob.html.haml
+++ b/app/views/projects/blob/_blob.html.haml
@@ -36,7 +36,7 @@
= render "download", blob: blob
- elsif blob.text?
- if blob_svg?(blob)
- = render "image", blob: sanitize_svg(blob)
+ = render "image", blob: blob
- else
= render "text", blob: blob
- elsif blob.image?
diff --git a/app/views/projects/blob/_image.html.haml b/app/views/projects/blob/_image.html.haml
index 51fa91b08e4..113dba5d832 100644
--- a/app/views/projects/blob/_image.html.haml
+++ b/app/views/projects/blob/_image.html.haml
@@ -1,2 +1,9 @@
.file-content.image_file
- %img{ src: namespace_project_raw_path(@project.namespace, @project, @id)}
+ - if blob_svg?(blob)
+ - # We need to scrub SVG but we cannot do so in the RawController: it would
+ - # be wrong/strange if RawController modified the data.
+ - blob.load_all_data!(@repository)
+ - blob = sanitize_svg(blob)
+ %img{src: "data:#{blob.mime_type};base64,#{Base64.encode64(blob.data)}"}
+ - else
+ %img{src: namespace_project_raw_path(@project.namespace, @project, @id)}
diff --git a/app/views/projects/diffs/_image.html.haml b/app/views/projects/diffs/_image.html.haml
index 058b71b21f5..752e92e2e6b 100644
--- a/app/views/projects/diffs/_image.html.haml
+++ b/app/views/projects/diffs/_image.html.haml
@@ -1,9 +1,11 @@
- diff = diff_file.diff
+- file_raw_path = namespace_project_raw_path(@project.namespace, @project, tree_join(@commit.id, diff.new_path))
+- old_file_raw_path = namespace_project_raw_path(@project.namespace, @project, tree_join(@commit.parent_id, diff.old_path))
- if diff.renamed_file || diff.new_file || diff.deleted_file
.image
%span.wrap
.frame{class: image_diff_class(diff)}
- %img{src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"}
+ %img{src: diff.deleted_file ? old_file_raw_path : file_raw_path}
%p.image-info= "#{number_to_human_size file.size}"
- else
.image
@@ -11,7 +13,7 @@
%span.wrap
.frame.deleted
%a{href: namespace_project_blob_path(@project.namespace, @project, tree_join(@commit.parent_id, diff.old_path))}
- %img{src: "data:#{old_file.mime_type};base64,#{Base64.encode64(old_file.data)}"}
+ %img{src: old_file_raw_path}
%p.image-info.hide
%span.meta-filesize= "#{number_to_human_size old_file.size}"
|
@@ -23,7 +25,7 @@
%span.wrap
.frame.added
%a{href: namespace_project_blob_path(@project.namespace, @project, tree_join(@commit.id, diff.new_path))}
- %img{src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"}
+ %img{src: file_raw_path}
%p.image-info.hide
%span.meta-filesize= "#{number_to_human_size file.size}"
|
@@ -36,10 +38,10 @@
%div.swipe.view.hide
.swipe-frame
.frame.deleted
- %img{src: "data:#{old_file.mime_type};base64,#{Base64.encode64(old_file.data)}"}
+ %img{src: old_file_raw_path}
.swipe-wrap
.frame.added
- %img{src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"}
+ %img{src: file_raw_path}
%span.swipe-bar
%span.top-handle
%span.bottom-handle
@@ -47,9 +49,9 @@
%div.onion-skin.view.hide
.onion-skin-frame
.frame.deleted
- %img{src: "data:#{old_file.mime_type};base64,#{Base64.encode64(old_file.data)}"}
+ %img{src: old_file_raw_path}
.frame.added
- %img{src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"}
+ %img{src: file_raw_path}
.controls
.transparent
.drag-track
diff --git a/app/views/projects/forks/index.html.haml b/app/views/projects/forks/index.html.haml
index c834bf63b28..42fa6fdb782 100644
--- a/app/views/projects/forks/index.html.haml
+++ b/app/views/projects/forks/index.html.haml
@@ -29,14 +29,15 @@
= link_to page_filter_path(sort: sort_value_oldest_updated, without: excluded_filters) do
= sort_title_oldest_updated
- - if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2
- = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn btn-new' do
- = icon('code-fork fw')
- Fork
- - else
- = link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn btn-new' do
- = icon('code-fork fw')
- Fork
+ - if current_user && can?(current_user, :fork_project, @project)
+ - if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2
+ = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn btn-new' do
+ = icon('code-fork fw')
+ Fork
+ - else
+ = link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn btn-new' do
+ = icon('code-fork fw')
+ Fork
.projects-list-holder
diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml
index f9cf4910df3..5b0edcfa978 100644
--- a/app/views/projects/issues/_issue.html.haml
+++ b/app/views/projects/issues/_issue.html.haml
@@ -15,6 +15,25 @@
%li
= link_to_member(@project, issue.assignee, name: false, title: "Assigned to :name")
+ - upvotes, downvotes = issue.upvotes, issue.downvotes
+ - if upvotes > 0 || downvotes > 0
+ %li
+ = icon('thumbs-up')
+ = upvotes
+ - else
+ %li{ class: 'issue-no-votes' }
+ = icon('thumbs-up')
+ = upvotes
+
+ - if upvotes > 0 || downvotes > 0
+ %li
+ = icon('thumbs-down')
+ = downvotes
+ - else
+ %li{ class: 'issue-no-votes' }
+ = icon('thumbs-down')
+ = downvotes
+
- note_count = issue.notes.user.count
- if note_count > 0
%li
diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml
index 121c775560f..69a0e2a0c4d 100644
--- a/app/views/projects/issues/show.html.haml
+++ b/app/views/projects/issues/show.html.haml
@@ -6,16 +6,6 @@
.issue
.detail-page-header
- .status-box{ class: "status-box-closed #{issue_button_visibility(@issue, false)}"} Closed
- .status-box{ class: "status-box-open #{issue_button_visibility(@issue, true)}"} Open
- %span.identifier
- Issue ##{@issue.iid}
- %span.creator
- &middot;
- opened by #{link_to_member(@project, @issue.author, size: 24)}
- &middot;
- = time_ago_with_tooltip(@issue.created_at, placement: 'bottom', html_class: 'issue_created_ago')
-
.pull-right
- if can?(current_user, :create_issue, @project)
= link_to new_namespace_project_issue_path(@project.namespace, @project), class: 'btn btn-nr btn-grouped new-issue-link btn-success', title: 'New Issue', id: 'new_issue_link' do
@@ -29,6 +19,19 @@
= icon('pencil-square-o')
Edit
+ .pull-left
+ .status-box{ class: "status-box-closed #{issue_button_visibility(@issue, false)}"} Closed
+ .status-box{ class: "status-box-open #{issue_button_visibility(@issue, true)}"} Open
+
+ .issue-meta
+ %span.identifier
+ Issue ##{@issue.iid}
+ %span.creator
+ &middot;
+ by #{link_to_member(@project, @issue.author, size: 24)}
+ &middot;
+ = time_ago_with_tooltip(@issue.created_at, placement: 'bottom', html_class: 'issue_created_ago')
+
.issue-details.issuable-details
.detail-page-description.content-block
%h2.title
@@ -46,8 +49,8 @@
Edited
= time_ago_with_tooltip(@issue.updated_at, placement: 'bottom', html_class: 'issue_edited_ago')
- .merge-requests
- = render 'merge_requests'
+ .merge-requests
+ = render 'merge_requests'
.content-block
= render 'votes/votes_block', votable: @issue
@@ -57,4 +60,4 @@
.issuable-discussion
= render 'projects/issues/discussion'
-= render 'shared/issuable/sidebar', issuable: @issue \ No newline at end of file
+= render 'shared/issuable/sidebar', issuable: @issue
diff --git a/app/views/projects/issues/update.js.haml b/app/views/projects/issues/update.js.haml
index a54733883b4..986d8c220db 100644
--- a/app/views/projects/issues/update.js.haml
+++ b/app/views/projects/issues/update.js.haml
@@ -1,3 +1,3 @@
$('aside.right-sidebar')[0].outerHTML = "#{escape_javascript(render 'shared/issuable/sidebar', issuable: @issue)}";
$('aside.right-sidebar').effect('highlight');
-new Issue(); \ No newline at end of file
+new IssuableContext();
diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml
index e25bf917b43..b230b3a0110 100644
--- a/app/views/projects/merge_requests/_merge_request.html.haml
+++ b/app/views/projects/merge_requests/_merge_request.html.haml
@@ -24,6 +24,25 @@
%li
= link_to_member(merge_request.source_project, merge_request.assignee, name: false, title: "Assigned to :name")
+ - upvotes, downvotes = merge_request.upvotes, merge_request.downvotes
+ - if upvotes > 0 || downvotes > 0
+ %li
+ = icon('thumbs-up')
+ = upvotes
+ - else
+ %li{ class: 'merge-request-no-votes' }
+ = icon('thumbs-up')
+ = upvotes
+
+ - if upvotes > 0 || downvotes > 0
+ %li
+ = icon('thumbs-down')
+ = downvotes
+ - else
+ %li{ class: 'merge-request-no-votes' }
+ = icon('thumbs-down')
+ = downvotes
+
- note_count = merge_request.mr_and_commit_notes.user.count
- if note_count > 0
%li
diff --git a/app/views/projects/merge_requests/update.js.haml b/app/views/projects/merge_requests/update.js.haml
index ce5157d69a2..9cce5660e1c 100644
--- a/app/views/projects/merge_requests/update.js.haml
+++ b/app/views/projects/merge_requests/update.js.haml
@@ -1,3 +1,3 @@
-$('aside.right-sidebar')[0].outerHTML= "#{escape_javascript(render 'shared/issuable/sidebar', issuable: @merge_request)}";
-$('aside.right-sidebar').effect('highlight')
-merge_request = new MergeRequest();
+$('aside.right-sidebar')[0].outerHTML = "#{escape_javascript(render 'shared/issuable/sidebar', issuable: @merge_request)}";
+$('aside.right-sidebar').effect('highlight');
+new IssuableContext();
diff --git a/app/views/shared/_sort_dropdown.html.haml b/app/views/shared/_sort_dropdown.html.haml
index f09ab25276d..e3a6a5a68b6 100644
--- a/app/views/shared/_sort_dropdown.html.haml
+++ b/app/views/shared/_sort_dropdown.html.haml
@@ -20,3 +20,7 @@
= sort_title_milestone_soon
= link_to page_filter_path(sort: sort_value_milestone_later) do
= sort_title_milestone_later
+ = link_to page_filter_path(sort: sort_value_upvotes) do
+ = sort_title_upvotes
+ = link_to page_filter_path(sort: sort_value_downvotes) do
+ = sort_title_downvotes
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index ae96a45453f..a45775f36b5 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -4,21 +4,18 @@
%span.issuable-count.pull-left
= issuable.iid
of
- = issuable_count(:all, @project)
+ = issuables_count(issuable)
%span.pull-right
%a.gutter-toggle{href: '#'}
- - if sidebar_gutter_collapsed?
- = icon('angle-double-left')
- - else
- = icon('angle-double-right')
+ = sidebar_gutter_toggle_icon
.issuable-nav.pull-right.btn-group{role: 'group', "aria-label" => '...'}
- - if has_prev_issuable?(@project, issuable.id)
- = link_to 'Prev', issuable_link_prev(@project, issuable), class: 'btn btn-default prev-btn'
+ - if prev_issuable = prev_issuable_for(issuable)
+ = link_to 'Prev', [@project.namespace.becomes(Namespace), @project, prev_issuable], class: 'btn btn-default prev-btn'
- else
%a.btn.btn-default.disabled{href: '#'}
Prev
- - if has_next_issuable?(@project, issuable.id)
- = link_to 'Next', issuable_link_next(@project, issuable), class: 'btn btn-default next-btn'
+ - if next_issuable = next_issuable_for(issuable)
+ = link_to 'Next', [@project.namespace.becomes(Namespace), @project, next_issuable], class: 'btn btn-default next-btn'
- else
%a.btn.btn-default.disabled{href: '#'}
Next
@@ -50,7 +47,7 @@
.block.milestone
.sidebar-collapsed-icon
- = icon('balance-scale')
+ = icon('clock-o')
%span
- if issuable.milestone
= issuable.milestone.title
@@ -118,7 +115,7 @@
- project_ref = cross_project_reference(@project, issuable)
.block.project-reference
.sidebar-collapsed-icon
- = icon('clipboard')
+ = clipboard_button(clipboard_text: project_ref)
.title
.cross-project-reference
%span
diff --git a/app/workers/repository_fork_worker.rb b/app/workers/repository_fork_worker.rb
index 2f991c52339..2572b9d6d98 100644
--- a/app/workers/repository_fork_worker.rb
+++ b/app/workers/repository_fork_worker.rb
@@ -27,6 +27,7 @@ class RepositoryForkWorker
return
end
+ project.repository.expire_emptiness_caches
project.import_finish
end
end
diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb
index e295a9ddd14..0b6f746e118 100644
--- a/app/workers/repository_import_worker.rb
+++ b/app/workers/repository_import_worker.rb
@@ -18,6 +18,7 @@ class RepositoryImportWorker
return
end
+ project.repository.expire_emptiness_caches
project.import_finish
end
end