GitLab steht Mittwoch, den 08. Juli, zwischen 09:00 und 13:00 Uhr aufgrund von Wartungsarbeiten nicht zur Verfügung.

Commit 0430c00c authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

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

parents fc9a2a3e b6454fef
......@@ -51,6 +51,7 @@ v 8.4.0 (unreleased)
- Add API support for managing build variables of project
- Allow broadcast messages to be edited
- Autosize Markdown textareas
- Import GitHub wiki into GitLab
v 8.3.4
- Use gitlab-workhorse 0.5.4 (fixes API routing bug)
......@@ -88,6 +89,7 @@ v 8.3.0
- Add open_issues_count to project API (Stan Hu)
- Expand character set of usernames created by Omniauth (Corey Hinshaw)
- Add button to automatically merge a merge request when the build succeeds (Zeger-Jan van de Weg)
- Add unsubscribe link in the email footer (Zeger-Jan van de Weg)
- Provide better diagnostic message upon project creation errors (Stan Hu)
- Bump devise to 3.5.3 to fix reset token expiring after account creation (Stan Hu)
- Remove api credentials from link to build_page
......
#= require latinise
class @Wikis
constructor: ->
$('.build-new-wiki').bind "click", (e) ->
$('[data-error~=slug]').addClass("hidden")
$('p.hint').show()
$('.build-new-wiki').bind 'click', (e) =>
$('[data-error~=slug]').addClass('hidden')
field = $('#new_wiki_path')
valid_slug_pattern = /^[\w\/-]+$/
slug = @slugify(field.val())
slug = field.val()
if slug.match valid_slug_pattern
if (slug.length > 0)
path = field.attr('data-wikis-path')
if(slug.length > 0)
location.href = path + "/" + slug
else
e.preventDefault()
$('p.hint').hide()
$('[data-error~=slug]').removeClass("hidden")
location.href = path + '/' + slug
dasherize: (value) ->
value.replace(/[_\s]+/g, '-')
slugify: (value) =>
@dasherize(value.trim().toLowerCase().latinise())
class SentNotificationsController < ApplicationController
skip_before_action :authenticate_user!
def unsubscribe
@sent_notification = SentNotification.for(params[:id])
return render_404 unless @sent_notification && @sent_notification.unsubscribable?
noteable = @sent_notification.noteable
noteable.unsubscribe(@sent_notification.recipient)
flash[:notice] = "You have been unsubscribed from this thread."
if current_user
case noteable
when Issue
redirect_to issue_path(noteable)
when MergeRequest
redirect_to merge_request_path(noteable)
else
redirect_to root_path
end
else
redirect_to new_user_session_path
end
end
end
......@@ -91,7 +91,7 @@ module GitlabMarkdownHelper
def render_wiki_content(wiki_page)
case wiki_page.format
when :markdown
markdown(wiki_page.content)
markdown(wiki_page.content, pipeline: :wiki, project_wiki: @project_wiki)
when :asciidoc
asciidoc(wiki_page.content)
else
......
module Emails
module Issues
def new_issue_email(recipient_id, issue_id)
issue_mail_with_notification(issue_id, recipient_id) do
mail_new_thread(@issue, issue_thread_options(@issue.author_id, recipient_id))
end
setup_issue_mail(issue_id, recipient_id)
mail_new_thread(@issue, issue_thread_options(@issue.author_id, recipient_id))
end
def reassigned_issue_email(recipient_id, issue_id, previous_assignee_id, updated_by_user_id)
issue_mail_with_notification(issue_id, recipient_id) do
@previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id
mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id))
end
setup_issue_mail(issue_id, recipient_id)
@previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id
mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id))
end
def closed_issue_email(recipient_id, issue_id, updated_by_user_id)
issue_mail_with_notification(issue_id, recipient_id) do
@updated_by = User.find updated_by_user_id
mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id))
end
setup_issue_mail(issue_id, recipient_id)
@updated_by = User.find updated_by_user_id
mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id))
end
def issue_status_changed_email(recipient_id, issue_id, status, updated_by_user_id)
issue_mail_with_notification(issue_id, recipient_id) do
@issue_status = status
@updated_by = User.find updated_by_user_id
mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id))
end
setup_issue_mail(issue_id, recipient_id)
@issue_status = status
@updated_by = User.find updated_by_user_id
mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id))
end
private
......@@ -38,14 +38,12 @@ module Emails
}
end
def issue_mail_with_notification(issue_id, recipient_id)
def setup_issue_mail(issue_id, recipient_id)
@issue = Issue.find(issue_id)
@project = @issue.project
@target_url = namespace_project_issue_url(@project.namespace, @project, @issue)
yield
SentNotification.record(@issue, recipient_id, reply_key)
@sent_notification = SentNotification.record(@issue, recipient_id, reply_key)
end
end
end
module Emails
module MergeRequests
def new_merge_request_email(recipient_id, merge_request_id)
@merge_request = MergeRequest.find(merge_request_id)
@project = @merge_request.project
@target_url = namespace_project_merge_request_url(@project.namespace,
@project,
@merge_request)
setup_merge_request_mail(merge_request_id, recipient_id)
mail_new_thread(@merge_request,
from: sender(@merge_request.author_id),
to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
SentNotification.record(@merge_request, recipient_id, reply_key)
end
def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id, updated_by_user_id)
@merge_request = MergeRequest.find(merge_request_id)
setup_merge_request_mail(merge_request_id, recipient_id)
@previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id
@project = @merge_request.project
@target_url = namespace_project_merge_request_url(@project.namespace,
@project,
@merge_request)
mail_answer_thread(@merge_request,
from: sender(updated_by_user_id),
to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
SentNotification.record(@merge_request, recipient_id, reply_key)
end
def closed_merge_request_email(recipient_id, merge_request_id, updated_by_user_id)
@merge_request = MergeRequest.find(merge_request_id)
setup_merge_request_mail(merge_request_id, recipient_id)
@updated_by = User.find updated_by_user_id
@project = @merge_request.project
@target_url = namespace_project_merge_request_url(@project.namespace,
@project,
@merge_request)
mail_answer_thread(@merge_request,
from: sender(updated_by_user_id),
to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
SentNotification.record(@merge_request, recipient_id, reply_key)
end
def merged_merge_request_email(recipient_id, merge_request_id, updated_by_user_id)
@merge_request = MergeRequest.find(merge_request_id)
@project = @merge_request.project
@target_url = namespace_project_merge_request_url(@project.namespace,
@project,
@merge_request)
setup_merge_request_mail(merge_request_id, recipient_id)
mail_answer_thread(@merge_request,
from: sender(updated_by_user_id),
to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
SentNotification.record(@merge_request, recipient_id, reply_key)
end
def merge_request_status_email(recipient_id, merge_request_id, status, updated_by_user_id)
@merge_request = MergeRequest.find(merge_request_id)
setup_merge_request_mail(merge_request_id, recipient_id)
@mr_status = status
@project = @merge_request.project
@updated_by = User.find updated_by_user_id
@target_url = namespace_project_merge_request_url(@project.namespace,
@project,
@merge_request)
mail_answer_thread(@merge_request,
from: sender(updated_by_user_id),
to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
end
private
def setup_merge_request_mail(merge_request_id, recipient_id)
@merge_request = MergeRequest.find(merge_request_id)
@project = @merge_request.project
@target_url = namespace_project_merge_request_url(@project.namespace,
@project,
@merge_request)
SentNotification.record(@merge_request, recipient_id, reply_key)
@sent_notification = SentNotification.record(@merge_request, recipient_id, reply_key)
end
end
end
module Emails
module Notes
def note_commit_email(recipient_id, note_id)
note_mail_with_notification(note_id, recipient_id) do
@commit = @note.noteable
@target_url = namespace_project_commit_url(*note_target_url_options)
mail_answer_thread(@commit,
from: sender(@note.author_id),
to: recipient(recipient_id),
subject: subject("#{@commit.title} (#{@commit.short_id})"))
end
setup_note_mail(note_id, recipient_id)
@commit = @note.noteable
@target_url = namespace_project_commit_url(*note_target_url_options)
mail_answer_thread(@commit,
from: sender(@note.author_id),
to: recipient(recipient_id),
subject: subject("#{@commit.title} (#{@commit.short_id})"))
end
def note_issue_email(recipient_id, note_id)
note_mail_with_notification(note_id, recipient_id) do
@issue = @note.noteable
@target_url = namespace_project_issue_url(*note_target_url_options)
mail_answer_thread(@issue, note_thread_options(recipient_id))
end
setup_note_mail(note_id, recipient_id)
@issue = @note.noteable
@target_url = namespace_project_issue_url(*note_target_url_options)
mail_answer_thread(@issue, note_thread_options(recipient_id))
end
def note_merge_request_email(recipient_id, note_id)
note_mail_with_notification(note_id, recipient_id) do
@merge_request = @note.noteable
@target_url = namespace_project_merge_request_url(*note_target_url_options)
mail_answer_thread(@merge_request, note_thread_options(recipient_id))
end
setup_note_mail(note_id, recipient_id)
@merge_request = @note.noteable
@target_url = namespace_project_merge_request_url(*note_target_url_options)
mail_answer_thread(@merge_request, note_thread_options(recipient_id))
end
private
......@@ -42,13 +42,11 @@ module Emails
}
end
def note_mail_with_notification(note_id, recipient_id)
def setup_note_mail(note_id, recipient_id)
@note = Note.find(note_id)
@project = @note.project
yield
SentNotification.record_note(@note, recipient_id, reply_key)
@sent_notification = SentNotification.record_note(@note, recipient_id, reply_key)
end
end
end
......@@ -107,10 +107,9 @@ class Notify < BaseMailer
end
headers["X-GitLab-#{model.class.name}-ID"] = model.id
headers['X-GitLab-Reply-Key'] = reply_key
if reply_key
headers['X-GitLab-Reply-Key'] = reply_key
if Gitlab::IncomingEmail.enabled?
address = Mail::Address.new(Gitlab::IncomingEmail.reply_address(reply_key))
address.display_name = @project.name_with_namespace
......
......@@ -119,6 +119,12 @@ module Issuable
update(subscribed: !subscribed?(user))
end
def unsubscribe(user)
subscriptions.
find_or_initialize_by(user_id: user.id).
update(subscribed: false)
end
def to_hook_data(user)
{
object_kind: self.class.name.underscore,
......
......@@ -38,6 +38,10 @@ class ProjectWiki
[Gitlab.config.gitlab.url, "/", path_with_namespace, ".git"].join('')
end
def wiki_base_path
["/", @project.path_with_namespace, "/wikis"].join('')
end
# Returns the Gollum::Wiki object.
def wiki
@wiki ||= begin
......
......@@ -25,8 +25,6 @@ class SentNotification < ActiveRecord::Base
class << self
def reply_key
return nil unless Gitlab::IncomingEmail.enabled?
SecureRandom.hex(16)
end
......@@ -59,11 +57,15 @@ class SentNotification < ActiveRecord::Base
def record_note(note, recipient_id, reply_key, params = {})
params[:line_code] = note.line_code
record(note.noteable, recipient_id, reply_key, params)
end
end
def unsubscribable?
!for_commit?
end
def for_commit?
noteable_type == "Commit"
end
......@@ -75,4 +77,8 @@ class SentNotification < ActiveRecord::Base
super
end
end
def to_param
self.reply_key
end
end
......@@ -169,7 +169,7 @@ class WikiPage
private
def set_attributes
attributes[:slug] = @page.escaped_url_path
attributes[:slug] = @page.url_path
attributes[:title] = @page.title
attributes[:format] = @page.format
end
......
......@@ -44,6 +44,10 @@
%br
-# Don't link the host is the line below, one link in the email is easier to quickly click than two.
You're receiving this email because of your account on #{Gitlab.config.gitlab.host}.
If you'd like to receive fewer emails, you can adjust your notification settings.
If you'd like to receive fewer emails, you can
- if @sent_notification && @sent_notification.unsubscribable?
= link_to "unsubscribe", unsubscribe_sent_notification_url(@sent_notification)
from this thread or
adjust your notification settings.
= email_action @target_url
\ No newline at end of file
= email_action @target_url
......@@ -5,12 +5,9 @@
%a.close{href: "#", "data-dismiss" => "modal"} ×
%h3.page-title New Wiki Page
.modal-body
= label_tag :new_wiki_path do
%span Page slug
= text_field_tag :new_wiki_path, nil, placeholder: 'how-to-setup', class: 'form-control', required: true, :'data-wikis-path' => namespace_project_wikis_path(@project.namespace, @project)
%p.hidden.text-danger{data: { error: "slug" }}
The page slug is invalid. Please don't use characters other then: a-z 0-9 _ - and /
%p.hint
Please don't use spaces.
.form-group
= label_tag :new_wiki_path do
%span Page slug
= text_field_tag :new_wiki_path, nil, placeholder: 'how-to-setup', class: 'form-control', required: true, :'data-wikis-path' => namespace_project_wikis_path(@project.namespace, @project)
.form-actions
= link_to 'Create Page', '#', class: 'build-new-wiki btn btn-create'
......@@ -88,6 +88,12 @@ Rails.application.routes.draw do
end
end
resources :sent_notifications, only: [], constraints: { id: /\h{32}/ } do
member do
get :unsubscribe
end
end
# Spam reports
resources :abuse_reports, only: [:new, :create]
......@@ -513,7 +519,7 @@ Rails.application.routes.draw do
end
end
WIKI_SLUG_ID = { id: /[a-zA-Z.0-9_\-\/]+/ } unless defined? WIKI_SLUG_ID
WIKI_SLUG_ID = { id: /\S+/ } unless defined? WIKI_SLUG_ID
scope do
# Order matters to give priority to these matches
......
......@@ -69,11 +69,6 @@ Feature: Project Wiki
And I click on the "Pages" button
Then I should see non-escaped link in the pages list
@javascript
Scenario: Creating an invalid new page
Given I create a New page with an invalid name
Then I should see an error message
@javascript
Scenario: Edit Wiki page that has a path
Given I create a New page with paths
......
......@@ -132,16 +132,6 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps
expect(current_path).to include 'one/two/three'
end
step 'I create a New page with an invalid name' do
click_on 'New Page'
fill_in 'Page slug', with: 'invalid name'
click_on 'Create Page'
end
step 'I should see an error message' do
expect(page).to have_content "The page slug is invalid"
end
step 'I should see non-escaped link in the pages list' do
expect(page).to have_xpath("//a[@href='/#{project.path_with_namespace}/wikis/one/two/three']")
end
......
require 'banzai'
require 'html/pipeline/filter'
module Banzai
module Filter
# HTML Filter for parsing Gollum's tags in HTML. It's only parses the
# following tags:
#
# - Link to internal pages:
#
# * [[Bug Reports]]
# * [[How to Contribute|Contributing]]
#
# - Link to external resources:
#
# * [[http://en.wikipedia.org/wiki/Git_(software)]]
# * [[Git|http://en.wikipedia.org/wiki/Git_(software)]]
#
# - Link internal images, the special attributes will be ignored:
#
# * [[images/logo.png]]
# * [[images/logo.png|alt=Logo]]
#
# - Link external images, the special attributes will be ignored:
#
# * [[http://example.com/images/logo.png]]
# * [[http://example.com/images/logo.png|alt=Logo]]
#
# Based on Gollum::Filter::Tags
#
# Context options:
# :project_wiki (required) - Current project wiki.
#
class GollumTagsFilter < HTML::Pipeline::Filter
include ActionView::Helpers::TagHelper
# Pattern to match tags content that should be parsed in HTML.
#
# Gollum's tags have been made to resemble the tags of other markups,
# especially MediaWiki. The basic syntax is:
#
# [[tag]]
#
# Some tags will accept attributes which are separated by pipe
# symbols.Some attributes must precede the tag and some must follow it:
#
# [[prefix-attribute|tag]]
# [[tag|suffix-attribute]]
#
# See https://github.com/gollum/gollum/wiki
#
# Rubular: http://rubular.com/r/7dQnE5CUCH
TAGS_PATTERN = %r{\[\[(.+?)\]\]}
# Pattern to match allowed image extensions
ALLOWED_IMAGE_EXTENSIONS = %r{.+(jpg|png|gif|svg|bmp)\z}i
def call
search_text_nodes(doc).each do |node|
content = node.content
next unless content.match(TAGS_PATTERN)
html = process_tag($1)
if html && html != node.content
node.replace(html)
end
end
doc
end
private
# Process a single tag into its final HTML form.
#
# tag - The String tag contents (the stuff inside the double brackets).
#
# Returns the String HTML version of the tag.
def process_tag(tag)
parts = tag.split('|')
return if parts.size.zero?
process_image_tag(parts) || process_page_link_tag(parts)
end
# Attempt to process the tag as an image tag.
#
# tag - The String tag contents (the stuff inside the double brackets).
#
# Returns the String HTML if the tag is a valid image tag or nil
# if it is not.
def process_image_tag(parts)
content = parts[0].strip
return unless image?(content)
if url?(content)
path = content
elsif file = project_wiki.find_file(content)
path = ::File.join project_wiki_base_path, file.path
end
if path
content_tag(:img, nil, src: path)
end
end
def image?(path)
path =~ ALLOWED_IMAGE_EXTENSIONS
end
def url?(path)
path.start_with?(*%w(http https))
end
# Attempt to process the tag as a page link tag.
#
# tag - The String tag contents (the stuff inside the double brackets).
#
# Returns the String HTML if the tag is a valid page link tag or nil
# if it is not.
def process_page_link_tag(parts)
if parts.size == 1
url = parts[0].strip
else
name, url = *parts.compact.map(&:strip)
end
content_tag(:a, name || url, href: url)
end
def project_wiki
context[:project_wiki]
end
def project_wiki_base_path
project_wiki && project_wiki.wiki_base_path
end
# Ensure that a :project_wiki key exists in context
#
# Note that while the key might exist, its value could be nil!
def validate
needs :project_wiki
end
end
end
end
require 'banzai'
module Banzai
module Pipeline
class WikiPipeline < FullPipeline
def self.filters
super.insert(1, Filter::GollumTagsFilter)
end
end
end
end
module Gitlab
module GithubImport
class Importer
include Gitlab::ShellAdapter
attr_reader :project, :client
def initialize(project)
......@@ -12,10 +14,7 @@ module Gitlab
end
def execute
import_issues
import_pull_requests
true
import_issues && import_pull_requests && import_wiki
end
private
......@@ -34,6 +33,10 @@ module Gitlab
end
end
end
true
rescue ActiveRecord::RecordInvalid
false
end
def import_pull_requests
......@@ -48,6 +51,10 @@ module Gitlab
import_comments_on_diff(pull_request.number, merge_request)
end
end
true
rescue ActiveRecord::RecordInvalid
false
end
def import_comments(issue_number, noteable)
......@@ -66,6 +73,18 @@ module Gitlab
noteable.notes.create!(comment.attributes)
end
end
def import_wiki
unless project.wiki_enabled?
wiki = WikiFormatter.new(project)
gitlab_shell.import_repository(wiki.path_with_namespace, wiki.import_url)
project.update_attribute(:wiki_enabled, true)
end
true
rescue Gitlab::Shell::Error
false