Merge branch 'master' into jrochkind/gitlab-ce-fix_2839_send_abuse_report_notify
[ci skip]
This commit is contained in:
commit
47194545c7
60 changed files with 579 additions and 182 deletions
|
@ -3,12 +3,17 @@ Please view this file on the master branch, on stable branches it's out of date.
|
||||||
v 8.2.0 (unreleased)
|
v 8.2.0 (unreleased)
|
||||||
- Show last project commit to default branch on project home page
|
- Show last project commit to default branch on project home page
|
||||||
- Highlight comment based on anchor in URL
|
- Highlight comment based on anchor in URL
|
||||||
|
- Adds ability to remove the forked relationship from project settings screen. (Han Loong Liauw)
|
||||||
|
- Improved performance of sorting milestone issues
|
||||||
|
- Allow users to select the Files view as default project view (Cristian Bica)
|
||||||
|
|
||||||
v 8.1.0 (unreleased)
|
v 8.1.0 (unreleased)
|
||||||
- Send an email to admin email when a user is reported for spam (Jonathan Rochkind)
|
- Send an email to admin email when a user is reported for spam (Jonathan Rochkind)
|
||||||
|
- Fix bug preventing mentioned issued from being closed when MR is merged using fast-forward merge.
|
||||||
- Fix nonatomic database update potentially causing project star counts to go negative (Stan Hu)
|
- Fix nonatomic database update potentially causing project star counts to go negative (Stan Hu)
|
||||||
- Fix error preventing displaying of commit data for a directory with a leading dot (Stan Hu)
|
- Fix error preventing displaying of commit data for a directory with a leading dot (Stan Hu)
|
||||||
- Speed up load times of issue detail pages by roughly 1.5x
|
- Speed up load times of issue detail pages by roughly 1.5x
|
||||||
|
- If a merge request is to close an issue, show this on the issue page (Zeger-Jan van de Weg)
|
||||||
- Add a system note and update relevant merge requests when a branch is deleted or re-added (Stan Hu)
|
- Add a system note and update relevant merge requests when a branch is deleted or re-added (Stan Hu)
|
||||||
- Make diff file view easier to use on mobile screens (Stan Hu)
|
- Make diff file view easier to use on mobile screens (Stan Hu)
|
||||||
- Improved performance of finding users by username or Email address
|
- Improved performance of finding users by username or Email address
|
||||||
|
|
|
@ -65,3 +65,48 @@
|
||||||
line-height: 42px;
|
line-height: 42px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cover-block {
|
||||||
|
text-align: center;
|
||||||
|
background: #f7f8fa;
|
||||||
|
margin: -$gl-padding;
|
||||||
|
margin-bottom: 0;
|
||||||
|
padding: 44px $gl-padding;
|
||||||
|
border-bottom: 1px solid $border-color;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.avatar-holder {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
|
||||||
|
.avatar, .identicon {
|
||||||
|
margin: 0 auto;
|
||||||
|
float: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.identicon {
|
||||||
|
@include border-radius(50%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cover-title {
|
||||||
|
color: $gl-header-color;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 23px;
|
||||||
|
font-weight: normal;
|
||||||
|
margin: 16px 0 5px 0;
|
||||||
|
color: #4c4e54;
|
||||||
|
font-size: 23px;
|
||||||
|
line-height: 1.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cover-desc {
|
||||||
|
padding: 0 $gl-padding;
|
||||||
|
color: $gl-text-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cover-controls {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -10,6 +10,10 @@
|
||||||
border-bottom: 1px solid #E7E9EE;
|
border-bottom: 1px solid #E7E9EE;
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
|
|
||||||
|
&.readme-holder {
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
table {
|
table {
|
||||||
@extend .table;
|
@extend .table;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
font-family: $monospace_font;
|
font-family: $monospace_font;
|
||||||
white-space: pre;
|
white-space: pre;
|
||||||
word-wrap: normal;
|
word-wrap: normal;
|
||||||
padding: 1px 2px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
kbd {
|
kbd {
|
||||||
|
|
|
@ -132,6 +132,11 @@ form.edit-issue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.issue-closed-by-widget {
|
||||||
|
padding: 16px 0;
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
.issue-form .select2-container {
|
.issue-form .select2-container {
|
||||||
width: 250px !important;
|
width: 250px !important;
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,3 +47,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.calendar-hint {
|
||||||
|
margin-top: -12px;
|
||||||
|
float: right;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
|
@ -544,5 +544,5 @@ pre.light-well {
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-show-readme .readme-holder {
|
.project-show-readme .readme-holder {
|
||||||
padding: 7px;
|
border-top: 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,14 +4,6 @@
|
||||||
margin-right: -$gl-padding;
|
margin-right: -$gl-padding;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tree_progress {
|
|
||||||
display: none;
|
|
||||||
margin: 20px;
|
|
||||||
&.loading {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tree-table {
|
.tree-table {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,9 @@ class Projects::IssuesController < Projects::ApplicationController
|
||||||
# Allow issues bulk update
|
# Allow issues bulk update
|
||||||
before_action :authorize_admin_issues!, only: [:bulk_update]
|
before_action :authorize_admin_issues!, only: [:bulk_update]
|
||||||
|
|
||||||
|
# Cross-reference merge requests
|
||||||
|
before_action :closed_by_merge_requests, only: [:show]
|
||||||
|
|
||||||
respond_to :html
|
respond_to :html
|
||||||
|
|
||||||
def index
|
def index
|
||||||
|
@ -112,6 +115,10 @@ class Projects::IssuesController < Projects::ApplicationController
|
||||||
render nothing: true
|
render nothing: true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def closed_by_merge_requests
|
||||||
|
@closed_by_merge_requests ||= @issue.closed_by_merge_requests(current_user)
|
||||||
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def issue
|
def issue
|
||||||
|
|
|
@ -259,7 +259,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
||||||
@commits = @merge_request.commits
|
@commits = @merge_request.commits
|
||||||
|
|
||||||
@merge_request_diff = @merge_request.merge_request_diff
|
@merge_request_diff = @merge_request.merge_request_diff
|
||||||
|
|
||||||
if @merge_request.locked_long_ago?
|
if @merge_request.locked_long_ago?
|
||||||
@merge_request.unlock_mr
|
@merge_request.unlock_mr
|
||||||
@merge_request.close
|
@merge_request.close
|
||||||
|
|
|
@ -75,11 +75,7 @@ class Projects::MilestonesController < Projects::ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def sort_issues
|
def sort_issues
|
||||||
@issues = @milestone.issues.where(id: params['sortable_issue'])
|
@milestone.sort_issues(params['sortable_issue'].map(&:to_i))
|
||||||
@issues.each do |issue|
|
|
||||||
issue.position = params['sortable_issue'].index(issue.id.to_s) + 1
|
|
||||||
issue.save
|
|
||||||
end
|
|
||||||
|
|
||||||
render json: { saved: true }
|
render json: { saved: true }
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
class ProjectsController < ApplicationController
|
class ProjectsController < ApplicationController
|
||||||
|
include ExtractsPath
|
||||||
|
|
||||||
prepend_before_filter :render_go_import, only: [:show]
|
prepend_before_filter :render_go_import, only: [:show]
|
||||||
skip_before_action :authenticate_user!, only: [:show, :activity]
|
skip_before_action :authenticate_user!, only: [:show, :activity]
|
||||||
before_action :project, except: [:new, :create]
|
before_action :project, except: [:new, :create]
|
||||||
before_action :repository, except: [:new, :create]
|
before_action :repository, except: [:new, :create]
|
||||||
|
before_action :assign_ref_vars, :tree, only: [:show], if: :repo_exists?
|
||||||
|
|
||||||
# Authorize
|
# Authorize
|
||||||
before_action :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer, :archive, :unarchive]
|
before_action :authorize_admin_project!, only: [:edit, :update]
|
||||||
before_action :event_filter, only: [:show, :activity]
|
before_action :event_filter, only: [:show, :activity]
|
||||||
|
|
||||||
layout :determine_layout
|
layout :determine_layout
|
||||||
|
@ -56,6 +59,8 @@ class ProjectsController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def transfer
|
def transfer
|
||||||
|
return access_denied! unless can?(current_user, :change_namespace, @project)
|
||||||
|
|
||||||
namespace = Namespace.find_by(id: params[:new_namespace_id])
|
namespace = Namespace.find_by(id: params[:new_namespace_id])
|
||||||
::Projects::TransferService.new(project, current_user).execute(namespace)
|
::Projects::TransferService.new(project, current_user).execute(namespace)
|
||||||
|
|
||||||
|
@ -64,6 +69,15 @@ class ProjectsController < ApplicationController
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def remove_fork
|
||||||
|
return access_denied! unless can?(current_user, :remove_fork_project, @project)
|
||||||
|
|
||||||
|
if @project.forked?
|
||||||
|
@project.forked_project_link.destroy
|
||||||
|
flash[:notice] = 'The fork relationship has been removed.'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def activity
|
def activity
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html
|
format.html
|
||||||
|
@ -139,6 +153,7 @@ class ProjectsController < ApplicationController
|
||||||
|
|
||||||
def archive
|
def archive
|
||||||
return access_denied! unless can?(current_user, :archive_project, @project)
|
return access_denied! unless can?(current_user, :archive_project, @project)
|
||||||
|
|
||||||
@project.archive!
|
@project.archive!
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
|
@ -148,6 +163,7 @@ class ProjectsController < ApplicationController
|
||||||
|
|
||||||
def unarchive
|
def unarchive
|
||||||
return access_denied! unless can?(current_user, :archive_project, @project)
|
return access_denied! unless can?(current_user, :archive_project, @project)
|
||||||
|
|
||||||
@project.unarchive!
|
@project.unarchive!
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
|
@ -225,4 +241,14 @@ class ProjectsController < ApplicationController
|
||||||
|
|
||||||
render "go_import", layout: false
|
render "go_import", layout: false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def repo_exists?
|
||||||
|
project.repository_exists? && !project.empty_repo?
|
||||||
|
end
|
||||||
|
|
||||||
|
# Override get_id from ExtractsPath, which returns the branch and file path
|
||||||
|
# for the blob/tree, which in this case is just the root of the default branch.
|
||||||
|
def get_id
|
||||||
|
project.repository.root_ref
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -83,6 +83,10 @@ module IssuesHelper
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def merge_requests_sentence(merge_requests)
|
||||||
|
merge_requests.map(&:to_reference).to_sentence(last_word_connector: ', or ')
|
||||||
|
end
|
||||||
|
|
||||||
# Required for Gitlab::Markdown::IssueReferenceFilter
|
# Required for Gitlab::Markdown::IssueReferenceFilter
|
||||||
module_function :url_for_issue
|
module_function :url_for_issue
|
||||||
end
|
end
|
||||||
|
|
|
@ -34,7 +34,8 @@ module PreferencesHelper
|
||||||
def project_view_choices
|
def project_view_choices
|
||||||
[
|
[
|
||||||
['Readme (default)', :readme],
|
['Readme (default)', :readme],
|
||||||
['Activity view', :activity]
|
['Activity view', :activity],
|
||||||
|
['Files view', :files]
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -46,8 +47,7 @@ module PreferencesHelper
|
||||||
Gitlab::ColorSchemes.for_user(current_user).css_class
|
Gitlab::ColorSchemes.for_user(current_user).css_class
|
||||||
end
|
end
|
||||||
|
|
||||||
def prefer_readme?
|
def default_project_view
|
||||||
!current_user ||
|
current_user ? current_user.project_view : 'readme'
|
||||||
current_user.project_view == 'readme'
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -70,6 +70,10 @@ module ProjectsHelper
|
||||||
"You are going to transfer #{project.name_with_namespace} to another owner. Are you ABSOLUTELY sure?"
|
"You are going to transfer #{project.name_with_namespace} to another owner. Are you ABSOLUTELY sure?"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def remove_fork_project_message(project)
|
||||||
|
"You are going to remove the fork relationship to source project #{@project.forked_from_project.name_with_namespace}. Are you ABSOLUTELY sure?"
|
||||||
|
end
|
||||||
|
|
||||||
def project_nav_tabs
|
def project_nav_tabs
|
||||||
@nav_tabs ||= get_project_nav_tabs(@project, current_user)
|
@nav_tabs ||= get_project_nav_tabs(@project, current_user)
|
||||||
end
|
end
|
||||||
|
|
|
@ -189,7 +189,8 @@ class Ability
|
||||||
:change_visibility_level,
|
:change_visibility_level,
|
||||||
:rename_project,
|
:rename_project,
|
||||||
:remove_project,
|
:remove_project,
|
||||||
:archive_project
|
:archive_project,
|
||||||
|
:remove_fork_project
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -85,6 +85,10 @@ module Issuable
|
||||||
assignee_id_changed?
|
assignee_id_changed?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def open?
|
||||||
|
opened? || reopened?
|
||||||
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# Votes
|
# Votes
|
||||||
#
|
#
|
||||||
|
|
|
@ -95,4 +95,14 @@ class Issue < ActiveRecord::Base
|
||||||
def source_project
|
def source_project
|
||||||
project
|
project
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# From all notes on this issue, we'll select the system notes about linked
|
||||||
|
# merge requests. Of those, the MRs closing `self` are returned.
|
||||||
|
def closed_by_merge_requests(current_user = nil)
|
||||||
|
return [] unless open?
|
||||||
|
|
||||||
|
notes.system.flat_map do |note|
|
||||||
|
note.all_references(current_user).merge_requests
|
||||||
|
end.uniq.select { |mr| mr.open? && mr.closes_issue?(self) }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -222,10 +222,6 @@ class MergeRequest < ActiveRecord::Base
|
||||||
self.target_project.events.where(target_id: self.id, target_type: "MergeRequest", action: Event::CLOSED).last
|
self.target_project.events.where(target_id: self.id, target_type: "MergeRequest", action: Event::CLOSED).last
|
||||||
end
|
end
|
||||||
|
|
||||||
def open?
|
|
||||||
opened? || reopened?
|
|
||||||
end
|
|
||||||
|
|
||||||
def work_in_progress?
|
def work_in_progress?
|
||||||
!!(title =~ /\A\[?WIP\]?:? /i)
|
!!(title =~ /\A\[?WIP\]?:? /i)
|
||||||
end
|
end
|
||||||
|
@ -294,6 +290,10 @@ class MergeRequest < ActiveRecord::Base
|
||||||
target_project
|
target_project
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def closes_issue?(issue)
|
||||||
|
closes_issues.include?(issue)
|
||||||
|
end
|
||||||
|
|
||||||
# Return the set of issues that will be closed if this merge request is accepted.
|
# Return the set of issues that will be closed if this merge request is accepted.
|
||||||
def closes_issues(current_user = self.author)
|
def closes_issues(current_user = self.author)
|
||||||
if target_branch == project.default_branch
|
if target_branch == project.default_branch
|
||||||
|
|
|
@ -105,4 +105,36 @@ class Milestone < ActiveRecord::Base
|
||||||
def author_id
|
def author_id
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Sorts the issues for the given IDs.
|
||||||
|
#
|
||||||
|
# This method runs a single SQL query using a CASE statement to update the
|
||||||
|
# position of all issues in the current milestone (scoped to the list of IDs).
|
||||||
|
#
|
||||||
|
# Given the ids [10, 20, 30] this method produces a SQL query something like
|
||||||
|
# the following:
|
||||||
|
#
|
||||||
|
# UPDATE issues
|
||||||
|
# SET position = CASE
|
||||||
|
# WHEN id = 10 THEN 1
|
||||||
|
# WHEN id = 20 THEN 2
|
||||||
|
# WHEN id = 30 THEN 3
|
||||||
|
# ELSE position
|
||||||
|
# END
|
||||||
|
# WHERE id IN (10, 20, 30);
|
||||||
|
#
|
||||||
|
# This method expects that the IDs given in `ids` are already Fixnums.
|
||||||
|
def sort_issues(ids)
|
||||||
|
pairs = []
|
||||||
|
|
||||||
|
ids.each_with_index do |id, index|
|
||||||
|
pairs << id
|
||||||
|
pairs << index + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
conditions = 'WHEN id = ? THEN ? ' * ids.length
|
||||||
|
|
||||||
|
issues.where(id: ids).
|
||||||
|
update_all(["position = CASE #{conditions} ELSE position END", *pairs])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -183,7 +183,7 @@ class User < ActiveRecord::Base
|
||||||
|
|
||||||
# User's Project preference
|
# User's Project preference
|
||||||
# Note: When adding an option, it MUST go on the end of the array.
|
# Note: When adding an option, it MUST go on the end of the array.
|
||||||
enum project_view: [:readme, :activity]
|
enum project_view: [:readme, :activity, :files]
|
||||||
|
|
||||||
alias_attribute :private_token, :authentication_token
|
alias_attribute :private_token, :authentication_token
|
||||||
|
|
||||||
|
|
|
@ -79,7 +79,7 @@ class GitPushService
|
||||||
|
|
||||||
authors = Hash.new do |hash, commit|
|
authors = Hash.new do |hash, commit|
|
||||||
email = commit.author_email
|
email = commit.author_email
|
||||||
return hash[email] if hash.has_key?(email)
|
next hash[email] if hash.has_key?(email)
|
||||||
|
|
||||||
hash[email] = commit_user(commit)
|
hash[email] = commit_user(commit)
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,6 +6,7 @@ module MergeRequests
|
||||||
#
|
#
|
||||||
class PostMergeService < MergeRequests::BaseService
|
class PostMergeService < MergeRequests::BaseService
|
||||||
def execute(merge_request)
|
def execute(merge_request)
|
||||||
|
close_issues(merge_request)
|
||||||
merge_request.mark_as_merged
|
merge_request.mark_as_merged
|
||||||
create_merge_event(merge_request, current_user)
|
create_merge_event(merge_request, current_user)
|
||||||
create_note(merge_request)
|
create_note(merge_request)
|
||||||
|
@ -15,6 +16,15 @@ module MergeRequests
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def close_issues(merge_request)
|
||||||
|
return unless merge_request.target_branch == project.default_branch
|
||||||
|
|
||||||
|
closed_issues = merge_request.closes_issues(current_user)
|
||||||
|
closed_issues.each do |issue|
|
||||||
|
Issues::CloseService.new(project, current_user, {}).execute(issue, merge_request)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def create_merge_event(merge_request, current_user)
|
def create_merge_event(merge_request, current_user)
|
||||||
EventCreateService.new.merge_mr(merge_request, current_user)
|
EventCreateService.new.merge_mr(merge_request, current_user)
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
%strong
|
%strong
|
||||||
= link_to user_path(@user) do
|
= link_to user_path(@user) do
|
||||||
= @user.username
|
= @user.username
|
||||||
= render 'users/profile', user: @user
|
= render 'admin/users/profile', user: @user
|
||||||
|
|
||||||
.panel.panel-default
|
.panel.panel-default
|
||||||
.panel-heading
|
.panel-heading
|
||||||
|
|
|
@ -41,4 +41,8 @@
|
||||||
#{link_to "view it on GitLab", @target_url}.
|
#{link_to "view it on GitLab", @target_url}.
|
||||||
- else
|
- else
|
||||||
#{link_to "View it on GitLab", @target_url}
|
#{link_to "View it on GitLab", @target_url}
|
||||||
|
%br
|
||||||
|
You're receiving this email because of your account on #{link_to Gitlab.config.gitlab.host, root_url}.
|
||||||
|
If you'd like to receive fewer emails, you can adjust your notification settings.
|
||||||
|
|
||||||
= email_action @target_url
|
= email_action @target_url
|
||||||
|
|
|
@ -38,7 +38,7 @@
|
||||||
.col-sm-10
|
.col-sm-10
|
||||||
= f.select :layout, layout_choices, {}, class: 'form-control'
|
= f.select :layout, layout_choices, {}, class: 'form-control'
|
||||||
.help-block
|
.help-block
|
||||||
Choose between fixed (max. 1200px) and fluid (100%) application layout
|
Choose between fixed (max. 1200px) and fluid (100%) application layout.
|
||||||
.form-group
|
.form-group
|
||||||
= f.label :dashboard, class: 'control-label' do
|
= f.label :dashboard, class: 'control-label' do
|
||||||
Default Dashboard
|
Default Dashboard
|
||||||
|
@ -52,6 +52,6 @@
|
||||||
.col-sm-10
|
.col-sm-10
|
||||||
= f.select :project_view, project_view_choices, {}, class: 'form-control'
|
= f.select :project_view, project_view_choices, {}, class: 'form-control'
|
||||||
.help-block
|
.help-block
|
||||||
Choose what content you want to see when visit project page
|
Choose what content you want to see on a project's home page.
|
||||||
.panel-footer
|
.panel-footer
|
||||||
= f.submit 'Save', class: 'btn btn-save'
|
= f.submit 'Save', class: 'btn btn-save'
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
= render 'projects/last_push'
|
|
||||||
.gray-content-block.activity-filter-block
|
.gray-content-block.activity-filter-block
|
||||||
- if current_user
|
- if current_user
|
||||||
.pull-right
|
.pull-right
|
||||||
|
|
6
app/views/projects/_files.html.haml
Normal file
6
app/views/projects/_files.html.haml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#tree-holder.tree-holder.clearfix
|
||||||
|
.gray-content-block.second-block
|
||||||
|
= render 'projects/tree/tree_header', tree: @tree
|
||||||
|
|
||||||
|
= render 'projects/tree/tree_content', tree: @tree
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
- if readme = @repository.readme
|
- if readme = @repository.readme
|
||||||
%article.readme-holder#README
|
%article.file-holder.readme-holder
|
||||||
.clearfix
|
.file-title
|
||||||
.pull-right
|
= blob_icon readme.mode, readme.name
|
||||||
|
= link_to namespace_project_blob_path(@project.namespace, @project, tree_join(@repository.root_ref, readme.name)) do
|
||||||
- if can?(current_user, :push_code, @project)
|
%strong
|
||||||
= link_to namespace_project_edit_blob_path(@project.namespace, @project, tree_join(@repository.root_ref, readme.name)), class: 'light' do
|
= readme.name
|
||||||
%i.fa-align.fa.fa-pencil
|
.file-content.wiki
|
||||||
.wiki
|
|
||||||
= cache(readme_cache_key) do
|
= cache(readme_cache_key) do
|
||||||
= render_readme(readme)
|
= render_readme(readme)
|
||||||
- else
|
- else
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
- page_title "Activity"
|
- page_title "Activity"
|
||||||
- header_title project_title(@project, "Activity", activity_project_path(@project))
|
- header_title project_title(@project, "Activity", activity_project_path(@project))
|
||||||
|
|
||||||
|
= render 'projects/last_push'
|
||||||
|
|
||||||
= render 'projects/activity'
|
= render 'projects/activity'
|
||||||
|
|
|
@ -1,19 +1,22 @@
|
||||||
%ul.breadcrumb.repo-breadcrumb
|
.gray-content-block.top-block
|
||||||
%li
|
.tree-ref-holder
|
||||||
%i.fa.fa-angle-right
|
= render 'shared/ref_switcher', destination: 'blob', path: @path
|
||||||
= link_to namespace_project_tree_path(@project.namespace, @project, @ref) do
|
|
||||||
= @project.path
|
%ul.breadcrumb.repo-breadcrumb
|
||||||
- tree_breadcrumbs(@tree, 6) do |title, path|
|
|
||||||
%li
|
%li
|
||||||
- if path
|
= link_to namespace_project_tree_path(@project.namespace, @project, @ref) do
|
||||||
- if path.end_with?(@path)
|
= @project.path
|
||||||
= link_to namespace_project_blob_path(@project.namespace, @project, path) do
|
- tree_breadcrumbs(@tree, 6) do |title, path|
|
||||||
%strong
|
%li
|
||||||
= truncate(title, length: 40)
|
- if path
|
||||||
|
- if path.end_with?(@path)
|
||||||
|
= link_to namespace_project_blob_path(@project.namespace, @project, path) do
|
||||||
|
%strong
|
||||||
|
= truncate(title, length: 40)
|
||||||
|
- else
|
||||||
|
= link_to truncate(title, length: 40), namespace_project_tree_path(@project.namespace, @project, path)
|
||||||
- else
|
- else
|
||||||
= link_to truncate(title, length: 40), namespace_project_tree_path(@project.namespace, @project, path)
|
= link_to title, '#'
|
||||||
- else
|
|
||||||
= link_to title, '#'
|
|
||||||
|
|
||||||
%ul.blob-commit-info.hidden-xs
|
%ul.blob-commit-info.hidden-xs
|
||||||
- blob_commit = @repository.last_commit_for_path(@commit.id, blob.path)
|
- blob_commit = @repository.last_commit_for_path(@commit.id, blob.path)
|
||||||
|
|
|
@ -3,9 +3,6 @@
|
||||||
|
|
||||||
= render 'projects/last_push'
|
= render 'projects/last_push'
|
||||||
|
|
||||||
%div.tree-ref-holder
|
|
||||||
= render 'shared/ref_switcher', destination: 'blob', path: @path
|
|
||||||
|
|
||||||
%div#tree-holder.tree-holder
|
%div#tree-holder.tree-holder
|
||||||
= render 'blob', blob: @blob
|
= render 'blob', blob: @blob
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
%tr
|
%tr
|
||||||
%th
|
%th
|
||||||
%th Service
|
%th Service
|
||||||
%th Desription
|
%th Description
|
||||||
%th Last edit
|
%th Last edit
|
||||||
- @services.sort_by(&:title).each do |service|
|
- @services.sort_by(&:title).each do |service|
|
||||||
%tr
|
%tr
|
||||||
|
|
|
@ -189,6 +189,21 @@
|
||||||
- else
|
- else
|
||||||
.nothing-here-block Only the project owner can transfer a project
|
.nothing-here-block Only the project owner can transfer a project
|
||||||
|
|
||||||
|
- if @project.forked?
|
||||||
|
- if can?(current_user, :remove_fork_project, @project)
|
||||||
|
= form_for([@project.namespace.becomes(Namespace), @project], url: remove_fork_namespace_project_path(@project.namespace, @project), method: :delete, remote: true, html: { class: 'transfer-project form-horizontal' }) do |f|
|
||||||
|
.panel.panel-default.panel.panel-danger
|
||||||
|
.panel-heading Remove fork relationship
|
||||||
|
.panel-body
|
||||||
|
%p
|
||||||
|
This will remove the fork relationship to source project
|
||||||
|
#{link_to @project.forked_from_project.name_with_namespace, project_path(@project.forked_from_project)}.
|
||||||
|
%br
|
||||||
|
%strong Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source.
|
||||||
|
= button_to 'Remove fork relationship', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_fork_project_message(@project) }
|
||||||
|
- else
|
||||||
|
.nothing-here-block Only the project owner can remove the fork relationship.
|
||||||
|
|
||||||
- if can?(current_user, :remove_project, @project)
|
- if can?(current_user, :remove_project, @project)
|
||||||
.panel.panel-default.panel.panel-danger
|
.panel.panel-default.panel.panel-danger
|
||||||
.panel-heading Remove project
|
.panel-heading Remove project
|
||||||
|
@ -201,7 +216,8 @@
|
||||||
|
|
||||||
= button_to 'Remove project', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_project_message(@project) }
|
= button_to 'Remove project', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_project_message(@project) }
|
||||||
- else
|
- else
|
||||||
.nothing-here-block Only project owner can remove a project
|
.nothing-here-block Only the project owner can remove a project.
|
||||||
|
|
||||||
|
|
||||||
.save-project-loader.hide
|
.save-project-loader.hide
|
||||||
.center
|
.center
|
||||||
|
|
3
app/views/projects/issues/_closed_by_box.html.haml
Normal file
3
app/views/projects/issues/_closed_by_box.html.haml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
.issue-closed-by-widget
|
||||||
|
= icon('check')
|
||||||
|
This issue will be closed automatically when merge request #{gfm(merge_requests_sentence(@closed_by_merge_requests.sort))} is accepted.
|
|
@ -46,6 +46,7 @@
|
||||||
= markdown(@issue.description)
|
= markdown(@issue.description)
|
||||||
%textarea.hidden.js-task-list-field
|
%textarea.hidden.js-task-list-field
|
||||||
= @issue.description
|
= @issue.description
|
||||||
|
- if @closed_by_merge_requests.present?
|
||||||
|
= render 'projects/issues/closed_by_box'
|
||||||
.issue-discussion
|
.issue-discussion
|
||||||
= render 'projects/issues/discussion'
|
= render 'projects/issues/discussion'
|
||||||
|
|
2
app/views/projects/remove_fork.js.haml
Normal file
2
app/views/projects/remove_fork.js.haml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
:plain
|
||||||
|
location.href = "#{edit_namespace_project_path(@project.namespace, @project)}";
|
|
@ -7,8 +7,7 @@
|
||||||
= render 'shared/no_ssh'
|
= render 'shared/no_ssh'
|
||||||
= render 'shared/no_password'
|
= render 'shared/no_password'
|
||||||
|
|
||||||
- if prefer_readme?
|
= render 'projects/last_push'
|
||||||
= render 'projects/last_push'
|
|
||||||
|
|
||||||
= render "home_panel"
|
= render "home_panel"
|
||||||
|
|
||||||
|
@ -28,7 +27,7 @@
|
||||||
= link_to project_files_path(@project) do
|
= link_to project_files_path(@project) do
|
||||||
= repository_size
|
= repository_size
|
||||||
|
|
||||||
- if !prefer_readme? && @repository.readme
|
- if default_project_view != 'readme' && @repository.readme
|
||||||
%li
|
%li
|
||||||
= link_to 'Readme', readme_path(@project)
|
= link_to 'Readme', readme_path(@project)
|
||||||
|
|
||||||
|
@ -68,14 +67,8 @@
|
||||||
.content-block.second-block.white
|
.content-block.second-block.white
|
||||||
= render 'projects/last_commit', commit: @repository.commit, project: @project
|
= render 'projects/last_commit', commit: @repository.commit, project: @project
|
||||||
|
|
||||||
%section
|
%div{class: "project-show-#{default_project_view}"}
|
||||||
- if prefer_readme?
|
= render default_project_view
|
||||||
.project-show-readme
|
|
||||||
= render 'projects/readme'
|
|
||||||
- else
|
|
||||||
.project-show-activity
|
|
||||||
= render 'projects/activity'
|
|
||||||
|
|
||||||
|
|
||||||
- if current_user
|
- if current_user
|
||||||
- access = user_max_access_in_project(current_user, @project)
|
- access = user_max_access_in_project(current_user, @project)
|
||||||
|
|
|
@ -4,5 +4,5 @@
|
||||||
%span.str-truncated
|
%span.str-truncated
|
||||||
= link_to blob_item.name, namespace_project_blob_path(@project.namespace, @project, tree_join(@id || @commit.id, blob_item.name))
|
= link_to blob_item.name, namespace_project_blob_path(@project.namespace, @project, tree_join(@id || @commit.id, blob_item.name))
|
||||||
%td.tree_time_ago.cgray
|
%td.tree_time_ago.cgray
|
||||||
= render 'spinner'
|
= render 'projects/tree/spinner'
|
||||||
%td.hidden-xs.tree_commit
|
%td.hidden-xs.tree_commit
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
%article.file-holder.readme-holder#README
|
%article.file-holder.readme-holder
|
||||||
.file-title
|
.file-title
|
||||||
= link_to '#README' do
|
= blob_icon readme.mode, readme.name
|
||||||
|
= link_to namespace_project_blob_path(@project.namespace, @project, tree_join(@repository.root_ref, readme.name)) do
|
||||||
%strong
|
%strong
|
||||||
%i.fa.fa-file
|
|
||||||
= readme.name
|
= readme.name
|
||||||
.file-content.wiki
|
.file-content.wiki
|
||||||
= render_readme(readme)
|
= render_readme(readme)
|
||||||
|
|
|
@ -1,35 +1,4 @@
|
||||||
.gray-content-block
|
%div.tree-content-holder
|
||||||
%ul.breadcrumb.repo-breadcrumb
|
|
||||||
%li
|
|
||||||
= link_to namespace_project_tree_path(@project.namespace, @project, @ref) do
|
|
||||||
= @project.path
|
|
||||||
- tree_breadcrumbs(tree, 6) do |title, path|
|
|
||||||
%li
|
|
||||||
- if path
|
|
||||||
= link_to truncate(title, length: 40), namespace_project_tree_path(@project.namespace, @project, path)
|
|
||||||
- else
|
|
||||||
= link_to title, '#'
|
|
||||||
- if allowed_tree_edit?
|
|
||||||
%li
|
|
||||||
%span.dropdown
|
|
||||||
%a.dropdown-toggle.btn.add-to-tree{href: '#', "data-toggle" => "dropdown"}
|
|
||||||
= icon('plus')
|
|
||||||
%ul.dropdown-menu
|
|
||||||
%li
|
|
||||||
= link_to namespace_project_new_blob_path(@project.namespace, @project, @id), title: 'Create file', id: 'new-file-link' do
|
|
||||||
= icon('pencil fw')
|
|
||||||
Create file
|
|
||||||
%li
|
|
||||||
= link_to '#modal-upload-blob', { 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal'} do
|
|
||||||
= icon('file fw')
|
|
||||||
Upload file
|
|
||||||
%li.divider
|
|
||||||
%li
|
|
||||||
= link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal'} do
|
|
||||||
= icon('folder fw')
|
|
||||||
New directory
|
|
||||||
|
|
||||||
%div#tree-content-holder.tree-content-holder
|
|
||||||
.tree-table-holder
|
.tree-table-holder
|
||||||
%table.table#tree-slider{class: "table_#{@hex_path} tree-table table-striped" }
|
%table.table#tree-slider{class: "table_#{@hex_path} tree-table table-striped" }
|
||||||
%thead
|
%thead
|
||||||
|
@ -60,8 +29,6 @@
|
||||||
- if tree.readme
|
- if tree.readme
|
||||||
= render "projects/tree/readme", readme: tree.readme
|
= render "projects/tree/readme", readme: tree.readme
|
||||||
|
|
||||||
%div.tree_progress
|
|
||||||
|
|
||||||
- if allowed_tree_edit?
|
- if allowed_tree_edit?
|
||||||
= render 'projects/blob/upload', title: 'Upload', placeholder: 'Upload new file', button_title: 'Upload file', form_path: namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post
|
= render 'projects/blob/upload', title: 'Upload', placeholder: 'Upload new file', button_title: 'Upload file', form_path: namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post
|
||||||
= render 'projects/blob/new_dir'
|
= render 'projects/blob/new_dir'
|
32
app/views/projects/tree/_tree_header.html.haml
Normal file
32
app/views/projects/tree/_tree_header.html.haml
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
.tree-ref-holder
|
||||||
|
= render 'shared/ref_switcher', destination: 'tree', path: @path
|
||||||
|
|
||||||
|
%ul.breadcrumb.repo-breadcrumb
|
||||||
|
%li
|
||||||
|
= link_to namespace_project_tree_path(@project.namespace, @project, @ref) do
|
||||||
|
= @project.path
|
||||||
|
- tree_breadcrumbs(tree, 6) do |title, path|
|
||||||
|
%li
|
||||||
|
- if path
|
||||||
|
= link_to truncate(title, length: 40), namespace_project_tree_path(@project.namespace, @project, path)
|
||||||
|
- else
|
||||||
|
= link_to title, '#'
|
||||||
|
- if allowed_tree_edit?
|
||||||
|
%li
|
||||||
|
%span.dropdown
|
||||||
|
%a.dropdown-toggle.btn.add-to-tree{href: '#', "data-toggle" => "dropdown"}
|
||||||
|
= icon('plus')
|
||||||
|
%ul.dropdown-menu
|
||||||
|
%li
|
||||||
|
= link_to namespace_project_new_blob_path(@project.namespace, @project, @id), title: 'Create file', id: 'new-file-link' do
|
||||||
|
= icon('pencil fw')
|
||||||
|
Create file
|
||||||
|
%li
|
||||||
|
= link_to '#modal-upload-blob', { 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal'} do
|
||||||
|
= icon('file fw')
|
||||||
|
Upload file
|
||||||
|
%li.divider
|
||||||
|
%li
|
||||||
|
= link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal'} do
|
||||||
|
= icon('folder fw')
|
||||||
|
New directory
|
|
@ -5,5 +5,5 @@
|
||||||
- path = flatten_tree(tree_item)
|
- path = flatten_tree(tree_item)
|
||||||
= link_to path, namespace_project_tree_path(@project.namespace, @project, tree_join(@id || @commit.id, path))
|
= link_to path, namespace_project_tree_path(@project.namespace, @project, tree_join(@id || @commit.id, path))
|
||||||
%td.tree_time_ago.cgray
|
%td.tree_time_ago.cgray
|
||||||
= render 'spinner'
|
= render 'projects/tree/spinner'
|
||||||
%td.hidden-xs.tree_commit
|
%td.hidden-xs.tree_commit
|
||||||
|
|
|
@ -6,12 +6,12 @@
|
||||||
|
|
||||||
= render 'projects/last_push'
|
= render 'projects/last_push'
|
||||||
|
|
||||||
.tree-ref-holder
|
|
||||||
= render 'shared/ref_switcher', destination: 'tree', path: @path
|
|
||||||
|
|
||||||
- if can? current_user, :download_code, @project
|
- if can? current_user, :download_code, @project
|
||||||
.tree-download-holder
|
.tree-download-holder
|
||||||
= render 'projects/repositories/download_archive', ref: @ref, btn_class: 'btn-group pull-right hidden-xs hidden-sm', split_button: true
|
= render 'projects/repositories/download_archive', ref: @ref, btn_class: 'btn-group pull-right hidden-xs hidden-sm', split_button: true
|
||||||
|
|
||||||
#tree-holder.tree-holder.clearfix
|
#tree-holder.tree-holder.clearfix
|
||||||
= render "tree", tree: @tree
|
.gray-content-block.top-block
|
||||||
|
= render 'projects/tree/tree_header', tree: @tree
|
||||||
|
|
||||||
|
= render 'projects/tree/tree_content', tree: @tree
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
%h4
|
|
||||||
Contributions calendar
|
|
||||||
.pull-right
|
|
||||||
%small Issues, merge requests and push events
|
|
||||||
#cal-heatmap.calendar
|
#cal-heatmap.calendar
|
||||||
:javascript
|
:javascript
|
||||||
new Calendar(
|
new Calendar(
|
||||||
|
@ -10,3 +6,5 @@
|
||||||
#{@starting_month},
|
#{@starting_month},
|
||||||
'#{user_calendar_activities_path}'
|
'#{user_calendar_activities_path}'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
.calendar-hint Summary of issues, merge requests and push events
|
||||||
|
|
|
@ -6,47 +6,72 @@
|
||||||
|
|
||||||
= render 'shared/show_aside'
|
= render 'shared/show_aside'
|
||||||
|
|
||||||
.row
|
.cover-block
|
||||||
|
.avatar-holder
|
||||||
|
= link_to avatar_icon(@user, 400), target: '_blank' do
|
||||||
|
= image_tag avatar_icon(@user, 90), class: "avatar s90", alt: ''
|
||||||
|
.cover-title
|
||||||
|
= @user.name
|
||||||
|
|
||||||
|
.cover-desc
|
||||||
|
%span
|
||||||
|
@#{@user.username}.
|
||||||
|
- if @user.bio.present?
|
||||||
|
%span
|
||||||
|
#{@user.bio}.
|
||||||
|
%span
|
||||||
|
Member since #{@user.created_at.stamp("Aug 21, 2011")}
|
||||||
|
|
||||||
|
.cover-desc
|
||||||
|
- unless @user.public_email.blank?
|
||||||
|
= link_to @user.public_email, "mailto:#{@user.public_email}"
|
||||||
|
- unless @user.skype.blank?
|
||||||
|
·
|
||||||
|
= link_to "Skype", "skype:#{@user.skype}"
|
||||||
|
- unless @user.linkedin.blank?
|
||||||
|
·
|
||||||
|
= link_to "LinkedIn", "http://www.linkedin.com/in/#{@user.linkedin}"
|
||||||
|
- unless @user.twitter.blank?
|
||||||
|
·
|
||||||
|
= link_to "Twitter", "http://www.twitter.com/#{@user.twitter}"
|
||||||
|
- unless @user.website_url.blank?
|
||||||
|
·
|
||||||
|
= link_to @user.short_website_url, @user.full_website_url
|
||||||
|
- unless @user.location.blank?
|
||||||
|
·
|
||||||
|
= @user.location
|
||||||
|
|
||||||
|
|
||||||
|
.cover-controls
|
||||||
|
- if @user == current_user
|
||||||
|
= link_to profile_path, class: 'btn btn-gray' do
|
||||||
|
= icon('pencil')
|
||||||
|
- elsif current_user
|
||||||
|
.report-abuse
|
||||||
|
- if @user.abuse_report
|
||||||
|
%button.btn.btn-danger{ title: 'Already reported for abuse',
|
||||||
|
data: { toggle: 'tooltip', placement: 'left', container: 'body' }}
|
||||||
|
= icon('exclamation-circle')
|
||||||
|
- else
|
||||||
|
= link_to new_abuse_report_path(user_id: @user.id), class: 'btn btn-gray',
|
||||||
|
title: 'Report abuse', data: {toggle: 'tooltip', placement: 'left', container: 'body'} do
|
||||||
|
= icon('exclamation-circle')
|
||||||
|
|
||||||
|
.gray-content-block.second-block
|
||||||
|
.user-calendar
|
||||||
|
%h4.center.light
|
||||||
|
%i.fa.fa-spinner.fa-spin
|
||||||
|
.user-calendar-activities
|
||||||
|
|
||||||
|
|
||||||
|
.row.prepend-top-20
|
||||||
%section.col-md-7
|
%section.col-md-7
|
||||||
.header-with-avatar
|
|
||||||
= link_to avatar_icon(@user, 400), target: '_blank' do
|
|
||||||
= image_tag avatar_icon(@user, 90), class: "avatar avatar-tile s90", alt: ''
|
|
||||||
%h3
|
|
||||||
= @user.name
|
|
||||||
- if @user == current_user
|
|
||||||
.pull-right.hidden-xs
|
|
||||||
= link_to profile_path, class: 'btn btn-sm' do
|
|
||||||
= icon('user')
|
|
||||||
Profile settings
|
|
||||||
- elsif current_user
|
|
||||||
.report_abuse.pull-right
|
|
||||||
- if @user.abuse_report
|
|
||||||
%span#report_abuse_btn.light.btn.btn-sm.btn-close{title: 'Already reported for abuse', data: {toggle: 'tooltip', placement: 'right', container: 'body'}}
|
|
||||||
= icon('exclamation-circle')
|
|
||||||
- else
|
|
||||||
%a.light.btn.btn-sm{href: new_abuse_report_path(user_id: @user.id), title: 'Report abuse', data: {toggle: 'tooltip', placement: 'right', container: 'body'}}
|
|
||||||
= icon('exclamation-circle')
|
|
||||||
|
|
||||||
.username
|
|
||||||
@#{@user.username}
|
|
||||||
.description
|
|
||||||
- if @user.bio.present?
|
|
||||||
= @user.bio
|
|
||||||
|
|
||||||
.clearfix
|
|
||||||
|
|
||||||
- if @groups.any?
|
- if @groups.any?
|
||||||
.prepend-top-20
|
.prepend-top-20
|
||||||
%h4 Groups
|
%h4 Groups
|
||||||
= render 'groups', groups: @groups
|
= render 'groups', groups: @groups
|
||||||
%hr
|
%hr
|
||||||
|
|
||||||
.hidden-xs
|
|
||||||
.user-calendar
|
|
||||||
%h4.center.light
|
|
||||||
%i.fa.fa-spinner.fa-spin
|
|
||||||
.user-calendar-activities
|
|
||||||
%hr
|
|
||||||
%h4
|
%h4
|
||||||
User Activity
|
User Activity
|
||||||
|
|
||||||
|
@ -59,7 +84,6 @@
|
||||||
.content_list
|
.content_list
|
||||||
= spinner
|
= spinner
|
||||||
%aside.col-md-5
|
%aside.col-md-5
|
||||||
= render 'profile', user: @user
|
|
||||||
= render 'projects', projects: @projects, contributed_projects: @contributed_projects
|
= render 'projects', projects: @projects, contributed_projects: @contributed_projects
|
||||||
|
|
||||||
:coffeescript
|
:coffeescript
|
||||||
|
|
|
@ -378,6 +378,7 @@ Gitlab::Application.routes.draw do
|
||||||
[:new, :create, :index], path: "/") do
|
[:new, :create, :index], path: "/") do
|
||||||
member do
|
member do
|
||||||
put :transfer
|
put :transfer
|
||||||
|
delete :remove_fork
|
||||||
post :archive
|
post :archive
|
||||||
post :unarchive
|
post :unarchive
|
||||||
post :toggle_star
|
post :toggle_star
|
||||||
|
|
|
@ -23,7 +23,7 @@ class Spinach::Features::AbuseReports < Spinach::FeatureSteps
|
||||||
end
|
end
|
||||||
|
|
||||||
step 'I should see a red "Report abuse" button' do
|
step 'I should see a red "Report abuse" button' do
|
||||||
expect(find(:css, '.report_abuse')).to have_selector(:css, 'span.btn-close')
|
expect(page).to have_button("Already reported for abuse")
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_mike
|
def user_mike
|
||||||
|
|
|
@ -86,13 +86,13 @@ class Spinach::Features::Project < Spinach::FeatureSteps
|
||||||
end
|
end
|
||||||
|
|
||||||
step 'I should see project "Forum" README' do
|
step 'I should see project "Forum" README' do
|
||||||
page.within('#README') do
|
page.within('.readme-holder') do
|
||||||
expect(page).to have_content 'Sample repo for testing gitlab features'
|
expect(page).to have_content 'Sample repo for testing gitlab features'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
step 'I should see project "Shop" README' do
|
step 'I should see project "Shop" README' do
|
||||||
page.within('#README') do
|
page.within('.readme-holder') do
|
||||||
expect(page).to have_content 'testme'
|
expect(page).to have_content 'testme'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -246,8 +246,8 @@ module API
|
||||||
# Example Request:
|
# Example Request:
|
||||||
# DELETE /projects/:id/fork
|
# DELETE /projects/:id/fork
|
||||||
delete ":id/fork" do
|
delete ":id/fork" do
|
||||||
authenticated_as_admin!
|
authorize! :remove_fork_project, user_project
|
||||||
unless user_project.forked_project_link.nil?
|
if user_project.forked?
|
||||||
user_project.forked_project_link.destroy
|
user_project.forked_project_link.destroy
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -27,7 +27,7 @@ module Gitlab
|
||||||
def references
|
def references
|
||||||
@references ||= Hash.new do |references, type|
|
@references ||= Hash.new do |references, type|
|
||||||
type = type.to_sym
|
type = type.to_sym
|
||||||
return references[type] if references.has_key?(type)
|
next references[type] if references.has_key?(type)
|
||||||
|
|
||||||
references[type] = pipeline_result(type)
|
references[type] = pipeline_result(type)
|
||||||
end
|
end
|
||||||
|
|
17
spec/benchmarks/models/milestone_spec.rb
Normal file
17
spec/benchmarks/models/milestone_spec.rb
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Milestone, benchmark: true do
|
||||||
|
describe '#sort_issues' do
|
||||||
|
let(:milestone) { create(:milestone) }
|
||||||
|
|
||||||
|
let(:issue1) { create(:issue, milestone: milestone) }
|
||||||
|
let(:issue2) { create(:issue, milestone: milestone) }
|
||||||
|
let(:issue3) { create(:issue, milestone: milestone) }
|
||||||
|
|
||||||
|
let(:issue_ids) { [issue3.id, issue2.id, issue1.id] }
|
||||||
|
|
||||||
|
benchmark_subject { milestone.sort_issues(issue_ids) }
|
||||||
|
|
||||||
|
it { is_expected.to iterate_per_second(500) }
|
||||||
|
end
|
||||||
|
end
|
|
@ -22,6 +22,34 @@ describe ProjectsController do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "rendering default project view" do
|
||||||
|
render_views
|
||||||
|
|
||||||
|
it "renders the activity view" do
|
||||||
|
allow(controller).to receive(:current_user).and_return(user)
|
||||||
|
allow(user).to receive(:project_view).and_return('activity')
|
||||||
|
|
||||||
|
get :show, namespace_id: public_project.namespace.path, id: public_project.path
|
||||||
|
expect(response).to render_template('_activity')
|
||||||
|
end
|
||||||
|
|
||||||
|
it "renders the readme view" do
|
||||||
|
allow(controller).to receive(:current_user).and_return(user)
|
||||||
|
allow(user).to receive(:project_view).and_return('readme')
|
||||||
|
|
||||||
|
get :show, namespace_id: public_project.namespace.path, id: public_project.path
|
||||||
|
expect(response).to render_template('_readme')
|
||||||
|
end
|
||||||
|
|
||||||
|
it "renders the files view" do
|
||||||
|
allow(controller).to receive(:current_user).and_return(user)
|
||||||
|
allow(user).to receive(:project_view).and_return('files')
|
||||||
|
|
||||||
|
get :show, namespace_id: public_project.namespace.path, id: public_project.path
|
||||||
|
expect(response).to render_template('_files')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context "when requested with case sensitive namespace and project path" do
|
context "when requested with case sensitive namespace and project path" do
|
||||||
it "redirects to the normalized path for case mismatch" do
|
it "redirects to the normalized path for case mismatch" do
|
||||||
get :show, namespace_id: public_project.namespace.path, id: public_project.path.upcase
|
get :show, namespace_id: public_project.namespace.path, id: public_project.path.upcase
|
||||||
|
@ -62,4 +90,50 @@ describe ProjectsController do
|
||||||
expect(user.starred?(public_project)).to be_falsey
|
expect(user.starred?(public_project)).to be_falsey
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "DELETE remove_fork" do
|
||||||
|
context 'when signed in' do
|
||||||
|
before do
|
||||||
|
sign_in(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with forked project' do
|
||||||
|
let(:project_fork) { create(:project, namespace: user.namespace) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
create(:forked_project_link, forked_to_project: project_fork)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should remove fork from project' do
|
||||||
|
delete(:remove_fork,
|
||||||
|
namespace_id: project_fork.namespace.to_param,
|
||||||
|
id: project_fork.to_param, format: :js)
|
||||||
|
|
||||||
|
expect(project_fork.forked?).to be_falsey
|
||||||
|
expect(flash[:notice]).to eq('The fork relationship has been removed.')
|
||||||
|
expect(response).to render_template(:remove_fork)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when project not forked' do
|
||||||
|
let(:unforked_project) { create(:project, namespace: user.namespace) }
|
||||||
|
|
||||||
|
it 'should do nothing if project was not forked' do
|
||||||
|
delete(:remove_fork,
|
||||||
|
namespace_id: unforked_project.namespace.to_param,
|
||||||
|
id: unforked_project.to_param, format: :js)
|
||||||
|
|
||||||
|
expect(flash[:notice]).to be_nil
|
||||||
|
expect(response).to render_template(:remove_fork)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "does nothing if user is not signed in" do
|
||||||
|
delete(:remove_fork,
|
||||||
|
namespace_id: project.namespace.to_param,
|
||||||
|
id: project.to_param, format: :js)
|
||||||
|
expect(response.status).to eq(401)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -34,6 +34,27 @@ feature 'Project', feature: true do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'remove forked relationship', js: true do
|
||||||
|
let(:user) { create(:user) }
|
||||||
|
let(:project) { create(:project, namespace: user.namespace) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
login_with user
|
||||||
|
create(:forked_project_link, forked_to_project: project)
|
||||||
|
visit edit_namespace_project_path(project.namespace, project)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should remove fork' do
|
||||||
|
expect(page).to have_content 'Remove fork relationship'
|
||||||
|
|
||||||
|
remove_with_confirm('Remove fork relationship', project.path)
|
||||||
|
|
||||||
|
expect(page).to have_content 'The fork relationship has been removed.'
|
||||||
|
expect(project.forked?).to be_falsey
|
||||||
|
expect(page).not_to have_content 'Remove fork relationship'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe 'removal', js: true do
|
describe 'removal', js: true do
|
||||||
let(:user) { create(:user) }
|
let(:user) { create(:user) }
|
||||||
let(:project) { create(:project, namespace: user.namespace) }
|
let(:project) { create(:project, namespace: user.namespace) }
|
||||||
|
@ -45,13 +66,13 @@ feature 'Project', feature: true do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should remove project' do
|
it 'should remove project' do
|
||||||
expect { remove_project }.to change {Project.count}.by(-1)
|
expect { remove_with_confirm('Remove project', project.path) }.to change {Project.count}.by(-1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove_project
|
def remove_with_confirm(button_text, confirm_with)
|
||||||
click_button "Remove project"
|
click_button button_text
|
||||||
fill_in 'confirm_name_input', with: project.path
|
fill_in 'confirm_name_input', with: confirm_with
|
||||||
click_button 'Confirm'
|
click_button 'Confirm'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -117,4 +117,14 @@ describe IssuesHelper do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "#merge_requests_sentence" do
|
||||||
|
subject { merge_requests_sentence(merge_requests)}
|
||||||
|
let(:merge_requests) do
|
||||||
|
[ build(:merge_request, iid: 1), build(:merge_request, iid: 2),
|
||||||
|
build(:merge_request, iid: 3)]
|
||||||
|
end
|
||||||
|
|
||||||
|
it { is_expected.to eq("!1, !2, or !3") }
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -68,7 +68,6 @@ describe Issue, "Issuable" do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
describe "#to_hook_data" do
|
describe "#to_hook_data" do
|
||||||
let(:hook_data) { issue.to_hook_data(user) }
|
let(:hook_data) { issue.to_hook_data(user) }
|
||||||
|
|
||||||
|
|
|
@ -68,6 +68,43 @@ describe Issue do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#closed_by_merge_requests' do
|
||||||
|
let(:project) { create(:project) }
|
||||||
|
let(:issue) { create(:issue, project: project, state: "opened")}
|
||||||
|
let(:closed_issue) { build(:issue, project: project, state: "closed")}
|
||||||
|
|
||||||
|
let(:mr) do
|
||||||
|
opts = {
|
||||||
|
title: 'Awesome merge_request',
|
||||||
|
description: "Fixes #{issue.to_reference}",
|
||||||
|
source_branch: 'feature',
|
||||||
|
target_branch: 'master'
|
||||||
|
}
|
||||||
|
MergeRequests::CreateService.new(project, project.owner, opts).execute
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:closed_mr) do
|
||||||
|
opts = {
|
||||||
|
title: 'Awesome merge_request 2',
|
||||||
|
description: "Fixes #{issue.to_reference}",
|
||||||
|
source_branch: 'feature',
|
||||||
|
target_branch: 'master',
|
||||||
|
state: 'closed'
|
||||||
|
}
|
||||||
|
MergeRequests::CreateService.new(project, project.owner, opts).execute
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns the merge request to close this issue' do
|
||||||
|
allow(mr).to receive(:closes_issue?).with(issue).and_return(true)
|
||||||
|
|
||||||
|
expect(issue.closed_by_merge_requests).to eq([mr])
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns an empty array when the current issue is closed already" do
|
||||||
|
expect(closed_issue.closed_by_merge_requests).to eq([])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
it_behaves_like 'an editable mentionable' do
|
it_behaves_like 'an editable mentionable' do
|
||||||
subject { create(:issue) }
|
subject { create(:issue) }
|
||||||
|
|
||||||
|
|
|
@ -140,4 +140,32 @@ describe Milestone do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#sort_issues' do
|
||||||
|
let(:milestone) { create(:milestone) }
|
||||||
|
|
||||||
|
let(:issue1) { create(:issue, milestone: milestone, position: 1) }
|
||||||
|
let(:issue2) { create(:issue, milestone: milestone, position: 2) }
|
||||||
|
let(:issue3) { create(:issue, milestone: milestone, position: 3) }
|
||||||
|
let(:issue4) { create(:issue, position: 42) }
|
||||||
|
|
||||||
|
it 'sorts the given issues' do
|
||||||
|
milestone.sort_issues([issue3.id, issue2.id, issue1.id])
|
||||||
|
|
||||||
|
issue1.reload
|
||||||
|
issue2.reload
|
||||||
|
issue3.reload
|
||||||
|
|
||||||
|
expect(issue1.position).to eq(3)
|
||||||
|
expect(issue2.position).to eq(2)
|
||||||
|
expect(issue3.position).to eq(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'ignores issues not part of the milestone' do
|
||||||
|
milestone.sort_issues([issue3.id, issue2.id, issue1.id, issue4.id])
|
||||||
|
|
||||||
|
issue4.reload
|
||||||
|
|
||||||
|
expect(issue4.position).to eq(42)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -606,28 +606,42 @@ describe API::API, api: true do
|
||||||
|
|
||||||
describe 'DELETE /projects/:id/fork' do
|
describe 'DELETE /projects/:id/fork' do
|
||||||
|
|
||||||
it "shouldn't available for non admin users" do
|
it "shouldn't be visible to users outside group" do
|
||||||
delete api("/projects/#{project_fork_target.id}/fork", user)
|
delete api("/projects/#{project_fork_target.id}/fork", user)
|
||||||
expect(response.status).to eq(403)
|
expect(response.status).to eq(404)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should make forked project unforked' do
|
context 'when users belong to project group' do
|
||||||
post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
|
let(:project_fork_target) { create(:project, group: create(:group)) }
|
||||||
project_fork_target.reload
|
|
||||||
expect(project_fork_target.forked_from_project).not_to be_nil
|
|
||||||
expect(project_fork_target.forked?).to be_truthy
|
|
||||||
delete api("/projects/#{project_fork_target.id}/fork", admin)
|
|
||||||
expect(response.status).to eq(200)
|
|
||||||
project_fork_target.reload
|
|
||||||
expect(project_fork_target.forked_from_project).to be_nil
|
|
||||||
expect(project_fork_target.forked?).not_to be_truthy
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should be idempotent if not forked' do
|
before do
|
||||||
expect(project_fork_target.forked_from_project).to be_nil
|
project_fork_target.group.add_owner user
|
||||||
delete api("/projects/#{project_fork_target.id}/fork", admin)
|
project_fork_target.group.add_developer user2
|
||||||
expect(response.status).to eq(200)
|
end
|
||||||
expect(project_fork_target.reload.forked_from_project).to be_nil
|
|
||||||
|
it 'should be forbidden to non-owner users' do
|
||||||
|
delete api("/projects/#{project_fork_target.id}/fork", user2)
|
||||||
|
expect(response.status).to eq(403)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should make forked project unforked' do
|
||||||
|
post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
|
||||||
|
project_fork_target.reload
|
||||||
|
expect(project_fork_target.forked_from_project).not_to be_nil
|
||||||
|
expect(project_fork_target.forked?).to be_truthy
|
||||||
|
delete api("/projects/#{project_fork_target.id}/fork", admin)
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
project_fork_target.reload
|
||||||
|
expect(project_fork_target.forked_from_project).to be_nil
|
||||||
|
expect(project_fork_target.forked?).not_to be_truthy
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should be idempotent if not forked' do
|
||||||
|
expect(project_fork_target.forked_from_project).to be_nil
|
||||||
|
delete api("/projects/#{project_fork_target.id}/fork", admin)
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
expect(project_fork_target.reload.forked_from_project).to be_nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue