Commit 66509917 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'ce-to-ee' into 'master'

Recent changes from master

See merge request !366
parents 7a73c974 9311f862
Please view this file on the master branch, on stable branches it's out of date.
v 7.10.0 (unreleased)
- Fix broken side-by-side diff view on merge request page (Stan Hu)
- Set Application controller default URL options to ensure all url_for calls are consistent (Stan Hu)
- Allow HTML tags in Markdown input
- Fix code unfold not working on Compare commits page (Stan Hu)
- Include missing events and fix save functionality in admin service template settings form (Stan Hu)
- Fix "Import projects from" button to show the correct instructions (Stan Hu)
- Fix dots in Wiki slugs causing errors (Stan Hu)
......@@ -25,6 +29,7 @@ v 7.10.0 (unreleased)
- Passing the name of pushed ref to CI service (requires GitLab CI 7.9+)
- Add location field to user profile
- Fix print view for markdown files and wiki pages
- Fix errors when deleting old backups
- Improve GitLab performance when working with git repositories
- Add tag message and last commit to tag hook (Kamil Trzciński)
- Restrict permissions on backup files
......@@ -40,7 +45,18 @@ v 7.9.0 (unreleased)
- Don't mark merge request as updated when merge status relative to target branch changes.
- Link note avatar to user.
- Make Git-over-SSH errors more descriptive.
- Fix EmailsOnPush.
- Refactor issue filtering
- AJAX selectbox for issue assignee and author filters
- Fix issue with missing options in issue filtering dropdown if selected one
- Prevent holding Control-Enter or Command-Enter from posting comment multiple times.
- Improve file icons rendering on tree (Sullivan Sénéchal)
v 7.9.0
- Send EmailsOnPush email when branch or tag is created or deleted.
- Faster merge request processing for large repository
- Prevent doubling AJAX request with each commit visit via Turbolink
- Prevent unnecessary doubling of js events on import pages and user calendar
v 7.9.0
- Add HipChat integration documentation (Stan Hu)
......
@Api =
groups_path: "/api/:version/groups.json"
group_path: "/api/:version/groups/:id.json"
users_path: "/api/:version/users.json"
user_path: "/api/:version/users/:id.json"
notes_path: "/api/:version/projects/:id/notes.json"
projects_path: "/api/:version/projects.json"
ldap_groups_path: "/api/:version/ldap/:provider/groups.json"
namespaces_path: "/api/:version/namespaces.json"
project_users_path: "/api/:version/projects/:id/users.json"
projects_path: "/api/:version/projects.json"
# Get 20 (depends on api) recent notes
# and sort the ascending from oldest to newest
notes: (project_id, callback) ->
url = Api.buildUrl(Api.notes_path)
url = url.replace(':id', project_id)
$.ajax(
url: url,
data:
private_token: gon.api_token
gfm: true
recent: true
dataType: "json"
).done (notes) ->
notes.sort (a, b) ->
return a.id - b.id
callback(notes)
user: (user_id, callback) ->
url = Api.buildUrl(Api.user_path)
url = url.replace(':id', user_id)
$.ajax(
url: url
data:
private_token: gon.api_token
dataType: "json"
).done (user) ->
callback(user)
# Return users list. Filtered by query
# Only active users retrieved
users: (query, skip_ldap, callback) ->
url = Api.buildUrl(Api.users_path)
$.ajax(
url: url
data:
private_token: gon.api_token
search: query
per_page: 20
active: true
skip_ldap: skip_ldap
dataType: "json"
).done (users) ->
callback(users)
group: (group_id, callback) ->
url = Api.buildUrl(Api.group_path)
......@@ -83,23 +32,6 @@
).done (groups) ->
callback(groups)
# Return project users list. Filtered by query
# Only active users retrieved
projectUsers: (project_id, query, callback) ->
url = Api.buildUrl(Api.project_users_path)
url = url.replace(':id', project_id)
$.ajax(
url: url
data:
private_token: gon.api_token
search: query
per_page: 20
active: true
dataType: "json"
).done (users) ->
callback(users)
# Return namespaces list. Filtered by query
namespaces: (query, callback) ->
url = Api.buildUrl(Api.namespaces_path)
......
class @calendar
class @Calendar
options =
month: "short"
day: "numeric"
......
......@@ -28,6 +28,8 @@ class Dispatcher
new Milestone()
when 'projects:milestones:new', 'projects:milestones:edit'
new ZenMode()
when 'projects:compare:show'
new Diff()
when 'projects:issues:new','projects:issues:edit'
GitLab.GfmAutoComplete.setup()
shortcut_handler = new ShortcutsNavigation()
......@@ -119,6 +121,8 @@ class Dispatcher
new Project()
new ProjectAvatar()
switch path[1]
when 'compare'
shortcut_handler = new ShortcutsNavigation()
when 'edit'
shortcut_handler = new ShortcutsNavigation()
new ProjectNew()
......@@ -127,7 +131,7 @@ class Dispatcher
when 'show'
new ProjectShow()
when 'issues', 'merge_requests'
new ProjectUsersSelect()
new UsersSelect()
when 'wikis'
new Wikis()
shortcut_handler = new ShortcutsNavigation()
......
......@@ -57,6 +57,7 @@ class @Notes
@notes_forms = '.js-main-target-form textarea, .js-discussion-note-form textarea'
# Chrome doesn't fire keypress or keyup for Command+Enter, so we need keydown.
$(document).on('keydown', @notes_forms, (e) ->
return if e.originalEvent.repeat
if e.keyCode == 10 || ((e.metaKey || e.ctrlKey) && e.keyCode == 13)
$(@).parents('form').submit()
)
......
class @ProjectUsersSelect
constructor: ->
$('.ajax-project-users-select').each (i, select) =>
project_id = $(select).data('project-id') || $('body').data('project-id')
$(select).select2
placeholder: $(select).data('placeholder') || "Search for a user"
multiple: $(select).hasClass('multiselect')
minimumInputLength: 0
query: (query) ->
Api.projectUsers project_id, query.term, (users) ->
data = { results: users }
if query.term.length == 0
nullUser = {
name: 'Unassigned',
avatar: null,
username: 'none',
id: -1
}
data.results.unshift(nullUser)
query.callback(data)
initSelection: (element, callback) ->
id = $(element).val()
if id != "" && id != "-1"
Api.user(id, callback)
formatResult: (args...) =>
@formatResult(args...)
formatSelection: (args...) =>
@formatSelection(args...)
dropdownCssClass: "ajax-project-users-dropdown"
dropdownAutoWidth: true
escapeMarkup: (m) -> # we do not want to escape markup since we are displaying html in results
m
formatResult: (user) ->
if user.avatar_url
avatar = user.avatar_url
else
avatar = gon.default_avatar_url
avatarMarkup = "<div class='user-image'><img class='avatar s24' src='#{avatar}'></div>"
"<div class='user-result'>
#{avatarMarkup}
<div class='user-name'>#{user.name}</div>
<div class='user-username'>#{user.username}</div>
</div>"
formatSelection: (user) ->
user.name
$ ->
$(":checkbox").change ->
$(".protected-branches-list :checkbox").change (e) ->
name = $(this).attr("name")
if name == "developers_can_push"
id = $(this).val()
......@@ -14,8 +14,8 @@ $ ->
developers_can_push: checked
success: ->
new Flash("Branch updated.", "notice")
location.reload true
row = $(e.target)
row.closest('tr').effect('highlight')
error: ->
new Flash("Failed to update branch!", "alert")
class @UsersSelect
constructor: ->
@usersPath = "/autocomplete/users.json"
@userPath = "/autocomplete/users/:id.json"
$('.ajax-users-select').each (i, select) =>
skip_ldap = $(select).hasClass('skip_ldap')
@skipLdap = $(select).hasClass('skip_ldap')
@projectId = $(select).data('project-id')
@groupId = $(select).data('group-id')
showNullUser = $(select).data('null-user')
showAnyUser = $(select).data('any-user')
$(select).select2
placeholder: "Search for a user"
multiple: $(select).hasClass('multiselect')
minimumInputLength: 0
query: (query) ->
Api.users query.term, skip_ldap, (users) ->
query: (query) =>
@users query.term, (users) =>
data = { results: users }
if query.term.length == 0
anyUser = {
name: 'Any',
avatar: null,
username: 'none',
id: null
}
nullUser = {
name: 'Unassigned',
avatar: null,
username: 'none',
id: 0
}
if showNullUser
data.results.unshift(nullUser)
if showAnyUser
data.results.unshift(anyUser)
query.callback(data)
initSelection: (element, callback) ->
initSelection: (element, callback) =>
id = $(element).val()
if id isnt ""
Api.user(id, callback)
if id != "" && id != "0"
@user(id, callback)
formatResult: (args...) =>
@formatResult(args...)
......@@ -39,4 +66,36 @@ class @UsersSelect
</div>"
formatSelection: (user) ->
user.name
\ No newline at end of file
user.name
user: (user_id, callback) =>
url = @buildUrl(@userPath)
url = url.replace(':id', user_id)
$.ajax(
url: url
dataType: "json"
).done (user) ->
callback(user)
# Return users list. Filtered by query
# Only active users retrieved
users: (query, callback) =>
url = @buildUrl(@usersPath)
$.ajax(
url: url
data:
search: query
per_page: 20
active: true
project_id: @projectId
group_id: @groupId
skip_ldap: @skipLdap
dataType: "json"
).done (users) ->
callback(users)
buildUrl: (url) ->
url = gon.relative_url_root + url if gon.relative_url_root?
return url
......@@ -182,6 +182,7 @@
.panel-heading {
padding: 6px 15px;
font-size: 13px;
font-weight: normal;
a {
color: #777;
}
......
......@@ -115,6 +115,12 @@ $panel-default-border: $border-color;
$panel-default-heading-bg: $background-color;
//== Wells
//
//##
$well-bg: #F9F9F9;
$well-border: #EEE;
//== Code
//
......
.filter-item {
margin-right: 15px;
}
.issues-state-filters {
li.active a {
border-color: #DDD !important;
&, &:hover, &:active, &.active {
background: #f5f5f5 !important;
border-bottom: 1px solid #f5f5f5 !important;
}
}
}
.issues-details-filters {
font-size: 13px;
background: #f5f5f5;
margin: -10px 0;
padding: 10px 15px;
margin-top: -15px;
border-left: 1px solid #DDD;
border-right: 1px solid #DDD;
.btn {
font-size: 13px;
}
}
@media (min-width: 800px) {
.issues-filters,
.issues_bulk_update {
select, .select2-container {
width: 120px !important;
display: inline-block;
}
}
}
@media (min-width: 1200px) {
.issues-filters,
.issues_bulk_update {
select, .select2-container {
width: 150px !important;
display: inline-block;
}
}
}
.issues-filters,
.issues_bulk_update {
.select2-container .select2-choice {
color: #444 !important;
}
}
......@@ -2,20 +2,25 @@
.select2-container, .select2-container.select2-drop-above {
.select2-choice {
background: #FFF;
border-color: #CCC;
border-color: #DDD;
height: 34px;
padding: 6px 14px;
font-size: 14px;
line-height: 1.42857143;
height: auto;
@include border-radius(4px);
.select2-arrow {
background: #FFF;
border-left: 1px solid #DDD;
border-left: none;
padding-top: 3px;
}
}
}
.select2-container-multi .select2-choices {
@include border-radius(4px)
@include border-radius(4px);
border-color: #CCC;
}
.select2-container-multi .select2-choices .select2-search-field input {
......@@ -28,6 +33,7 @@
.select2-drop-active {
border: 1px solid #BBB !important;
margin-top: 4px;
font-size: 13px;
&.select2-drop-above {
margin-bottom: 8px;
......@@ -115,3 +121,7 @@
font-weight: bolder;
}
}
.ajax-users-dropdown {
min-width: 225px !important;
}
......@@ -41,12 +41,9 @@
}
.check-all-holder {
height: 36px;
line-height: 36px;
float: left;
margin-right: 12px;
padding: 6px 15px;
border: 1px solid #ccc;
@include border-radius(4px);
margin-right: 15px;
}
.issues_content {
......@@ -59,30 +56,6 @@
}
}
@media (min-width: 800px) {
.issues_bulk_update {
select, .select2-container {
width: 120px !important;
display: inline-block;
}
}
}
@media (min-width: 1200px) {
.issues_bulk_update {
select, .select2-container {
width: 160px !important;
display: inline-block;
}
}
}
.issues_bulk_update {
.select2-container .select2-choice {
color: #444 !important;
}
}
.participants {
margin-bottom: 20px;
}
......@@ -120,12 +93,12 @@ form.edit-issue {
}
&.closed {
background: #F5f5f5;
background: #F9F9F9;
border-color: #E5E5E5;
}
&.merged {
background: #F5f5f5;
background: #F9F9F9;
border-color: #E5E5E5;
}
}
......
......@@ -90,8 +90,13 @@ ul.notes {
}
// Diff code in discussion view
.discussion-body .diff-file .line_content {
white-space: pre-wrap;
.discussion-body .diff-file {
.diff-header > span {
margin-right: 10px;
}
.line_content {
white-space: pre-wrap;
}
}
.diff-file .notes_holder {
......
......@@ -178,6 +178,18 @@ class ApplicationController < ActionController::Base
response.headers["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT"
end
def default_url_options
if !Rails.env.test?
port = Gitlab.config.gitlab.port unless Gitlab.config.gitlab_on_standard_port?
{ host: Gitlab.config.gitlab.host,
protocol: Gitlab.config.gitlab.protocol,
port: port,
script_name: Gitlab.config.gitlab.relative_url_root }
else
super
end
end
def default_headers
headers['X-Frame-Options'] = 'DENY'
headers['X-XSS-Protection'] = '1; mode=block'
......
class AutocompleteController < ApplicationController
def users
@users =
if params[:project_id].present?
project = Project.find(params[:project_id])
if can?(current_user, :read_project, project)
project.team.users
end
elsif params[:group_id]
group = Group.find(params[:group_id])
if can?(current_user, :read_group, group)
group.users
end
else
User.all
end
@users = @users.non_ldap if params[:skip_ldap] == 'true'
@users = @users.search(params[:search]) if params[:search].present?
@users = @users.active
@users = @users.page(params[:page]).per(PER_PAGE)
render json: @users, only: [:name, :username, :id], methods: [:avatar_url]
end
def user
@user = User.find(params[:id])
render json: @user, only: [:name, :username, :id], methods: [:avatar_url]
end
end
......@@ -19,6 +19,8 @@
require_relative 'projects_finder'
class IssuableFinder
NONE = '0'
attr_accessor :current_user, :params
def execute(current_user, params)
......@@ -112,7 +114,7 @@ class IssuableFinder
def by_milestone(items)
if params[:milestone_id].present?
items = items.where(milestone_id: (params[:milestone_id] == '0' ? nil : params[:milestone_id]))
items = items.where(milestone_id: (params[:milestone_id] == NONE ? nil : params[:milestone_id]))
end
items
......@@ -120,7 +122,7 @@ class IssuableFinder
def by_assignee(items)
if params[:assignee_id].present?
items = items.where(assignee_id: (params[:assignee_id] == '0' ? nil : params[:assignee_id]))
items = items.where(assignee_id: (params[:assignee_id] == NONE ? nil : params[:assignee_id]))
end
items
......@@ -128,7 +130,7 @@ class IssuableFinder
def by_author(items)
if params[:author_id].present?
items = items.where(author_id: (params[:author_id] == '0' ? nil : params[:author_id]))
items = items.where(author_id: (params[:author_id] == NONE ? nil : params[:author_id]))
end
items
......
......@@ -275,7 +275,9 @@ module ApplicationHelper
'https://' + promo_host
end
def page_filter_path(options={})
def page_filter_path(options = {})
without = options.delete(:without)
exist_opts = {
state: params[:state],
scope: params[:scope],
......@@ -288,6 +290,12 @@ module ApplicationHelper
options = exist_opts.merge(options)
if without.present?
without.each do |key|
options.delete(key)
end
end
path = request.path
path << "?#{options.to_param}"
path
......
......@@ -61,4 +61,12 @@ module BlobHelper
'Preview changes'
end
end
# Return an image icon depending on the file mode and extension
#
# mode - File unix mode
# mode - File name
def blob_icon(mode, name)
icon("#{file_type_icon_class('file', mode, name)} fw")
end
end
......@@ -121,6 +121,8 @@ module DiffHelper
def inline_diff_btn
params_copy = params.dup
params_copy[:view] = 'inline'
# Always use HTML to handle case where JSON diff rendered this button
params_copy.delete(:format)
link_to url_for(params_copy), id: "commit-diff-viewtype", class: (params[:view] != 'parallel' ? 'btn btn-sm active' : 'btn btn-sm') do
'Inline'
......@@ -130,6 +132,8 @@ module DiffHelper
def parallel_diff_btn
params_copy = params.dup
params_copy[:view] = 'parallel'
# Always use HTML to handle case where JSON diff rendered this button
params_copy.delete(:format)
link_to url_for(params_copy), id: "commit-diff-viewtype", class: (params[:view] == 'parallel' ? 'btn active btn-sm' : 'btn btn-sm') do
'Side-by-side'
......
......@@ -35,7 +35,6 @@ module GitlabMarkdownHelper
user_color_scheme_class,
{
# see https://github.com/vmg/redcarpet#darling-i-packed-you-a-couple-renderers-for-lunch-
filter_html: true,
with_toc_data: true,
safe_links_only: true
}.merge(options))
......
......@@ -36,4 +36,48 @@ module IconsHelper
def private_icon
icon('lock')
end
def file_type_icon_class(type, mode, name)
if type == 'folder'
icon_class = 'folder'
elsif mode == 0120000
icon_class = 'share'
else
# Guess which icon to choose based on file extension.
# If you think a file extension is missing, feel free to add it on PR
case File.extname(name).downcase
when '.pdf'
icon_class = 'file-pdf-o'
when '.jpg', '.jpeg', '.jif', '.jfif',
'.jp2', '.jpx', '.j2k', '.j2c',
'.png', '.gif', '.tif', '.tiff',
'.svg', '.ico', '.bmp'
icon_class = 'file-image-o'
when '.zip', '.zipx', '.tar', '.gz', '.bz', '.bzip',
'.xz', '.rar', '.7z'
icon_class = 'file-archive-o'
when '.mp3', '.wma', '.ogg', '.oga', '.wav', '.flac', '.aac'
icon_class = 'file-audio-o'
when '.mp4', '.m4p', '.m4v',
'.mpg', '.mp2', '.mpeg', '.mpe', '.mpv',
'.mpg', '.mpeg', '.m2v',
'.avi', '.mkv', '.flv', '.ogv', '.mov',
'.3gp', '.3g2'
icon_class = 'file-video-o'
when '.doc', '.dot', '.docx', '.docm', '.dotx', '.dotm', '.docb'
icon_class = 'file-word-o'
when '.xls', '.xlt', '.xlm', '.xlsx', '.xlsm', '.xltx', '.xltm',
'.xlsb', '.xla', '.xlam', '.xll', '.xlw'
icon_class = 'file-excel-o'
when '.ppt', '.pot', '.pps', '.pptx', '.pptm', '.potx', '.potm',
'.ppam', '.ppsx', '.ppsm', '.sldx', '.sldm'
icon_class = 'file-powerpoint-o'
else
icon_class = 'file-text-o'
end
end
icon_class
end
end
......@@ -47,4 +47,8 @@ module LabelsHelper
"#FFF"
end
end
def project_labels_options(project)
options_from_collection_for_select(project.labels, 'name', 'name', params[:label_name])
end
end
......@@ -19,4 +19,15 @@ module MilestonesHelper
content_tag :div, nil, options
end
end
def projects_milestones_options
milestones =
if @project
@project.milestones
else
Milestone.where(project_id: @projects)
end.active
options_from_collection_for_select(milestones, 'id', 'title', params[:milestone_id])
end
end
......@@ -5,18 +5,27 @@ module SelectsHelper
css_class << "skip_ldap " if opts[:skip_ldap]
css_class << (opts[:class] || '')
value = opts[:selected] || ''
placeholder = opts[:placeholder] || 'Search for a user'
hidden_field_tag(id, value, class: css_class)
end
null_user = opts[:null_user] || false
any_user = opts[:any_user] || false
def project_users_select_tag(id, opts = {})
css_class = "ajax-project-users-select "
css_class << "multiselect " if opts[:multiple]
css_class << (opts[:class] || '')
value = opts[:selected] || ''
placeholder = opts[:placeholder] || 'Select user'
project_id = opts[:project_id] || @project.id
hidden_field_tag(id, value, class: css_class, 'data-placeholder' => placeholder, 'data-project-id' => project_id)
html = {
class: css_class,
'data-placeholder' => placeholder,
'data-null-user' => null_user,
'data-any-user' => any_user,
}
unless opts[:scope] == :all
if @project
html['data-project-id'] = @project.id
elsif @group
html['data-group-id'] = @group.id
end
end
hidden_field_tag(id, value, html)
end
def ldap_server_select_options
......
......@@ -34,12 +34,13 @@ module TreeHelper
end
end
# Return an image icon depending on the file type
# Return an image icon depending on the file type and mode
#
# type - String type of the tree item; either 'folder' or 'file'
def tree_icon(type)
icon_class = type == 'folder' ? 'folder' : 'file-o'
icon(icon_class)
# mode - File unix mode
# name - File name
def tree_icon(type, mode, name)
icon("#{file_type_icon_class(type, mode, name)} fw")
end
def tree_hex_class(content)
......
......@@ -20,7 +20,11 @@
require "addressable/uri"
# Buildbox renamed to Buildkite, but for backwards compatability with the STI
# of Services, the class name is kept as "Buildbox"
class BuildboxService < CiService
ENDPOINT = "https://buildkite.com"
prop_accessor :project_url, :token
validates :project_url, presence: true, if: :activated?
......@@ -29,7 +33,7 @@ class BuildboxService < CiService
after_save :compose_service_hook, if: :activated?
def webhook_url
"#{buildbox_endpoint('webhook')}/deliver/#{webhook_token}"
"#{buildkite_endpoint('webhook')}/deliver/#{webhook_token}"
end
def compose_service_hook
......@@ -59,7 +63,7 @@ class BuildboxService < CiService
end
def commit_status_path(sha)
"#{buildbox_endpoint('gitlab')}/status/#{status_token}.json?commit=#{sha}"
"#{buildkite_endpoint('gitlab')}/status/#{status_token}.json?commit=#{sha}"
end
def build_page(sha, ref)
......@@ -71,11 +75,11 @@ class BuildboxService < CiService
end
def status_img_path
"#{buildbox_endpoint('badge')}/#{status_token}.svg"
"#{buildkite_endpoint('badge')}/#{status_token}.svg"
end
def title
'Buildbox'
'Buildkite'
end
def description
......@@ -83,18 +87,18 @@ class BuildboxService < CiService
end
def to_param
'buildbox'
'buildkite'
end
def fields
[
{ type: 'text',
name: 'token',
placeholder: 'Buildbox project GitLab token' },
placeholder: 'Buildkite project GitLab token' },
{ type: 'text',
name: 'project_url',
placeholder: 'https://buildbox.io/example/project' }
placeholder: "#{ENDPOINT}/example/project" }
]
end
......@@ -116,11 +120,9 @@ class BuildboxService < CiService
end
end
def buildbox_endpoint(subdomain = nil)
endpoint = 'https://buildbox.io'
def buildkite_endpoint(subdomain = nil)
if subdomain.present?
uri = Addressable::URI.parse(endpoint)
uri = Addressable::URI.parse(ENDPOINT)
new_endpoint = "#{uri.scheme || 'http'}://#{subdomain}.#{uri.host}"
if uri.port.present?
......@@ -129,7 +131,7 @@ class BuildboxService < CiService
new_endpoint
end
else
endpoint
ENDPOINT
end
end
end
......@@ -14,8 +14,8 @@ module Issues
issue.update_nth_task(params[:task_num].to_i, false)
end
params[:assignee_id] = "" if params[:assignee_id] == "-1"
params[:milestone_id] = "" if params[:milestone_id] == "-1"
params[:assignee_id] = "" if params[:assignee_id] == IssuableFinder::NONE
params[:milestone_id] = "" if params[:milestone_id] == IssuableFinder::NONE
old_labels = issue.labels.to_a
......
......@@ -23,8 +23,8 @@ module MergeRequests
merge_request.update_nth_task(params[:task_num].to_i, false)
end
params[:assignee_id] = "" if params[:assignee_id] == "-1"
params[:milestone_id] = "" if params[:milestone_id] == "-1"
params[:assignee_id] = "" if params[:assignee_id] == IssuableFinder::NONE
params[:milestone_id] = "" if params[:milestone_id] == IssuableFinder::NONE
old_labels = merge_request.labels.to_a
......
......@@ -43,6 +43,9 @@ module Projects
project.namespace = new_namespace
project.save!
# Notifications
project.send_move_instructions
# Move main repository
unless gitlab_shell.mv_repository(old_path, new_path)
raise TransferError.new('Cannot move project')
......
......@@ -68,6 +68,11 @@
%strong.cred
does not exist
- if @project.archived?
%li
%span.light archived:
%strong repository is read-only
%li
%span.light access:
%strong
......@@ -97,7 +102,7 @@
%strong #{@group.name}
group members (#{@group.group_members.count})
.pull-right
= link_to admin_group_path(@group), class: 'btn btn-sm' do
= link_to admin_group_path(@group), class: 'btn btn-xs' do
%i.fa.fa-pencil-square-o
%ul.well-list
- @group_members.each do |member|
......
......@@ -10,7 +10,7 @@
- @service.errors.full_messages.each do |msg|
%p= msg
- if @service.help.present?
.alert.alert-info
.well
= preserve do
= markdown @service.help
......
......@@ -42,5 +42,4 @@
= button_tag "Import", class: "btn js-add-to-import"
:coffeescript
$ ->
new ImporterStatus("#{jobs_import_bitbucket_path}", "#{import_bitbucket_path}")
new ImporterStatus("#{jobs_import_bitbucket_path}", "#{import_bitbucket_path}")
......@@ -42,5 +42,4 @@
= button_tag "Import", class: "btn js-add-to-import"
:coffeescript
$ ->
new ImporterStatus("#{jobs_import_github_path}", "#{import_github_path}")
new ImporterStatus("#{jobs_import_github_path}", "#{import_github_path}")
......@@ -42,5 +42,4 @@
= button_tag "Import", class: "btn js-add-to-import"
:coffeescript
$ ->
new ImporterStatus("#{jobs_import_gitlab_path}", "#{import_gitlab_path}")
new ImporterStatus("#{jobs_import_gitlab_path}", "#{import_gitlab_path}")
......@@ -42,5 +42,4 @@
= button_tag "Import", class: "btn js-add-to-import"
:coffeescript
$ ->
new ImporterStatus("#{jobs_import_gitorious_path}", "#{import_gitorious_path}")
new ImporterStatus("#{jobs_import_gitorious_path}", "#{import_gitorious_path}")
!!!
%html
%head
%meta{:charset => "utf-8"}
%meta{:content => "IE=edge", "http-equiv" => "X-UA-Compatible"}
%meta{:content => "width=device-width, initial-scale=1.0", :name => "viewport"}
%title Doorkeeper
= stylesheet_link_tag "doorkeeper/admin/application"
= csrf_meta_tags
%body
.navbar.navbar-inverse.navbar-fixed-top{:role => "navigation"}
.container
.navbar-header
= link_to 'OAuth2 Provider', oauth_applications_path, class: 'navbar-brand'
%ul.nav.navbar-nav
= content_tag :li, class: "#{'active' if request.path == oauth_applications_path}" do
= link_to 'Applications', oauth_applications_path
.container
- if flash[:notice].present?
.alert.alert-info
= flash[:notice]
= yield
\ No newline at end of file
!!!
%html
%head
%title OAuth authorize required
%meta{:charset => "utf-8"}
%meta{:content => "IE=edge", "http-equiv" => "X-UA-Compatible"}
%meta{:content => "width=device-width, initial-scale=1.0", :name => "viewport"}
= stylesheet_link_tag "doorkeeper/application"
= csrf_meta_tags
%body
#container
- if flash[:notice].present?
.alert.alert-info
= flash[:notice]
= yield
\ No newline at end of file
......@@ -35,8 +35,8 @@
%i.fa.fa-user
Assign to
.col-sm-10
= project_users_select_tag("#{issuable.class.model_name.param_key}[assignee_id]",
placeholder: 'Select a user', class: 'custom-form-control',
= users_select_tag("#{issuable.class.model_name.param_key}[assignee_id]",
placeholder: 'Select a user', class: 'custom-form-control', null_user: true,
selected: issuable.assignee_id)
&nbsp;
= link_to 'Assign to me', '#', class: 'btn assign-to-me-link'
......
......@@ -22,7 +22,7 @@
%div#tree-content-holder.tree-content-holder
%article.file-holder
.file-title
%i.fa.fa-file
= blob_icon blob.mode, blob.name
%strong
= blob.name
%small
......
......@@ -48,5 +48,4 @@
= preserve(gfm(escape_once(@commit.description)))
:coffeescript
$ ->
$(".commit-info-row.branches").load("#{branches_namespace_project_commit_path(@project.namespace, @project, @commit.id)}")
$(".commit-info-row.branches").load("#{branches_namespace_project_commit_path(@project.namespace, @project, @commit.id)}")
......@@ -31,14 +31,11 @@
= render "visibility_level", f: f, visibility_level: @project.visibility_level, can_change_visibility_level: can?(current_user, :change_visibility_level, @project)
%fieldset.features
%legend
Tags:
.form-group
= f.label :tag_list, "Tags", class: 'control-label'
.col-sm-10
= f.text_field :tag_list, maxlength: 2000, class: "form-control"
%p.hint Separate tags with commas.
.form-group
= f.label :tag_list, "Tags", class: 'control-label'
.col-sm-10
= f.text_field :tag_list, maxlength: 2000, class: "form-control"
%p.help-block Separate tags with commas.
%fieldset.features
%legend
......
......@@ -12,7 +12,7 @@
%span Import existing git repo
.col-sm-10
= f.text_field :import_url, class: 'form-control', placeholder: 'https://github.com/randx/six.git'
.alert.alert-info
.well.prepend-top-20
This URL must be publicly accessible or you can add a username and password like this: https://username:password@gitlab.com/company/project.git.
%br
The import will time out after 4 minutes. For big repositories, use a clone/push combination.
......
......@@ -8,7 +8,7 @@
- else
none
- if can?(current_user, :modify_issue, @issue)
= project_users_select_tag('issue[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: @issue.assignee_id)
= users_select_tag('issue[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: @issue.assignee_id, null_user: true)
%div.prepend-top-20.clearfix
.issuable-context-title
......@@ -43,7 +43,4 @@
You're receiving notifications because you're subscribed to this thread.
:coffeescript
$ ->
new Subscription("#{toggle_subscription_namespace_project_issue_path(@issue.project.namespace, @project, @issue)}")
new Subscription("#{toggle_subscription_namespace_project_issue_path(@issue.project.namespace, @project, @issue)}")
......@@ -15,15 +15,5 @@
= render 'shared/issuable_filter'
.clearfix
.issues_bulk_update.hide
= form_tag bulk_update_namespace_project_issues_path(@project.namespace, @project), method: :post do
= select_tag('update[state_event]', options_for_select([['Open', 'reopen'], ['Closed', 'close']]), prompt: "Status", class: 'form-control')
= project_users_select_tag('update[assignee_id]', placeholder: 'Assignee')
= select_tag('update[milestone_id]', bulk_update_milestone_options, prompt: "Milestone")
= hidden_field_tag 'update[issues_ids]', []
= hidden_field_tag :state_event, params[:state_event]
= button_tag "Update issues", class: "btn update_selected_issues btn-save"
.issues-holder
= render "issues"
......@@ -13,5 +13,5 @@
$('select.select2').select2({width: 'resolve', dropdownAutoWidth: true})
$('.edit-issue.inline-update input[type="submit"]').hide();
new ProjectUsersSelect();
new UsersSelect()
new Issue();
......@@ -39,7 +39,7 @@
%i.fa.fa-user
Assign to
.col-sm-10
= project_users_select_tag('merge_request[assignee_id]', placeholder: 'Select a user', class: 'custom-form-control', selected: @merge_request.assignee_id, project_id: @merge_request.target_project_id)
= users_select_tag('merge_request[assignee_id]', placeholder: 'Select a user', class: 'custom-form-control', selected: @merge_request.assignee_id, project_id: @merge_request.target_project_id)
&nbsp;
= link_to 'Assign to me', '#', class: 'btn assign-to-me-link'
.form-group
......
......@@ -9,7 +9,7 @@
none
.issuable-context-selectbox
- if can?(current_user, :modify_merge_request, @merge_request)
= project_users_select_tag('merge_request[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: @merge_request.assignee_id)
= users_select_tag('merge_request[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: @merge_request.assignee_id, null_user: true)
%div.prepend-top-20.clearfix
.issuable-context-title
......@@ -45,7 +45,4 @@
You're receiving notifications because you're subscribed to this thread.
:coffeescript
$ ->
new Subscription("#{toggle_subscription_namespace_project_merge_request_path(@merge_request.project.namespace, @project, @merge_request)}")
\ No newline at end of file
new Subscription("#{toggle_subscription_namespace_project_merge_request_path(@merge_request.project.namespace, @project, @merge_request)}")
......@@ -29,7 +29,7 @@
%h4
Merge in progress...
%p
GitLab tries to merge it right now. During this time merge request is locked and can not be closed.
Merging is in progress. While merging this request is locked and cannot be closed.
- unless @commits.any?
%h4 Nothing to merge
......
......@@ -2,7 +2,7 @@
$('.context').html("#{escape_javascript(render partial: 'projects/merge_requests/show/context', locals: { issue: @issue })}");
$('.context').effect('highlight');
new ProjectUsersSelect();
new UsersSelect()
$('select.select2').select2({width: 'resolve', dropdownAutoWidth: true});
merge_request = new MergeRequest();
......@@ -72,7 +72,7 @@
%span Git repository URL
.col-sm-10
= f.text_field :import_url, class: 'form-control', placeholder: 'https://username:password@gitlab.company.com/group/project.git'
.alert.alert-info.prepend-top-10
.well.prepend-top-20
%ul
%li
The repository must be accessible over HTTP(S). If it is not publicly accessible, you can add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>.
......@@ -109,9 +109,8 @@
%p Please wait a moment, this page will automatically refresh when ready.
:coffeescript
$ ->
$('.how_to_import_link').bind 'click', (e) ->
e.preventDefault()
import_modal = $(this).next(".modal").show()
$('.modal-header .close').bind 'click', ->
$(".modal").hide()
$('.how_to_import_link').bind 'click', (e) ->
e.preventDefault()
import_modal = $(this).next(".modal").show()
$('.modal-header .close').bind 'click', ->
$(".modal").hide()
= form_for @project_member, as: :project_member, url: namespace_project_project_members_path(@project.namespace, @project), html: { class: 'form-horizontal users-project-form' } do |f|
.form-group
= f.label :user_ids, "People", class: 'control-label'
.col-sm-10= users_select_tag(:user_ids, multiple: true, class: 'input-large')
.col-sm-10= users_select_tag(:user_ids, multiple: true, class: 'input-large', scope: :all)
.form-group
= f.label :access_level, "Project Access", class: 'control-label'
......
......@@ -2,7 +2,7 @@
%p.light Keep stable branches secure and force developers to use Merge Requests
%hr
.alert.alert-info
.well.append-bottom-20
%p Protected branches are designed to
%ul
%li prevent pushes from everybody except #{link_to "masters", help_page_path("permissions", "permissions"), class: "vlink"}
......
......@@ -18,7 +18,7 @@
%li= msg
- if @service.help.present?
.alert.alert-info
.well
= preserve do
= markdown @service.help
......
%tr{ class: "tree-item #{tree_hex_class(blob_item)}" }
%td.tree-item-file-name
= tree_icon(type)
= tree_icon(type, blob_item.mode, blob_item.name)
%span.str-truncated
= link_to blob_item.name, namespace_project_blob_path(@project.namespace, @project, tree_join(@id || @commit.id, blob_item.name))
%td.tree_time_ago.cgray
......
%tr{ class: "tree-item" }
%td.tree-item-file-name
%i.fa.fa-archive
%i.fa.fa-archive.fa-fw
= submodule_link(submodule_item, @ref)
%td
%td.hidden-xs
%tr{ class: "tree-item #{tree_hex_class(tree_item)}" }
%td.tree-item-file-name
= tree_icon(type)
= tree_icon(type, tree_item.mode, tree_item.name)
%span.str-truncated
- path = flatten_tree(tree_item)
= link_to path, namespace_project_tree_path(@project.namespace, @project, tree_join(@id || @commit.id, path))
......
......@@ -14,106 +14,45 @@
%i.fa.fa-compass
All
%div
- if controller.controller_name == 'issues'
.check-all-holder
= check_box_tag "check_all_issues", nil, false,
class: "check_all_issues left",
disabled: !can?(current_user, :modify_issue, @project)
.issues-other-filters
.dropdown.inline.assignee-filter
%button.dropdown-toggle.btn{type: 'button', "data-toggle" => "dropdown"}
%i.fa.fa-user
%span.light assignee:
- if @assignee.present?
%strong= @assignee.name
- elsif params[:assignee_id] == "0"
Unassigned
- else
Any
%b.caret
%ul.dropdown-menu
%li
= link_to page_filter_path(assignee_id: nil) do
Any
= link_to page_filter_path(assignee_id: 0) do
Unassigned
- @assignees.sort_by(&:name).each do |user|
%li
= link_to page_filter_path(assignee_id: user.id) do
= image_tag avatar_icon(user.email), class: "avatar s16", alt: ''
= user.name
.issues-details-filters
= form_tag page_filter_path(without: [:assignee_id, :author_id, :milestone_id, :label_name]), method: :get, class: 'filter-form' do
- if controller.controller_name == 'issues'
.check-all-holder
= check_box_tag "check_all_issues", nil, false,
class: "check_all_issues left",
disabled: !can?(current_user, :modify_issue, @project)
.issues-other-filters
.filter-item.inline
= users_select_tag(:assignee_id, selected: params[:assignee_id],
placeholder: 'Assignee', class: 'trigger-submit', any_user: true, null_user: true)
.filter-item.inline
= users_select_tag(:author_id, selected: params[:author_id],
placeholder: 'Author', class: 'trigger-submit', any_user: true)
.filter-item.inline.milestone-filter
= select_tag('milestone_id', projects_milestones_options, class: "select2 trigger-submit", prompt: 'Milestone')
.dropdown.inline.prepend-left-10.author-filter
%button.dropdown-toggle.btn{type: 'button', "data-toggle" => "dropdown"}
%i.fa.fa-user
%span.light author:
- if @author.present?
%strong= @author.name
- elsif params[:author_id] == "0"
Unassigned
- else
Any
%b.caret
%ul.dropdown-menu
%li
= link_to page_filter_path(author_id: nil) do
Any
= link_to page_filter_path(author_id: 0) do
Unassigned
- @authors.sort_by(&:name).each do |user|
%li
= link_to page_filter_path(author_id: user.id) do
= image_tag avatar_icon(user.email), class: "avatar s16", alt: ''
= user.name
- if @project
.filter-item.inline.labels-filter
= select_tag('label_name', project_labels_options(@project), class: "select2 trigger-submit", prompt: 'Label')
.dropdown.inline.prepend-left-10.milestone-filter
%button.dropdown-toggle.btn{type: 'button', "data-toggle" => "dropdown"}
%i.fa.fa-clock-o
%span.light milestone:
- if @milestone.present?
%strong= @milestone.title
- elsif params[:milestone_id] == "0"
None (backlog)
- else
Any
%b.caret
%ul.dropdown-menu
%li
= link_to page_filter_path(milestone_id: nil) do
Any
= link_to page_filter_path(milestone_id: 0) do
None (backlog)
- @milestones.each do |milestone|
%li
= link_to page_filter_path(milestone_id: milestone.id) do
%strong= milestone.title
%small.light= milestone.expires_at
.pull-right
= render 'shared/sort_dropdown'
- if controller.controller_name == 'issues'
.issues_bulk_update.hide
= form_tag bulk_update_namespace_project_issues_path(@project.namespace, @project), method: :post do
= select_tag('update[state_event]', options_for_select([['Open', 'reopen'], ['Closed', 'close']]), prompt: "Status", class: 'form-control')
= users_select_tag('update[assignee_id]', placeholder: 'Assignee', null_user: true)
= select_tag('update[milestone_id]', bulk_update_milestone_options, prompt: "Milestone")
= hidden_field_tag 'update[issues_ids]', []
= hidden_field_tag :state_event, params[:state_event]
= button_tag "Update issues", class: "btn update_selected_issues btn-save"
- if @project
.dropdown.inline.prepend-left-10.labels-filter
%button.dropdown-toggle.btn{type: 'button', "data-toggle" => "dropdown"}
%i.fa.fa-tags
%span.light label:
- if params[:label_name].present?
%strong= params[:label_name]
- else
Any
%b.caret
%ul.dropdown-menu
%li
= link_to page_filter_path(label_name: nil) do
Any
- if @project.labels.any?
- @project.labels.each do |label|
%li
= link_to page_filter_path(label_name: label.name) do
= render_colored_label(label)
- else
%li
= link_to generate_namespace_project_labels_path(@project.namespace, @project, redirect: request.original_url), method: :post do
%i.fa.fa-plus-circle
Create default labels
:coffeescript
new UsersSelect()
.pull-right
= render 'shared/sort_dropdown'
$('form.filter-form').on 'submit', (event) ->
event.preventDefault()
Turbolinks.visit @.action + '&' + $(@).serialize()
......@@ -4,7 +4,7 @@
%small Issues, merge requests and push events
#cal-heatmap.calendar
:javascript
new calendar(
new Calendar(
#{@timestamps.to_json},
#{@starting_year},
#{@starting_month},
......
......@@ -47,5 +47,4 @@
= render 'projects'
:coffeescript
$ ->
$(".user-calendar").load("#{user_calendar_path}")
$(".user-calendar").load("#{user_calendar_path}")
class EmailsOnPushWorker
include Sidekiq::Worker
def perform(project_id, recipients, push_data, send_from_committer_email: false, disable_diffs: false)
def perform(project_id, recipients, push_data, options = {})
options.symbolize_keys!
options.reverse_merge!(
send_from_committer_email: false,
disable_diffs: false
)
send_from_committer_email = options[:send_from_committer_email]
disable_diffs = options[:disable_diffs]
project = Project.find(project_id)
before_sha = push_data["before"]
after_sha = push_data["after"]
......
......@@ -2,6 +2,11 @@
# GitLab application config file #
# # # # # # # # # # # # # # # # # #
#
########################### NOTE #####################################
# This file should not receive new settings. All configuration options #
# are being moved to ApplicationSetting model! #
########################################################################
#
# How to use:
# 1. Copy file as gitlab.yml
# 2. Update gitlab -> host with your fully qualified domain name
......
......@@ -8,6 +8,11 @@ Gitlab::Application.routes.draw do
authorizations: 'oauth/authorizations'
end
# Autocomplete
get '/autocomplete/users' => 'autocomplete#users'
get '/autocomplete/users/:id' => 'autocomplete#user'
# Search
get 'search' => 'search#show'
get 'search/autocomplete' => 'search#autocomplete', as: :search_autocomplete
......
......@@ -441,6 +441,8 @@ Note that inline HTML is disabled in the default Gitlab configuration, although
<dd>Does *not* work **very** well. Use HTML <em>tags</em>.</dd>
</dl>
See the documentation for HTML::Pipeline's [SanitizationFilter](http://www.rubydoc.info/gems/html-pipeline/HTML/Pipeline/SanitizationFilter#WHITELIST-constant) class for the list of allowed HTML tags and attributes. In addition to the default `SanitizationFilter` whitelist, GitLab allows the `class`, `id`, and `style` attributes.
## Horizontal Rule
```
......
......@@ -41,6 +41,11 @@ If a user is a GitLab administrator they receive all permissions.
## Group
In order for a group to appear as public and be browsable, it must contain at
least one public project.
Any user can remove themselves from a group, unless they are the last Owner of the group.
| Action | Guest | Reporter | Developer | Master | Owner |
|-------------------------|-------|----------|-----------|--------|-------|
| Browse group | ✓ | ✓ | ✓ | ✓ | ✓ |
......@@ -48,5 +53,3 @@ If a user is a GitLab administrator they receive all permissions.
| Create project in group | | | | ✓ | ✓ |
| Manage group members | | | | | ✓ |
| Remove group | | | | | ✓ |
Any user can remove themselves from a group, unless they are the last Owner of the group.
......@@ -68,6 +68,6 @@ You can't add the same deploy key twice with the 'New Deploy Key' option.
If you want to add the same key to another project, please enable it in the
list that says 'Deploy keys from projects available to you'. All the deploy
keys of all the projects you have access to are available. This project
access can happen through being a direct member of the projecti, or through
access can happen through being a direct member of the project, or through
a group. See `def accessible_deploy_keys` in `app/models/user.rb` for more
information.
......@@ -26,11 +26,7 @@ RUN mkdir -p /opt/gitlab/sv/sshd/supervise \
# Expose web & ssh
EXPOSE 80 22
# Declare volumes
VOLUME ["/var/opt/gitlab", "/var/log/gitlab", "/etc/gitlab"]
# Copy assets
COPY assets/gitlab.rb /etc/gitlab/
COPY assets/wrapper /usr/local/bin/
# Wrapper to handle signal, trigger runit and reconfigure GitLab
......
......@@ -8,14 +8,15 @@ GitLab offers git repository management, code reviews, issue tracking, activity
![GitLab Logo](https://gitlab.com/uploads/appearance/logo/1/brand_logo-c37eb221b456bb4b472cc1084480991f.png)
How to use this image
How to use these images
======================
At this moment GitLab doesn't have official Docker images.
Build your own based on the Omnibus packages with the following command (it assumes you're in the GitLab repo root directory):
At this moment GitLab doesn't have official Docker images. For convinience we will use suffix _xy where xy is current version of GitLab.
Build your own based on the Omnibus packages with the following commands (it assumes you're in the GitLab repo root directory):
```bash
sudo docker build --tag gitlab_image docker/
sudo docker build --tag gitlab_data_image docker/data/
sudo docker build --tag gitlab_app_image_xy docker/
```
We assume using a data volume container, this will simplify migrations and backups.
......@@ -30,16 +31,16 @@ The directories on data container are:
Create the data container with:
```bash
sudo docker run --name gitlab_data gitlab_image /bin/true
sudo docker run --name gitlab_data gitlab_data_image /bin/true
```
After creating this run GitLab:
After creating data container run GitLab container:
```bash
sudo docker run --detach --name gitlab_app --publish 8080:80 --publish 2222:22 --volumes-from gitlab_data gitlab_image
sudo docker run --detach --name gitlab_app_xy --publish 8080:80 --publish 2222:22 --volumes-from gitlab_data gitlab_app_image_xy
```
It might take a while before the docker container is responding to queries. You can follow the configuration process with `docker logs -f gitlab_app`.
It might take a while before the docker container is responding to queries. You can follow the configuration process with `sudo docker logs -f gitlab_app_xy`.
You can then go to `http://localhost:8080/` (or `http://192.168.59.103:8080/` if you use boot2docker).
You can login with username `root` and password `5iveL!fe`.
......@@ -54,7 +55,7 @@ This container uses the official Omnibus GitLab distribution, so all configurati
To access GitLab configuration, you can start an interactive command line in a new container using the shared data volume container, you will be able to browse the 3 directories and use your favorite text editor:
```bash
docker run -ti -e TERM=linux --rm --volumes-from gitlab_data ubuntu
sudo docker run -ti -e TERM=linux --rm --volumes-from gitlab_data ubuntu
vi /etc/gitlab/gitlab.rb
```
......@@ -62,6 +63,25 @@ vi /etc/gitlab/gitlab.rb
You can find all available options in [Omnibus GitLab documentation](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/README.md#configuration).
How to upgrade GitLab
========================
To updgrade GitLab to new versions, stop running container, create new docker image and container from that image.
It Assumes that you're upgrading from 7.8 to 7.9 and you're in the updated GitLab repo root directory:
```bash
sudo docker stop gitlab_app_78
sudo docker build --tag gitlab_app_image_79 docker/
sudo docker run --detach --name gitlab_app_79 --publish 8080:80 --publish 2222:22 --volumes-from gitlab_data gitlab_app_image_79
```
On the first run GitLab will reconfigure and update itself. If everything runs OK don't forget to cleanup old container and image:
```bash
sudo docker rm gitlab_app_78
sudo docker rmi gitlab_app_image_78
```
Troubleshooting
=========================
......
FROM busybox
# Declare volumes
VOLUME ["/var/opt/gitlab", "/var/log/gitlab", "/etc/gitlab"]
# Copy assets
COPY assets/gitlab.rb /etc/gitlab/
CMD /bin/sh
......@@ -10,10 +10,12 @@ Feature: Dashboard Issues
Scenario: I should see assigned issues
Then I should see issues assigned to me
@javascript
Scenario: I should see authored issues
When I click "Authored by me" link
Then I should see issues authored by me
@javascript
Scenario: I should see all issues
When I click "All" link
Then I should see all issues
......@@ -10,10 +10,12 @@ Feature: Dashboard Merge Requests
Scenario: I should see assigned merge_requests
Then I should see merge requests assigned to me
@javascript
Scenario: I should see authored merge_requests
When I click "Authored by me" link
Then I should see merge requests authored by me
@javascript
Scenario: I should see all merge_requests
When I click "All" link
Then I should see all merge requests
......@@ -21,10 +21,13 @@ Feature: Project Commits
And I click side-by-side diff button
Then I see inline diff button
@javascript
Scenario: I compare refs
Given I visit compare refs page
And I fill compare fields with refs
Then I see compared refs
And I unfold diff
Then I should see additional file lines
Scenario: I browse commits for a specific path
Given I visit my project's commits page for a specific path
......
......@@ -8,11 +8,7 @@ Feature: Project Issues Filter Labels
And project "Shop" has issue "Feature1" with labels: "feature"
Given I visit project "Shop" issues page
Scenario: I should see project issues
Then I should see "bug" in labels filter
And I should see "feature" in labels filter
And I should see "enhancement" in labels filter
@javascript
Scenario: I filter by one label
Given I click link "bug"
Then I should see "Bugfix1" in issues list
......
......@@ -171,6 +171,13 @@ Feature: Project Merge Requests
And I click Side-by-side Diff tab
Then I should see comments on the side-by-side diff page
@javascript
Scenario: I view diffs on a merge request
Given project "Shop" have "Bug NS-05" open merge request with diffs inside
And I visit merge request page "Bug NS-05"
And I click on the Changes tab via Javascript
Then I should see the proper Inline and Side-by-side links
# Task status in issues list
Scenario: Merge requests list should display task status
......
class Spinach::Features::DashboardIssues < Spinach::FeatureSteps
include SharedAuthentication
include SharedPaths
include Select2Helper
step 'I should see issues assigned to me' do
should_see(assigned_issue)
......@@ -35,21 +36,13 @@ class Spinach::Features::DashboardIssues < Spinach::FeatureSteps
end
step 'I click "Authored by me" link' do
within ".assignee-filter" do
click_link "Any"
end
within ".author-filter" do
click_link current_user.name
end
select2(current_user.id, from: "#author_id")
select2(nil, from: "#assignee_id")
end
step 'I click "All" link' do
within ".author-filter" do
click_link "Any"
end
within ".assignee-filter" do
click_link "Any"
end
select2(nil, from: "#author_id")
select2(nil, from: "#assignee_id")
end
def should_see(issue)
......
class Spinach::Features::DashboardMergeRequests < Spinach::FeatureSteps
include SharedAuthentication
include SharedPaths
include Select2Helper
step 'I should see merge requests assigned to me' do
should_see(assigned_merge_request)
......@@ -39,21 +40,13 @@ class Spinach::Features::DashboardMergeRequests < Spinach::FeatureSteps
end
step 'I click "Authored by me" link' do
within ".assignee-filter" do
click_link "Any"
end
within ".author-filter" do
click_link current_user.name
end
select2(current_user.id, from: "#author_id")
select2(nil, from: "#assignee_id")
end
step 'I click "All" link' do
within ".author-filter" do
click_link "Any"
end
within ".assignee-filter" do
click_link "Any"
end
select2(nil, from: "#author_id")
select2(nil, from: "#assignee_id")
end
def should_see(merge_request)
......
......@@ -38,6 +38,18 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps
click_button "Compare"
end
step 'I unfold diff' do
@diff = first('.js-unfold')
@diff.click
sleep 2
end
step 'I should see additional file lines' do
within @diff.parent do
first('.new_line').text.should_not have_content "..."
end
end
step 'I see compared refs' do
page.should have_content "Compare View"
page.should have_content "Commits (1)"
......
......@@ -2,24 +2,7 @@ class Spinach::Features::ProjectIssuesFilterLabels < Spinach::FeatureSteps
include SharedAuthentication
include SharedProject
include SharedPaths
step 'I should see "bug" in labels filter' do
within ".labels-filter" do
page.should have_content "bug"
end
end
step 'I should see "feature" in labels filter' do
within ".labels-filter" do
page.should have_content "feature"
end
end
step 'I should see "enhancement" in labels filter' do
within ".labels-filter" do
page.should have_content "enhancement"
end
end
include Select2Helper
step 'I should see "Bugfix1" in issues list' do
within ".issues-list" do
......@@ -46,9 +29,7 @@ class Spinach::Features::ProjectIssuesFilterLabels < Spinach::FeatureSteps
end
step 'I click link "bug"' do
within ".labels-filter" do
click_link "bug"
end
select2('bug', from: "#label_name")
end
step 'I click link "feature"' do
......
......@@ -117,6 +117,20 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request)
end
step 'I click on the Changes tab via Javascript' do
find('.diffs-tab').click
sleep 2
end
step 'I should see the proper Inline and Side-by-side links' do
buttons = all('#commit-diff-viewtype')
expect(buttons.count).to eq(2)
buttons.each do |b|
expect(b['href']).should_not have_content('json')
end
end
step 'I switch to the merge request\'s comments tab' do
visit namespace_project_merge_request_path(project.namespace, project, merge_request)
end
......
......@@ -64,10 +64,10 @@ module API
authenticated_as_admin!
required_attributes! [:email, :password, :name, :username]
attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :projects_limit, :username, :bio, :can_create_group, :admin, :confirm]
user = User.build_user(attrs)
admin = attrs.delete(:admin)
user.admin = admin unless admin.nil?
confirm = !(attrs.delete(:confirm) =~ (/(false|f|no|0)$/i))
user = User.build_user(attrs)
user.admin = admin unless admin.nil?
user.skip_confirmation! unless confirm
identity_attrs = attributes_for_keys [:provider, :extern_uid]
......
......@@ -70,16 +70,17 @@ module Backup
# delete backups
$progress.print "Deleting old backups ... "
keep_time = Gitlab.config.backup.keep_time.to_i
path = Gitlab.config.backup.path
if keep_time > 0
removed = 0
file_list = Dir.glob(Rails.root.join(path, "*_gitlab_backup.tar"))
file_list.map! { |f| $1.to_i if f =~ /(\d+)_gitlab_backup.tar/ }
file_list.sort.each do |timestamp|
if Time.at(timestamp) < (Time.now - keep_time)
if Kernel.system(*%W(rm #{timestamp}_gitlab_backup.tar))
removed += 1
Dir.chdir(Gitlab.config.backup.path) do
file_list = Dir.glob('*_gitlab_backup.tar')
file_list.map! { |f| $1.to_i if f =~ /(\d+)_gitlab_backup.tar/ }
file_list.sort.each do |timestamp|
if Time.at(timestamp) < (Time.now - keep_time)
if Kernel.system(*%W(rm #{timestamp}_gitlab_backup.tar))
removed += 1
end
end
end
end
......
......@@ -79,15 +79,35 @@ module Gitlab
# Used markdown pipelines in GitLab:
# GitlabEmojiFilter - performs emoji replacement.
# SanitizationFilter - remove unsafe HTML tags and attributes
#
# see https://gitlab.com/gitlab-org/html-pipeline-gitlab for more filters
filters = [
HTML::Pipeline::Gitlab::GitlabEmojiFilter
HTML::Pipeline::Gitlab::GitlabEmojiFilter,
HTML::Pipeline::SanitizationFilter
]
whitelist = HTML::Pipeline::SanitizationFilter::WHITELIST
whitelist[:attributes][:all].push('class', 'id')
whitelist[:elements].push('span')
# Remove the rel attribute that the sanitize gem adds, and remove the
# href attribute if it contains inline javascript
fix_anchors = lambda do |env|
name, node = env[:node_name], env[:node]
if name == 'a'
node.remove_attribute('rel')
if node['href'] && node['href'].match('javascript:')
node.remove_attribute('href')
end
end
end
whitelist[:transformers].push(fix_anchors)
markdown_context = {
asset_root: Gitlab.config.gitlab.url,
asset_host: Gitlab::Application.config.asset_host
asset_host: Gitlab::Application.config.asset_host,
whitelist: whitelist
}
markdown_pipeline = HTML::Pipeline::Gitlab.new(filters).pipeline
......@@ -97,18 +117,14 @@ module Gitlab
if options[:xhtml]
saveoptions |= Nokogiri::XML::Node::SaveOptions::AS_XHTML
end
text = result[:output].to_html(save_with: saveoptions)
allowed_attributes = ActionView::Base.sanitized_allowed_attributes
allowed_tags = ActionView::Base.sanitized_allowed_tags
text = result[:output].to_html(save_with: saveoptions)
text = sanitize text.html_safe,
attributes: allowed_attributes + %w(id class style),
tags: allowed_tags + %w(table tr td th)
if options[:parse_tasks]
text = parse_tasks(text)
end
text
text.html_safe
end
private
......
......@@ -99,11 +99,7 @@ module Gitlab
heads = repo.heads.map(&:name)
# update or create the parking branch
if heads.include? PARKING_BRANCH
repo.git.checkout({}, PARKING_BRANCH)
else
repo.git.checkout(default_options({ b: true }), PARKING_BRANCH)
end
repo.git.checkout(default_options({ B: true }), PARKING_BRANCH)
# remove the parking branch from the list of heads ...
heads.delete(PARKING_BRANCH)
......
require 'spec_helper'
describe AutocompleteController do
let!(:project) { create(:project) }
let!(:user) { create(:user) }
let!(:user2) { create(:user) }
context 'project members' do
before do
sign_in(user)
project.team << [user, :master]
get(:users, project_id: project.id)
end
let(:body) { JSON.parse(response.body) }
it { body.should be_kind_of(Array) }
it { body.size.should eq(1) }
it { body.first["username"].should == user.username }
end
context 'group members' do
let(:group) { create(:group) }
before do
sign_in(user)
group.add_owner(user)
get(:users, group_id: group.id)
end
let(:body) { JSON.parse(response.body) }
it { body.should be_kind_of(Array) }
it { body.size.should eq(1) }
it { body.first["username"].should == user.username }
end
context 'all users' do
before do
sign_in(user)
get(:users)
end
let(:body) { JSON.parse(response.body) }
it { body.should be_kind_of(Array) }
it { body.size.should eq(User.count) }
end
end
......@@ -95,7 +95,7 @@ describe 'Issues', feature: true do
let(:issue) { @issue }
it 'should allow filtering by issues with no specified milestone' do
visit namespace_project_issues_path(project.namespace, project, milestone_id: '0')
visit namespace_project_issues_path(project.namespace, project, milestone_id: IssuableFinder::NONE)
expect(page).not_to have_content 'foobar'
expect(page).to have_content 'barbaz'
......@@ -111,7 +111,7 @@ describe 'Issues', feature: true do
end
it 'should allow filtering by issues with no specified assignee' do
visit namespace_project_issues_path(project.namespace, project, assignee_id: '0')
visit namespace_project_issues_path(project.namespace, project, assignee_id: IssuableFinder::NONE)
expect(page).to have_content 'foobar'
expect(page).not_to have_content 'barbaz'
......
......@@ -776,6 +776,36 @@ describe GitlabMarkdownHelper do
expected = ""
expect(markdown(actual)).to match(expected)
end
it 'should allow whitelisted HTML tags from the user' do
actual = '<dl><dt>Term</dt><dd>Definition</dd></dl>'
expect(markdown(actual)).to match(actual)
end
it 'should sanitize tags that are not whitelisted' do
actual = '<textarea>no inputs allowed</textarea> <blink>no blinks</blink>'
expected = 'no inputs allowed no blinks'
expect(markdown(actual)).to match(expected)
expect(markdown(actual)).not_to match('<.textarea>')
expect(markdown(actual)).not_to match('<.blink>')
end
it 'should allow whitelisted tag attributes from the user' do
actual = '<a class="custom">link text</a>'
expect(markdown(actual)).to match(actual)
end
it 'should sanitize tag attributes that are not whitelisted' do
actual = '<a href="http://example.com/bar.html" foo="bar">link text</a>'
expected = '<a href="http://example.com/bar.html">link text</a>'
expect(markdown(actual)).to match(expected)
end
it 'should sanitize javascript in attributes' do
actual = %q(<a href="javascript:alert('foo')">link text</a>)
expected = '<a>link text</a>'
expect(markdown(actual)).to match(expected)
end
end
describe 'markdown for empty repository' do
......
require 'spec_helper'
describe IconsHelper do
describe 'file_type_icon_class' do
it 'returns folder class' do
expect(file_type_icon_class('folder', 0, 'folder_name')).to eq 'folder'
end
it 'returns share class' do
expect(file_type_icon_class('file', 0120000, 'link')).to eq 'share'
end
it 'returns file-pdf-o class with .pdf' do
expect(file_type_icon_class('file', 0, 'filename.pdf')).to eq 'file-pdf-o'
end
it 'returns file-image-o class with .jpg' do
expect(file_type_icon_class('file', 0, 'filename.jpg')).to eq 'file-image-o'
end
it 'returns file-image-o class with .JPG' do
expect(file_type_icon_class('file', 0, 'filename.JPG')).to eq 'file-image-o'
end
it 'returns file-image-o class with .png' do
expect(file_type_icon_class('file', 0, 'filename.png')).to eq 'file-image-o'
end
it 'returns file-archive-o class with .tar' do
expect(file_type_icon_class('file', 0, 'filename.tar')).to eq 'file-archive-o'
end
it 'returns file-archive-o class with .TAR' do
expect(file_type_icon_class('file', 0, 'filename.TAR')).to eq 'file-archive-o'
end
it 'returns file-archive-o class with .tar.gz' do
expect(file_type_icon_class('file', 0, 'filename.tar.gz')).to eq 'file-archive-o'
end
it 'returns file-audio-o class with .mp3' do
expect(file_type_icon_class('file', 0, 'filename.mp3')).to eq 'file-audio-o'
end
it 'returns file-audio-o class with .MP3' do
expect(file_type_icon_class('file', 0, 'filename.MP3')).to eq 'file-audio-o'
end
it 'returns file-audio-o class with .wav' do
expect(file_type_icon_class('file', 0, 'filename.wav')).to eq 'file-audio-o'
end
it 'returns file-video-o class with .avi' do
expect(file_type_icon_class('file', 0, 'filename.avi')).to eq 'file-video-o'
end
it 'returns file-video-o class with .AVI' do
expect(file_type_icon_class('file', 0, 'filename.AVI')).to eq 'file-video-o'
end
it 'returns file-video-o class with .mp4' do
expect(file_type_icon_class('file', 0, 'filename.mp4')).to eq 'file-video-o'
end
it 'returns file-word-o class with .doc' do
expect(file_type_icon_class('file', 0, 'filename.doc')).to eq 'file-word-o'
end
it 'returns file-word-o class with .DOC' do
expect(file_type_icon_class('file', 0, 'filename.DOC')).to eq 'file-word-o'
end
it 'returns file-word-o class with .docx' do
expect(file_type_icon_class('file', 0, 'filename.docx')).to eq 'file-word-o'
end
it 'returns file-excel-o class with .xls' do
expect(file_type_icon_class('file', 0, 'filename.xls')).to eq 'file-excel-o'
end
it 'returns file-excel-o class with .XLS' do
expect(file_type_icon_class('file', 0, 'filename.XLS')).to eq 'file-excel-o'
end
it 'returns file-excel-o class with .xlsx' do
expect(file_type_icon_class('file', 0, 'filename.xlsx')).to eq 'file-excel-o'
end
it 'returns file-excel-o class with .ppt' do
expect(file_type_icon_class('file', 0, 'filename.ppt')).to eq 'file-powerpoint-o'
end
it 'returns file-excel-o class with .PPT' do
expect(file_type_icon_class('file', 0, 'filename.PPT')).to eq 'file-powerpoint-o'
end
it 'returns file-excel-o class with .pptx' do
expect(file_type_icon_class('file', 0, 'filename.pptx')).to eq 'file-powerpoint-o'
end
it 'returns file-text-o class with .unknow' do
expect(file_type_icon_class('file', 0, 'filename.unknow')).to eq 'file-text-o'
end
it 'returns file-text-o class with no extension' do
expect(file_type_icon_class('file', 0, 'CHANGELOG')).to eq 'file-text-o'
end
end
end
......@@ -36,7 +36,7 @@ describe BuildboxService do
@service.stub(
project: @project,
service_hook: true,
project_url: 'https://buildbox.io/account-name/example-project',
project_url: 'https://buildkite.com/account-name/example-project',
token: 'secret-sauce-webhook-token:secret-sauce-status-token'
)
end
......@@ -44,7 +44,7 @@ describe BuildboxService do
describe :webhook_url do
it 'returns the webhook url' do
expect(@service.webhook_url).to eq(
'https://webhook.buildbox.io/deliver/secret-sauce-webhook-token'
'https://webhook.buildkite.com/deliver/secret-sauce-webhook-token'
)
end
end
......@@ -52,7 +52,7 @@ describe BuildboxService do
describe :commit_status_path do
it 'returns the correct status page' do
expect(@service.commit_status_path('2ab7834c')).to eq(
'https://gitlab.buildbox.io/status/secret-sauce-status-token.json?commit=2ab7834c'
'https://gitlab.buildkite.com/status/secret-sauce-status-token.json?commit=2ab7834c'
)
end
end
......@@ -60,7 +60,7 @@ describe BuildboxService do
describe :build_page do
it 'returns the correct build page' do
expect(@service.build_page('2ab7834c', nil)).to eq(
'https://buildbox.io/account-name/example-project/builds?commit=2ab7834c'
'https://buildkite.com/account-name/example-project/builds?commit=2ab7834c'
)
end
end
......@@ -68,14 +68,14 @@ describe BuildboxService do
describe :builds_page do
it 'returns the correct path to the builds page' do
expect(@service.builds_path).to eq(
'https://buildbox.io/account-name/example-project/builds?branch=default-brancho'
'https://buildkite.com/account-name/example-project/builds?branch=default-brancho'
)
end
end
describe :status_img_path do
it 'returns the correct path to the status image' do
expect(@service.status_img_path).to eq('https://badge.buildbox.io/secret-sauce-status-token.svg')
expect(@service.status_img_path).to eq('https://badge.buildkite.com/secret-sauce-status-token.svg')
end
end
end
......
......@@ -17,9 +17,9 @@ module Select2Helper
selector = options[:from]
if options[:multiple]
execute_script("$('#{selector}').select2('val', ['#{value}']);")
execute_script("$('#{selector}').select2('val', ['#{value}'], true);")
else
execute_script("$('#{selector}').select2('val', '#{value}');")
execute_script("$('#{selector}').select2('val', '#{value}', true);")
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment