Commit 3a07a0c0 authored by Robert Speicher's avatar Robert Speicher

Merge branch 'master' into 8-10-stable

parents 27b60a93 4f25e317
...@@ -23,10 +23,12 @@ v 8.10.0 (unreleased) ...@@ -23,10 +23,12 @@ v 8.10.0 (unreleased)
- Add Sidekiq queue duration to transaction metrics. - Add Sidekiq queue duration to transaction metrics.
- Add a new column `artifacts_size` to table `ci_builds` !4964 - Add a new column `artifacts_size` to table `ci_builds` !4964
- Let Workhorse serve format-patch diffs - Let Workhorse serve format-patch diffs
- Display tooltip for mentioned users and groups !5261 (winniehell)
- Added day name to contribution calendar tooltips - Added day name to contribution calendar tooltips
- Make images fit to the size of the viewport !4810 - Make images fit to the size of the viewport !4810
- Fix check for New Branch button on Issue page !4630 (winniehell) - Fix check for New Branch button on Issue page !4630 (winniehell)
- Fix MR-auto-close text added to description. !4836 - Fix MR-auto-close text added to description. !4836
- Support U2F devices in Firefox. !5177
- Fix issue, preventing users w/o push access to sort tags !5105 (redetection) - Fix issue, preventing users w/o push access to sort tags !5105 (redetection)
- Add Spring EmojiOne updates. - Add Spring EmojiOne updates.
- Add syntax for multiline blockquote using `>>>` fence !3954 - Add syntax for multiline blockquote using `>>>` fence !3954
...@@ -57,6 +59,7 @@ v 8.10.0 (unreleased) ...@@ -57,6 +59,7 @@ v 8.10.0 (unreleased)
- Throttle the update of `project.pushes_since_gc` to 1 minute. - Throttle the update of `project.pushes_since_gc` to 1 minute.
- Allow expanding and collapsing files in diff view (!4990) - Allow expanding and collapsing files in diff view (!4990)
- Collapse large diffs by default (!4990) - Collapse large diffs by default (!4990)
- Fix mentioned users list on diff notes
- Check for conflicts with existing Project's wiki path when creating a new project. - Check for conflicts with existing Project's wiki path when creating a new project.
- Show last push widget in upstream after push to fork - Show last push widget in upstream after push to fork
- Cache todos pending/done dashboard query counts. - Cache todos pending/done dashboard query counts.
...@@ -95,7 +98,11 @@ v 8.10.0 (unreleased) ...@@ -95,7 +98,11 @@ v 8.10.0 (unreleased)
- Redesign Builds and Pipelines pages - Redesign Builds and Pipelines pages
- Change status color and icon for running builds - Change status color and icon for running builds
- Fix markdown rendering for: consecutive labels references, label references that begin with a digit or contains `.` - Fix markdown rendering for: consecutive labels references, label references that begin with a digit or contains `.`
- Project export filename now includes the project and namespace path
- Fix last update timestamp on issues not preserved on gitlab.com and project imports - Fix last update timestamp on issues not preserved on gitlab.com and project imports
- Fix issues importing projects from EE to CE
- Fix creating group with space in group path
- Create Todos for Issue author when assign or mention himself (Katarzyna Kobierska)
v 8.9.6 v 8.9.6
- Fix importing of events under notes for GitLab projects. !5154 - Fix importing of events under notes for GitLab projects. !5154
......
...@@ -61,7 +61,7 @@ gem 'gitlab_omniauth-ldap', '~> 1.2.1', require: 'omniauth-ldap' ...@@ -61,7 +61,7 @@ gem 'gitlab_omniauth-ldap', '~> 1.2.1', require: 'omniauth-ldap'
# Git Wiki # Git Wiki
# Required manually in config/initializers/gollum.rb to control load order # Required manually in config/initializers/gollum.rb to control load order
gem 'gollum-lib', '~> 4.1.0', require: false gem 'gollum-lib', '~> 4.2', require: false
gem 'gollum-rugged_adapter', '~> 0.4.2', require: false gem 'gollum-rugged_adapter', '~> 0.4.2', require: false
# Language detection # Language detection
...@@ -105,7 +105,7 @@ gem 'seed-fu', '~> 2.3.5' ...@@ -105,7 +105,7 @@ gem 'seed-fu', '~> 2.3.5'
# Markdown and HTML processing # Markdown and HTML processing
gem 'html-pipeline', '~> 1.11.0' gem 'html-pipeline', '~> 1.11.0'
gem 'task_list', '~> 1.0.2', require: 'task_list/railtie' gem 'task_list', '~> 1.0.2', require: 'task_list/railtie'
gem 'github-markup', '~> 1.3.1' gem 'github-markup', '~> 1.4'
gem 'redcarpet', '~> 3.3.3' gem 'redcarpet', '~> 3.3.3'
gem 'RedCloth', '~> 4.3.2' gem 'RedCloth', '~> 4.3.2'
gem 'rdoc', '~>3.6' gem 'rdoc', '~>3.6'
...@@ -113,7 +113,7 @@ gem 'org-ruby', '~> 0.9.12' ...@@ -113,7 +113,7 @@ gem 'org-ruby', '~> 0.9.12'
gem 'creole', '~> 0.5.0' gem 'creole', '~> 0.5.0'
gem 'wikicloth', '0.8.1' gem 'wikicloth', '0.8.1'
gem 'asciidoctor', '~> 1.5.2' gem 'asciidoctor', '~> 1.5.2'
gem 'rouge', '~> 1.11' gem 'rouge', '~> 2.0'
# See https://groups.google.com/forum/#!topic/ruby-security-ann/aSbgDiwb24s # See https://groups.google.com/forum/#!topic/ruby-security-ann/aSbgDiwb24s
# and https://groups.google.com/forum/#!topic/ruby-security-ann/Dy7YiKb_pMM # and https://groups.google.com/forum/#!topic/ruby-security-ann/Dy7YiKb_pMM
......
...@@ -264,7 +264,7 @@ GEM ...@@ -264,7 +264,7 @@ GEM
escape_utils (~> 1.1.0) escape_utils (~> 1.1.0)
mime-types (>= 1.19) mime-types (>= 1.19)
rugged (>= 0.23.0b) rugged (>= 0.23.0b)
github-markup (1.3.3) github-markup (1.4.0)
gitlab-flowdock-git-hook (1.0.1) gitlab-flowdock-git-hook (1.0.1)
flowdock (~> 0.7) flowdock (~> 0.7)
gitlab-grit (>= 2.4.1) gitlab-grit (>= 2.4.1)
...@@ -287,13 +287,13 @@ GEM ...@@ -287,13 +287,13 @@ GEM
rubyntlm (~> 0.3) rubyntlm (~> 0.3)
globalid (0.3.6) globalid (0.3.6)
activesupport (>= 4.1.0) activesupport (>= 4.1.0)
gollum-grit_adapter (1.0.0) gollum-grit_adapter (1.0.1)
gitlab-grit (~> 2.7, >= 2.7.1) gitlab-grit (~> 2.7, >= 2.7.1)
gollum-lib (4.1.0) gollum-lib (4.2.1)
github-markup (~> 1.3.3) github-markup (~> 1.4.0)
gollum-grit_adapter (~> 1.0) gollum-grit_adapter (~> 1.0)
nokogiri (~> 1.6.4) nokogiri (~> 1.6.4)
rouge (~> 1.9) rouge (~> 2.0)
sanitize (~> 2.1.0) sanitize (~> 2.1.0)
stringex (~> 2.5.1) stringex (~> 2.5.1)
gollum-rugged_adapter (0.4.2) gollum-rugged_adapter (0.4.2)
...@@ -578,7 +578,7 @@ GEM ...@@ -578,7 +578,7 @@ GEM
railties (>= 4.2.0, < 5.1) railties (>= 4.2.0, < 5.1)
rinku (2.0.0) rinku (2.0.0)
rotp (2.1.2) rotp (2.1.2)
rouge (1.11.0) rouge (2.0.3)
rqrcode (0.7.0) rqrcode (0.7.0)
chunky_png chunky_png
rqrcode-rails3 (0.1.7) rqrcode-rails3 (0.1.7)
...@@ -859,12 +859,12 @@ DEPENDENCIES ...@@ -859,12 +859,12 @@ DEPENDENCIES
gemnasium-gitlab-service (~> 0.2) gemnasium-gitlab-service (~> 0.2)
gemojione (~> 2.6) gemojione (~> 2.6)
github-linguist (~> 4.7.0) github-linguist (~> 4.7.0)
github-markup (~> 1.3.1) github-markup (~> 1.4)
gitlab-flowdock-git-hook (~> 1.0.1) gitlab-flowdock-git-hook (~> 1.0.1)
gitlab_git (~> 10.2) gitlab_git (~> 10.2)
gitlab_meta (= 7.0) gitlab_meta (= 7.0)
gitlab_omniauth-ldap (~> 1.2.1) gitlab_omniauth-ldap (~> 1.2.1)
gollum-lib (~> 4.1.0) gollum-lib (~> 4.2)
gollum-rugged_adapter (~> 0.4.2) gollum-rugged_adapter (~> 0.4.2)
gon (~> 6.0.1) gon (~> 6.0.1)
grape (~> 0.13.0) grape (~> 0.13.0)
...@@ -933,7 +933,7 @@ DEPENDENCIES ...@@ -933,7 +933,7 @@ DEPENDENCIES
request_store (~> 1.3.0) request_store (~> 1.3.0)
rerun (~> 0.11.0) rerun (~> 0.11.0)
responders (~> 2.0) responders (~> 2.0)
rouge (~> 1.11) rouge (~> 2.0)
rqrcode-rails3 (~> 0.1.7) rqrcode-rails3 (~> 0.1.7)
rspec-rails (~> 3.5.0) rspec-rails (~> 3.5.0)
rspec-retry (~> 0.4.5) rspec-retry (~> 0.4.5)
......
...@@ -53,7 +53,6 @@ ...@@ -53,7 +53,6 @@
#= require_directory ./u2f #= require_directory ./u2f
#= require_directory . #= require_directory .
#= require fuzzaldrin-plus #= require fuzzaldrin-plus
#= require u2f
window.slugify = (text) -> window.slugify = (text) ->
text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase() text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase()
......
...@@ -39,8 +39,6 @@ class Dispatcher ...@@ -39,8 +39,6 @@ class Dispatcher
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
new GLForm($('.issue-form')) new GLForm($('.issue-form'))
new IssuableForm($('.issue-form')) new IssuableForm($('.issue-form'))
new LabelsSelect()
new MilestoneSelect()
when 'projects:merge_requests:new', 'projects:merge_requests:edit' when 'projects:merge_requests:new', 'projects:merge_requests:edit'
new Diff() new Diff()
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
......
...@@ -184,22 +184,20 @@ class @LabelsSelect ...@@ -184,22 +184,20 @@ class @LabelsSelect
.value() .value()
if $dropdown.hasClass 'js-extra-options' if $dropdown.hasClass 'js-extra-options'
extraData = []
if showAny
extraData.push(
isAny: true
title: 'Any Label'
)
if showNo if showNo
extraData.push( data.unshift(
id: 0 id: 0
title: 'No Label' title: 'No Label'
) )
if extraData.length if showAny
extraData.push 'divider' data.unshift(
data = extraData.concat(data) isAny: true
title: 'Any Label'
)
if data.length > 2
data.splice 2, 0, 'divider'
callback data callback data
...@@ -289,12 +287,6 @@ class @LabelsSelect ...@@ -289,12 +287,6 @@ class @LabelsSelect
defaultLabel defaultLabel
fieldName: $dropdown.data('field-name') fieldName: $dropdown.data('field-name')
id: (label) -> id: (label) ->
if $dropdown.hasClass('js-issuable-form-dropdown')
if label.id is 0
return
else
return label.id
if $dropdown.hasClass("js-filter-submit") and not label.isAny? if $dropdown.hasClass("js-filter-submit") and not label.isAny?
label.title label.title
else else
...@@ -308,9 +300,6 @@ class @LabelsSelect ...@@ -308,9 +300,6 @@ class @LabelsSelect
$selectbox.hide() $selectbox.hide()
# display:block overrides the hide-collapse rule # display:block overrides the hide-collapse rule
$value.removeAttr('style') $value.removeAttr('style')
return if $dropdown.hasClass('js-issuable-form-dropdown')
if $dropdown.hasClass 'js-multiselect' if $dropdown.hasClass 'js-multiselect'
if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex) if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex)
selectedLabels = $dropdown selectedLabels = $dropdown
...@@ -332,7 +321,7 @@ class @LabelsSelect ...@@ -332,7 +321,7 @@ class @LabelsSelect
clicked: (label) -> clicked: (label) ->
_this.enableBulkLabelDropdown() _this.enableBulkLabelDropdown()
if $dropdown.hasClass('js-filter-bulk-update') or $dropdown.hasClass('js-issuable-form-dropdown') if $dropdown.hasClass('js-filter-bulk-update')
return return
page = $('body').data 'page' page = $('body').data 'page'
......
...@@ -62,7 +62,7 @@ class @MilestoneSelect ...@@ -62,7 +62,7 @@ class @MilestoneSelect
title: 'Upcoming' title: 'Upcoming'
) )
if extraOptions.length > 0 if extraOptions.length > 2
extraOptions.push 'divider' extraOptions.push 'divider'
callback(extraOptions.concat(data)) callback(extraOptions.concat(data))
......
...@@ -6,8 +6,20 @@ ...@@ -6,8 +6,20 @@
class @U2FAuthenticate class @U2FAuthenticate
constructor: (@container, u2fParams) -> constructor: (@container, u2fParams) ->
@appId = u2fParams.app_id @appId = u2fParams.app_id
@challenges = u2fParams.challenges @challenge = u2fParams.challenge
@signRequests = u2fParams.sign_requests
# The U2F Javascript API v1.1 requires a single challenge, with
# _no challenges per-request_. The U2F Javascript API v1.0 requires a
# challenge per-request, which is done by copying the single challenge
# into every request.
#
# In either case, we don't need the per-request challenges that the server
# has generated, so we can remove them.
#
# Note: The server library fixes this behaviour in (unreleased) version 1.0.0.
# This can be removed once we upgrade.
# https://github.com/castle/ruby-u2f/commit/103f428071a81cd3d5f80c2e77d522d5029946a4
@signRequests = u2fParams.sign_requests.map (request) -> _(request).omit('challenge')
start: () => start: () =>
if U2FUtil.isU2FSupported() if U2FUtil.isU2FSupported()
...@@ -16,7 +28,7 @@ class @U2FAuthenticate ...@@ -16,7 +28,7 @@ class @U2FAuthenticate
@renderNotSupported() @renderNotSupported()
authenticate: () => authenticate: () =>
u2f.sign(@appId, @challenges, @signRequests, (response) => u2f.sign(@appId, @challenge, @signRequests, (response) =>
if response.errorCode if response.errorCode
error = new U2FError(response.errorCode) error = new U2FError(response.errorCode)
@renderError(error); @renderError(error);
......
class @U2FUtil
@isU2FSupported: ->
window.u2f
# Helper class for U2F (universal 2nd factor) device registration and authentication.
class @U2FUtil
@isU2FSupported: ->
if @testMode
true
else
gon.u2f.browser_supports_u2f
@enableTestMode: ->
@testMode = true
<% if Rails.env.test? %>
U2FUtil.enableTestMode();
<% end %>
...@@ -155,13 +155,11 @@ class @UsersSelect ...@@ -155,13 +155,11 @@ class @UsersSelect
# display:block overrides the hide-collapse rule # display:block overrides the hide-collapse rule
$value.css('display', '') $value.css('display', '')
clicked: (user, $el, e) -> clicked: (user) ->
page = $('body').data 'page' page = $('body').data 'page'
isIssueIndex = page is 'projects:issues:index' isIssueIndex = page is 'projects:issues:index'
isMRIndex = page is page is 'projects:merge_requests:index' isMRIndex = page is page is 'projects:merge_requests:index'
if $dropdown.hasClass('js-filter-bulk-update') or $dropdown.hasClass('js-issuable-form-dropdown') if $dropdown.hasClass('js-filter-bulk-update')
e.preventDefault()
selectedId = user.id
return return
if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex) if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex)
...@@ -174,8 +172,7 @@ class @UsersSelect ...@@ -174,8 +172,7 @@ class @UsersSelect
.closest('.selectbox') .closest('.selectbox')
.find("input[name='#{$dropdown.data('field-name')}']").val() .find("input[name='#{$dropdown.data('field-name')}']").val()
assignTo(selected) assignTo(selected)
id: (user) ->
user.id
renderRow: (user) -> renderRow: (user) ->
username = if user.username then "@#{user.username}" else "" username = if user.username then "@#{user.username}" else ""
avatar = if user.avatar_url then user.avatar_url else false avatar = if user.avatar_url then user.avatar_url else false
......
...@@ -56,7 +56,7 @@ ...@@ -56,7 +56,7 @@
position: absolute; position: absolute;
top: 50%; top: 50%;
right: 6px; right: 6px;
margin-top: -6px; margin-top: -4px;
color: $dropdown-toggle-icon-color; color: $dropdown-toggle-icon-color;
font-size: 10px; font-size: 10px;
} }
......
...@@ -324,10 +324,6 @@ ...@@ -324,10 +324,6 @@
.issuable-form-select-holder { .issuable-form-select-holder {
display: inline-block; display: inline-block;
width: 250px; width: 250px;
.dropdown-menu-toggle {
width: 100%;
}
} }
.table-holder { .table-holder {
......
...@@ -344,10 +344,6 @@ class ApplicationController < ActionController::Base ...@@ -344,10 +344,6 @@ class ApplicationController < ActionController::Base
session[:skip_tfa] && session[:skip_tfa] > Time.current session[:skip_tfa] && session[:skip_tfa] > Time.current
end end
def browser_supports_u2f?
browser.chrome? && browser.version.to_i >= 41 && !browser.device.mobile?
end
def redirect_to_home_page_url? def redirect_to_home_page_url?
# If user is not signed-in and tries to access root_path - redirect him to landing page # If user is not signed-in and tries to access root_path - redirect him to landing page
# Don't redirect to the default URL to prevent endless redirections # Don't redirect to the default URL to prevent endless redirections
......
...@@ -57,7 +57,7 @@ module AuthenticatesWithTwoFactor ...@@ -57,7 +57,7 @@ module AuthenticatesWithTwoFactor
# Authenticate using the response from a U2F (universal 2nd factor) device # Authenticate using the response from a U2F (universal 2nd factor) device
def authenticate_with_two_factor_via_u2f(user) def authenticate_with_two_factor_via_u2f(user)
if U2fRegistration.authenticate(user, u2f_app_id, user_params[:device_response], session[:challenges]) if U2fRegistration.authenticate(user, u2f_app_id, user_params[:device_response], session[:challenge])
# Remove any lingering user data from login # Remove any lingering user data from login
session.delete(:otp_user_id) session.delete(:otp_user_id)
session.delete(:challenges) session.delete(:challenges)
...@@ -77,11 +77,9 @@ module AuthenticatesWithTwoFactor ...@@ -77,11 +77,9 @@ module AuthenticatesWithTwoFactor
if key_handles.present? if key_handles.present?
sign_requests = u2f.authentication_requests(key_handles) sign_requests = u2f.authentication_requests(key_handles)
challenges = sign_requests.map(&:challenge) session[:challenge] ||= u2f.challenge
session[:challenges] = challenges gon.push(u2f: { challenge: session[:challenge], app_id: u2f_app_id,
gon.push(u2f: { challenges: challenges, app_id: u2f_app_id, sign_requests: sign_requests })
sign_requests: sign_requests,
browser_supports_u2f: browser_supports_u2f? })
end end
end end
end end
...@@ -100,7 +100,6 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController ...@@ -100,7 +100,6 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
gon.push(u2f: { challenges: session[:challenges], app_id: u2f_app_id, gon.push(u2f: { challenges: session[:challenges], app_id: u2f_app_id,
register_requests: registration_requests, register_requests: registration_requests,
sign_requests: sign_requests, sign_requests: sign_requests })
browser_supports_u2f: browser_supports_u2f? })
end end
end end
module BlobHelper module BlobHelper
def highlighter(blob_name, blob_content, repository: nil, nowrap: false) def highlight(blob_name, blob_content, repository: nil, plain: false)
Gitlab::Highlight.new(blob_name, blob_content, nowrap: nowrap, repository: repository) highlighted = Gitlab::Highlight.highlight(blob_name, blob_content, plain: plain, repository: repository)
end raw %(<pre class="code highlight"><code>#{highlighted}</code></pre>)
def highlight(blob_name, blob_content, repository: nil, nowrap: false, plain: false)
Gitlab::Highlight.highlight(blob_name, blob_content, nowrap: nowrap, plain: plain, repository: repository)
end end
def no_highlight_files def no_highlight_files
......
...@@ -9,7 +9,7 @@ module IssuablesHelper ...@@ -9,7 +9,7 @@ module IssuablesHelper
def multi_label_name(current_labels, default_label) def multi_label_name(current_labels, default_label)
# current_labels may be a string from before # current_labels may be a string from before
if current_labels.is_a?(Array) && current_labels.any? if current_labels.is_a?(Array)
if current_labels.count > 1 if current_labels.count > 1
"#{current_labels[0]} +#{current_labels.count - 1} more" "#{current_labels[0]} +#{current_labels.count - 1} more"
else else
......
module U2fHelper
def inject_u2f_api?
browser.chrome? && browser.version.to_i >= 41 && !browser.device.mobile?
end
end
...@@ -215,6 +215,8 @@ module Ci ...@@ -215,6 +215,8 @@ module Ci
end end
def keep_around_commits def keep_around_commits
return unless project
project.repository.keep_around(self.sha) project.repository.keep_around(self.sha)
project.repository.keep_around(self.before_sha) project.repository.keep_around(self.before_sha)
end end
......
...@@ -14,14 +14,14 @@ module Mentionable ...@@ -14,14 +14,14 @@ module Mentionable
attr = attr.to_s attr = attr.to_s
mentionable_attrs << [attr, options] mentionable_attrs << [attr, options]
end end
end
included do
# Accessor for attributes marked mentionable. # Accessor for attributes marked mentionable.
def mentionable_attrs cattr_accessor :mentionable_attrs, instance_accessor: false do
@mentionable_attrs ||= [] []
end
end end
included do
if self < Participable if self < Participable
participant -> (user, ext) { all_references(user, extractor: ext) } participant -> (user, ext) { all_references(user, extractor: ext) }
end end
......
...@@ -41,9 +41,12 @@ module Participable ...@@ -41,9 +41,12 @@ module Participable
def participant(attr) def participant(attr)
participant_attrs << attr participant_attrs << attr
end end
end
def participant_attrs included do
@participant_attrs ||= [] # Accessor for participant attributes.
cattr_accessor :participant_attrs, instance_accessor: false do
[]
end end
end end
......
...@@ -38,7 +38,7 @@ class LegacyDiffNote < Note ...@@ -38,7 +38,7 @@ class LegacyDiffNote < Note
end end
def diff_line def diff_line
@diff_line ||= diff_file.line_for_line_code(self.line_code) @diff_line ||= diff_file.line_for_line_code(self.line_code) if diff_file
end end
def for_line?(line) def for_line?(line)
...@@ -55,7 +55,7 @@ class LegacyDiffNote < Note ...@@ -55,7 +55,7 @@ class LegacyDiffNote < Note
def active? def active?
return @active if defined?(@active) return @active if defined?(@active)
return true if for_commit? return true if for_commit?
return true unless self.diff return true unless diff_line
return false unless noteable return false unless noteable
noteable_diff = find_noteable_diff noteable_diff = find_noteable_diff
......
...@@ -10,7 +10,7 @@ module Projects ...@@ -10,7 +10,7 @@ module Projects
def save_all def save_all
if [version_saver, project_tree_saver, uploads_saver, repo_saver, wiki_repo_saver].all?(&:save) if [version_saver, project_tree_saver, uploads_saver, repo_saver, wiki_repo_saver].all?(&:save)
Gitlab::ImportExport::Saver.save(shared: @shared) Gitlab::ImportExport::Saver.save(project: project, shared: @shared)
notify_success notify_success
else else
cleanup_and_notify cleanup_and_notify
......
...@@ -194,7 +194,7 @@ class TodoService ...@@ -194,7 +194,7 @@ class TodoService
end end
def create_assignment_todo(issuable, author) def create_assignment_todo(issuable, author)
if issuable.assignee && issuable.assignee != author if issuable.assignee
attributes = attributes_for_todo(issuable.project, issuable, author, Todo::ASSIGNED) attributes = attributes_for_todo(issuable.project, issuable, author, Todo::ASSIGNED)
create_todos(issuable.assignee, attributes) create_todos(issuable.assignee, attributes)
end end
...@@ -239,7 +239,6 @@ class TodoService ...@@ -239,7 +239,6 @@ class TodoService
def filter_mentioned_users(project, target, author) def filter_mentioned_users(project, target, author)
mentioned_users = target.mentioned_users(author) mentioned_users = target.mentioned_users(author)
mentioned_users = reject_users_without_access(mentioned_users, project, target) mentioned_users = reject_users_without_access(mentioned_users, project, target)
mentioned_users.delete(author)
mentioned_users.uniq mentioned_users.uniq
end end
......
- if inject_u2f_api?
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('u2f.js')
%div %div
.login-box .login-box
.login-heading .login-heading
......
...@@ -44,7 +44,7 @@ ...@@ -44,7 +44,7 @@
name: "#{j(@project.name)}" name: "#{j(@project.name)}"
}; };
- if @group and @group.path - if @group && @group.persisted? && @group.path
:javascript :javascript
gl.groupOptions = gl.groupOptions || {}; gl.groupOptions = gl.groupOptions || {};
gl.groupOptions["#{j(@group.path)}"] = { gl.groupOptions["#{j(@group.path)}"] = {
......
- if @note.diff_note? - if @note.diff_note? && @note.diff_file
%p.details %p.details
New comment on diff for New comment on diff for
= link_to @note.diff_file.file_path, @target_url = link_to @note.diff_file.file_path, @target_url
......
...@@ -2,6 +2,10 @@ ...@@ -2,6 +2,10 @@
- header_title "Two-Factor Authentication", profile_two_factor_auth_path - header_title "Two-Factor Authentication", profile_two_factor_auth_path
= render 'profiles/head' = render 'profiles/head'
- if inject_u2f_api?
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('u2f.js')
.row.prepend-top-default .row.prepend-top-default
.col-lg-3 .col-lg-3
%h4.prepend-top-0 %h4.prepend-top-0
......
...@@ -21,10 +21,10 @@ ...@@ -21,10 +21,10 @@
placeholder: "Search assignee", data: { any_user: "Any Assignee", first_user: (current_user.username if current_user), null_user: true, current_user: true, project_id: (@project.id if @project), selected: params[:assignee_id], field_name: "assignee_id", default_label: "Assignee" } }) placeholder: "Search assignee", data: { any_user: "Any Assignee", first_user: (current_user.username if current_user), null_user: true, current_user: true, project_id: (@project.id if @project), selected: params[:assignee_id], field_name: "assignee_id", default_label: "Assignee" } })
.filter-item.inline.milestone-filter .filter-item.inline.milestone-filter
= render "shared/issuable/milestone_dropdown", selected: params[:milestone_title], name: :milestone_title, show_any: true, show_upcoming: true = render "shared/issuable/milestone_dropdown"
.filter-item.inline.labels-filter .filter-item.inline.labels-filter
= render "shared/issuable/label_dropdown", selected: params[:label_name], data_options: { field_name: "label_name[]" } = render "shared/issuable/label_dropdown"
.pull-right .pull-right
= render 'shared/sort_dropdown' = render 'shared/sort_dropdown'
......
...@@ -52,24 +52,38 @@ ...@@ -52,24 +52,38 @@
= f.label :assignee_id, "Assignee", class: "control-label #{"col-lg-4" if has_due_date}" = f.label :assignee_id, "Assignee", class: "control-label #{"col-lg-4" if has_due_date}"
.col-sm-10{ class: ("col-lg-8" if has_due_date) } .col-sm-10{ class: ("col-lg-8" if has_due_date) }
.issuable-form-select-holder .issuable-form-select-holder
- project = @target_project || @project = users_select_tag("#{issuable.class.model_name.param_key}[assignee_id]",
- if issuable.assignee_id placeholder: 'Select assignee', class: 'custom-form-control', null_user: true,
= hidden_field_tag("#{issuable.class.model_name.param_key}[assignee_id]", issuable.assignee_id) selected: issuable.assignee_id, project: @target_project || @project,
= dropdown_tag(user_dropdown_label(issuable.assignee_id, "Assignee"), options: { toggle_class: "js-user-search js-issuable-form-dropdown js-assignee-search", title: "Filter by assignee", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee js-filter-submit", first_user: true, current_user: true, include_blank: true)
placeholder: "Search assignee", data: { first_user: (current_user.username if current_user), null_user: true, current_user: true, project_id: (project.id if project), selected: issuable.assignee_id, field_name: "#{issuable.class.model_name.param_key}[assignee_id]", default_label: "Assignee" } }) %div
= link_to 'Assign to me', '#', class: 'assign-to-me-link prepend-top-5 inline'
.form-group.issue-milestone .form-group.issue-milestone
= f.label :milestone_id, "Milestone", class: "control-label #{"col-lg-4" if has_due_date}" = f.label :milestone_id, "Milestone", class: "control-label #{"col-lg-4" if has_due_date}"
.col-sm-10{ class: ("col-lg-8" if has_due_date) } .col-sm-10{ class: ("col-lg-8" if has_due_date) }
- if milestone_options(issuable).present?
.issuable-form-select-holder .issuable-form-select-holder
= render "shared/issuable/milestone_dropdown", selected: issuable.milestone_id, name: "#{issuable.class.model_name.param_key}[milestone_id]", show_any: false, show_upcoming: false = f.select(:milestone_id, milestone_options(issuable),
{ include_blank: true }, { class: 'select2', data: { placeholder: 'Select milestone' } })
- else
.prepend-top-10
%span.light No open milestones available.
- if can? current_user, :admin_milestone, issuable.project
%div
= link_to 'Create new milestone', new_namespace_project_milestone_path(issuable.project.namespace, issuable.project), target: :blank, class: "prepend-top-5 inline"
.form-group .form-group
- has_labels = issuable.project.labels.any? - has_labels = issuable.project.labels.any?
- selected_labels = issuable.label_ids.any? ? issuable.label_ids : nil
- label_dropdown_toggle = issuable.labels.map { |label| label.title }
= f.label :label_ids, "Labels", class: "control-label #{"col-lg-4" if has_due_date}" = f.label :label_ids, "Labels", class: "control-label #{"col-lg-4" if has_due_date}"
.col-sm-10{ class: "#{"col-lg-8" if has_due_date} #{'issuable-form-padding-top' if !has_labels}" } .col-sm-10{ class: "#{"col-lg-8" if has_due_date} #{'issuable-form-padding-top' if !has_labels}" }
- if has_labels
.issuable-form-select-holder .issuable-form-select-holder
= render "shared/issuable/label_dropdown", classes: ["js-issuable-form-dropdown"], selected: selected_labels, selected_toggle: label_dropdown_toggle, data_options: { field_name: "#{issuable.class.model_name.param_key}[label_ids][]", show_any: "false" } = 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.
- if can? current_user, :admin_label, issuable.project
%div
= link_to 'Create new label', new_namespace_project_label_path(issuable.project.namespace, issuable.project), target: :blank, class: "prepend-top-5 inline"
- if has_due_date - if has_due_date
.col-lg-6 .col-lg-6
.form-group .form-group
......
...@@ -4,21 +4,19 @@ ...@@ -4,21 +4,19 @@
- show_footer = local_assigns.fetch(:show_footer, true) - show_footer = local_assigns.fetch(:show_footer, true)
- data_options = local_assigns.fetch(:data_options, {}) - data_options = local_assigns.fetch(:data_options, {})
- classes = local_assigns.fetch(:classes, []) - classes = local_assigns.fetch(:classes, [])
- selected = local_assigns.fetch(:selected, nil) - dropdown_data = {toggle: 'dropdown', field_name: 'label_name[]', show_no: "true", show_any: "true", selected: params[:label_name], project_id: @project.try(:id), labels: labels_filter_path, default_label: "Label"}
- selected_toggle = local_assigns.fetch(:selected_toggle, nil)
- dropdown_data = {toggle: 'dropdown', field_name: "label_name[]", show_no: "true", show_any: "true", selected: selected, project_id: @project.try(:id), labels: labels_filter_path, default_label: "Label"}
- dropdown_data.merge!(data_options) - dropdown_data.merge!(data_options)
- classes << 'js-extra-options' if extra_options - classes << 'js-extra-options' if extra_options
- classes << 'js-filter-submit' if filter_submit - classes << 'js-filter-submit' if filter_submit
- if selected.present? - if params[:label_name].present?
- if selected.respond_to?('any?') - if params[:label_name].respond_to?('any?')
- selected.each do |label| - params[:label_name].each do |label|
= hidden_field_tag data_options[:field_name], label, id: nil = hidden_field_tag "label_name[]", label, id: nil
.dropdown .dropdown
%button.dropdown-menu-toggle.js-label-select.js-multiselect{class: classes.join(' '), type: "button", data: dropdown_data} %button.dropdown-menu-toggle.js-label-select.js-multiselect{class: classes.join(' '), type: "button", data: dropdown_data}
%span.dropdown-toggle-text %span.dropdown-toggle-text
= h(multi_label_name(selected_toggle || selected, "Label")) = h(multi_label_name(params[:label_name], "Label"))
= icon('chevron-down') = icon('chevron-down')
.dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable .dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable
= render partial: "shared/issuable/label_page_default", locals: { title: "Filter by label", show_footer: show_footer, show_create: show_create } = render partial: "shared/issuable/label_page_default", locals: { title: "Filter by label", show_footer: show_footer, show_create: show_create }
......
- if selected.present? - if params[:milestone_title].present?
= hidden_field_tag(name, selected) = hidden_field_tag(:milestone_title, params[:milestone_title])
= dropdown_tag(milestone_dropdown_label(selected), options: { title: "Filter by milestone", toggle_class: 'js-milestone-select js-filter-submit', filter: true, dropdown_class: "dropdown-menu-selectable", = dropdown_tag(milestone_dropdown_label(params[:milestone_title]), options: { title: "Filter by milestone", toggle_class: 'js-milestone-select js-filter-submit', filter: true, dropdown_class: "dropdown-menu-selectable",
placeholder: "Search milestones", footer_content: @project.present?, data: { show_no: true, show_any: show_any, show_upcoming: show_upcoming, field_name: name, selected: selected, project_id: @project.try(:id), milestones: milestones_filter_dropdown_path, default_label: "Milestone" } }) do placeholder: "Search milestones", footer_content: @project.present?, data: { show_no: true, show_any: true, show_upcoming: true, field_name: "milestone_title", selected: params[:milestone_title], project_id: @project.try(:id), milestones: milestones_filter_dropdown_path, default_label: "Milestone" } }) do
- if @project - if @project
%ul.dropdown-footer-list %ul.dropdown-footer-list
- if can? current_user, :admin_milestone, @project - if can? current_user, :admin_milestone, @project
......
...@@ -87,6 +87,7 @@ module Gitlab ...@@ -87,6 +87,7 @@ module Gitlab
config.assets.precompile << "profile/application.js" config.assets.precompile << "profile/application.js"
config.assets.precompile << "lib/utils/*.js" config.assets.precompile << "lib/utils/*.js"
config.assets.precompile << "lib/*.js" config.assets.precompile << "lib/*.js"
config.assets.precompile << "u2f.js"
# Version of your assets, change this if you want to expire all your assets # Version of your assets, change this if you want to expire all your assets
config.assets.version = '1.0' config.assets.version = '1.0'
......
...@@ -44,7 +44,7 @@ it organized and easy to find. ...@@ -44,7 +44,7 @@ it organized and easy to find.
- When introducing a new document, be careful for the headings to be - When introducing a new document, be careful for the headings to be
grammatically and syntactically correct. It is advised to mention one or all grammatically and syntactically correct. It is advised to mention one or all
of the following GitLab members for a review: `@axil`, `@rspeicher`, of the following GitLab members for a review: `@axil`, `@rspeicher`,
`@dblessing`, `@ashleys`, `@nearlythere`. This is to ensure that no document `@dblessing`, `@ashleys`. This is to ensure that no document
with wrong heading is going live without an audit, thus preventing dead links with wrong heading is going live without an audit, thus preventing dead links
and redirection issues when corrected and redirection issues when corrected
- Leave exactly one newline after a heading - Leave exactly one newline after a heading
......
...@@ -37,7 +37,6 @@ Feature: Project Issues ...@@ -37,7 +37,6 @@ Feature: Project Issues
And I submit new issue "500 error on profile" And I submit new issue "500 error on profile"
Then I should see issue "500 error on profile" Then I should see issue "500 error on profile"
@javascript
Scenario: I submit new unassigned issue with labels Scenario: I submit new unassigned issue with labels
Given project "Shop" has labels: "bug", "feature", "enhancement" Given project "Shop" has labels: "bug", "feature", "enhancement"
And I click link "New Issue" And I click link "New Issue"
......
...@@ -135,17 +135,19 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps ...@@ -135,17 +135,19 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps
end end
step 'I click "Assign to" dropdown"' do step 'I click "Assign to" dropdown"' do
click_button 'Assignee' first('.ajax-users-select').click
end end
step 'I should see the target project ID in the input selector' do step 'I should see the target project ID in the input selector' do
expect(find('.js-assignee-search')["data-project-id"]).to eq "#{@project.id}" expect(page).to have_selector("input[data-project-id=\"#{@project.id}\"]")
end end
step 'I should see the users from the target project ID' do step 'I should see the users from the target project ID' do
expect(page).to have_content 'Unassigned' expect(page).to have_selector('.user-result', visible: true, count: 3)
expect(page).to have_content current_user.name users = page.all('.user-name')
expect(page).to have_content @project.users.first.name expect(users[0].text).to eq 'Unassigned'
expect(users[1].text).to eq current_user.name
expect(users[2].text).to eq @project.users.first.name
end end
# Verify a link is generated against the correct project # Verify a link is generated against the correct project
......
...@@ -82,8 +82,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps ...@@ -82,8 +82,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
step 'I submit new issue "500 error on profile" with label \'bug\'' do step 'I submit new issue "500 error on profile" with label \'bug\'' do
fill_in "issue_title", with: "500 error on profile" fill_in "issue_title", with: "500 error on profile"
click_button "Label" select 'bug', from: "Labels"
click_link "bug"
click_button "Submit issue" click_button "Submit issue"
end end
......
...@@ -19,8 +19,15 @@ module Banzai ...@@ -19,8 +19,15 @@ module Banzai
language = node.attr('class') language = node.attr('class')
code = node.text code = node.text
lexer = Rouge::Lexer.find_fancy(language)
formatter = Rouge::Formatters::HTML.new
css_classes = "code highlight js-syntax-highlight #{lexer.tag}"
begin begin
highlighted = block_code(code, language) highlighted = ''
highlighted << %(<pre class="#{css_classes}"><code>)
highlighted << formatter.format(lexer.lex(code))
highlighted << %(</code></pre>)
rescue rescue
# Gracefully handle syntax highlighter bugs/errors to ensure # Gracefully handle syntax highlighter bugs/errors to ensure
# users can still access an issue/comment/etc. # users can still access an issue/comment/etc.
...@@ -40,8 +47,7 @@ module Banzai ...@@ -40,8 +47,7 @@ module Banzai
# Override Rouge::Plugins::Redcarpet#rouge_formatter # Override Rouge::Plugins::Redcarpet#rouge_formatter
def rouge_formatter(lexer) def rouge_formatter(lexer)
Rouge::Formatters::HTMLGitlab.new( Rouge::Formatters::HTML.new
cssclass: "code highlight js-syntax-highlight #{lexer.tag}")
end end
end end
end end
......
...@@ -112,7 +112,7 @@ module Banzai ...@@ -112,7 +112,7 @@ module Banzai
data = data_attribute(project: project.id, author: author.try(:id)) data = data_attribute(project: project.id, author: author.try(:id))
text = link_text || User.reference_prefix + 'all' text = link_text || User.reference_prefix + 'all'
link_tag(url, data, text) link_tag(url, data, text, 'All Project and Group Members')
end end
def link_to_namespace(namespace, link_text: nil) def link_to_namespace(namespace, link_text: nil)
...@@ -128,7 +128,7 @@ module Banzai ...@@ -128,7 +128,7 @@ module Banzai
data = data_attribute(group: namespace.id) data = data_attribute(group: namespace.id)
text = link_text || Group.reference_prefix + group text = link_text || Group.reference_prefix + group
link_tag(url, data, text) link_tag(url, data, text, namespace.name)
end end
def link_to_user(user, namespace, link_text: nil) def link_to_user(user, namespace, link_text: nil)
...@@ -136,11 +136,11 @@ module Banzai ...@@ -136,11 +136,11 @@ module Banzai
data = data_attribute(user: namespace.owner_id) data = data_attribute(user: namespace.owner_id)
text = link_text || User.reference_prefix + user text = link_text || User.reference_prefix + user
link_tag(url, data, text) link_tag(url, data, text, namespace.owner_name)
end end
def link_tag(url, data, text) def link_tag(url, data, text, title)
%(<a href="#{url}" #{data} class="#{link_class}">#{escape_once(text)}</a>) %(<a href="#{url}" #{data} class="#{link_class}" title="#{escape_once(title)}">#{escape_once(text)}</a>)
end end
end end
end end
......
module Gitlab module Gitlab
class Highlight class Highlight
def self.highlight(blob_name, blob_content, repository: nil, nowrap: true, plain: false) def self.highlight(blob_name, blob_content, repository: nil, plain: false)
new(blob_name, blob_content, nowrap: nowrap, repository: repository). new(blob_name, blob_content, repository: repository).
highlight(blob_content, continue: false, plain: plain) highlight(blob_content, continue: false, plain: plain)
end end
...@@ -13,30 +13,34 @@ module Gitlab ...@@ -13,30 +13,34 @@ module Gitlab
highlight(file_name, blob.data, repository: repository).lines.map!(&:html_safe) highlight(file_name, blob.data, repository: repository).lines.map!(&:html_safe)
end end
attr_reader :lexer def initialize(blob_name, blob_content, repository: nil)
def initialize(blob_name, blob_content, repository: nil, nowrap: true) @formatter = Rouge::Formatters::HTMLGitlab.new
@repository = repository
@blob_name = blob_name @blob_name = blob_name
@blob_content = blob_content @blob_content = blob_content
@repository = repository
@formatter = rouge_formatter(nowrap: nowrap)
@lexer = custom_language || begin
Rouge::Lexer.guess(filename: blob_name, source: blob_content).new
rescue Rouge::Lexer::AmbiguousGuess => e
e.alternatives.sort_by(&:tag).first
end
end end
def highlight(text, continue: true, plain: false) def highlight(text, continue: true, plain: false)
if plain if plain
@formatter.format(Rouge::Lexers::PlainText.lex(text)).html_safe hl_lexer = Rouge::Lexers::PlainText
continue = false
else else
@formatter.format(@lexer.lex(text, continue: continue)).html_safe hl_lexer = self.lexer
end end
@formatter.format(hl_lexer.lex(text, continue: continue)).html_safe
rescue rescue
@formatter.format(Rouge::Lexers::PlainText.lex(text)).html_safe @formatter.format(Rouge::Lexers::PlainText.lex(text)).html_safe
end end
def lexer
@lexer ||= custom_language || begin
Rouge::Lexer.guess(filename: @blob_name, source: @blob_content).new
rescue Rouge::Guesser::Ambiguous => e
e.alternatives.sort_by(&:tag).first
end
end
private private
def custom_language def custom_language
...@@ -46,16 +50,5 @@ module Gitlab ...@@ -46,16 +50,5 @@ module Gitlab
Rouge::Lexer.find_fancy(language_name) Rouge::Lexer.find_fancy(language_name)
end end
def rouge_formatter(options = {})
options = options.reverse_merge(
nowrap: true,
cssclass: 'code highlight',
lineanchors: true,
lineanchorsid: 'LC'
)
Rouge::Formatters::HTMLGitlab.new(options)
end
end end
end end
...@@ -3,6 +3,7 @@ module Gitlab ...@@ -3,6 +3,7 @@ module Gitlab
extend self extend self
VERSION = '0.1.1' VERSION = '0.1.1'
FILENAME_LIMIT = 50
def export_path(relative_path:) def export_path(relative_path:)
File.join(storage_path, relative_path) File.join(storage_path, relative_path)
...@@ -28,6 +29,12 @@ module Gitlab ...@@ -28,6 +29,12 @@ module Gitlab
'VERSION' 'VERSION'
end end
def export_filename(project:)
basename = "#{Time.now.strftime('%Y-%m-%d_%H-%M-%3N')}_#{project.namespace.path}_#{project.path}"
"#{basename[0..FILENAME_LIMIT]}_export.tar.gz"
end
def version def version
VERSION VERSION
end end
......
...@@ -87,7 +87,7 @@ module Gitlab ...@@ -87,7 +87,7 @@ module Gitlab
project_id = @relation_hash.delete('project_id') project_id = @relation_hash.delete('project_id')
# project_id may not be part of the export, but we always need to populate it if required. # project_id may not be part of the export, but we always need to populate it if required.
@relation_hash['project_id'] = project_id if relation_class.column_names.include?('project_id') @relation_hash['project_id'] = project_id
@relation_hash['gl_project_id'] = project_id if @relation_hash['gl_project_id'] @relation_hash['gl_project_id'] = project_id if @relation_hash['gl_project_id']
@relation_hash['target_project_id'] = project_id if @relation_hash['target_project_id'] @relation_hash['target_project_id'] = project_id if @relation_hash['target_project_id']
@relation_hash['source_project_id'] = -1 if @relation_hash['source_project_id'] @relation_hash['source_project_id'] = -1 if @relation_hash['source_project_id']
...@@ -111,7 +111,7 @@ module Gitlab ...@@ -111,7 +111,7 @@ module Gitlab
end end
def imported_object def imported_object
imported_object = relation_class.new(@relation_hash) imported_object = relation_class.new(parsed_relation_hash)
yield(imported_object) if block_given? yield(imported_object) if block_given?
imported_object.importing = true if imported_object.respond_to?(:importing) imported_object.importing = true if imported_object.respond_to?(:importing)
imported_object imported_object
...@@ -125,6 +125,10 @@ module Gitlab ...@@ -125,6 +125,10 @@ module Gitlab
def admin_user? def admin_user?
@user.is_admin? @user.is_admin?
end end
def parsed_relation_hash
@relation_hash.reject { |k, _v| !relation_class.attribute_method?(k) }
end
end end
end end
end end
...@@ -7,7 +7,8 @@ module Gitlab ...@@ -7,7 +7,8 @@ module Gitlab
new(*args).save new(*args).save
end end
def initialize(shared:) def initialize(project:, shared:)
@project = project
@shared = shared @shared = shared
end end
...@@ -36,7 +37,7 @@ module Gitlab ...@@ -36,7 +37,7 @@ module Gitlab
end end
def archive_file def archive_file
@archive_file ||= File.join(@shared.export_path, '..', "#{Time.now.strftime('%Y-%m-%d_%H-%M-%3N')}_project_export.tar.gz") @archive_file ||= File.join(@shared.export_path, '..', Gitlab::ImportExport.export_filename(project: @project))
end end
end end
end end
......
require 'cgi'
module Rouge module Rouge
module Formatters module Formatters
class HTMLGitlab < Rouge::Formatter class HTMLGitlab < Rouge::Formatters::HTML
tag 'html_gitlab' tag 'html_gitlab'
# Creates a new <tt>Rouge::Formatter::HTMLGitlab</tt> instance. # Creates a new <tt>Rouge::Formatter::HTMLGitlab</tt> instance.
# #
# [+nowrap+] If set to True, don't wrap the output at all, not
# even inside a <tt><pre></tt> tag (default: false).
# [+cssclass+] CSS class for the wrapping <tt><div></tt> tag
# (default: 'highlight').
# [+linenos+] If set to 'table', output line numbers as a table
# with two cells, one containing the line numbers,
# the other the whole code. This is copy paste friendly,
# but may cause alignment problems with some browsers
# or fonts. If set to 'inline', the line numbers will
# be integrated in the <tt><pre></tt> tag that contains
# the code (default: nil).
# [+linenostart+] The line number for the first line (default: 1). # [+linenostart+] The line number for the first line (default: 1).
# [+lineanchors+] If set to true the formatter will wrap each output def initialize(linenostart: 1)
# line in an anchor tag with a name of L-linenumber.
# This allows easy linking to certain lines
# (default: false).
# [+lineanchorsid+] If lineanchors is true the name of the anchors can
# be changed with lineanchorsid to e.g. foo-linenumber
# (default: 'L').
# [+anchorlinenos+] If set to true, will wrap line numbers in <tt><a></tt>
# tags. Used in combination with linenos and lineanchors
# (default: false).
# [+inline_theme+] Inline CSS styles for the <pre> tag (default: false).
def initialize(
nowrap: false,
cssclass: 'highlight',
linenos: nil,
linenostart: 1,
lineanchors: false,
lineanchorsid: 'L',
anchorlinenos: false,
inline_theme: nil
)
@nowrap = nowrap
@cssclass = cssclass
@linenos = linenos
@linenostart = linenostart @linenostart = linenostart
@lineanchors = lineanchors @line_number = linenostart
@lineanchorsid = lineanchorsid
@anchorlinenos = anchorlinenos
@inline_theme = Theme.find(inline_theme).new if inline_theme.is_a?(String)
end
def render(tokens)
case @linenos
when 'table'
render_tableized(tokens)
when 'inline'
render_untableized(tokens)
else
render_untableized(tokens)
end
end
alias_method :format, :render
private
def render_untableized(tokens)
data = process_tokens(tokens)
html = ''
html << "<pre class=\"#{@cssclass}\"><code>" unless @nowrap
html << wrap_lines(data[:code])
html << "</code></pre>\n" unless @nowrap
html
end
def render_tableized(tokens)
data = process_tokens(tokens)
html = ''
html << "<div class=\"#{@cssclass}\">" unless @nowrap
html << '<table><tbody>'
html << "<td class=\"linenos\"><pre>"
html << wrap_linenos(data[:numbers])
html << '</pre></td>'
html << "<td class=\"lines\"><pre><code>"
html << wrap_lines(data[:code])
html << '</code></pre></td>'
html << '</tbody></table>'
html << '</div>' unless @nowrap
html
end
def process_tokens(tokens)
rendered = []
current_line = ''
tokens.each do |tok, val|
# In the case of multi-line values (e.g. comments), we need to apply
# styling to each line since span elements are inline.
val.lines.each do |line|
stripped = line.chomp
current_line << span(tok, stripped)
if line.end_with?("\n")
rendered << current_line
current_line = ''
end
end end
end
# Add leftover text
rendered << current_line if current_line.present?
num_lines = rendered.size def stream(tokens, &b)
numbers = (@linenostart..num_lines + @linenostart - 1).to_a is_first = true
token_lines(tokens) do |line|
yield "\n" unless is_first
is_first = false
{ numbers: numbers, code: rendered } yield %(<span id="LC#{@line_number}" class="line">)
end line.each { |token, value| yield span(token, value) }
yield %(</span>)
def wrap_linenos(numbers) @line_number += 1
if @anchorlinenos
numbers.map! do |number|
"<a href=\"##{@lineanchorsid}#{number}\">#{number}</a>"
end
end
numbers.join("\n")
end
def wrap_lines(lines)
if @lineanchors
lines = lines.each_with_index.map do |line, index|
number = index + @linenostart
if @linenos == 'inline'
"<a name=\"L#{number}\"></a>" \
"<span class=\"linenos\">#{number}</span>" \
"<span id=\"#{@lineanchorsid}#{number}\" class=\"line\">#{line}" \
'</span>'
else
"<span id=\"#{@lineanchorsid}#{number}\" class=\"line\">#{line}" \
'</span>'
end
end
elsif @linenos == 'inline'
lines = lines.each_with_index.map do |line, index|
number = index + @linenostart
"<span class=\"linenos\">#{number}</span>#{line}"
end
end
lines.join("\n")
end
def span(tok, val)
# http://stackoverflow.com/a/1600584/2587286
val = CGI.escapeHTML(val)
if tok.shortname.empty?
val
else
if @inline_theme
rules = @inline_theme.style_for(tok).rendered_rules
"<span style=\"#{rules.to_a.join(';')}\"#{val}</span>"
else
"<span class=\"#{tok.shortname}\">#{val}</span>"
end
end end
end end
end end
......
require 'spec_helper' require 'spec_helper'
feature 'Group', feature: true do feature 'Group', feature: true do
describe 'description' do
let(:group) { create(:group) }
let(:path) { group_path(group) }
before do before do
login_as(:admin) login_as(:admin)
end end
describe 'creating a group with space in group path' do
it 'renders new group form with validation errors' do
visit new_group_path
fill_in 'Group path', with: 'space group'
click_button 'Create group'
expect(current_path).to eq(groups_path)
expect(page).to have_content("Path can contain only letters, digits, '_', '-' and '.'. Cannot start with '-' or end in '.'.")
end
end
describe 'description' do
let(:group) { create(:group) }
let(:path) { group_path(group) }
it 'parses Markdown' do it 'parses Markdown' do
group.update_attribute(:description, 'This is **my** group') group.update_attribute(:description, 'This is **my** group')
visit path visit path
......
...@@ -55,7 +55,7 @@ feature 'issue move to another project' do ...@@ -55,7 +55,7 @@ feature 'issue move to another project' do
first('.select2-choice').click first('.select2-choice').click
end end
fill_in('s2id_autogen1_search', with: new_project_search.name) fill_in('s2id_autogen2_search', with: new_project_search.name)
page.within '.select2-drop' do page.within '.select2-drop' do
expect(page).to have_content(new_project_search.name) expect(page).to have_content(new_project_search.name)
......
...@@ -50,8 +50,9 @@ describe 'Issues', feature: true do ...@@ -50,8 +50,9 @@ describe 'Issues', feature: true do
expect(page).to have_content "Assignee #{@user.name}" expect(page).to have_content "Assignee #{@user.name}"
first('.js-user-search').click first('#s2id_issue_assignee_id').click
click_link 'Unassigned' sleep 2 # wait for ajax stuff to complete
first('.user-result').click
click_button 'Save changes' click_button 'Save changes'
......
require 'spec_helper' require 'spec_helper'
feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature: true, js: true do feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature: true, js: true do
before { allow_any_instance_of(U2fHelper).to receive(:inject_u2f_api?).and_return(true) }
def register_u2f_device(u2f_device = nil) def register_u2f_device(u2f_device = nil)
u2f_device ||= FakeU2fDevice.new(page) u2f_device ||= FakeU2fDevice.new(page)
u2f_device.respond_to_u2f_registration u2f_device.respond_to_u2f_registration
...@@ -208,21 +210,52 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature: ...@@ -208,21 +210,52 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
expect(page.body).to match('Authentication via U2F device failed') expect(page.body).to match('Authentication via U2F device failed')
end end
end end
describe "when more than one device has been registered by the same user" do
it "allows logging in with either device" do
# Register first device
user = login_as(:user)
user.update_attribute(:otp_required_for_login, true)
visit profile_two_factor_auth_path
expect(page).to have_content("Your U2F device needs to be set up.")
first_device = register_u2f_device
# Register second device
visit profile_two_factor_auth_path
expect(page).to have_content("Your U2F device needs to be set up.")
second_device = register_u2f_device
logout
# Authenticate as both devices
[first_device, second_device].each do |device|
login_as(user)
device.respond_to_u2f_authentication
click_on "Login Via U2F Device"
expect(page.body).to match('We heard back from your U2F device')
click_on "Authenticate via U2F Device"
expect(page.body).to match('Signed in successfully')
logout
end
end
end end
describe "when two-factor authentication is disabled" do describe "when two-factor authentication is disabled" do
let(:user) { create(:user) } let(:user) { create(:user) }
before do before do
login_as(user) user = login_as(:user)
user.update_attribute(:otp_required_for_login, true) user.update_attribute(:otp_required_for_login, true)
visit profile_account_path visit profile_account_path
click_on 'Manage Two-Factor Authentication' click_on 'Manage Two-Factor Authentication'
expect(page).to have_content("Your U2F device needs to be set up.")
register_u2f_device register_u2f_device
end end
it "deletes u2f registrations" do it "deletes u2f registrations" do
expect { click_on "Disable" }.to change { U2fRegistration.count }.from(1).to(0) expect { click_on "Disable" }.to change { U2fRegistration.count }.by(-1)
end
end end
end end
end end
...@@ -121,7 +121,7 @@ ...@@ -121,7 +121,7 @@
:type: old :type: old
:number: 9 :number: 9
:text: | :text: |
-<span id="LC9" class="line"> <span class="k">raise</span> <span class="s2">&quot;System commands must be given as an array of strings&quot;</span></span> -<span id="LC9" class="line"> <span class="k">raise</span> <span class="s2">"System commands must be given as an array of strings"</span></span>
:line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_9_9 :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_9_9
:position: !ruby/object:Gitlab::Diff::Position :position: !ruby/object:Gitlab::Diff::Position
attributes: attributes:
...@@ -136,7 +136,7 @@ ...@@ -136,7 +136,7 @@
:type: new :type: new
:number: 9 :number: 9
:text: | :text: |
+<span id="LC9" class="line"> <span class="k">raise</span> <span class="no"><span class='idiff left'>RuntimeError</span></span><span class="p"><span class='idiff'>,</span></span><span class='idiff right'> </span><span class="s2">&quot;System commands must be given as an array of strings&quot;</span></span> +<span id="LC9" class="line"> <span class="k">raise</span> <span class="no"><span class='idiff left'>RuntimeError</span></span><span class="p"><span class='idiff'>,</span></span><span class='idiff right'> </span><span class="s2">"System commands must be given as an array of strings"</span></span>
:line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9 :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9
:position: !ruby/object:Gitlab::Diff::Position :position: !ruby/object:Gitlab::Diff::Position
attributes: attributes:
...@@ -241,7 +241,7 @@ ...@@ -241,7 +241,7 @@
:type: old :type: old
:number: 13 :number: 13
:text: | :text: |
-<span id="LC13" class="line"> <span class="n">vars</span> <span class="o">=</span> <span class="p">{</span> <span class="s2">&quot;PWD&quot;</span> <span class="o">=&gt;</span> <span class="n">path</span> <span class="p">}</span></span> -<span id="LC13" class="line"> <span class="n">vars</span> <span class="o">=</span> <span class="p">{</span> <span class="s2">"PWD"</span> <span class="o">=&gt;</span> <span class="n">path</span> <span class="p">}</span></span>
:line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_13_13 :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_13_13
:position: !ruby/object:Gitlab::Diff::Position :position: !ruby/object:Gitlab::Diff::Position
attributes: attributes:
...@@ -315,7 +315,7 @@ ...@@ -315,7 +315,7 @@
:type: new :type: new
:number: 15 :number: 15
:text: | :text: |
+<span id="LC15" class="line"> <span class="s2">&quot;PWD&quot;</span> <span class="o">=&gt;</span> <span class="n">path</span></span> +<span id="LC15" class="line"> <span class="s2">"PWD"</span> <span class="o">=&gt;</span> <span class="n">path</span></span>
:line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_15 :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_15
:position: !ruby/object:Gitlab::Diff::Position :position: !ruby/object:Gitlab::Diff::Position
attributes: attributes:
...@@ -623,7 +623,7 @@ ...@@ -623,7 +623,7 @@
:type: :type:
:number: 20 :number: 20
:text: |2 :text: |2
<span id="LC26" class="line"> <span class="vi">@cmd_output</span> <span class="o">=</span> <span class="s2">&quot;&quot;</span></span> <span id="LC26" class="line"> <span class="vi">@cmd_output</span> <span class="o">=</span> <span class="s2">""</span></span>
:line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_20_26 :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_20_26
:position: !ruby/object:Gitlab::Diff::Position :position: !ruby/object:Gitlab::Diff::Position
attributes: attributes:
...@@ -638,7 +638,7 @@ ...@@ -638,7 +638,7 @@
:type: :type:
:number: 26 :number: 26
:text: |2 :text: |2
<span id="LC26" class="line"> <span class="vi">@cmd_output</span> <span class="o">=</span> <span class="s2">&quot;&quot;</span></span> <span id="LC26" class="line"> <span class="vi">@cmd_output</span> <span class="o">=</span> <span class="s2">""</span></span>
:line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_20_26 :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_20_26
:position: !ruby/object:Gitlab::Diff::Position :position: !ruby/object:Gitlab::Diff::Position
attributes: attributes:
......
...@@ -16,19 +16,19 @@ describe BlobHelper do ...@@ -16,19 +16,19 @@ describe BlobHelper do
describe '#highlight' do describe '#highlight' do
it 'should return plaintext for unknown lexer context' do it 'should return plaintext for unknown lexer context' do
result = helper.highlight(blob_name, no_context_content, nowrap: true) result = helper.highlight(blob_name, no_context_content)
expect(result).to eq('<span id="LC1" class="line">:type &quot;assem&quot;))</span>') expect(result).to eq(%[<pre class="code highlight"><code><span id="LC1" class="line">:type "assem"))</span></code></pre>])
end end
it 'should highlight single block' do it 'should highlight single block' do
expected = %Q[<span id="LC1" class="line"><span class="p">(</span><span class="nb">make-pathname</span> <span class="ss">:defaults</span> <span class="nv">name</span></span> expected = %Q[<pre class="code highlight"><code><span id="LC1" class="line"><span class="p">(</span><span class="nb">make-pathname</span> <span class="ss">:defaults</span> <span class="nv">name</span></span>
<span id="LC2" class="line"><span class="ss">:type</span> <span class="s">&quot;assem&quot;</span><span class="p">))</span></span>] <span id="LC2" class="line"><span class="ss">:type</span> <span class="s">"assem"</span><span class="p">))</span></span></code></pre>]
expect(helper.highlight(blob_name, blob_content, nowrap: true)).to eq(expected) expect(helper.highlight(blob_name, blob_content)).to eq(expected)
end end
it 'should highlight multi-line comments' do it 'should highlight multi-line comments' do
result = helper.highlight(blob_name, multiline_content, nowrap: true) result = helper.highlight(blob_name, multiline_content)
html = Nokogiri::HTML(result) html = Nokogiri::HTML(result)
lines = html.search('.s') lines = html.search('.s')
expect(lines.count).to eq(3) expect(lines.count).to eq(3)
...@@ -41,33 +41,19 @@ describe BlobHelper do ...@@ -41,33 +41,19 @@ describe BlobHelper do
let(:blob_name) { 'test.diff' } let(:blob_name) { 'test.diff' }
let(:blob_content) { "+aaa\n+bbb\n- ccc\n ddd\n"} let(:blob_content) { "+aaa\n+bbb\n- ccc\n ddd\n"}
let(:expected) do let(:expected) do
%q(<span id="LC1" class="line"><span class="gi">+aaa</span></span> %q(<pre class="code highlight"><code><span id="LC1" class="line"><span class="gi">+aaa</span></span>
<span id="LC2" class="line"><span class="gi">+bbb</span></span> <span id="LC2" class="line"><span class="gi">+bbb</span></span>
<span id="LC3" class="line"><span class="gd">- ccc</span></span> <span id="LC3" class="line"><span class="gd">- ccc</span></span>
<span id="LC4" class="line"> ddd</span>) <span id="LC4" class="line"> ddd</span></code></pre>)
end end
it 'should highlight each line properly' do it 'should highlight each line properly' do
result = helper.highlight(blob_name, blob_content, nowrap: true) result = helper.highlight(blob_name, blob_content)
expect(result).to eq(expected) expect(result).to eq(expected)
end end
end end
end end
describe "#highlighter" do
it 'should highlight continued blocks' do
# Both lines have LC1 as ID since formatter doesn't support continue at the moment
expected = [
'<span id="LC1" class="line"><span class="p">(</span><span class="nb">make-pathname</span> <span class="ss">:defaults</span> <span class="nv">name</span></span>',
'<span id="LC1" class="line"><span class="ss">:type</span> <span class="s">&quot;assem&quot;</span><span class="p">))</span></span>'
]
highlighter = helper.highlighter(blob_name, blob_content, nowrap: true)
result = split_content.map{ |content| highlighter.highlight(content) }
expect(result).to eq(expected)
end
end
describe "#sanitize_svg" do describe "#sanitize_svg" do
let(:input_svg_path) { File.join(Rails.root, 'spec', 'fixtures', 'unsanitized.svg') } let(:input_svg_path) { File.join(Rails.root, 'spec', 'fixtures', 'unsanitized.svg') }
let(:data) { open(input_svg_path).read } let(:data) { open(input_svg_path).read }
......
...@@ -57,7 +57,7 @@ describe EventsHelper do ...@@ -57,7 +57,7 @@ describe EventsHelper do
expected = '<pre class="code highlight js-syntax-highlight ruby">' \ expected = '<pre class="code highlight js-syntax-highlight ruby">' \
"<code><span class=\"k\">def</span> <span class=\"nf\">test</span>\n" \ "<code><span class=\"k\">def</span> <span class=\"nf\">test</span>\n" \
" <span class=\"s1\">\'hello world\'</span>\n" \ " <span class=\"s1\">\'hello world\'</span>\n" \
"<span class=\"k\">end</span>" \ "<span class=\"k\">end</span>\n" \
'</code></pre>' '</code></pre>'
expect(helper.event_note(input)).to eq(expected) expect(helper.event_note(input)).to eq(expected)
end end
......
...@@ -5,13 +5,12 @@ ...@@ -5,13 +5,12 @@
#= require ./mock_u2f_device #= require ./mock_u2f_device
describe 'U2FAuthenticate', -> describe 'U2FAuthenticate', ->
U2FUtil.enableTestMode()
fixture.load('u2f/authenticate') fixture.load('u2f/authenticate')
beforeEach -> beforeEach ->
@u2fDevice = new MockU2FDevice @u2fDevice = new MockU2FDevice
@container = $("#js-authenticate-u2f") @container = $("#js-authenticate-u2f")
@component = new U2FAuthenticate(@container, {}, "token") @component = new U2FAuthenticate(@container, {sign_requests: []}, "token")
@component.start() @component.start()
it 'allows authenticating via a U2F device', -> it 'allows authenticating via a U2F device', ->
......
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
#= require ./mock_u2f_device #= require ./mock_u2f_device
describe 'U2FRegister', -> describe 'U2FRegister', ->
U2FUtil.enableTestMode()
fixture.load('u2f/register') fixture.load('u2f/register')
beforeEach -> beforeEach ->
......
...@@ -5,11 +5,11 @@ describe Banzai::Filter::SyntaxHighlightFilter, lib: true do ...@@ -5,11 +5,11 @@ describe Banzai::Filter::SyntaxHighlightFilter, lib: true do
it 'highlights valid code blocks' do it 'highlights valid code blocks' do
result = filter('<pre><code>def fun end</code>') result = filter('<pre><code>def fun end</code>')
expect(result.to_html).to eq("<pre class=\"code highlight js-syntax-highlight plaintext\"><code>def fun end</code></pre>\n") expect(result.to_html).to eq("<pre class=\"code highlight js-syntax-highlight plaintext\"><code>def fun end</code></pre>")
end end
it 'passes through invalid code blocks' do it 'passes through invalid code blocks' do
allow_any_instance_of(described_class).to receive(:block_code).and_raise(StandardError) allow_any_instance_of(Rouge::Formatter).to receive(:format).and_raise(StandardError)
result = filter('<pre><code>This is a test</code></pre>') result = filter('<pre><code>This is a test</code></pre>')
expect(result.to_html).to eq('<pre>This is a test</pre>') expect(result.to_html).to eq('<pre>This is a test</pre>')
......
...@@ -28,13 +28,13 @@ describe Gitlab::Diff::Highlight, lib: true do ...@@ -28,13 +28,13 @@ describe Gitlab::Diff::Highlight, lib: true do
end end
it 'highlights and marks removed lines' do it 'highlights and marks removed lines' do
code = %Q{-<span id="LC9" class="line"> <span class="k">raise</span> <span class="s2">&quot;System commands must be given as an array of strings&quot;</span></span>\n} code = %Q{-<span id="LC9" class="line"> <span class="k">raise</span> <span class="s2">"System commands must be given as an array of strings"</span></span>\n}
expect(subject[4].text).to eq(code) expect(subject[4].text).to eq(code)
end end
it 'highlights and marks added lines' do it 'highlights and marks added lines' do
code = %Q{+<span id="LC9" class="line"> <span class="k">raise</span> <span class="no"><span class='idiff left'>RuntimeError</span></span><span class="p"><span class='idiff'>,</span></span><span class='idiff right'> </span><span class="s2">&quot;System commands must be given as an array of strings&quot;</span></span>\n} code = %Q{+<span id="LC9" class="line"> <span class="k">raise</span> <span class="no"><span class='idiff left'>RuntimeError</span></span><span class="p"><span class='idiff'>,</span></span><span class='idiff right'> </span><span class="s2">"System commands must be given as an array of strings"</span></span>\n}
expect(subject[5].text).to eq(code) expect(subject[5].text).to eq(code)
end end
......
require 'spec_helper'
describe Gitlab::ImportExport, services: true do
describe 'export filename' do
let(:project) { create(:project, :public, path: 'project-path') }
it 'contains the project path' do
expect(described_class.export_filename(project: project)).to include(project.path)
end
it 'contains the namespace path' do
expect(described_class.export_filename(project: project)).to include(project.namespace.path)
end
it 'does not go over a certain length' do
project.path = 'a' * 100
expect(described_class.export_filename(project: project).length).to be < 70
end
end
end
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
"deleted_at": null, "deleted_at": null,
"due_date": null, "due_date": null,
"moved_to_id": null, "moved_to_id": null,
"test_ee_field": "test",
"notes": [ "notes": [
{ {
"id": 351, "id": 351,
......
...@@ -16,10 +16,10 @@ describe LegacyDiffNote, models: true do ...@@ -16,10 +16,10 @@ describe LegacyDiffNote, models: true do
end end
describe '#active?' do describe '#active?' do
it 'is always true when the note has no associated diff' do it 'is always true when the note has no associated diff line' do
note = build(:legacy_diff_note_on_merge_request) note = build(:legacy_diff_note_on_merge_request)
expect(note).to receive(:diff).and_return(nil) expect(note).to receive(:diff_line).and_return(nil)
expect(note).to be_active expect(note).to be_active
end end
...@@ -27,7 +27,7 @@ describe LegacyDiffNote, models: true do ...@@ -27,7 +27,7 @@ describe LegacyDiffNote, models: true do
it 'is never true when the note has no noteable associated' do it 'is never true when the note has no noteable associated' do
note = build(:legacy_diff_note_on_merge_request) note = build(:legacy_diff_note_on_merge_request)
expect(note).to receive(:diff).and_return(double) expect(note).to receive(:diff_line).and_return(double)
expect(note).to receive(:noteable).and_return(nil) expect(note).to receive(:noteable).and_return(nil)
expect(note).not_to be_active expect(note).not_to be_active
...@@ -47,7 +47,7 @@ describe LegacyDiffNote, models: true do ...@@ -47,7 +47,7 @@ describe LegacyDiffNote, models: true do
merge = build_stubbed(:merge_request, :simple) merge = build_stubbed(:merge_request, :simple)
note = build(:legacy_diff_note_on_merge_request, noteable: merge) note = build(:legacy_diff_note_on_merge_request, noteable: merge)
allow(note).to receive(:diff).and_return(double) allow(note).to receive(:diff_line).and_return(double)
expect(note).to receive(:find_noteable_diff).and_return(nil) expect(note).to receive(:find_noteable_diff).and_return(nil)
expect(note).not_to be_active expect(note).not_to be_active
......
...@@ -35,8 +35,11 @@ describe TodoService, services: true do ...@@ -35,8 +35,11 @@ describe TodoService, services: true do
should_not_create_any_todo { service.new_issue(unassigned_issue, author) } should_not_create_any_todo { service.new_issue(unassigned_issue, author) }
end end
it 'does not create a todo if assignee is the current user' do it 'creates a todo if assignee is the current user' do
should_not_create_any_todo { service.new_issue(unassigned_issue, john_doe) } unassigned_issue.update_attribute(:assignee, john_doe)
service.new_issue(unassigned_issue, john_doe)
should_create_todo(user: john_doe, target: unassigned_issue, author: john_doe, action: Todo::ASSIGNED)
end end
it 'creates a todo for each valid mentioned user' do it 'creates a todo for each valid mentioned user' do
...@@ -44,7 +47,7 @@ describe TodoService, services: true do ...@@ -44,7 +47,7 @@ describe TodoService, services: true do
should_create_todo(user: member, target: issue, action: Todo::MENTIONED) should_create_todo(user: member, target: issue, action: Todo::MENTIONED)
should_create_todo(user: guest, target: issue, action: Todo::MENTIONED) should_create_todo(user: guest, target: issue, action: Todo::MENTIONED)
should_not_create_todo(user: author, target: issue, action: Todo::MENTIONED) should_create_todo(user: author, target: issue, action: Todo::MENTIONED)
should_not_create_todo(user: john_doe, target: issue, action: Todo::MENTIONED) should_not_create_todo(user: john_doe, target: issue, action: Todo::MENTIONED)
should_not_create_todo(user: non_member, target: issue, action: Todo::MENTIONED) should_not_create_todo(user: non_member, target: issue, action: Todo::MENTIONED)
end end
...@@ -57,7 +60,7 @@ describe TodoService, services: true do ...@@ -57,7 +60,7 @@ describe TodoService, services: true do
should_create_todo(user: member, target: confidential_issue, author: john_doe, action: Todo::MENTIONED) should_create_todo(user: member, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED) should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
should_not_create_todo(user: guest, target: confidential_issue, author: john_doe, action: Todo::MENTIONED) should_not_create_todo(user: guest, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
should_not_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED) should_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
end end
context 'when a private group is mentioned' do context 'when a private group is mentioned' do
...@@ -87,7 +90,7 @@ describe TodoService, services: true do ...@@ -87,7 +90,7 @@ describe TodoService, services: true do
should_create_todo(user: member, target: issue, action: Todo::MENTIONED) should_create_todo(user: member, target: issue, action: Todo::MENTIONED)
should_create_todo(user: guest, target: issue, action: Todo::MENTIONED) should_create_todo(user: guest, target: issue, action: Todo::MENTIONED)
should_create_todo(user: john_doe, target: issue, action: Todo::MENTIONED) should_create_todo(user: john_doe, target: issue, action: Todo::MENTIONED)
should_not_create_todo(user: author, target: issue, action: Todo::MENTIONED) should_create_todo(user: author, target: issue, action: Todo::MENTIONED)
should_not_create_todo(user: non_member, target: issue, action: Todo::MENTIONED) should_not_create_todo(user: non_member, target: issue, action: Todo::MENTIONED)
end end
...@@ -105,7 +108,7 @@ describe TodoService, services: true do ...@@ -105,7 +108,7 @@ describe TodoService, services: true do
should_create_todo(user: member, target: confidential_issue, author: john_doe, action: Todo::MENTIONED) should_create_todo(user: member, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED) should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
should_not_create_todo(user: guest, target: confidential_issue, author: john_doe, action: Todo::MENTIONED) should_not_create_todo(user: guest, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
should_not_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED) should_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
end end
context 'issues with a task list' do context 'issues with a task list' do
...@@ -156,10 +159,11 @@ describe TodoService, services: true do ...@@ -156,10 +159,11 @@ describe TodoService, services: true do
should_not_create_any_todo { service.reassigned_issue(issue, author) } should_not_create_any_todo { service.reassigned_issue(issue, author) }
end end
it 'does not create a todo if new assignee is the current user' do it 'creates a todo if new assignee is the current user' do
unassigned_issue.update_attribute(:assignee, john_doe) unassigned_issue.update_attribute(:assignee, john_doe)
service.reassigned_issue(unassigned_issue, john_doe)
should_not_create_any_todo { service.reassigned_issue(unassigned_issue, john_doe) } should_create_todo(user: john_doe, target: unassigned_issue, author: john_doe, action: Todo::ASSIGNED)
end end
end end
...@@ -250,7 +254,7 @@ describe TodoService, services: true do ...@@ -250,7 +254,7 @@ describe TodoService, services: true do
should_create_todo(user: member, target: issue, author: john_doe, action: Todo::MENTIONED, note: note) should_create_todo(user: member, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
should_create_todo(user: guest, target: issue, author: john_doe, action: Todo::MENTIONED, note: note) should_create_todo(user: guest, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
should_create_todo(user: author, target: issue, author: john_doe, action: Todo::MENTIONED, note: note) should_create_todo(user: author, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
should_not_create_todo(user: john_doe, target: issue, author: john_doe, action: Todo::MENTIONED, note: note) should_create_todo(user: john_doe, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
should_not_create_todo(user: non_member, target: issue, author: john_doe, action: Todo::MENTIONED, note: note) should_not_create_todo(user: non_member, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
end end
...@@ -262,7 +266,7 @@ describe TodoService, services: true do ...@@ -262,7 +266,7 @@ describe TodoService, services: true do
should_create_todo(user: member, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue) should_create_todo(user: member, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue) should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
should_not_create_todo(user: guest, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue) should_not_create_todo(user: guest, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
should_not_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue) should_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
end end
it 'creates a todo for each valid mentioned user when leaving a note on commit' do it 'creates a todo for each valid mentioned user when leaving a note on commit' do
...@@ -270,7 +274,7 @@ describe TodoService, services: true do ...@@ -270,7 +274,7 @@ describe TodoService, services: true do
should_create_todo(user: member, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit) should_create_todo(user: member, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit)
should_create_todo(user: author, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit) should_create_todo(user: author, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit)
should_not_create_todo(user: john_doe, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit) should_create_todo(user: john_doe, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit)
should_not_create_todo(user: non_member, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit) should_not_create_todo(user: non_member, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit)
end end
...@@ -312,7 +316,7 @@ describe TodoService, services: true do ...@@ -312,7 +316,7 @@ describe TodoService, services: true do
should_create_todo(user: member, target: mr_assigned, action: Todo::MENTIONED) should_create_todo(user: member, target: mr_assigned, action: Todo::MENTIONED)
should_create_todo(user: guest, target: mr_assigned, action: Todo::MENTIONED) should_create_todo(user: guest, target: mr_assigned, action: Todo::MENTIONED)
should_not_create_todo(user: author, target: mr_assigned, action: Todo::MENTIONED) should_create_todo(user: author, target: mr_assigned, action: Todo::MENTIONED)
should_not_create_todo(user: john_doe, target: mr_assigned, action: Todo::MENTIONED) should_not_create_todo(user: john_doe, target: mr_assigned, action: Todo::MENTIONED)
should_not_create_todo(user: non_member, target: mr_assigned, action: Todo::MENTIONED) should_not_create_todo(user: non_member, target: mr_assigned, action: Todo::MENTIONED)
end end
...@@ -325,7 +329,7 @@ describe TodoService, services: true do ...@@ -325,7 +329,7 @@ describe TodoService, services: true do
should_create_todo(user: member, target: mr_assigned, action: Todo::MENTIONED) should_create_todo(user: member, target: mr_assigned, action: Todo::MENTIONED)
should_create_todo(user: guest, target: mr_assigned, action: Todo::MENTIONED) should_create_todo(user: guest, target: mr_assigned, action: Todo::MENTIONED)
should_create_todo(user: john_doe, target: mr_assigned, action: Todo::MENTIONED) should_create_todo(user: john_doe, target: mr_assigned, action: Todo::MENTIONED)
should_not_create_todo(user: author, target: mr_assigned, action: Todo::MENTIONED) should_create_todo(user: author, target: mr_assigned, action: Todo::MENTIONED)
should_not_create_todo(user: non_member, target: mr_assigned, action: Todo::MENTIONED) should_not_create_todo(user: non_member, target: mr_assigned, action: Todo::MENTIONED)
end end
...@@ -382,10 +386,11 @@ describe TodoService, services: true do ...@@ -382,10 +386,11 @@ describe TodoService, services: true do
should_not_create_any_todo { service.reassigned_merge_request(mr_assigned, author) } should_not_create_any_todo { service.reassigned_merge_request(mr_assigned, author) }
end end
it 'does not create a todo if new assignee is the current user' do it 'creates a todo if new assignee is the current user' do
mr_assigned.update_attribute(:assignee, john_doe) mr_assigned.update_attribute(:assignee, john_doe)
service.reassigned_merge_request(mr_assigned, john_doe)
should_not_create_any_todo { service.reassigned_merge_request(mr_assigned, john_doe) } should_create_todo(user: john_doe, target: mr_assigned, author: john_doe, action: Todo::ASSIGNED)
end end
end end
...@@ -435,6 +440,24 @@ describe TodoService, services: true do ...@@ -435,6 +440,24 @@ describe TodoService, services: true do
should_create_todo(user: author, target: mr_unassigned, action: Todo::MARKED) should_create_todo(user: author, target: mr_unassigned, action: Todo::MARKED)
end end
end end
describe '#new_note' do
let(:mention) { john_doe.to_reference }
let(:diff_note_on_merge_request) { create(:diff_note_on_merge_request, project: project, noteable: mr_unassigned, author: author, note: "Hey #{mention}") }
let(:legacy_diff_note_on_merge_request) { create(:legacy_diff_note_on_merge_request, project: project, noteable: mr_unassigned, author: author, note: "Hey #{mention}") }
it 'creates a todo for mentioned user on new diff note' do
service.new_note(diff_note_on_merge_request, author)
should_create_todo(user: john_doe, target: mr_unassigned, author: author, action: Todo::MENTIONED, note: diff_note_on_merge_request)
end
it 'creates a todo for mentioned user on legacy diff note' do
service.new_note(legacy_diff_note_on_merge_request, author)
should_create_todo(user: john_doe, target: mr_unassigned, author: author, action: Todo::MENTIONED, note: legacy_diff_note_on_merge_request)
end
end
end end
it 'updates cached counts when a todo is created' do it 'updates cached counts when a todo is created' do
......
...@@ -18,8 +18,8 @@ class FakeU2fDevice ...@@ -18,8 +18,8 @@ class FakeU2fDevice
def respond_to_u2f_authentication def respond_to_u2f_authentication
app_id = @page.evaluate_script('gon.u2f.app_id') app_id = @page.evaluate_script('gon.u2f.app_id')
challenges = @page.evaluate_script('gon.u2f.challenges') challenge = @page.evaluate_script('gon.u2f.challenge')
json_response = u2f_device(app_id).sign_response(challenges[0]) json_response = u2f_device(app_id).sign_response(challenge)
@page.execute_script(" @page.execute_script("
u2f.sign = function(appId, challenges, signRequests, callback) { u2f.sign = function(appId, challenges, signRequests, callback) {
......
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