Commit 706255b9 authored by Jeroen Nijhof's avatar Jeroen Nijhof

Merge gitlab.com:gitlab-org/gitlab-ce

parents e0436005 f394ccd5
......@@ -2,6 +2,7 @@ Please view this file on the master branch, on stable branches it's out of date.
v 8.4.0 (unreleased)
- Add housekeeping function to project settings page
- The default GitLab logo now acts as a loading indicator
- Accept 2xx status codes for successful Web hook triggers (Stan Hu)
- Fix missing date of month in network graph when commits span a month (Stan Hu)
- Expire view caches when application settings change (e.g. Gravatar disabled) (Stan Hu)
......@@ -22,6 +23,7 @@ v 8.4.0 (unreleased)
- Add system hook messages for project rename and transfer (Steve Norman)
- Fix version check image in Safari
- Show 'All' tab by default in the builds page
- Add Open Graph and Twitter Card data to all pages
- Fix API project lookups when querying with a namespace with dots (Stan Hu)
- Update version check images to use SVG
- Validate README format before displaying
......@@ -32,9 +34,13 @@ v 8.4.0 (unreleased)
- API: Add support for deleting a tag via the API (Robert Schilling)
v 8.3.3 (unreleased)
- Preserve CE behavior with JIRA integration by only calling API if URL is set
- Get "Merge when build succeeds" to work when commits were pushed to MR target branch while builds were running
- Suppress e-mails on failed builds if allow_failure is set (Stan Hu)
- Fix project transfer e-mail sending incorrect paths in e-mail notification (Stan Hu)
- Enable "Add key" button when user fills in a proper key (Stan Hu)
- Fix error in processing reply-by-email messages (Jason Lee)
- Fix Error 500 when visiting build page of project with nil runners_token (Stan Hu)
v 8.3.2
- Change single user API endpoint to return more detailed data (Michael Potthoff)
......
......@@ -66,7 +66,7 @@ class @DropzoneInput
success: (header, response) ->
child = $(dropzone[0]).children("textarea")
$(child).val $(child).val() + formatLink(response.link) + "\n"
$(child).val $(child).val() + response.link.markdown + "\n"
return
error: (temp, errorMessage) ->
......@@ -99,11 +99,6 @@ class @DropzoneInput
child = $(dropzone[0]).children("textarea")
formatLink = (link) ->
text = "[#{link.alt}](#{link.url})"
text = "!#{text}" if link.is_image
text
handlePaste = (event) ->
pasteEvent = event.originalEvent
if pasteEvent.clipboardData and pasteEvent.clipboardData.items
......@@ -162,7 +157,7 @@ class @DropzoneInput
closeAlertMessage()
success: (e, textStatus, response) ->
insertToTextArea(filename, formatLink(response.responseJSON.link))
insertToTextArea(filename, response.responseJSON.link.markdown)
error: (response) ->
showError(response.responseJSON.message)
......@@ -202,8 +197,3 @@ class @DropzoneInput
e.preventDefault()
$(@).closest('.gfm-form').find('.div-dropzone').click()
return
formatLink: (link) ->
text = "[#{link.alt}](#{link.url})"
text = "!#{text}" if link.is_image
text
......@@ -206,7 +206,7 @@ module Ci
def trace
trace = raw_trace
if project && trace.present?
if project && trace.present? && project.runners_token.present?
trace.gsub(project.runners_token, 'xxxxxx')
else
trace
......
......@@ -50,6 +50,7 @@ class Project < ActiveRecord::Base
include Sortable
include AfterCommitQueue
include CaseSensitivity
include TokenAuthenticatable
extend Gitlab::ConfigHelper
......@@ -193,10 +194,8 @@ class Project < ActiveRecord::Base
if: ->(project) { project.avatar.present? && project.avatar_changed? }
validates :avatar, file_size: { maximum: 200.kilobytes.to_i }
before_validation :set_runners_token_token
def set_runners_token_token
self.runners_token = SecureRandom.hex(15) if self.runners_token.blank?
end
add_authentication_token_field :runners_token
before_save :ensure_runners_token
mount_uploader :avatar, AvatarUploader
......@@ -900,4 +899,8 @@ class Project < ActiveRecord::Base
return true unless forked?
Gitlab::VisibilityLevel.allowed_fork_levels(forked_from_project.visibility_level).include?(level.to_i)
end
def runners_token
ensure_runners_token!
end
end
......@@ -73,12 +73,16 @@ class BuildsEmailService < Service
when 'success'
!notify_only_broken_builds?
when 'failed'
true
!allow_failure?(data)
else
false
end
end
def allow_failure?(data)
data[:build_allow_failure] == true
end
def all_recipients(data)
all_recipients = recipients.split(',')
......
......@@ -116,6 +116,7 @@ class JiraService < IssueTrackerService
end
def test_settings
return unless api_url.present?
result = JiraService.get(
jira_api_test_url,
headers: {
......@@ -213,6 +214,7 @@ class JiraService < IssueTrackerService
end
def send_message(url, message)
return unless api_url.present?
result = JiraService.post(
url,
body: message,
......@@ -238,6 +240,7 @@ class JiraService < IssueTrackerService
end
def existing_comment?(issue_name, new_comment)
return unless api_url.present?
result = JiraService.get(
comment_url(issue_name),
headers: {
......
......@@ -16,13 +16,7 @@ module Projects
uploader.download!(@url)
uploader.store!
filename = uploader.image? ? uploader.file.basename : uploader.file.filename
{
'alt' => filename,
'url' => uploader.secure_url,
'is_image' => uploader.image?
}
uploader.to_h
end
private
......
......@@ -10,13 +10,7 @@ module Projects
uploader = FileUploader.new(@project)
uploader.store!(@file)
filename = uploader.image? ? uploader.file.basename : uploader.file.filename
{
alt: filename,
url: uploader.secure_url,
is_image: uploader.image?
}
uploader.to_h
end
private
......
......@@ -30,4 +30,19 @@ class FileUploader < CarrierWave::Uploader::Base
def secure_url
File.join("/uploads", @secret, file.filename)
end
def to_h
filename = image? ? self.file.basename : self.file.filename
escaped_filename = filename.gsub("]", "\\]")
markdown = "[#{escaped_filename}](#{self.secure_url})"
markdown.prepend("!") if image?
{
alt: filename,
url: self.secure_url,
is_image: image?,
markdown: markdown
}
end
end
......@@ -17,6 +17,7 @@ class NamespaceValidator < ActiveModel::EachValidator
hooks
issues
merge_requests
new
notes
profile
projects
......
......@@ -23,6 +23,9 @@
data: { confirm: "USER #{user.name} WILL BE REMOVED! Are you sure?" }, remote: true, method: :delete, class: "btn btn-xs btn-remove js-remove-tr"
%td
- if user
- if user && !user.blocked?
= link_to 'Block user', block_admin_user_path(user), data: {confirm: 'USER WILL BE BLOCKED! Are you sure?'}, method: :put, class: "btn btn-xs"
- else
.btn.btn-xs.disabled
Already Blocked
= link_to 'Remove report', [:admin, abuse_report], remote: true, method: :delete, class: "btn btn-xs btn-close js-remove-tr"
......@@ -47,14 +47,14 @@
= f.label :issues_events, class: 'list-label' do
%strong Issues events
%p.light
This url will be triggered when an issue is created
This url will be triggered when an issue is created/updated/merged
%div
= f.check_box :merge_requests_events, class: 'pull-left'
.prepend-left-20
= f.label :merge_requests_events, class: 'list-label' do
%strong Merge Request events
%p.light
This url will be triggered when a merge request is created
This url will be triggered when a merge request is created/updated/merged
%div
= f.check_box :build_events, class: 'pull-left'
.prepend-left-20
......
......@@ -50,7 +50,7 @@
= form.label :issues_events, class: 'list-label' do
%strong Issues events
%p.light
This url will be triggered when an issue is created
This url will be triggered when an issue is created/updated/merged
- if @service.supported_events.include?("merge_request")
%div
= form.check_box :merge_requests_events, class: 'pull-left'
......@@ -58,7 +58,7 @@
= form.label :merge_requests_events, class: 'list-label' do
%strong Merge Request events
%p.light
This url will be triggered when a merge request is created
This url will be triggered when a merge request is created/updated/merged
- if @service.supported_events.include?("build")
%div
= form.check_box :build_events, class: 'pull-left'
......
......@@ -74,7 +74,7 @@
.title
.cross-project-reference
%span#cross-project-reference
References:
Reference:
%a{href: '#', title:project_ref}
= project_ref
= clipboard_button(clipboard_target: 'span#cross-project-reference')
......
......@@ -482,6 +482,34 @@ Parameters:
- `id` (required) - The ID of a project
## Uploads
### Upload a file
Uploads a file to the specified project to be used in an issue or merge request description, or a comment.
```
POST /projects/:id/uploads
```
Parameters:
- `id` (required) - The ID of the project
- `file` (required) - The file to be uploaded
```json
{
"alt": "dk",
"url": "/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png",
"is_image": true,
"markdown": "![dk](/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png)"
}
```
**Note**: The returned `url` is relative to the project path.
In Markdown contexts, the link is automatically expanded when the format in `markdown` is used.
## Team members
### List project team members
......
......@@ -552,6 +552,6 @@ Apart from the always supported markdown style there are other rich text files t
If you see this message when attempting to clone a repository hosted by GitLab,
this is likely due to an outdated Nginx or Apache configuration, or a missing or
misconfigured `gitlab-git-http-server` instance. Double-check that you've
[installed Go](#3-go), [installed gitlab-git-http-server](#install-gitlab-git-http-server),
misconfigured gitlab-workhorse instance. Double-check that you've
[installed Go](#3-go), [installed gitlab-workhorse](#install-gitlab-workhorse),
and correctly [configured Nginx](#site-configuration).
# Project users
You can manage the groups and users and their access levels in all of your projects. You can also personalize the access level you give each user, per project.
You can manage the groups and users and their access levels in all of your
projects. You can also personalize the access level you give each user,
per-project.
Here's how to add or import users to your projects.
You should have 'master' or 'owner' permissions to add or import a new user
You should have `master` or `owner` permissions to add or import a new user
to your project.
To add or import a user, go to your project and click on "Members" on the left side of your screen:
The first step to add or import a user, go to your project and click on
**Members** on the left side of your screen.
![Members](img/add_user_members_menu.png)
---
## Add a user
Right next to **People**, start typing the name or username of the user you
want to add.
![Search for people](img/add_user_search_people.png)
---
Select the user and the [permission level](../../permissions/permissions.md)
that you'd like to give the user. Note that you can select more than one user.
![Give user permissions](img/add_user_give_permissions.png)
---
Once done, hit **Add users to project** and they will be immediately added to
your project with the permissions you gave them above.
![List members](img/add_user_list_members.png)
---
From there on, you can either remove an existing user or change their access
level to the project.
## Import users from another project
You can import another project's users in your own project by hitting the
**Import members** button on the upper right corner of the **Members** menu.
In the dropdown menu, you can see only the projects you are Master on.
![Import members from another project](img/add_user_import_members_from_another_project.png)
---
Select the one you want and hit **Import project members**. A flash message
notifying you that the import was successful will appear, and the new members
are now in the project's members list. Notice that the permissions that they
had on the project you imported from are retained.
![Members list of new members](img/add_user_imported_members.png)
---
## Invite people using their e-mail address
If a user you want to give access to doesn't have an account on your GitLab
instance, you can invite them just by typing their e-mail address in the
user search field.
![Invite user by mail](img/add_user_email_search.png)
---
![Members](images/members.png)
As you can imagine, you can mix inviting multiple people and adding existing
GitLab users to the project.
Select "Add members" or "Import members" on the right side of your screen:
![Invite user by mail ready to submit](img/add_user_email_ready.png)
![Add or Import](images/add-members.png)
---
If you are adding a user, select the user and the [permission level](doc/permissions/permissions.md) that you'd like to
give the user:
Once done, hit **Add users to project** and watch that there is a new member
with the e-mail address we used above. From there on, you can resend the
invitation, change their access level or even delete them.
![Add or Import](images/new-member.png)
![Invite user members list](img/add_user_email_accept.png)
If you are importing a user, follow the steps to select the project where you'd like to import the user from:
---
![Add or Import](images/select-project.png)
Once the user accepts the invitation, they will be prompted to create a new
GitLab account using the same e-mail address the invitation was sent to.
......@@ -278,6 +278,16 @@ module API
user_project.forked_project_link.destroy
end
end
# Upload a file
#
# Parameters:
# id: (required) - The ID of the project
# file: (required) - The file to be uploaded
post ":id/uploads" do
::Projects::UploadService.new(user_project, params[:file]).execute
end
# search for projects current_user has access to
#
# Parameters:
......
......@@ -23,6 +23,7 @@ module Gitlab
build_started_at: build.started_at,
build_finished_at: build.finished_at,
build_duration: build.duration,
build_allow_failure: build.allow_failure,
# TODO: do we still need it?
project_id: project.id,
......
......@@ -82,10 +82,7 @@ module Gitlab
attachments = Email::AttachmentUploader.new(message).execute(sent_notification.project)
attachments.each do |link|
text = "[#{link[:alt]}](#{link[:url]})"
text.prepend("!") if link[:is_image]
reply << "\n\n#{text}"
reply << "\n\n#{link[:markdown]}"
end
reply
......
......@@ -232,9 +232,7 @@ module Gitlab
return nil if res.nil?
text = "[#{res['alt']}](#{res['url']})"
text = "!#{text}" if res['is_image']
text
res[:markdown]
end
def build_attachment_url(rel_url)
......
......@@ -7,4 +7,10 @@ describe Banzai::Filter::TaskListFilter, lib: true do
exp = act = %(<ul><li>Item</li></ul>)
expect(filter(act).to_html).to eq exp
end
it 'applies `task-list` to single-item task lists' do
act = filter('<ul><li>[ ] Task 1</li></ul>')
expect(act.to_html).to start_with '<ul class="task-list">'
end
end
......@@ -14,6 +14,7 @@ describe 'Gitlab::BuildDataBuilder' do
it { expect(data[:tag]).to eq(build.tag) }
it { expect(data[:build_id]).to eq(build.id) }
it { expect(data[:build_status]).to eq(build.status) }
it { expect(data[:build_allow_failure]).to eq(false) }
it { expect(data[:project_id]).to eq(build.project.id) }
it { expect(data[:project_name]).to eq(build.project.name_with_namespace) }
end
......
......@@ -107,13 +107,16 @@ describe Gitlab::Email::Receiver, lib: true do
end
context "when everything is fine" do
let(:markdown) { "![image](uploads/image.png)" }
before do
allow_any_instance_of(Gitlab::Email::AttachmentUploader).to receive(:execute).and_return(
[
{
url: "uploads/image.png",
is_image: true,
alt: "image"
alt: "image",
markdown: markdown
}
]
)
......@@ -132,7 +135,7 @@ describe Gitlab::Email::Receiver, lib: true do
note = noteable.notes.last
expect(note.note).to include("![image](uploads/image.png)")
expect(note.note).to include(markdown)
end
end
end
require 'spec_helper'
describe Ci::Build, models: true do
let(:build) { create(:ci_build) }
let(:test_trace) { 'This is a test' }
describe '#trace' do
it 'obfuscates project runners token' do
allow(build).to receive(:raw_trace).and_return("Test: #{build.project.runners_token}")
expect(build.trace).to eq("Test: xxxxxx")
end
it 'empty project runners token' do
allow(build).to receive(:raw_trace).and_return(test_trace)
# runners_token can't normally be set to nil
allow(build.project).to receive(:runners_token).and_return(nil)
expect(build.trace).to eq(test_trace)
end
end
end
require 'spec_helper'
describe BuildsEmailService do
let(:build) { create(:ci_build) }
let(:data) { Gitlab::BuildDataBuilder.build(build) }
let(:service) { BuildsEmailService.new }
describe :execute do
it "sends email" do
service.recipients = 'test@gitlab.com'
data[:build_status] = 'failed'
expect(BuildEmailWorker).to receive(:perform_async)
service.execute(data)
end
it "does not sends email with failed build and allowed_failure on" do
data[:build_status] = 'failed'
data[:build_allow_failure] = true
expect(BuildEmailWorker).not_to receive(:perform_async)
service.execute(data)
end
end
end
......@@ -353,6 +353,20 @@ describe API::API, api: true do
end
end
describe "POST /projects/:id/uploads" do
before { project }
it "uploads the file and returns its info" do
post api("/projects/#{project.id}/uploads", user), file: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")
expect(response.status).to be(201)
expect(json_response['alt']).to eq("dk")
expect(json_response['url']).to start_with("/uploads/")
expect(json_response['url']).to end_with("/dk.png")
expect(json_response['is_image']).to eq(true)
end
end
describe 'GET /projects/:id' do
before { project }
before { project_member }
......
......@@ -33,12 +33,12 @@ describe Projects::DownloadService, services: true do
@link_to_file = download_file(@project, url)
end
it { expect(@link_to_file).to have_key('alt') }
it { expect(@link_to_file).to have_key('url') }
it { expect(@link_to_file).to have_key('is_image') }
it { expect(@link_to_file['is_image']).to be true }
it { expect(@link_to_file['url']).to match('rails_sample.jpg') }
it { expect(@link_to_file['alt']).to eq('rails_sample') }
it { expect(@link_to_file).to have_key(:alt) }
it { expect(@link_to_file).to have_key(:url) }
it { expect(@link_to_file).to have_key(:is_image) }
it { expect(@link_to_file[:is_image]).to be true }
it { expect(@link_to_file[:url]).to match('rails_sample.jpg') }
it { expect(@link_to_file[:alt]).to eq('rails_sample') }
end
context 'a txt file' do
......@@ -47,12 +47,12 @@ describe Projects::DownloadService, services: true do
@link_to_file = download_file(@project, url)
end
it { expect(@link_to_file).to have_key('alt') }
it { expect(@link_to_file).to have_key('url') }
it { expect(@link_to_file).to have_key('is_image') }
it { expect(@link_to_file['is_image']).to be false }
it { expect(@link_to_file['url']).to match('doc_sample.txt') }
it { expect(@link_to_file['alt']).to eq('doc_sample.txt') }
it { expect(@link_to_file).to have_key(:alt) }
it { expect(@link_to_file).to have_key(:url) }
it { expect(@link_to_file).to have_key(:is_image) }
it { expect(@link_to_file[:is_image]).to be false }
it { expect(@link_to_file[:url]).to match('doc_sample.txt') }
it { expect(@link_to_file[:alt]).to eq('doc_sample.txt') }
end
end
end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment