提交 a4592434 编写于 作者: A Alfredo Sumaran

Merge remote-tracking branch 'origin/master' into issue_14904

......@@ -728,7 +728,7 @@ Metrics/ParameterLists:
# A complexity metric geared towards measuring complexity for a human reader.
Metrics/PerceivedComplexity:
Enabled: true
Max: 17
Max: 18
#################### Lint ################################
......
Please view this file on the master branch, on stable branches it's out of date.
v 8.8.0 (unreleased)
v 8.7.0 (unreleased)
- The number of InfluxDB points stored per UDP packet can now be configured
- Transactions for /internal/allowed now have an "action" tag set
- Method instrumentation now uses Module#prepend instead of aliasing methods
- Repository.clean_old_archives is now instrumented
......@@ -11,6 +14,7 @@ v 8.7.0 (unreleased)
- Developers can now add custom tags to transactions
- Loading of an issue's referenced merge requests and related branches is now done asynchronously
- Enable gzip for assets, makes the page size significantly smaller. !3544 / !3632 (Connor Shea)
- Add support to cherry-pick any commit into any branch in the web interface (Minqi Pan)
- Project switcher uses new dropdown styling
- Load award emoji images separately unless opening the full picker. Saves several hundred KBs of data for most pages. (Connor Shea)
- Do not include award_emojis in issue and merge_request comment_count !3610 (Lucas Charles)
......@@ -21,12 +25,14 @@ v 8.7.0 (unreleased)
- Allow projects to be transfered to a lower visibility level group
- Fix `signed_in_ip` being set to 127.0.0.1 when using a reverse proxy !3524
- Improved Markdown rendering performance !3389
- Make shared runners text in box configurable
- Don't attempt to look up an avatar in repo if repo directory does not exist (Stan Hu)
- API: Ability to subscribe and unsubscribe from issues and merge requests (Robert Schilling)
- Expose project badges in project settings
- Make /profile/keys/new redirect to /profile/keys for back-compat. !3717
- Preserve time notes/comments have been updated at when moving issue
- Make HTTP(s) label consistent on clone bar (Stan Hu)
- Add support for `after_script`, requires Runner 1.2 (Kamil Trzciński)
- Expose label description in API (Mariusz Jachimowicz)
- API: Ability to update a group (Robert Schilling)
- API: Ability to move issues (Robert Schilling)
......@@ -34,6 +40,8 @@ v 8.7.0 (unreleased)
- Fix a bug whith trailing slash in teamcity_url (Charles May)
- Allow back dating on issues when created or updated through the API
- Allow back dating on issue notes when created through the API
- Propose license template when creating a new LICENSE file
- API: Expose /licenses and /licenses/:key
- Fix avatar stretching by providing a cropping feature
- API: Expose `subscribed` for issues and merge requests (Robert Schilling)
- Allow SAML to handle external users based on user's information !3530
......@@ -51,6 +59,7 @@ v 8.7.0 (unreleased)
- Use rugged to change HEAD in Project#change_head (P.S.V.R)
- API: Ability to filter milestones by state `active` and `closed` (Robert Schilling)
- API: Fix milestone filtering by `iid` (Robert Schilling)
- Make before_script and after_script overridable on per-job (Kamil Trzciński)
- API: Delete notes of issues, snippets, and merge requests (Robert Schilling)
- Implement 'Groups View' as an option for dashboard preferences !3379 (Elias W.)
- Better errors handling when creating milestones inside groups
......@@ -71,21 +80,38 @@ v 8.7.0 (unreleased)
- API: Do not leak group existence via return code (Robert Schilling)
- ClosingIssueExtractor regex now also works with colons. e.g. "Fixes: #1234" !3591
- Update number of Todos in the sidebar when it's marked as "Done". !3600
- Sanitize branch names created for confidential issues
- API: Expose 'updated_at' for issue, snippet, and merge request notes (Robert Schilling)
- API: User can leave a project through the API when not master or owner. !3613
- Fix repository cache invalidation issue when project is recreated with an empty repo (Stan Hu)
- Fix: Allow empty recipients list for builds emails service when pushed is added (Frank Groeneveld)
- Improved markdown forms
- Delete tags using Rugged for performance reasons (Robert Schilling)
- Add Slack notifications when Wiki is edited (Sebastian Klier)
- Diffs load at the correct point when linking from from number
- Selected diff rows highlight
- Fix emoji categories in the emoji picker
- API: Properly display annotated tags for GET /projects/:id/repository/tags (Robert Schilling)
- Add encrypted credentials for imported projects and migrate old ones
- Properly format all merge request references with ! rather than # !3740 (Ben Bodenmiller)
- Author and participants are displayed first on users autocompletion
- Show number sign on external issue reference text (Florent Baldino)
- Updated print style for issues
- Use GitHub Issue/PR number as iid to keep references
- Add 'l' shortcut to open Label dropdown on issuables and 'i' to create new issue on a project
- Import GitHub labels
- Import GitHub milestones
- Fix emoji catgories in the emoji picker
- Execute system web hooks on push to the project
- Allow enable/disable push events for system hooks
- Fix GitHub project's link in the import page when provider has a custom URL
- Add RAW build trace output and button on build page
- Add incremental build trace update into CI API
v 8.6.7 (unreleased)
- Fix persistent XSS vulnerability in `commit_person_link` helper
- Fix persistent XSS vulnerability in Label and Milestone dropdowns
- Fix vulnerability that made it possible to enumerate private projects belonging to group
v 8.6.6
- Expire the exists cache before deletion to ensure project dir actually exists (Stan Hu). !3413
......
......@@ -190,6 +190,9 @@ gem 'babosa', '~> 1.0.2'
# Sanitizes SVG input
gem "loofah", "~> 2.0.3"
# Working with license
gem 'licensee', '~> 8.0.0'
# Protect against bruteforcing
gem "rack-attack", '~> 4.3.1'
......@@ -315,7 +318,7 @@ end
gem "newrelic_rpm", '~> 3.14'
gem 'octokit', '~> 3.8.0'
gem 'octokit', '~> 4.3.0'
gem "mail_room", "~> 0.6.1"
......
......@@ -452,6 +452,8 @@ GEM
addressable (~> 2.3)
letter_opener (1.1.2)
launchy (~> 2.2)
licensee (8.0.0)
rugged (>= 0.24b)
listen (3.0.5)
rb-fsevent (>= 0.9.3)
rb-inotify (>= 0.9)
......@@ -485,8 +487,8 @@ GEM
multi_json (~> 1.3)
multi_xml (~> 0.5)
rack (~> 1.2)
octokit (3.8.0)
sawyer (~> 0.6.0, >= 0.5.3)
octokit (4.3.0)
sawyer (~> 0.7.0, >= 0.5.3)
omniauth (1.3.1)
hashie (>= 1.2, < 4)
rack (>= 1.0, < 3)
......@@ -712,8 +714,8 @@ GEM
sprockets (>= 2.8, < 4.0)
sprockets-rails (>= 2.0, < 4.0)
tilt (>= 1.1, < 3)
sawyer (0.6.0)
addressable (~> 2.3.5)
sawyer (0.7.0)
addressable (>= 2.3.5, < 2.5)
faraday (~> 0.8, < 0.10)
scss_lint (0.47.1)
rake (>= 0.9, < 11)
......@@ -957,6 +959,7 @@ DEPENDENCIES
jquery-ui-rails (~> 5.0.0)
kaminari (~> 0.16.3)
letter_opener (~> 1.1.2)
licensee (~> 8.0.0)
loofah (~> 2.0.3)
mail_room (~> 0.6.1)
method_source (~> 0.8)
......@@ -968,7 +971,7 @@ DEPENDENCIES
newrelic_rpm (~> 3.14)
nokogiri (~> 1.6.7, >= 1.6.7.2)
oauth2 (~> 1.0.0)
octokit (~> 3.8.0)
octokit (~> 4.3.0)
omniauth (~> 1.3.1)
omniauth-auth0 (~> 1.4.1)
omniauth-azure-oauth2 (~> 0.0.6)
......
......@@ -5,6 +5,7 @@
group_projects_path: "/api/:version/groups/:id/projects.json"
projects_path: "/api/:version/projects.json"
labels_path: "/api/:version/projects/:id/labels"
license_path: "/api/:version/licenses/:key"
group: (group_id, callback) ->
url = Api.buildUrl(Api.group_path)
......@@ -92,6 +93,16 @@
).done (projects) ->
callback(projects)
# Return text for a specific license
licenseText: (key, data, callback) ->
url = Api.buildUrl(Api.license_path).replace(':key', key)
$.ajax(
url: url
data: data
).done (license) ->
callback(license)
buildUrl: (url) ->
url = gon.relative_url_root + url if gon.relative_url_root?
return url.replace(':version', gon.api_version)
class @BlobLicenseSelector
licenseRegex: /^(.+\/)?(licen[sc]e|copying)($|\.)/i
constructor: (editor) ->
@$licenseSelector = $('.js-license-selector')
$fileNameInput = $('#file_name')
initialFileNameValue = if $fileNameInput.length
$fileNameInput.val()
else if $('.editor-file-name').length
$('.editor-file-name').text().trim()
@toggleLicenseSelector(initialFileNameValue)
if $fileNameInput
$fileNameInput.on 'keyup blur', (e) =>
@toggleLicenseSelector($(e.target).val())
$('select.license-select').on 'change', (e) ->
data =
project: $(this).data('project')
fullname: $(this).data('fullname')
Api.licenseText $(this).val(), data, (license) ->
editor.setValue(license.content, -1)
toggleLicenseSelector: (fileName) =>
if @licenseRegex.test(fileName)
@$licenseSelector.show()
else
@$licenseSelector.hide()
class @EditBlob
constructor: (assets_path, mode)->
ace.config.set "modePath", assets_path + '/ace'
constructor: (assets_path, ace_mode = null) ->
ace.config.set "modePath", "#{assets_path}/ace"
ace.config.loadModule "ace/ext/searchbox"
if mode
ace_mode = mode
editor = ace.edit("editor")
editor.focus()
@editor = editor
if ace_mode
editor.getSession().setMode "ace/mode/" + ace_mode
@editor = ace.edit("editor")
@editor.focus()
@editor.getSession().setMode "ace/mode/#{ace_mode}" if ace_mode
# Before a form submission, move the content from the Ace editor into the
# submitted textarea
$('form').submit ->
$("#file-content").val(editor.getValue())
$('form').submit =>
$("#file-content").val(@editor.getValue())
@initModePanesAndLinks()
new BlobLicenseSelector(@editor)
editModePanes = $(".js-edit-mode-pane")
editModeLinks = $(".js-edit-mode a")
editModeLinks.click (event) ->
event.preventDefault()
currentLink = $(this)
paneId = currentLink.attr("href")
currentPane = editModePanes.filter(paneId)
editModeLinks.parent().removeClass "active hover"
currentLink.parent().addClass "active hover"
editModePanes.hide()
if paneId is "#preview"
currentPane.fadeIn 200
$.post currentLink.data("preview-url"),
content: editor.getValue()
, (response) ->
currentPane.empty().append response
currentPane.syntaxHighlight()
return
initModePanesAndLinks: ->
@$editModePanes = $(".js-edit-mode-pane")
@$editModeLinks = $(".js-edit-mode a")
@$editModeLinks.click @editModeLinkClickHandler
else
currentPane.fadeIn 200
editor.focus()
return
editModeLinkClickHandler: (event) =>
event.preventDefault()
currentLink = $(event.target)
paneId = currentLink.attr("href")
currentPane = @$editModePanes.filter(paneId)
@$editModeLinks.parent().removeClass "active hover"
currentLink.parent().addClass "active hover"
@$editModePanes.hide()
currentPane.fadeIn 200
if paneId is "#preview"
$.post currentLink.data("preview-url"),
content: @editor.getValue()
, (response) ->
currentPane.empty().append response
currentPane.syntaxHighlight()
editor: ->
return @editor
else
@editor.focus()
class @NewBlob
constructor: (assets_path, mode)->
ace.config.set "modePath", assets_path + '/ace'
ace.config.loadModule "ace/ext/searchbox"
if mode
ace_mode = mode
editor = ace.edit("editor")
editor.focus()
@editor = editor
if ace_mode
editor.getSession().setMode "ace/mode/" + ace_mode
# Before a form submission, move the content from the Ace editor into the
# submitted textarea
$('form').submit ->
$("#file-content").val(editor.getValue())
editor: ->
return @editor
......@@ -154,6 +154,9 @@ class GitLabDropdown
@fullData = data
@parseData @fullData
if @options.filterable
@filterInput.trigger 'keyup'
}
# Init filterable
......
......@@ -9,21 +9,30 @@ class @IssuableContext
$(".issuable-sidebar .inline-update").on "change", ".js-assignee", ->
$(this).submit()
$(document).off("click", ".edit-link").on "click",".edit-link", (e) ->
$block = $(@).parents('.block')
$selectbox = $block.find('.selectbox')
if $selectbox.is(':visible')
$selectbox.hide()
$block.find('.value').show()
else
$selectbox.show()
$block.find('.value').hide()
if $selectbox.is(':visible')
setTimeout (->
$block.find('.dropdown-menu-toggle').trigger 'click'
), 0
$(document)
.off 'click', '.dropdown-content a'
.on 'click', '.dropdown-content a', (e) ->
e.preventDefault()
$(document)
.off 'click', '.edit-link'
.on 'click', '.edit-link', (e) ->
e.preventDefault()
$block = $(@).parents('.block')
$selectbox = $block.find('.selectbox')
if $selectbox.is(':visible')
$selectbox.hide()
$block.find('.value').show()
else
$selectbox.show()
$block.find('.value').hide()
if $selectbox.is(':visible')
setTimeout ->
$block.find('.dropdown-menu-toggle').trigger 'click'
, 0
$(".right-sidebar").niceScroll()
......
......@@ -33,13 +33,13 @@ class @LabelsSelect
if issueUpdateURL
labelHTMLTemplate = _.template(
'<% _.each(labels, function(label){ %>
<a href="<%= ["",issueURLSplit[1], issueURLSplit[2],""].join("/") %>issues?label_name=<%= label.title %>">
<span class="label has-tooltip color-label" title="<%= label.description %>" style="background-color: <%= label.color %>;">
<%= label.title %>
<a href="<%= ["",issueURLSplit[1], issueURLSplit[2],""].join("/") %>issues?label_name=<%= _.escape(label.title) %>">
<span class="label has-tooltip color-label" title="<%= _.escape(label.description) %>" style="background-color: <%= label.color %>;">
<%= _.escape(label.title) %>
</span>
</a>
<% }); %>'
);
)
labelNoneHTMLTemplate = _.template('<div class="light">None</div>')
if newLabelField.length and $dropdown.hasClass 'js-extra-options'
......@@ -211,7 +211,7 @@ class @LabelsSelect
"<li>
<a href='#' class='#{selectedClass}'>
#{color}
#{label.title}
#{_.escape(label.title)}
</a>
</li>"
filterable: true
......
......@@ -24,7 +24,7 @@ class @MilestoneSelect
if issueUpdateURL
milestoneLinkTemplate = _.template(
'<a href="/<%= namespace %>/<%= path %>/milestones/<%= iid %>"><%= title %></a>'
'<a href="/<%= namespace %>/<%= path %>/milestones/<%= iid %>"><%= _.escape(title) %></a>'
)
milestoneLinkNoneTemplate = '<div class="light">None</div>'
......@@ -71,7 +71,7 @@ class @MilestoneSelect
defaultLabel
fieldName: $dropdown.data('field-name')
text: (milestone) ->
milestone.title
_.escape(milestone.title)
id: (milestone) ->
if !useId
milestone.name
......
......@@ -26,6 +26,10 @@
line-height: 42px;
padding-top: 7px;
padding-bottom: 7px;
.pull-right {
height: 20px;
}
}
.editor-ref {
......@@ -53,4 +57,9 @@
.select2 {
float: right;
}
.encoding-selector,
.license-selector {
display: inline-block;
}
}
......@@ -42,6 +42,7 @@
.note-textarea {
display: block;
padding: 10px 0;
color: $gl-gray;
font-family: $regular_font;
border: 0;
......
......@@ -75,6 +75,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:admin_notification_email,
:user_oauth_applications,
:shared_runners_enabled,
:shared_runners_text,
:max_artifacts_size,
:metrics_enabled,
:metrics_host,
......@@ -92,6 +93,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:akismet_api_key,
:email_author_in_body,
:repository_checks_enabled,
:metrics_packet_size,
restricted_visibility_levels: [],
import_sources: []
)
......
......@@ -39,6 +39,6 @@ class Admin::HooksController < Admin::ApplicationController
end
def hook_params
params.require(:hook).permit(:url, :enable_ssl_verification)
params.require(:hook).permit(:url, :enable_ssl_verification, :push_events, :tag_push_events)
end
end
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]
before_action :authorize_update_build!, except: [:index, :show, :status, :raw]
layout 'project'
def index
......@@ -62,6 +62,14 @@ class Projects::BuildsController < Projects::ApplicationController
notice: "Build has been sucessfully erased!"
end
def raw
if @build.has_trace?
send_file @build.path_to_trace, type: 'text/plain; charset=utf-8', disposition: 'inline'
else
render_404
end
end
private
def build
......
......@@ -12,7 +12,7 @@ class Projects::CommitController < Projects::ApplicationController
before_action :authorize_read_commit_status!, only: [:builds]
before_action :commit
before_action :define_show_vars, only: [:show, :builds]
before_action :authorize_edit_tree!, only: [:revert]
before_action :authorize_edit_tree!, only: [:revert, :cherry_pick]
def show
apply_diff_view_cookie!
......@@ -60,27 +60,32 @@ class Projects::CommitController < Projects::ApplicationController
end
def revert
assign_revert_commit_vars
assign_change_commit_vars(@commit.revert_branch_name)
return render_404 if @target_branch.blank?
create_commit(Commits::RevertService, success_notice: "The #{revert_type_title} has been successfully reverted.",
success_path: successful_revert_path, failure_path: failed_revert_path)
create_commit(Commits::RevertService, success_notice: "The #{@commit.change_type_title} has been successfully reverted.",
success_path: successful_change_path, failure_path: failed_change_path)
end
def cherry_pick
assign_change_commit_vars(@commit.cherry_pick_branch_name)
return render_404 if @target_branch.blank?
private
def revert_type_title
@commit.merged_merge_request ? 'merge request' : 'commit'
create_commit(Commits::CherryPickService, success_notice: "The #{@commit.change_type_title} has been successfully cherry-picked.",
success_path: successful_change_path, failure_path: failed_change_path)
end
def successful_revert_path
private
def successful_change_path
return referenced_merge_request_url if @commit.merged_merge_request
namespace_project_commits_url(@project.namespace, @project, @target_branch)
end
def failed_revert_path
def failed_change_path
return referenced_merge_request_url if @commit.merged_merge_request
namespace_project_commit_url(@project.namespace, @project, params[:id])
......@@ -111,14 +116,13 @@ class Projects::CommitController < Projects::ApplicationController
@statuses = ci_commit.statuses if ci_commit
end
def assign_revert_commit_vars
def assign_change_commit_vars(mr_source_branch)
@commit = project.commit(params[:id])
@target_branch = params[:target_branch]
@mr_source_branch = @commit.revert_branch_name
@mr_source_branch = mr_source_branch
@mr_target_branch = @target_branch
@commit_params = {
commit: @commit,
revert_type_title: revert_type_title,
create_merge_request: params[:create_merge_request].present? || different_project?
}
end
......
......@@ -7,10 +7,12 @@ class Projects::GroupLinksController < Projects::ApplicationController
end
def create
link = project.project_group_links.new
link.group_id = params[:link_group_id]
link.group_access = params[:link_group_access]
link.save
group = Group.find(params[:link_group_id])
return render_404 unless can?(current_user, :read_group, group)
project.project_group_links.create(
group: group, group_access: params[:link_group_access]
)
redirect_to namespace_project_group_links_path(project.namespace, project)
end
......
......@@ -128,10 +128,7 @@ class Projects::IssuesController < Projects::ApplicationController
end
def related_branches
merge_requests = @issue.referenced_merge_requests(current_user)
@related_branches = @issue.related_branches -
merge_requests.map(&:source_branch)
@related_branches = @issue.related_branches(current_user)
respond_to do |format|
format.json do
......
......@@ -6,7 +6,7 @@ class Projects::ServicesController < Projects::ApplicationController
:description, :issues_url, :new_issue_url, :restrict_to_branch, :channel,
:colorize_messages, :channels,
:push_events, :issues_events, :merge_requests_events, :tag_push_events,
:note_events, :build_events,
:note_events, :build_events, :wiki_page_events,
:notify_only_broken_builds, :add_pusher,
:send_from_committer_email, :disable_diffs, :external_wiki_url,
:notify, :color,
......
......@@ -44,7 +44,7 @@ class Projects::WikisController < Projects::ApplicationController
return render('empty') unless can?(current_user, :create_wiki, @project)
if @page.update(content, format, message)
if @page = WikiPages::UpdateService.new(@project, current_user, wiki_params).execute(@page)
redirect_to(
namespace_project_wiki_path(@project.namespace, @project, @page),
notice: 'Wiki was successfully updated.'
......@@ -55,9 +55,9 @@ class Projects::WikisController < Projects::ApplicationController
end
def create
@page = WikiPage.new(@project_wiki)
@page = WikiPages::CreateService.new(@project, current_user, wiki_params).execute
if @page.create(wiki_params)
if @page.persisted?
redirect_to(
namespace_project_wiki_path(@project.namespace, @project, @page),
notice: 'Wiki was successfully updated.'
......@@ -122,15 +122,4 @@ class Projects::WikisController < Projects::ApplicationController
params[:wiki].slice(:title, :content, :format, :message)
end
def content
params[:wiki][:content]
end
def format
params[:wiki][:format]
end
def message
params[:wiki][:message]
end
end
......@@ -15,6 +15,10 @@ module ApplicationSettingsHelper
current_application_settings.sign_in_text
end
def shared_runners_text
current_application_settings.shared_runners_text
end
def user_oauth_applications?
current_application_settings.user_oauth_applications
end
......
......@@ -173,4 +173,15 @@ module BlobHelper
response.etag = @blob.id
!stale
end
def licenses_for_select
return @licenses_for_select if defined?(@licenses_for_select)
licenses = Licensee::License.all
@licenses_for_select = {
Popular: licenses.select(&:featured).map { |license| [license.name, license.key] },
Other: licenses.reject(&:featured).map { |license| [license.name, license.key] }
}
end
end
......@@ -126,12 +126,10 @@ module CommitsHelper
def revert_commit_link(commit, continue_to_path, btn_class: nil)
return unless current_user
tooltip = "Revert this #{revert_commit_type(commit)} in a new merge request"
tooltip = "Revert this #{commit.change_type_title} in a new merge request"
if can_collaborate_with_project?
content_tag :span, 'data-toggle' => 'modal', 'data-target' => '#modal-revert-commit' do
link_to 'Revert', '#modal-revert-commit', 'data-toggle' => 'tooltip', 'data-container' => 'body', title: tooltip, class: "btn btn-default btn-grouped btn-#{btn_class}"
end
link_to 'Revert', '#modal-revert-commit', 'data-toggle' => 'modal', 'data-container' => 'body', title: tooltip, class: "btn btn-default btn-grouped btn-#{btn_class} has-tooltip"
elsif can?(current_user, :fork_project, @project)
continue_params = {
to: continue_to_path,
......@@ -146,11 +144,24 @@ module CommitsHelper
end
end
def revert_commit_type(commit)
if commit.merged_merge_request
'merge request'
else
'commit'
def cherry_pick_commit_link(commit, continue_to_path, btn_class: nil)
return unless current_user
tooltip = "Cherry-pick this #{commit.change_type_title} in a new merge request"
if can_collaborate_with_project?
link_to 'Cherry-pick', '#modal-cherry-pick-commit', 'data-toggle' => 'modal', 'data-container' => 'body', title: tooltip, class: "btn btn-default btn-grouped btn-#{btn_class} has-tooltip"
elsif can?(current_user, :fork_project, @project)
continue_params = {
to: continue_to_path,
notice: edit_in_new_fork_notice + ' Try to cherry-pick this commit again.',
notice_now: edit_in_new_fork_notice_now
}
fork_path = namespace_project_forks_path(@project.namespace, @project,
namespace_key: current_user.namespace.id,
continue: continue_params)
link_to 'Cherry-pick', fork_path, class: 'btn btn-grouped btn-close', method: :post, 'data-toggle' => 'tooltip', 'data-container' => 'body', title: tooltip
end
end
......@@ -183,7 +194,7 @@ module CommitsHelper
options = {
class: "commit-#{options[:source]}-link has-tooltip",
data: { 'original-title'.to_sym => sanitize(source_email) }
title: source_email
}
if user.nil?
......
module ImportHelper
def github_project_link(path_with_namespace)
link_to path_with_namespace, github_project_url(path_with_namespace), target: '_blank'
end
private
def github_project_url(path_with_namespace)
"#{github_root_url}/#{path_with_namespace}"
end
def github_root_url
return @github_url if defined?(@github_url)
provider = Gitlab.config.omniauth.providers.find { |p| p.name == 'github' }
@github_url = provider.fetch('url', 'https://github.com') if provider
end
end
......@@ -52,7 +52,7 @@ module ProjectsHelper
link_to(author_html, user_path(author), class: "author_link #{"#{opts[:mobile_classes]}" if opts[:mobile_classes]}").html_safe
else
title = opts[:title].sub(":name", sanitize(author.name))
link_to(author_html, user_path(author), class: "author_link has-tooltip", data: { 'original-title'.to_sym => title, container: 'body' } ).html_safe
link_to(author_html, user_path(author), class: "author_link has-tooltip", title: title, data: { container: 'body' } ).html_safe
end
end
......@@ -216,40 +216,14 @@ module ProjectsHelper
end
end
def add_contribution_guide_path(project)
if project && !project.repository.contribution_guide
namespace_project_new_blob_path(
project.namespace,
project,
project.default_branch,
file_name: "CONTRIBUTING.md",
commit_message: "Add contribution guide"
)
end
end
def add_changelog_path(project)
if project && !project.repository.changelog
namespace_project_new_blob_path(
project.namespace,
project,
project.default_branch,
file_name: "CHANGELOG",
commit_message: "Add changelog"
)
end
end
def add_license_path(project)
if project && !project.repository.license
namespace_project_new_blob_path(
project.namespace,
project,
project.default_branch,
file_name: "LICENSE",
commit_message: "Add license"
)
end
def add_special_file_path(project, file_name:, commit_message: nil)
namespace_project_new_blob_path(
project.namespace,
project,
project.default_branch || 'master',
file_name: file_name,
commit_message: commit_message || "Add #{file_name.downcase}"
)
end
def contribution_guide_path(project)
......@@ -272,7 +246,7 @@ module ProjectsHelper
end
def license_path(project)
filename_path(project, :license)
filename_path(project, :license_blob)
end
def version_path(project)
......@@ -306,6 +280,13 @@ module ProjectsHelper
namespace_project_new_blob_path(@project.namespace, @project, tree_join(ref), file_name: 'README.md')
end
def new_license_path
ref = @repository.root_ref if @repository
ref ||= 'master'
namespace_project_new_blob_path(@project.namespace, @project, tree_join(ref), file_name: 'LICENSE')
end
def last_push_event
if current_user
current_user.recent_push(@project.id)
......@@ -335,6 +316,12 @@ module ProjectsHelper
@ref || @repository.try(:root_ref)
end
def license_short_name(project)
license = Licensee::License.new(project.repository.license_key)
license.nickname || license.name
end
private
def filename_path(project, filename)
......
......@@ -2,32 +2,29 @@ module SelectsHelper
def users_select_tag(id, opts = {})
css_class = "ajax-users-select "
css_class << "multiselect " if opts[:multiple]
css_class << "skip_ldap " if opts[:skip_ldap]
css_class << (opts[:class] || '')
value = opts[:selected] || ''
placeholder = opts[:placeholder] || 'Search for a user'
null_user = opts[:null_user] || false
any_user = opts[:any_user] || false
email_user = opts[:email_user] || false
first_user = opts[:first_user] && current_user ? current_user.username : false
current_user = opts[:current_user] || false
author_id = opts[:author_id] || ''
project = opts[:project] || @project
first_user = opts[:first_user] && current_user ? current_user.username : false
html = {
class: css_class,
data: {
placeholder: placeholder,
null_user: null_user,
any_user: any_user,
email_user: email_user,
placeholder: opts[:placeholder] || 'Search for a user',
null_user: opts[:null_user] || false,
any_user: opts[:any_user] || false,
email_user: opts[:email_user] || false,
first_user: first_user,
current_user: current_user,
author_id: author_id
current_user: opts[:current_user] || false,
"push-code-to-protected-branches" => opts[:push_code_to_protected_branches],
author_id: opts[:author_id] || ''
}
}
unless opts[:scope] == :all
project = opts[:project] || @project
if project
html['data-project-id'] = project.id
elsif @group
......
......@@ -66,7 +66,7 @@ module TreeHelper
ref
else
project = tree_edit_project(project)
project.repository.next_patch_branch
project.repository.next_branch('patch')
end
end
......
......@@ -56,7 +56,7 @@ module Emails
{
from: sender(sender_id),
to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (##{@merge_request.iid})")
subject: subject("#{@merge_request.title} (#{@merge_request.to_reference})")
}
end
end
......
......@@ -38,7 +38,7 @@ module Emails
{
from: sender(@note.author_id),
to: recipient(recipient_id),
subject: subject("#{@note.noteable.title} (##{@note.noteable.iid})")
subject: subject("#{@note.noteable.title} (#{@note.noteable.to_reference})")
}
end
......
......@@ -8,7 +8,7 @@ class RepositoryCheckMailer < BaseMailer
mail(
to: User.admins.pluck(:email),
subject: @message
subject: "GitLab Admin | #{@message}"
)
end
end
......@@ -230,12 +230,33 @@ module Ci
end
end
def trace_length
if raw_trace
raw_trace.length
else
0
end
end
def trace=(trace)
recreate_trace_dir
File.write(path_to_trace, trace)
end
def recreate_trace_dir
unless Dir.exists?(dir_to_trace)
FileUtils.mkdir_p(dir_to_trace)
end
end
private :recreate_trace_dir
File.write(path_to_trace, trace)
def append_trace(trace_part, offset)
recreate_trace_dir
File.truncate(path_to_trace, offset) if File.exist?(path_to_trace)
File.open(path_to_trace, 'a') do |f|
f.write(trace_part)
end
end
def dir_to_trace
......
......@@ -15,8 +15,8 @@ class Commit
DIFF_SAFE_LINES = Gitlab::Git::DiffCollection::DEFAULT_LIMITS[:max_lines]
# Commits above this size will not be rendered in HTML
DIFF_HARD_LIMIT_FILES = 1000 unless defined?(DIFF_HARD_LIMIT_FILES)
DIFF_HARD_LIMIT_LINES = 50000 unless defined?(DIFF_HARD_LIMIT_LINES)
DIFF_HARD_LIMIT_FILES = 1000
DIFF_HARD_LIMIT_LINES = 50000
class << self
def decorate(commits, project)
......@@ -218,6 +218,10 @@ class Commit
def revert_branch_name
"revert-#{short_id}"
end
def cherry_pick_branch_name
project.repository.next_branch("cherry-pick-#{short_id}", mild: true)
end
def revert_description
if merged_merge_request
......@@ -253,6 +257,10 @@ class Commit
end.any? { |commit_ref| commit_ref.reverts_commit?(self) }
end
def change_type_title
merged_merge_request ? 'merge request' : 'commit'
end
private
def repo_changes
......
......@@ -15,7 +15,6 @@
#
require 'carrierwave/orm/activerecord'
require 'file_size_validator'
class Group < Namespace
include Gitlab::ConfigHelper
......
......@@ -21,10 +21,9 @@
class ProjectHook < WebHook
belongs_to :project
scope :push_hooks, -> { where(push_events: true) }
scope :tag_push_hooks, -> { where(tag_push_events: true) }
scope :issue_hooks, -> { where(issues_events: true) }
scope :note_hooks, -> { where(note_events: true) }
scope :merge_request_hooks, -> { where(merge_requests_events: true) }
scope :build_hooks, -> { where(build_events: true) }
scope :wiki_page_hooks, -> { where(wiki_page_events: true) }
end
......@@ -19,4 +19,7 @@
#
class SystemHook < WebHook
def async_execute(data, hook_name)
Sidekiq::Client.enqueue(SystemHookWorker, id, data, hook_name)
end
end
......@@ -30,6 +30,9 @@ class WebHook < ActiveRecord::Base
default_value_for :build_events, false
default_value_for :enable_ssl_verification, true
scope :push_hooks, -> { where(push_events: true) }
scope :tag_push_hooks, -> { where(tag_push_events: true) }
# HTTParty timeout
default_timeout Gitlab.config.gitlab.webhook_timeout
......
......@@ -20,7 +20,6 @@
#
require 'carrierwave/orm/activerecord'
require 'file_size_validator'
class Issue < ActiveRecord::Base
include InternalId
......@@ -104,10 +103,16 @@ class Issue < ActiveRecord::Base
end
end
def related_branches
project.repository.branch_names.select do |branch|
# All branches containing the current issue's ID, except for
# those with a merge request open referencing the current issue.
def related_branches(current_user)
branches_with_iid = project.repository.branch_names.select do |branch|
branch =~ /\A#{iid}-(?!\d+-stable)/i
end
branches_with_merge_request = self.referenced_merge_requests(current_user).map(&:source_branch)
branches_with_iid - branches_with_merge_request
end
# Reset issue events cache
......@@ -151,13 +156,17 @@ class Issue < ActiveRecord::Base
end
def to_branch_name
"#{iid}-#{title.parameterize}"
if self.confidential?
"#{iid}-confidential-issue"
else
"#{iid}-#{title.parameterize}"
end
end
def can_be_worked_on?(current_user)
!self.closed? &&
!self.project.forked? &&
self.related_branches.empty? &&
self.related_branches(current_user).empty? &&
self.closed_by_merge_requests(current_user).empty?
end
end
......@@ -27,9 +27,6 @@
# merge_commit_sha :string
#
require Rails.root.join("app/models/commit")
require Rails.root.join("lib/static_model")
class MergeRequest < ActiveRecord::Base
include InternalId
include Issuable
......@@ -605,4 +602,8 @@ class MergeRequest < ActiveRecord::Base
def can_be_reverted?(current_user = nil)
merge_commit && !merge_commit.has_been_reverted?(current_user, self)
end
def can_be_cherry_picked?
merge_commit
end
end
......@@ -11,8 +11,6 @@
# updated_at :datetime
#
require Rails.root.join("app/models/commit")
class MergeRequestDiff < ActiveRecord::Base
include Sortable
......
......@@ -20,7 +20,6 @@
#
require 'carrierwave/orm/activerecord'
require 'file_size_validator'
class Note < ActiveRecord::Base
include Gitlab::CurrentSettings
......
......@@ -40,7 +40,6 @@
#
require 'carrierwave/orm/activerecord'
require 'file_size_validator'
class Project < ActiveRecord::Base
include Gitlab::ConfigHelper
......@@ -831,8 +830,8 @@ class Project < ActiveRecord::Base
end
end
def hook_attrs
{
def hook_attrs(backward: true)
attrs = {
name: name,
description: description,
web_url: web_url,
......@@ -843,12 +842,19 @@ class Project < ActiveRecord::Base
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
}
# Backward compatibility
if backward
attrs.merge!({
homepage: web_url,
url: url_to_repo,
ssh_url: ssh_url_to_repo,
http_url: http_url_to_repo
})
end
attrs
end
# Reset events cache related to this project
......
......@@ -8,7 +8,6 @@
#
require 'carrierwave/orm/activerecord'
require 'file_size_validator'
class ProjectImportData < ActiveRecord::Base
belongs_to :project
......
......@@ -183,7 +183,7 @@ class HipchatService < Service
title = obj_attr[:title]
merge_request_url = "#{project_url}/merge_requests/#{merge_request_id}"
merge_request_link = "<a href=\"#{merge_request_url}\">merge request ##{merge_request_id}</a>"
merge_request_link = "<a href=\"#{merge_request_url}\">merge request !#{merge_request_id}</a>"
message = "#{user_name} #{state} #{merge_request_link} in " \
"#{project_link}: <b>#{title}</b>"
......@@ -224,7 +224,7 @@ class HipchatService < Service
when "MergeRequest"
subj_attr = HashWithIndifferentAccess.new(data[:merge_request])
subject_id = subj_attr[:iid]
subject_desc = "##{subject_id}"
subject_desc = "!#{subject_id}"
subject_type = "merge request"
title = format_title(subj_attr[:title])
when "Snippet"
......
......@@ -60,7 +60,7 @@ class SlackService < Service
end
def supported_events
%w(push issue merge_request note tag_push build)
%w(push issue merge_request note tag_push build wiki_page)
end
def execute(data)
......@@ -90,6 +90,8 @@ class SlackService < Service
NoteMessage.new(data)
when "build"
BuildMessage.new(data) if should_build_be_notified?(data)
when "wiki_page"
WikiPageMessage.new(data)
end
opt = {}
......@@ -133,3 +135,4 @@ require "slack_service/push_message"
require "slack_service/merge_message"
require "slack_service/note_message"
require "slack_service/build_message"
require "slack_service/wiki_page_message"
......@@ -50,7 +50,7 @@ class SlackService
end
def merge_request_link
"[merge request ##{merge_request_id}](#{merge_request_url})"
"[merge request !#{merge_request_id}](#{merge_request_url})"
end
def merge_request_url
......
......@@ -58,7 +58,7 @@ class SlackService
def create_merge_note(merge_request)
commented_on_message(
"[merge request ##{merge_request[:iid]}](#{@note_url})",
"[merge request !#{merge_request[:iid]}](#{@note_url})",
format_title(merge_request[:title]))
end
......
class SlackService
class WikiPageMessage < BaseMessage
attr_reader :user_name
attr_reader :title
attr_reader :project_name
attr_reader :project_url
attr_reader :wiki_page_url
attr_reader :action
attr_reader :description
def initialize(params)
@user_name = params[:user][:name]
@project_name = params[:project_name]
@project_url = params[:project_url]
obj_attr = params[:object_attributes]
obj_attr = HashWithIndifferentAccess.new(obj_attr)
@title = obj_attr[:title]
@wiki_page_url = obj_attr[:url]
@description = obj_attr[:content]
@action =
case obj_attr[:action]
when "create"
"created"
when "update"
"edited"
end
end
def attachments
description_message
end
private
def message
"#{user_name} #{action} #{wiki_page_link} in #{project_link}: *#{title}*"
end
def description_message
[{ text: format(@description), color: attachment_color }]
end
def project_link
"[#{project_name}](#{project_url})"
end
def wiki_page_link
"[wiki page](#{wiki_page_url})"
end
end
end
......@@ -228,7 +228,8 @@ class Repository
def cache_keys
%i(size branch_names tag_names commit_count
readme version contribution_guide changelog license)
readme version contribution_guide changelog
license_blob license_key)
end
def build_cache
......@@ -461,27 +462,21 @@ class Repository
end
end
def license
cache.fetch(:license) do
licenses = tree(:head).blobs.find_all do |file|
file.name =~ /\A(copying|license|licence)/i
end
preferences = [
/\Alicen[sc]e\z/i, # LICENSE, LICENCE
/\Alicen[sc]e\./i, # LICENSE.md, LICENSE.txt
/\Acopying\z/i, # COPYING
/\Acopying\.(?!lesser)/i, # COPYING.txt
/Acopying.lesser/i # COPYING.LESSER
]
def license_blob
return nil if !exists? || empty?
license = nil
preferences.each do |r|
license = licenses.find { |l| l.name =~ r }
break if license
cache.fetch(:license_blob) do
if licensee_project.license
blob_at_branch(root_ref, licensee_project.matched_file.filename)
end
end
end
license
def license_key
return nil if !exists? || empty?
cache.fetch(:license_key) do
licensee_project.license.try(:key) || 'no-license'
end
end
......@@ -549,15 +544,18 @@ class Repository
commit(sha)
end
def next_patch_branch
patch_branch_ids = self.branch_names.map do |n|
result = n.match(/\Apatch-([0-9]+)\z/)
def next_branch(name, opts={})
branch_ids = self.branch_names.map do |n|
next 1 if n == name
result = n.match(/\A#{name}-([0-9]+)\z/)
result[1].to_i if result
end.compact
highest_patch_branch_id = patch_branch_ids.max || 0
highest_branch_id = branch_ids.max || 0
"patch-#{highest_patch_branch_id + 1}"
return name if opts[:mild] && 0 == highest_branch_id
"#{name}-#{highest_branch_id + 1}"
end
# Remove archives older than 2 hours
......@@ -760,6 +758,28 @@ class Repository
end
end
def cherry_pick(user, commit, base_branch, cherry_pick_tree_id = nil)
source_sha = find_branch(base_branch).target
cherry_pick_tree_id ||= check_cherry_pick_content(commit, base_branch)
return false unless cherry_pick_tree_id
commit_with_hooks(user, base_branch) do |ref|
committer = user_to_committer(user)
source_sha = Rugged::Commit.create(rugged,
message: commit.message,
author: {
email: commit.author_email,
name: commit.author_name,
time: commit.authored_date
},
committer: committer,
tree: cherry_pick_tree_id,
parents: [rugged.lookup(source_sha)],
update_ref: ref)
end
end
def check_revert_content(commit, base_branch)
source_sha = find_branch(base_branch).target
args = [commit.id, source_sha]
......@@ -774,6 +794,20 @@ class Repository
tree_id
end
def check_cherry_pick_content(commit, base_branch)
source_sha = find_branch(base_branch).target
args = [commit.id, source_sha]
args << 1 if commit.merge_commit?
cherry_pick_index = rugged.cherrypick_commit(*args)
return false if cherry_pick_index.conflicts?
tree_id = cherry_pick_index.write_tree(rugged)
return false unless diff_exists?(source_sha, tree_id)
tree_id
end
def diff_exists?(sha1, sha2)
rugged.diff(sha1, sha2).size > 0
end
......@@ -925,4 +959,8 @@ class Repository
def cache
@cache ||= RepositoryCache.new(path_with_namespace)
end
def licensee_project
@licensee_project ||= Licensee.project(path)
end
end
......@@ -32,6 +32,7 @@ class Service < ActiveRecord::Base
default_value_for :tag_push_events, true
default_value_for :note_events, true
default_value_for :build_events, true
default_value_for :wiki_page_events, true
after_initialize :initialize_properties
......@@ -53,6 +54,7 @@ class Service < ActiveRecord::Base
scope :merge_request_hooks, -> { where(merge_requests_events: true, active: true) }
scope :note_hooks, -> { where(note_events: true, active: true) }
scope :build_hooks, -> { where(build_events: true, active: true) }
scope :wiki_page_hooks, -> { where(wiki_page_events: true, active: true) }
default_value_for :category, 'common'
......@@ -94,7 +96,7 @@ class Service < ActiveRecord::Base
end
def supported_events
%w(push tag_push issue merge_request)
%w(push tag_push issue merge_request wiki_page)
end
def execute(data)
......
......@@ -63,7 +63,6 @@
#
require 'carrierwave/orm/activerecord'
require 'file_size_validator'
class User < ActiveRecord::Base
extend Gitlab::ConfigHelper
......
......@@ -29,6 +29,10 @@ class WikiPage
# new Page values before writing to the Gollum repository.
attr_accessor :attributes
def hook_attrs
attributes
end
def initialize(wiki, page = nil, persisted = false)
@wiki = wiki
@page = page
......
module Commits
class ChangeService < ::BaseService
class ValidationError < StandardError; end
class ChangeError < StandardError; end
def execute
@source_project = params[:source_project] || @project
@target_branch = params[:target_branch]
@commit = params[:commit]
@create_merge_request = params[:create_merge_request].present?
check_push_permissions unless @create_merge_request
commit
rescue Repository::CommitError, Gitlab::Git::Repository::InvalidBlobName, GitHooksService::PreReceiveError,
ValidationError, ChangeError => ex
error(ex.message)
end
def commit
raise NotImplementedError
end
private
def check_push_permissions
allowed = ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(@target_branch)
unless allowed
raise ValidationError.new('You are not allowed to push into this branch')
end
true
end
def create_target_branch(new_branch)
# Temporary branch exists and contains the change commit
return success if repository.find_branch(new_branch)
result = CreateBranchService.new(@project, current_user)
.execute(new_branch, @target_branch, source_project: @source_project)
if result[:status] == :error
raise ChangeError, "There was an error creating the source branch: #{result[:message]}"
end
end
end
end
module Commits
class CherryPickService < ChangeService
def commit
cherry_pick_into = @create_merge_request ? @commit.cherry_pick_branch_name : @target_branch
cherry_pick_tree_id = repository.check_cherry_pick_content(@commit, @target_branch)
if cherry_pick_tree_id
create_target_branch(cherry_pick_into) if @create_merge_request
repository.cherry_pick(current_user, @commit, cherry_pick_into, cherry_pick_tree_id)
success
else
error_msg = "Sorry, we cannot cherry-pick this #{@commit.change_type_title} automatically.
It may have already been cherry-picked, or a more recent commit may have updated some of its content."
raise ChangeError, error_msg
end
end
end
end
module Commits
class RevertService < ::BaseService
class ValidationError < StandardError; end
class ReversionError < StandardError; end
def execute
@source_project = params[:source_project] || @project
@target_branch = params[:target_branch]
@commit = params[:commit]
@create_merge_request = params[:create_merge_request].present?
check_push_permissions unless @create_merge_request
commit
rescue Repository::CommitError, Gitlab::Git::Repository::InvalidBlobName, GitHooksService::PreReceiveError,
ValidationError, ReversionError => ex
error(ex.message)
end
class RevertService < ChangeService
def commit
revert_into = @create_merge_request ? @commit.revert_branch_name : @target_branch
revert_tree_id = repository.check_revert_content(@commit, @target_branch)
......@@ -26,34 +10,10 @@ module Commits
repository.revert(current_user, @commit, revert_into, revert_tree_id)
success
else
error_msg = "Sorry, we cannot revert this #{params[:revert_type_title]} automatically.
error_msg = "Sorry, we cannot revert this #{@commit.change_type_title} automatically.
It may have already been reverted, or a more recent commit may have updated some of its content."
raise ReversionError, error_msg
raise ChangeError, error_msg
end
end
private
def create_target_branch(new_branch)
# Temporary branch exists and contains the revert commit
return success if repository.find_branch(new_branch)
result = CreateBranchService.new(@project, current_user)
.execute(new_branch, @target_branch, source_project: @source_project)
if result[:status] == :error
raise ReversionError, "There was an error creating the source branch: #{result[:message]}"
end
end
def check_push_permissions
allowed = ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(@target_branch)
unless allowed
raise ValidationError.new('You are not allowed to push into this branch')
end
true
end
end
end
......@@ -73,6 +73,7 @@ class GitPushService < BaseService
@project.update_merge_requests(params[:oldrev], params[:newrev], params[:ref], current_user)
EventCreateService.new.push(@project, current_user, build_push_data)
SystemHooksService.new.execute_hooks(build_push_data_system_hook.dup, :push_hooks)
@project.execute_hooks(build_push_data.dup, :push_hooks)
@project.execute_services(build_push_data.dup, :push_hooks)
CreateCommitBuildsService.new.execute(@project, current_user, build_push_data)
......@@ -138,6 +139,11 @@ class GitPushService < BaseService
build(@project, current_user, params[:oldrev], params[:newrev], params[:ref], push_commits)
end
def build_push_data_system_hook
@push_data_system ||= Gitlab::PushDataBuilder.
build(@project, current_user, params[:oldrev], params[:newrev], params[:ref], [])
end
def push_to_existing_branch?
# Return if this is not a push to a branch (e.g. new commits)
Gitlab::Git.branch_ref?(params[:ref]) && !Gitlab::Git.blank_ref?(params[:oldrev])
......
class GitTagPushService
attr_accessor :project, :user, :push_data
class GitTagPushService < BaseService
attr_accessor :push_data
def execute(project, user, oldrev, newrev, ref)
def execute
project.repository.before_push_tag
@project, @user = project, user
@push_data = build_push_data(oldrev, newrev, ref)
@push_data = build_push_data
EventCreateService.new.push(project, user, @push_data)
EventCreateService.new.push(project, current_user, @push_data)
SystemHooksService.new.execute_hooks(build_system_push_data.dup, :tag_push_hooks)
project.execute_hooks(@push_data.dup, :tag_push_hooks)
project.execute_services(@push_data.dup, :tag_push_hooks)
CreateCommitBuildsService.new.execute(project, @user, @push_data)
CreateCommitBuildsService.new.execute(project, current_user, @push_data)
ProjectCacheWorker.perform_async(project.id)
true
......@@ -18,14 +18,14 @@ class GitTagPushService
private
def build_push_data(oldrev, newrev, ref)
def build_push_data
commits = []
message = nil
if !Gitlab::Git.blank_ref?(newrev)
tag_name = Gitlab::Git.ref_name(ref)
if !Gitlab::Git.blank_ref?(params[:newrev])
tag_name = Gitlab::Git.ref_name(params[:ref])
tag = project.repository.find_tag(tag_name)
if tag && tag.target == newrev
if tag && tag.target == params[:newrev]
commit = project.commit(tag.target)
commits = [commit].compact
message = tag.message
......@@ -33,6 +33,11 @@ class GitTagPushService
end
Gitlab::PushDataBuilder.
build(project, user, oldrev, newrev, ref, commits, message)
build(project, current_user, params[:oldrev], params[:newrev], params[:ref], commits, message)
end
def build_system_push_data
Gitlab::PushDataBuilder.
build(project, current_user, params[:oldrev], params[:newrev], params[:ref], [], '')
end
end
......@@ -3,17 +3,13 @@ class SystemHooksService
execute_hooks(build_event_data(model, event))
end
private
def execute_hooks(data)
SystemHook.all.each do |sh|
async_execute_hook(sh, data, 'system_hooks')
def execute_hooks(data, hooks_scope = :all)
SystemHook.send(hooks_scope).each do |hook|
hook.async_execute(data, 'system_hooks')
end
end
def async_execute_hook(hook, data, hook_name)
Sidekiq::Client.enqueue(SystemHookWorker, hook.id, data, hook_name)
end
private
def build_event_data(model, event)
data = {
......
module WikiPages
class BaseService < ::BaseService
def hook_data(page, action)
hook_data = {
object_kind: page.class.name.underscore,
user: current_user.hook_attrs,
project: @project.hook_attrs,
object_attributes: page.hook_attrs,
# DEPRECATED
repository: @project.hook_attrs.slice(:name, :url, :description, :homepage)
}
page_url = Gitlab::UrlBuilder.build(page)
hook_data[:object_attributes].merge!(url: page_url, action: action)
hook_data
end
private
def execute_hooks(page, action = 'create')
page_data = hook_data(page, action)
@project.execute_hooks(page_data, :wiki_page_hooks)
@project.execute_services(page_data, :wiki_page_hooks)
end
end
end
module WikiPages
class CreateService < WikiPages::BaseService
def execute
page = WikiPage.new(@project.wiki)
if page.create(@params)
execute_hooks(page, 'create')
end
page
end
end
end
module WikiPages
class UpdateService < WikiPages::BaseService
def execute(page)
if page.update(@params[:content], @params[:format], @params[:message])
execute_hooks(page, 'update')
end
page
end
end
end
......@@ -155,7 +155,11 @@
= f.label :shared_runners_enabled do
= f.check_box :shared_runners_enabled
Enable shared runners for new projects
.form-group
= f.label :shared_runners_text, class: 'control-label col-sm-2'
.col-sm-10
= f.text_area :shared_runners_text, class: 'form-control', rows: 4
.help-block Markdown enabled
.form-group
= f.label :max_artifacts_size, 'Maximum artifacts size (MB)', class: 'control-label col-sm-2'
.col-sm-10
......@@ -214,6 +218,13 @@
.help-block
The sampling interval in seconds. Sampled data includes memory usage,
retained Ruby objects, file descriptors and so on.
.form-group
= f.label :metrics_packet_size, 'Metrics per packet', class: 'control-label col-sm-2'
.col-sm-10
= f.number_field :metrics_packet_size, class: 'form-control'
.help-block
The amount of points to store in a single UDP packet. More points
results in fewer but larger UDP packets being sent.
%fieldset
%legend Spam and Anti-bot Protection
......@@ -282,7 +293,7 @@
= f.check_box :repository_checks_enabled
Enable Repository Checks
.help-block
GitLab will periodically run
GitLab will periodically run
%a{ href: 'https://www.kernel.org/pub/software/scm/git/docs/git-fsck.html', target: 'blank' } 'git fsck'
in all project and wiki repositories to look for silent disk corruption issues.
.form-group
......@@ -290,7 +301,7 @@
= link_to 'Clear all repository checks', clear_repository_check_states_admin_application_settings_path, data: { confirm: 'This will clear repository check states for ALL projects in the database. This cannot be undone. Are you sure?' }, method: :put, class: "btn btn-sm btn-remove"
.help-block
If you got a lot of false alarms from repository checks you can choose to clear all repository check information from the database.
.form-actions
= f.submit 'Save', class: 'btn btn-save'
......@@ -16,6 +16,27 @@
= f.label :url, "URL:", class: 'control-label'
.col-sm-10
= f.text_field :url, class: "form-control"
.form-group
= f.label :url, "Trigger", class: 'control-label'
.col-sm-10.prepend-top-10
%div
System hook will be triggered on set of events like creating project
or adding ssh key. But you can also enable extra triggers like Push events.
%div.prepend-top-default
= f.check_box :push_events, class: 'pull-left'
.prepend-left-20
= f.label :push_events, class: 'list-label' do
%strong Push events
%p.light
This url will be triggered by a push to the repository
%div
= f.check_box :tag_push_events, class: 'pull-left'
.prepend-left-20
= f.label :tag_push_events, class: 'list-label' do
%strong Tag push events
%p.light
This url will be triggered when a new tag is pushed to the repository
.form-group
= f.label :enable_ssl_verification, "SSL verification", class: 'control-label checkbox'
.col-sm-10
......@@ -31,13 +52,16 @@
.panel.panel-default
.panel-heading
System hooks (#{@hooks.count})
%ul.well-list
%ul.content-list
- @hooks.each do |hook|
%li
.list-item-name
%strong= hook.url
%p SSL Verification: #{hook.enable_ssl_verification ? "enabled" : "disabled"}
.pull-right
.controls
= link_to 'Test Hook', admin_hook_test_path(hook), class: "btn btn-sm"
= link_to 'Remove', admin_hook_path(hook), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-remove btn-sm"
.monospace= hook.url
%div
- %w(push_events tag_push_events issues_events note_events merge_requests_events build_events).each do |trigger|
- if hook.send(trigger)
%span.label.label-gray= trigger.titleize
%span.label.label-gray SSL Verification: #{hook.enable_ssl_verification ? "enabled" : "disabled"}
......@@ -26,7 +26,7 @@
- @already_added_projects.each do |project|
%tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"}
%td
= link_to project.import_source, "https://github.com/#{project.import_source}", target: "_blank"
= github_project_link(project.import_source)
%td
= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
%td.job-status
......@@ -43,7 +43,7 @@
- @repos.each do |repo|
%tr{id: "repo_#{repo.id}"}
%td
= link_to repo.full_name, "https://github.com/#{repo.full_name}", target: "_blank"
= github_project_link(repo.full_name)
%td.import-target
= repo.full_name
%td.import-actions.job-status
......
%p
= "Merge Request ##{@merge_request.iid} was closed by #{@updated_by.name}"
= "Merge Request #{@merge_request.to_reference} was closed by #{@updated_by.name}"
= "Merge Request ##{@merge_request.iid} was closed by #{@updated_by.name}"
= "Merge Request #{@merge_request.to_reference} was closed by #{@updated_by.name}"
Merge Request url: #{namespace_project_merge_request_url(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request)}
......
%p
= "Merge Request ##{@merge_request.iid} was #{@mr_status} by #{@updated_by.name}"
= "Merge Request #{@merge_request.to_reference} was #{@mr_status} by #{@updated_by.name}"
= "Merge Request ##{@merge_request.iid} was #{@mr_status} by #{@updated_by.name}"
= "Merge Request #{@merge_request.to_reference} was #{@mr_status} by #{@updated_by.name}"
Merge Request url: #{namespace_project_merge_request_url(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request)}
......
%p
= "Merge Request ##{@merge_request.iid} was merged"
= "Merge Request #{@merge_request.to_reference} was merged"
= "Merge Request ##{@merge_request.iid} was merged"
= "Merge Request #{@merge_request.to_reference} was merged"
Merge Request url: #{namespace_project_merge_request_url(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request)}
......
New Merge Request #<%= @merge_request.iid %>
New Merge Request <%= @merge_request.to_reference %>
<%= url_for(namespace_project_merge_request_url(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request)) %>
......
New comment for Merge Request <%= @merge_request.iid %>
New comment for Merge Request <%= @merge_request.to_reference %>
<%= url_for(namespace_project_merge_request_url(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request, anchor: "note_#{@note.id}")) %>
......
......@@ -9,7 +9,7 @@
- else
.gray-content-block.second-block.center
%h3.page-title
This project does not have README yet
This project does not have a README yet
- if can?(current_user, :push_code, @project)
%p
A
......@@ -18,5 +18,5 @@
distributed with computer software, forming part of its documentation.
%p
We recommend you to
= link_to "add README", new_readme_path, class: 'underlined-link'
= link_to "add a README", new_readme_path, class: 'underlined-link'
file to the repository and GitLab will render it here instead of this message.
......@@ -13,7 +13,11 @@
required: true, class: 'form-control new-file-name'
.pull-right
= select_tag :encoding, options_for_select([ "base64", "text" ], "text"), class: 'select2'
.license-selector.js-license-selector.hide
= select_tag :license_type, grouped_options_for_select(licenses_for_select, @project.repository.license_key), include_blank: true, class: 'select2 license-select', data: {placeholder: 'Choose a license template', project: @project.name, fullname: @project.namespace.human_name}
.encoding-selector
= select_tag :encoding, options_for_select([ "base64", "text" ], "text"), class: 'select2'
.file-content.code
%pre.js-edit-mode-pane#editor #{params[:content] || local_assigns[:blob_data]}
......
......@@ -14,5 +14,5 @@
cancel_path: namespace_project_tree_path(@project.namespace, @project, @id)
:javascript
blob = new NewBlob(gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}", null)
blob = new EditBlob(gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}")
new NewCommitForm($('.js-new-blob-form'))
......@@ -10,7 +10,7 @@
- merge_request = @build.merge_request
- if merge_request
via
= link_to "merge request ##{merge_request.iid}", merge_request_path(merge_request)
= link_to "merge request #{merge_request.to_reference}", merge_request_path(merge_request)
#up-build-trace
- builds = @build.commit.matrix_builds(@build)
......@@ -110,7 +110,7 @@
= icon('folder-open')
Browse
.build-widget
.build-widget.build-controls
%h4.title
Build ##{@build.id}
- if can?(current_user, :update_build, @project)
......@@ -127,6 +127,9 @@
data: { confirm: 'Are you sure you want to erase this build?' } do
= icon('eraser')
Erase
- if @build.has_trace?
= link_to 'Raw', raw_namespace_project_build_path(@project.namespace, @project, @build),
class: 'btn btn-sm btn-success', target: '_blank'
.clearfix
- if @build.duration
......
#modal-revert-commit.modal
- case type.to_s
- when 'revert'
- label = 'Revert'
- target_label = 'Revert in branch'
- when 'cherry-pick'
- label = 'Cherry-pick'
- target_label = 'Pick into branch'
.modal{id: "modal-#{type}-commit"}
.modal-dialog
.modal-content
.modal-header
%a.close{href: "#", "data-dismiss" => "modal"} ×
%h3.page-title== Revert this #{revert_commit_type(commit)}
%h3.page-title== #{label} this #{commit.change_type_title}
.modal-body
= form_tag revert_namespace_project_commit_path(@project.namespace, @project, commit.id), method: :post, remote: false, class: 'form-horizontal js-create-dir-form js-requires-input' do
= form_tag send("#{type.underscore}_namespace_project_commit_path", @project.namespace, @project, commit.id), method: :post, remote: false, class: 'form-horizontal js-#{type}-form js-requires-input' do
.form-group.branch
= label_tag 'target_branch', 'Revert in branch', class: 'control-label'
= label_tag 'target_branch', target_label, class: 'control-label'
.col-sm-10
= select_tag "target_branch", grouped_options_refs, class: "select2 select2-sm js-target-branch"
- if can?(current_user, :push_code, @project)
......@@ -20,7 +28,7 @@
- else
= hidden_field_tag 'create_merge_request', 1
.form-actions
= submit_tag "Revert", class: 'btn btn-create'
= submit_tag label, class: 'btn btn-create'
= link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
- unless can?(current_user, :push_code, @project)
......@@ -28,4 +36,4 @@
= commit_in_fork_help
:javascript
new NewCommitForm($('.js-create-dir-form'))
new NewCommitForm($('.js-#{type}-form'))
......@@ -18,6 +18,7 @@
Browse Files
- unless @commit.has_been_reverted?(current_user)
= revert_commit_link(@commit, namespace_project_commit_path(@project.namespace, @project, @commit.id))
= cherry_pick_commit_link(@commit, namespace_project_commit_path(@project.namespace, @project, @commit.id))
%div
%p
......
......@@ -13,4 +13,5 @@
diff_refs: @diff_refs
= render "projects/notes/notes_with_form"
- if can_collaborate_with_project?
= render "projects/commit/revert", commit: @commit, title: @commit.title
- %w(revert cherry-pick).each do |type|
= render "projects/commit/change", type: type, commit: @commit, title: @commit.title
......@@ -31,5 +31,5 @@
by
= commit_author_link(commit, avatar: true, size: 24)
.committed_ago
#{time_ago_with_tooltip(commit.committed_date, skip_js: true)} &nbsp;
#{time_ago_with_tooltip(commit.committed_date)} &nbsp;
= link_to_browse_code(project, commit)
......@@ -14,8 +14,10 @@
%p
If you already have files you can push them using command line instructions below.
%p
Otherwise you can start with
= link_to "adding README", new_readme_path, class: 'underlined-link'
Otherwise you can start with adding a
= link_to "README", new_readme_path, class: 'underlined-link'
or a
= link_to "LICENSE", add_special_file_path(@project, file_name: 'LICENSE'), class: 'underlined-link'
file to this project.
- if can?(current_user, :push_code, @project)
......
......@@ -74,16 +74,15 @@
.panel.panel-default
.panel-heading
Webhooks (#{@hooks.count})
%ul.well-list
%ul.content-list
- @hooks.each do |hook|
%li
.pull-right
.controls
= link_to 'Test Hook', test_namespace_project_hook_path(@project.namespace, @project, hook), class: "btn btn-sm btn-grouped"
= link_to 'Remove', namespace_project_hook_path(@project.namespace, @project, hook), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove btn-sm btn-grouped"
.clearfix
%span.monospace= hook.url
%p
.monospace= hook.url
%div
- %w(push_events tag_push_events issues_events note_events merge_requests_events build_events).each do |trigger|
- if hook.send(trigger)
%span.label.label-gray= trigger.titleize
SSL Verification: #{hook.enable_ssl_verification ? "enabled" : "disabled"}
%span.label.label-gray SSL Verification: #{hook.enable_ssl_verification ? "enabled" : "disabled"}
......@@ -86,8 +86,10 @@
= spinner
= render 'shared/issuable/sidebar', issuable: @merge_request
- if @merge_request.can_be_reverted?
= render "projects/commit/revert", commit: @merge_request.merge_commit, title: @merge_request.title
- if @merge_request.can_be_reverted?(current_user)
= render "projects/commit/change", type: 'revert', commit: @merge_request.merge_commit, title: @merge_request.title
- if @merge_request.can_be_cherry_picked?
= render "projects/commit/change", type: 'cherry-pick', commit: @merge_request.merge_commit, title: @merge_request.title
:javascript
var merge_request;
......
- page_title "Edit", "#{@merge_request.title} (##{@merge_request.iid})", "Merge Requests"
- page_title "Edit", "#{@merge_request.title} (#{@merge_request.to_reference}", "Merge Requests"
= render "header_title"
%h3.page-title
Edit Merge Request ##{@merge_request.iid}
Edit Merge Request #{@merge_request.to_reference}
%hr
= render 'form'
- page_title "#{@merge_request.title} (##{@merge_request.iid})", "Merge Requests"
- page_title "#{@merge_request.title} (#{merge_request.to_reference}", "Merge Requests"
= render "header_title"
.merge-request
......
......@@ -44,3 +44,8 @@
$('.remove_source_branch_in_progress').hide();
$('.remove_source_branch_widget.failed').show();
});
- else
%p
The changes were merged into
#{link_to @merge_request.target_branch, namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch), class: "label-branch"}.
= render 'projects/merge_requests/widget/merged_buttons'
- source_branch_exists = local_assigns.fetch(:source_branch_exists, false)
- mr_can_be_reverted = @merge_request.can_be_reverted?
- can_remove_source_branch = local_assigns.fetch(:source_branch_exists, false) && @merge_request.can_remove_source_branch?(current_user)
- mr_can_be_reverted = @merge_request.can_be_reverted?(current_user)
- mr_can_be_cherry_picked = @merge_request.can_be_cherry_picked?
- if source_branch_exists || mr_can_be_reverted
- if can_remove_source_branch || mr_can_be_reverted || mr_can_be_cherry_picked
.btn-group
- if source_branch_exists
- if can_remove_source_branch
= link_to namespace_project_branch_path(@merge_request.source_project.namespace, @merge_request.source_project, @merge_request.source_branch), remote: true, method: :delete, class: "btn btn-default btn-grouped btn-sm remove_source_branch" do
= icon('trash-o')
Remove Source Branch
- if mr_can_be_reverted
= revert_commit_link(@merge_request.merge_commit, namespace_project_merge_request_path(@project.namespace, @project, @merge_request), btn_class: 'sm')
- if mr_can_be_cherry_picked
= cherry_pick_commit_link(@merge_request.merge_commit, namespace_project_merge_request_path(@project.namespace, @project, @merge_request), btn_class: 'sm')
......@@ -8,7 +8,9 @@
.note-header
= link_to_member(note.project, note.author, avatar: false)
.inline.note-headline-light
= "#{note.author.to_reference} commented"
= "#{note.author.to_reference}"
- if !note.system
commented
%a{ href: "##{dom_id(note)}" }
= time_ago_with_tooltip(note.created_at, placement: 'bottom', html_class: 'note-created-ago')
.note-actions
......
%h3 Shared runners
.bs-callout.bs-callout-warning
GitLab Runners do not offer secure isolation between projects that they do builds for. You are TRUSTING all GitLab users who can push code to project A, B or C to run shell scripts on the machine hosting runner X.
.bs-callout.bs-callout-warning.shared-runners-description
- if shared_runners_text.present?
= markdown(shared_runners_text, pipeline: 'plain_markdown')
- else
Shared runners execute code of different projects on the same Runner unless you configure GitLab Runner Autoscale with MaxBuilds 1 (which it is on GitLab.com).
%hr
- if @project.shared_runners_enabled?
= link_to toggle_shared_runners_namespace_project_runners_path(@project.namespace, @project), class: 'btn btn-warning', method: :post do
......
......@@ -36,9 +36,9 @@
%li
= link_to 'Changelog', changelog_path(@project)
- if @repository.license
- if @repository.license_blob
%li
= link_to 'License', license_path(@project)
= link_to license_short_name(@project), license_path(@project)
- if @repository.contribution_guide
%li
......@@ -47,15 +47,15 @@
- if current_user && can_push_branch?(@project, @project.default_branch)
- unless @repository.changelog
%li.missing
= link_to add_changelog_path(@project) do
= link_to add_special_file_path(@project, file_name: 'CHANGELOG') do
Add Changelog
- unless @repository.license
- unless @repository.license_blob
%li.missing
= link_to add_license_path(@project) do
= link_to add_special_file_path(@project, file_name: 'LICENSE') do
Add License
- unless @repository.contribution_guide
%li.missing
= link_to add_contribution_guide_path(@project) do
= link_to add_special_file_path(@project, file_name: 'CONTRIBUTING.md', commit_message: 'Add contribution guide') do
Add Contribution guide
- if @repository.commit
......
......@@ -3,3 +3,6 @@
%p
= link_to "See the affected projects in the GitLab admin panel", admin_namespaces_projects_url(last_repository_check_failed: 1)
%p
You are receiving this message because you are a GitLab administrator for #{Gitlab.config.gitlab.url}.
#{@message}.
\
View details: #{admin_namespaces_projects_url(last_repository_check_failed: 1)}
You are receiving this message because you are a GitLab administrator
for #{Gitlab.config.gitlab.url}.
......@@ -2,7 +2,7 @@
%h4
= link_to [merge_request.target_project.namespace.becomes(Namespace), merge_request.target_project, merge_request] do
%span.term.str-truncated= merge_request.title
.pull-right ##{merge_request.iid}
.pull-right #{merge_request.to_reference}
- if merge_request.description.present?
.description.term
= preserve do
......
......@@ -62,6 +62,14 @@
%strong Build events
%p.light
This url will be triggered when a build status changes
- if @service.supported_events.include?("wiki_page")
%div
= form.check_box :wiki_page_events, class: 'pull-left'
.prepend-left-20
= form.label :wiki_page_events, class: 'list-label' do
%strong Wiki Page events
%p.light
This url will be triggered when a wiki page is created/updated
- @service.fields.each do |field|
......
......@@ -75,8 +75,9 @@
= f.label :label_ids, "Labels", class: 'control-label'
.col-sm-10{ class: ('issuable-form-padding-top' if !has_labels) }
- if has_labels
= f.collection_select :label_ids, issuable.project.labels.all, :id, :name,
{ selected: issuable.label_ids }, multiple: true, class: 'select2', data: { placeholder: "Select labels" }
.issuable-form-select-holder
= f.collection_select :label_ids, issuable.project.labels.all, :id, :name,
{ selected: issuable.label_ids }, multiple: true, class: 'select2', data: { placeholder: "Select labels" }
- else
%span.light No labels yet.
&nbsp;
......@@ -88,9 +89,10 @@
.form-group
= label_tag :move_to_project_id, 'Move', class: 'control-label'
.col-sm-10
- projects = project_options(issuable, current_user, ability: :admin_issue)
= select_tag(:move_to_project_id, projects, include_blank: true,
class: 'select2', data: { placeholder: 'Select project' })
.issuable-form-select-holder
- projects = project_options(issuable, current_user, ability: :admin_issue)
= select_tag(:move_to_project_id, projects, include_blank: true,
class: 'select2', data: { placeholder: 'Select project' })
&nbsp;
%span{ data: { toggle: 'tooltip', placement: 'auto top' }, style: 'cursor: default',
title: 'Moving an issue will copy the discussion to a different project and close it here. All participants will be notified of the new location.' }
......@@ -102,13 +104,15 @@
.form-group
= f.label :source_branch, class: 'control-label'
.col-sm-10
= f.select(:source_branch, [@merge_request.source_branch], { }, { class: 'source_branch select2 span2', disabled: true })
.issuable-form-select-holder
= f.select(:source_branch, [@merge_request.source_branch], { }, { class: 'source_branch select2 span2', disabled: true })
.form-group
= f.label :target_branch, class: 'control-label'
.col-sm-10
= f.select(:target_branch, @merge_request.target_branches, { include_blank: true }, { class: 'target_branch select2 span2', disabled: @merge_request.new_record?, data: {placeholder: "Select branch"} })
.issuable-form-select-holder
= f.select(:target_branch, @merge_request.target_branches, { include_blank: true }, { class: 'target_branch select2 span2', disabled: @merge_request.new_record?, data: {placeholder: "Select branch"} })
- if @merge_request.new_record?
%p.help-block
&nbsp;
= link_to 'Change branches', mr_change_branches_path(@merge_request)
- is_footer = !(issuable.is_a?(MergeRequest) && issuable.new_record?)
......
......@@ -118,6 +118,7 @@
Manage labels
- else
View labels
= dropdown_loading
= render "shared/issuable/participants", participants: issuable.participants(current_user)
- if current_user
......@@ -150,6 +151,6 @@
:javascript
new MilestoneSelect('{"namespace":"#{@project.namespace.path}","path":"#{@project.path}"}');
new LabelsSelect();
new IssuableContext('#{current_user.to_json(only: [:username, :id, :name])}');
new IssuableContext('#{escape_javascript(current_user.to_json(only: [:username, :id, :name]))}');
new Subscription('.subscription')
new Sidebar();
......@@ -23,5 +23,5 @@
- if assignee
= link_to polymorphic_path(base_url_args, { milestone_title: @milestone.title, assignee_id: issuable.assignee_id, state: 'all' }),
class: 'has-tooltip', data: { 'original-title' => "Assigned to #{sanitize(assignee.name)}", container: 'body' } do
class: 'has-tooltip', title: "Assigned to #{assignee.name}", data: { container: 'body' } do
- image_tag(avatar_icon(issuable.assignee, 16), class: "avatar s16", alt: '')
......@@ -6,8 +6,6 @@
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, user_url(@user, format: :atom), title: "#{@user.name} activity")
= render 'shared/show_aside'
.user-profile
.cover-block
.cover-controls
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册