Commit 6a0f4503 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

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

CE to EE

@rspeicher please fix TODO in `lib/gitlab/reference_extractor.rb`

See merge request !381
parents 72e8e18f 1ccb912f
Please view this file on the master branch, on stable branches it's out of date.
v 7.11.0 (unreleased)
- Get Gitorious importer to work again.
- Fix clone URL field and X11 Primary selection (Dmitry Medvinsky)
- Ignore invalid lines in .gitmodules
- Fix "Cannot move project" error message from popping up after a successful transfer (Stan Hu)
- Redirect to sign in page after signing out.
- Fix "Hello @username." references not working by no longer allowing usernames to end in period.
-
- Add "Reply quoting selected text" shortcut key (`r`)
- Fix bug causing `@whatever` inside an issue's first code block to be picked up as a user mention.
- Fix bug causing `@whatever` inside an inline code snippet (backtick-style) to be picked up as a user mention.
- Added GitLab Event header for project hooks
-
- Show Atom feed buttons everywhere where applicable.
- Add project activity atom feed.
- Don't crash when an MR from a fork has a cross-reference comment from the target project on of its commits.
- Include commit comments in MR from a forked project.
-
-
-
- Add default project and snippet visibility settings to the admin web UI.
- Fix bug where Slack service channel was not saved in admin template settings. (Stan Hu)
- Move snippets UI to fluid layout
- Improve UI for sidebar. Increase separation between navigation and content
- Improve new project command options (Ben Bodenmiller)
- Prevent sending empty messages to HipChat (Chulki Lee)
- Improve UI for mobile phones on dashboard and project pages
- Add room notification and message color option for HipChat
v 7.10.0 (unreleased)
v 7.10.0
- Ignore submodules that are defined in .gitmodules but are checked in as directories.
- Allow projects to be imported from Google Code.
- Remove access control for uploaded images to fix broken images in emails (Hannes Rosenögger)
......
......@@ -159,7 +159,7 @@ gem "slack-notifier", "~> 1.0.0"
gem 'asana', '~> 0.0.6'
# d3
gem "d3_rails", "~> 3.1.4"
gem 'd3_rails', '~> 3.5.5'
#cal-heatmap
gem "cal-heatmap-rails", "~> 0.0.1"
......@@ -189,7 +189,7 @@ gem 'turbolinks'
gem 'jquery-turbolinks'
gem 'select2-rails'
gem 'jquery-atwho-rails', "~> 0.3.3"
gem 'jquery-atwho-rails', '~> 1.0.0'
gem "jquery-rails"
gem "jquery-ui-rails"
gem "jquery-scrollto-rails"
......@@ -224,14 +224,13 @@ end
group :development, :test do
gem 'coveralls', require: false
gem 'rubocop', '0.28.0', require: false
# gem 'rails-dev-tweaks'
gem 'spinach-rails'
gem "rspec-rails", '2.99'
gem "capybara", '~> 2.2.1'
gem 'capybara', '~> 2.2.1'
gem 'capybara-screenshot', '~> 1.0.0'
gem "pry-rails"
gem "awesome_print"
gem "database_cleaner"
gem "launchy"
gem 'factory_girl_rails'
# Prevent occasions where minitest is not bundled in packaged versions of ruby (see #3826)
......
......@@ -85,6 +85,9 @@ GEM
rack (>= 1.0.0)
rack-test (>= 0.5.4)
xpath (~> 2.0)
capybara-screenshot (1.0.9)
capybara (>= 1.0, < 3)
launchy
carrierwave (0.9.0)
activemodel (>= 3.2.0)
activesupport (>= 3.2.0)
......@@ -116,7 +119,7 @@ GEM
crack (0.4.1)
safe_yaml (~> 0.9.0)
creole (0.3.8)
d3_rails (3.1.10)
d3_rails (3.5.5)
railties (>= 3.1.0)
daemons (1.1.9)
database_cleaner (1.3.0)
......@@ -298,7 +301,7 @@ GEM
phantomjs (>= 1.9)
railties (>= 3.2.0)
sprockets-rails
jquery-atwho-rails (0.3.3)
jquery-atwho-rails (1.0.1)
jquery-rails (3.1.0)
railties (>= 3.0, < 5.0)
thor (>= 0.14, < 2.0)
......@@ -677,13 +680,14 @@ DEPENDENCIES
byebug
cal-heatmap-rails (~> 0.0.1)
capybara (~> 2.2.1)
capybara-screenshot (~> 1.0.0)
carrierwave
charlock_holmes
coffee-rails
colored
coveralls
creole (~> 0.3.6)
d3_rails (~> 3.1.4)
d3_rails (~> 3.5.5)
database_cleaner
default_value_for (~> 3.0.0)
devise (= 3.2.4)
......@@ -720,13 +724,12 @@ DEPENDENCIES
httparty
jasmine (~> 2.2.0)
jasmine-rails
jquery-atwho-rails (~> 0.3.3)
jquery-atwho-rails (~> 1.0.0)
jquery-rails
jquery-scrollto-rails
jquery-turbolinks
jquery-ui-rails
kaminari (~> 0.15.1)
launchy
letter_opener
minitest (~> 5.3.0)
mousetrap-rails
......
## Canonical source
The source of GitLab Community Edition is [hosted on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/) and there are mirrors to make [contributing](CONTRIBUTING.md) as easy as possible.
The source of GitLab Enterprise Edition is [hosted on GitLab.com](https://gitlab.com/subscribers/gitlab-ee) and acessible only to [subscribers](https://about.gitlab.com/subscription/).
# ![logo](https://about.gitlab.com/images/gitlab_logo.png) GitLab
## Subscriber onboarding information
......@@ -43,10 +49,6 @@ There are two editions of GitLab.
*GitLab Enterprise Edition (EE)* includes [extra features](https://about.gitlab.com/features/#compare) that are most useful for organizations with more than 100 users.
To get access to the EE and support please [become a subscriber](https://about.gitlab.com/pricing/).
## Canonical source
- The source of GitLab Enterprise Edition is [hosted on GitLab.com](https://dev.gitlab.org/gitlab/gitlab-ee/) and acessible only to [subscribers](https://about.gitlab.com/subscription/).
## Code status
- [![build status](https://ci.gitlab.org/projects/1/status.png?ref=master)](https://ci.gitlab.org/projects/1?ref=master) on ci.gitlab.org (master branch)
......
7.10.0.pre-ee
7.11.0.pre-ee
......@@ -132,10 +132,17 @@ $ ->
), 1
# Initialize tooltips
$('.has_tooltip').tooltip()
# Bottom tooltip
$('.has_bottom_tooltip').tooltip(placement: 'bottom')
$('body').tooltip({
selector: '.has_tooltip, [data-toggle="tooltip"], .page-sidebar-collapsed .nav-sidebar a'
placement: (_, el) ->
$el = $(el)
if $el.attr('id') == 'js-shortcuts-home'
# Place the logo tooltip on the right when collapsed, bottom when expanded
$el.parents('header').hasClass('header-collapsed') and 'right' or 'bottom'
else
# Otherwise use the data-placement attribute like normal
$el.data('placement')
})
# Form submitter
$('.trigger-submit').on 'change', ->
......
......@@ -2,19 +2,19 @@
window.GitLab ?= {}
GitLab.GfmAutoComplete =
# private_token: ''
dataSource: ''
# Emoji
Emoji:
template: '<li data-value="${insert}">${name} <img alt="${name}" height="20" src="${image}" width="20" /></li>'
template: '<li>${name} <img alt="${name}" height="20" src="${path}" width="20" /></li>'
# Team Members
Members:
template: '<li data-value="${username}">${username} <small>${name}</small></li>'
template: '<li>${username} <small>${name}</small></li>'
# Issues and MergeRequests
Issues:
template: '<li data-value="${id}"><small>${id}</small> ${title} </li>'
template: '<li><small>${id}</small> ${title}</li>'
# Add GFM auto-completion to all input fields, that accept GFM input.
setup: ->
......@@ -23,45 +23,46 @@ GitLab.GfmAutoComplete =
# Emoji
input.atwho
at: ':'
tpl: @Emoji.template
callbacks:
before_save: (emojis) =>
$.map emojis, (em) => name: em.name, insert: em.name+ ':', image: em.path
displayTpl: @Emoji.template
insertTpl: ':${name}:'
# Team Members
input.atwho
at: '@'
tpl: @Members.template
search_key: 'search'
displayTpl: @Members.template
insertTpl: '${atwho-at}${username}'
searchKey: 'search'
callbacks:
before_save: (members) =>
$.map members, (m) => name: m.name, username: m.username, search: "#{m.username} #{m.name}"
beforeSave: (members) ->
$.map members, (m) -> name: m.name, username: m.username, search: "#{m.username} #{m.name}"
input.atwho
at: '#'
alias: 'issues'
search_key: 'search'
tpl: @Issues.template
searchKey: 'search'
displayTpl: @Issues.template
insertTpl: '${atwho-at}${id}'
callbacks:
before_save: (issues) ->
beforeSave: (issues) ->
$.map issues, (i) -> id: i.iid, title: sanitize(i.title), search: "#{i.iid} #{i.title}"
input.atwho
at: '!'
alias: 'mergerequests'
search_key: 'search'
tpl: @Issues.template
searchKey: 'search'
displayTpl: @Issues.template
insertTpl: '${atwho-at}${id}'
callbacks:
before_save: (merges) ->
beforeSave: (merges) ->
$.map merges, (m) -> id: m.iid, title: sanitize(m.title), search: "#{m.iid} #{m.title}"
input.one "focus", =>
input.one 'focus', =>
$.getJSON(@dataSource).done (data) ->
# load members
input.atwho 'load', "@", data.members
input.atwho 'load', '@', data.members
# load issues
input.atwho 'load', "issues", data.issues
input.atwho 'load', 'issues', data.issues
# load merge requests
input.atwho 'load', "mergerequests", data.mergerequests
input.atwho 'load', 'mergerequests', data.mergerequests
# load emojis
input.atwho 'load', ":", data.emojis
input.atwho 'load', ':', data.emojis
......@@ -117,7 +117,7 @@
color: #888;
text-shadow: 0 1px 1px #fff;
}
i[class~="fa"] {
i.fa {
line-height: 14px;
}
}
......
......@@ -106,7 +106,6 @@
p > code {
font-size: inherit;
font-weight: inherit;
color: #555;
}
li {
......
......@@ -6,7 +6,7 @@
.issue-box {
display: inline-block;
padding: 7px 13px;
padding: 4px 13px;
font-weight: normal;
margin-right: 5px;
......
......@@ -4,6 +4,11 @@
margin-top: 20px;
}
.container-fluid {
padding-left: 5px;
padding-right: 5px;
}
.nav.nav-tabs > li > a {
padding: 10px;
font-size: 12px;
......@@ -27,6 +32,34 @@
.project-home-links {
display: none;
}
.project-avatar {
display: none;
}
.project-home-panel {
padding-left: 0 !important;
.project-home-row {
.project-home-desc {
margin-right: 0 !important;
float: none !important;
}
.project-repo-buttons {
position: static;
margin-top: 15px;
width: 100%;
float: none;
text-align: left;
}
}
}
.navbar-inner .title {
margin-left: 6px !important;
max-width: 70% !important;
}
}
@media (max-width: $screen-sm-max) {
......
......@@ -25,17 +25,8 @@
display: inline-block;
}
.issue-actions {
display: none;
position: absolute;
top: 10px;
right: 15px;
}
&:hover {
.issue-actions {
display: block;
}
.issue-no-comments {
opacity: 0.5;
}
}
}
......
......@@ -91,11 +91,16 @@
.merge-request-info {
color: #999;
font-size: 13px;
.merge-request-labels {
display: inline-block;
}
}
}
.merge-request-labels {
display: inline-block;
}
.merge-request-no-comments {
opacity: 0.5;
}
}
......
......@@ -136,7 +136,7 @@ ul.notes {
display: none;
float: right;
[class~="fa"] {
i.fa {
font-size: 16px;
line-height: 16px;
vertical-align: middle;
......
......@@ -40,6 +40,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:home_page_url,
:help_text,
:max_attachment_size,
:default_project_visibility,
:default_snippet_visibility,
restricted_visibility_levels: []
)
end
......
......@@ -33,7 +33,7 @@ class Admin::HooksController < Admin::ApplicationController
owner_name: "Someone",
owner_email: "example@gitlabhq.com"
}
@hook.execute(data)
@hook.execute(data, 'system_hooks')
redirect_to :back
end
......
......@@ -40,15 +40,6 @@ class Admin::ServicesController < Admin::ApplicationController
def application_services_params
params.permit(:id,
service: [
:title, :token, :type, :active, :api_key, :subdomain,
:room, :recipients, :project_url, :webhook,
:user_key, :device, :priority, :sound, :bamboo_url, :username, :password,
:build_key, :server, :teamcity_url, :build_type,
:description, :issues_url, :new_issue_url, :restrict_to_branch,
:send_from_committer_email, :disable_diffs,
:push_events, :tag_push_events, :note_events, :issues_events,
:merge_requests_events
])
service: Projects::ServicesController::ALLOWED_PARAMS)
end
end
......@@ -6,7 +6,7 @@ class Import::GitoriousController < Import::BaseController
def callback
session[:gitorious_repos] = params[:repos]
redirect_to status_import_gitorious_url
redirect_to status_import_gitorious_path
end
def status
......
......@@ -10,11 +10,11 @@ class Projects::CommitController < Projects::ApplicationController
def show
return git_not_found! unless @commit
@line_notes = commit.notes(@project).inline
@line_notes = commit.notes.inline
@diffs = @commit.diffs
@note = @project.build_commit_note(commit)
@notes_count = commit.notes(@project).count
@notes = commit.notes(@project).not_inline.fresh
@notes_count = commit.notes.count
@notes = commit.notes.not_inline.fresh
@noteable = @commit
@comments_allowed = @reply_allowed = true
@comments_target = {
......@@ -36,6 +36,6 @@ class Projects::CommitController < Projects::ApplicationController
end
def commit
@commit ||= @project.repository.commit(params[:id])
@commit ||= @project.commit(params[:id])
end
end
......@@ -146,7 +146,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def branch_to
@target_project = selected_target_project
@commit = @target_project.repository.commit(params[:ref]) if params[:ref].present?
@commit = @target_project.commit(params[:ref]) if params[:ref].present?
end
def update_branches
......
class Projects::ServicesController < Projects::ApplicationController
ALLOWED_PARAMS = [:title, :token, :type, :active, :api_key, :subdomain,
:room, :recipients, :project_url, :webhook,
:user_key, :device, :priority, :sound, :bamboo_url, :username, :password,
:build_key, :server, :teamcity_url, :build_type,
:description, :issues_url, :new_issue_url, :restrict_to_branch, :channel,
:colorize_messages, :channels,
:push_events, :issues_events, :merge_requests_events, :tag_push_events,
:note_events, :send_from_committer_email, :disable_diffs, :external_wiki_url,
:jira_issue_transition_id, :notify, :color]
# Authorize
before_action :authorize_admin_project!
before_action :service, only: [:edit, :update, :test]
......@@ -45,16 +55,6 @@ class Projects::ServicesController < Projects::ApplicationController
end
def service_params
params.require(:service).permit(
:title, :token, :type, :active, :api_key, :subdomain,
:room, :recipients, :project_url, :webhook,
:user_key, :device, :priority, :sound, :bamboo_url, :username, :password,
:build_key, :server, :teamcity_url, :build_type,
:description, :issues_url, :new_issue_url, :restrict_to_branch, :channel,
:colorize_messages, :channels,
:push_events, :issues_events, :merge_requests_events, :tag_push_events,
:note_events, :send_from_committer_email, :disable_diffs, :external_wiki_url,
:jira_issue_transition_id
)
params.require(:service).permit(ALLOWED_PARAMS)
end
end
class Projects::UploadsController < Projects::ApplicationController
layout 'project'
# We want to skip these filters for only the `show` action if `image?` is true,
# but `skip_before_action` doesn't work with both `only` and `if`, so we accomplish the same like this.
skipped_filters = [:authenticate_user!, :reject_blocked!, :project, :repository]
skip_before_action *skipped_filters, only: [:show]
before_action *skipped_filters, only: [:show], unless: :image?
skip_before_action :authenticate_user!, :reject_blocked!, :project,
:repository, if: -> { action_name == 'show' && image? }
def create
link_to_file = ::Projects::UploadService.new(project, params[:file]).
......
......@@ -66,8 +66,6 @@ class ProjectsController < ApplicationController
return
end
limit = (params[:limit] || 20).to_i
@show_star = !(current_user && current_user.starred?(@project))
respond_to do |format|
......@@ -85,11 +83,14 @@ class ProjectsController < ApplicationController
end
format.json do
@events = @project.events.recent
@events = event_filter.apply_filter(@events).with_associations
@events = @events.limit(limit).offset(params[:offset] || 0)
load_events
pager_json('events/_events', @events.count)
end
format.atom do
load_events
render layout: false
end
end
end
......@@ -167,6 +168,13 @@ class ProjectsController < ApplicationController
current_user ? 'projects' : 'public_projects'
end
def load_events
@events = @project.events.recent
@events = event_filter.apply_filter(@events).with_associations
limit = (params[:limit] || 20).to_i
@events = @events.limit(limit).offset(params[:offset] || 0)
end
def project_params
params.require(:project).permit(
:name, :path, :description, :issues_tracker, :tag_list,
......
......@@ -318,4 +318,18 @@ module ApplicationHelper
profile_key_path(key)
end
end
def state_filters_text_for(entity, project)
entity_title = entity.to_s.humanize
count =
if project.nil?
""
elsif current_controller?(:issues)
" (#{project.issues.send(entity).count})"
elsif current_controller?(:merge_requests)
" (#{project.merge_requests.send(entity).count})"
end
"#{entity_title}#{count}"
end
end
......@@ -10,7 +10,21 @@ module VisibilityLevelHelper
end
end
def visibility_level_description(level)
# Return the description for the +level+ argument.
#
# +level+ One of the Gitlab::VisibilityLevel constants
# +form_model+ Either a model object (Project, Snippet, etc.) or the name of
# a Project or Snippet class.
def visibility_level_description(level, form_model)
case form_model.is_a?(String) ? form_model : form_model.class.name
when 'PersonalSnippet', 'ProjectSnippet', 'Snippet'
snippet_visibility_level_description(level)
when 'Project'
project_visibility_level_description(level)
end
end
def project_visibility_level_description(level)
capture_haml do
haml_tag :span do
case level
......@@ -64,4 +78,12 @@ module VisibilityLevelHelper
return [] if current_user.is_admin? && !show_all
current_application_settings.restricted_visibility_levels || []
end
def default_project_visibility
current_application_settings.default_project_visibility
end
def default_snippet_visibility
current_application_settings.default_snippet_visibility
end
end
......@@ -79,7 +79,7 @@ module Emails
@disable_diffs = disable_diffs
if @compare
@commits = Commit.decorate(compare.commits)
@commits = Commit.decorate(compare.commits, @project)
@diffs = compare.diffs
end
......@@ -101,8 +101,8 @@ module Emails
if @commits.length > 1
@target_url = namespace_project_compare_url(@project.namespace,
@project,
from: Commit.new(@compare.base),
to: Commit.new(@compare.head))
from: Commit.new(@compare.base, @project),
to: Commit.new(@compare.head, @project))
@subject << "Deleted " if @reverse_compare
@subject << "#{@commits.length} commits: #{@commits.first.title}"
else
......
......@@ -18,6 +18,8 @@
# help_text :text
# restricted_visibility_levels :text
# max_attachment_size :integer default(10)
# default_project_visibility :integer
# default_snippet_visibility :integer
#
class ApplicationSetting < ActiveRecord::Base
......@@ -52,7 +54,9 @@ class ApplicationSetting < ActiveRecord::Base
gravatar_enabled: Settings.gravatar['enabled'],
sign_in_text: Settings.extra['sign_in_text'],
restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'],
max_attachment_size: Settings.gitlab['max_attachment_size']
max_attachment_size: Settings.gitlab['max_attachment_size'],
default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level']
)
end
......
......@@ -3,8 +3,12 @@ class Commit
include StaticModel
extend ActiveModel::Naming
include Mentionable
include Participable
attr_mentionable :safe_message
participant :author, :committer, :notes, :mentioned_users
attr_accessor :project
# Safe amount of changes (files and lines) in one commit to render
# Used to prevent 500 error on huge commits by suppressing diff
......@@ -18,12 +22,12 @@ class Commit
DIFF_HARD_LIMIT_LINES = 50000 unless defined?(DIFF_HARD_LIMIT_LINES)
class << self
def decorate(commits)
def decorate(commits, project)
commits.map do |commit|
if commit.kind_of?(Commit)
commit
else
self.new(commit)
self.new(commit, project)
end
end
end
......@@ -41,10 +45,11 @@ class Commit
attr_accessor :raw
def initialize(raw_commit)
def initialize(raw_commit, project)
raise "Nil as raw commit passed" unless raw_commit
@raw = raw_commit
@project = project
end
def id
......@@ -100,7 +105,7 @@ class Commit
description.present?
end
def hook_attrs(project)
def hook_attrs
path_with_namespace = project.path_with_namespace
{
......@@ -117,7 +122,7 @@ class Commit
# Discover issues should be closed when this commit is pushed to a project's
# default branch.
def closes_issues(project, current_user = self.committer)
def closes_issues(current_user = self.committer)
Gitlab::ClosingIssueExtractor.new(project, current_user).closed_by_message(safe_message)
end
......@@ -134,22 +139,7 @@ class Commit
User.find_for_commit(committer_email, committer_name)
end
def participants(project, current_user = nil)
users = []
users << author
users << committer
users.push *self.mentioned_users(current_user, project)
notes(project).each do |note|
users << note.author
users.push *note.mentioned_users(current_user, project)
end
users.uniq
end
def notes(project)
def notes
project.notes.for_commit_id(self.id)
end
......@@ -169,6 +159,6 @@ class Commit
end
def parents
@parents ||= Commit.decorate(super)
@parents ||= Commit.decorate(super, project)
end
end
# CommitRange makes it easier to work with commit ranges
#
# Examples:
#
# range = CommitRange.new('f3f85602...e86e1013')
# range.exclude_start? # => false
# range.reference_title # => "Commits f3f85602 through e86e1013"
# range.to_s # => "f3f85602...e86e1013"
#
# range = CommitRange.new('f3f856029bc5f966c5a7ee24cf7efefdd20e6019..e86e1013709735be5bb767e2b228930c543f25ae')
# range.exclude_start? # => true
# range.reference_title # => "Commits f3f85602^ through e86e1013"
# range.to_param # => {from: "f3f856029bc5f966c5a7ee24cf7efefdd20e6019^", to: "e86e1013709735be5bb767e2b228930c543f25ae"}
# range.to_s # => "f3f85602..e86e1013"
#
# # Assuming `project` is a Project with a repository containing both commits:
# range.project = project
# range.valid_commits? # => true
#
class CommitRange
include ActiveModel::Conversion
attr_reader :sha_from, :notation, :sha_to
# Optional Project model
attr_accessor :project
# See `exclude_start?`
attr_reader :exclude_start
# The beginning and ending SHA sums can be between 6 and 40 hex characters,
# and the range selection can be double- or triple-dot.
PATTERN = /\h{6,40}\.{2,3}\h{6,40}/
# Initialize a CommitRange
#
# range_string - The String commit range.
# project - An optional Project model.
#
# Raises ArgumentError if `range_string` does not match `PATTERN`.
def initialize(range_string, project = nil)
range_string.strip!
unless range_string.match(/\A#{PATTERN}\z/)
raise ArgumentError, "invalid CommitRange string format: #{range_string}"
end
@exclude_start = !range_string.include?('...')
@sha_from, @notation, @sha_to = range_string.split(/(\.{2,3})/, 2)
@project = project
end
def inspect
%(#<#{self.class}:#{object_id} #{to_s}>)
end
def to_s
"#{sha_from[0..7]}#{notation}#{sha_to[0..7]}"
end
# Returns a String for use in a link's title attribute
def reference_title
"Commits #{suffixed_sha_from} through #{sha_to}"
end
# Return a Hash of parameters for passing to a URL helper
#
# See `namespace_project_compare_url`
def to_param
{ from: suffixed_sha_from, to: sha_to }
end
def exclude_start?
exclude_start
end
# Check if both the starting and ending commit IDs exist in a project's
# repository
#
# project - An optional Project to check (default: `project`)
def valid_commits?(project = project)
return nil unless project.present?
return false unless project.valid_repo?
commit_from.present? && commit_to.present?
end
def persisted?
true
end
def commit_from
@commit_from ||= project.repository.commit(suffixed_sha_from)
end
def commit_to
@commit_to ||= project.repository.commit(sha_to)
end
private
def suffixed_sha_from
sha_from + (exclude_start? ? '^' : '')
end
end
......@@ -7,6 +7,7 @@
module Issuable
extend ActiveSupport::Concern
include Mentionable
include Participable
included do
belongs_to :author, class_name: "User"
......@@ -45,6 +46,7 @@ module Issuable
prefix: true
attr_mentionable :title, :description
participant :author, :assignee, :notes, :mentioned_users
end
module ClassMethods
......@@ -117,22 +119,6 @@ module Issuable
upvotes + downvotes
end
# Return all users participating on the discussion
def participants(current_user = self.author)
users = []
users << author
users << assignee if is_assigned?
users.push *self.mentioned_users(current_user)
notes.each do |note|
users << note.author
users.push *note.mentioned_users(current_user)
end
users.uniq
end
def subscribed?(user)
subscription = subscriptions.find_by_user_id(user.id)
......
......@@ -64,7 +64,7 @@ module Mentionable
def create_cross_references!(p = project, a = author, without = [])
refs = references(p) - without
refs.each do |ref|
Note.create_cross_reference_note(ref, local_reference, a, p)
Note.create_cross_reference_note(ref, local_reference, a)
end
end
......
# == Participable concern
#
# Contains functionality related to objects that can have participants, such as
# an author, an assignee and people mentioned in its description or comments.
#
# Used by Issue, Note, MergeRequest, Snippet and Commit.
#
# Usage:
#
# class Issue < ActiveRecord::Base
# include Participable
#
# # ...
#
# participant :author, :assignee, :mentioned_users, :notes
# end
#
# issue = Issue.last
# users = issue.participants
# # `users` will contain the issue's author, its assignee,
# # all users returned by its #mentioned_users method,
# # as well as all participants to all of the issue's notes,
# # since Note implements Participable as well.
#
module Participable
extend ActiveSupport::Concern
module ClassMethods
def participant(*attrs)
participant_attrs.concat(attrs.map(&:to_s))
end
def participant_attrs
@participant_attrs ||= []
end
end
def participants(current_user = self.author)
self.class.participant_attrs.flat_map do |attr|
meth = method(attr)
value =
if meth.arity == 1
meth.call(current_user)
else
meth.call
end
participants_for(value, current_user)
end.compact.uniq
end
private
def participants_for(value, current_user = nil)
case value
when User
[value]
when Enumerable, ActiveRecord::Relation
value.flat_map { |v| participants_for(v, current_user) }
when Participable
value.participants(current_user)
end
end
end
......@@ -30,12 +30,15 @@ class WebHook < ActiveRecord::Base
validates :url, presence: true,
format: { with: /\A#{URI.regexp(%w(http https))}\z/, message: "should be a valid url" }
def execute(data)
def execute(data, hook_name)
parsed_url = URI.parse(url)
if parsed_url.userinfo.blank?
WebHook.post(url,
body: data.to_json,
headers: { "Content-Type" => "application/json" },
headers: {
"Content-Type" => "application/json",
"X-Gitlab-Event" => hook_name.singularize.titleize
},
verify: false)
else
post_url = url.gsub("#{parsed_url.userinfo}@", "")
......@@ -45,7 +48,10 @@ class WebHook < ActiveRecord::Base
}
WebHook.post(post_url,
body: data.to_json,
headers: { "Content-Type" => "application/json" },
headers: {
"Content-Type" => "application/json",
"X-Gitlab-Event" => hook_name.singularize.titleize
},
verify: false,
basic_auth: auth)
end
......@@ -54,7 +60,7 @@ class WebHook < ActiveRecord::Base
false
end
def async_execute(data)
Sidekiq::Client.enqueue(ProjectWebHookWorker, id, data)
def async_execute(data, hook_name)
Sidekiq::Client.enqueue(ProjectWebHookWorker, id, data, hook_name)
end
end
......@@ -216,10 +216,13 @@ class MergeRequest < ActiveRecord::Base
commits_for_notes_limit = 100
commit_ids = commits.last(commits_for_notes_limit).map(&:id)
project.notes.where(
"(noteable_type = 'MergeRequest' AND noteable_id = :mr_id) OR (noteable_type = 'Commit' AND commit_id IN (:commit_ids))",
Note.where(
"(project_id = :target_project_id AND noteable_type = 'MergeRequest' AND noteable_id = :mr_id) OR" +
"(project_id = :source_project_id AND noteable_type = 'Commit' AND commit_id IN (:commit_ids))",
mr_id: id,
commit_ids: commit_ids
commit_ids: commit_ids,
target_project_id: target_project_id,
source_project_id: source_project_id
)
end
......@@ -245,7 +248,7 @@ class MergeRequest < ActiveRecord::Base
}
unless last_commit.nil?
attrs.merge!(last_commit: last_commit.hook_attrs(source_project))
attrs.merge!(last_commit: last_commit.hook_attrs)
end
attributes.merge!(attrs)
......@@ -262,7 +265,7 @@ class MergeRequest < ActiveRecord::Base
# Return the set of issues that will be closed if this merge request is accepted.
def closes_issues(current_user = self.author)
if target_branch == project.default_branch
issues = commits.flat_map { |c| c.closes_issues(project, current_user) }
issues = commits.flat_map { |c| c.closes_issues(current_user) }
issues.push(*Gitlab::ClosingIssueExtractor.new(project, current_user).
closed_by_message(description))
issues.uniq.sort_by(&:id)
......
......@@ -67,7 +67,7 @@ class MergeRequestDiff < ActiveRecord::Base
end
def load_commits(array)
array.map { |hash| Commit.new(Gitlab::Git::Commit.new(hash)) }
array.map { |hash| Commit.new(Gitlab::Git::Commit.new(hash), merge_request.source_project) }
end
def dump_diffs(diffs)
......@@ -88,7 +88,7 @@ class MergeRequestDiff < ActiveRecord::Base
commits = compare_result.commits
if commits.present?
commits = Commit.decorate(commits).
commits = Commit.decorate(commits, merge_request.source_project).
sort_by(&:created_at).
reverse
end
......
......@@ -60,15 +60,24 @@ class Namespace < ActiveRecord::Base
def clean_path(path)
path = path.dup
# Get the email username by removing everything after an `@` sign.
path.gsub!(/@.*\z/, "")
# Usernames can't end in .git, so remove it.
path.gsub!(/\.git\z/, "")
# Remove dashes at the start of the username.
path.gsub!(/\A-+/, "")
# Remove periods at the end of the username.
path.gsub!(/\.+\z/, "")
# Remove everything that's not in the list of allowed characters.
path.gsub!(/[^a-zA-Z0-9_\-\.]/, "")
# Users with the great usernames of "." or ".." would end up with a blank username.
# Work around that by setting their username to "blank", followed by a counter.
path = "blank" if path.blank?
counter = 0
base = path
while Namespace.by_path(path).present?
while Namespace.find_by_path_or_name(path)
counter += 1
path = "#{base}#{counter}"
end
......
......@@ -23,10 +23,12 @@ require 'file_size_validator'
class Note < ActiveRecord::Base
include Mentionable
include Gitlab::CurrentSettings
include Participable
default_value_for :system, false
attr_mentionable :note
participant :author, :mentioned_users
belongs_to :project
belongs_to :noteable, polymorphic: true
......@@ -77,8 +79,10 @@ class Note < ActiveRecord::Base
# +mentioner+'s description or an associated Note.
# Create a system Note associated with +noteable+ with a GFM back-reference
# to +mentioner+.
def create_cross_reference_note(noteable, mentioner, author, project)
gfm_reference = mentioner_gfm_ref(noteable, mentioner, project)
def create_cross_reference_note(noteable, mentioner, author)
gfm_reference = mentioner_gfm_ref(noteable, mentioner)
project = noteable.project
note_options = {
project: project,
......@@ -93,7 +97,6 @@ class Note < ActiveRecord::Base
note_options.merge!(noteable: noteable)
end
if noteable.is_a?(ExternalIssue)
project.issues_tracker.create_cross_reference_note(noteable, mentioner, author)
else
......@@ -241,7 +244,7 @@ class Note < ActiveRecord::Base
# Determine whether or not a cross-reference note already exists.
def cross_reference_exists?(noteable, mentioner)
gfm_reference = mentioner_gfm_ref(noteable, mentioner)
gfm_reference = mentioner_gfm_ref(noteable, mentioner, true)
notes = if noteable.is_a?(Commit)
where(commit_id: noteable.id, noteable_type: 'Commit')
else
......@@ -274,43 +277,19 @@ class Note < ActiveRecord::Base
# Prepend the mentioner's namespaced project path to the GFM reference for
# cross-project references. For same-project references, return the
# unmodified GFM reference.
def mentioner_gfm_ref(noteable, mentioner, project = nil)
if mentioner.is_a?(Commit)
if project.nil?
return mentioner.gfm_reference.sub('commit ', 'commit %')
else
mentioning_project = project
end
else
mentioning_project = mentioner.project
end
noteable_project_id = noteable_project_id(noteable, mentioning_project)
full_gfm_reference(mentioning_project, noteable_project_id, mentioner)
end
# Return the ID of the project that +noteable+ belongs to, or nil if
# +noteable+ is a commit and is not part of the project that owns
# +mentioner+.
def noteable_project_id(noteable, mentioning_project)
if noteable.is_a?(Commit)
if mentioning_project.repository.commit(noteable.id)
# The noteable commit belongs to the mentioner's project
mentioning_project.id
else
nil
end
else
noteable.project.id
def mentioner_gfm_ref(noteable, mentioner, cross_reference = false)
if mentioner.is_a?(Commit) && cross_reference
return mentioner.gfm_reference.sub('commit ', 'commit %')
end
full_gfm_reference(mentioner.project, noteable.project, mentioner)
end
# Return the +mentioner+ GFM reference. If the mentioner and noteable
# projects are not the same, add the mentioning project's path to the
# returned value.
def full_gfm_reference(mentioning_project, noteable_project_id, mentioner)
if mentioning_project.id == noteable_project_id
def full_gfm_reference(mentioning_project, noteable_project, mentioner)
if mentioning_project == noteable_project
mentioner.gfm_reference
else
if mentioner.is_a?(Commit)
......@@ -517,7 +496,7 @@ class Note < ActiveRecord::Base
# override to return commits, which are not active record
def noteable
if for_commit?
project.repository.commit(commit_id)
project.commit(commit_id)
else
super
end
......
......@@ -259,7 +259,11 @@ class Project < ActiveRecord::Base
end
def repository
@repository ||= Repository.new(path_with_namespace)
@repository ||= Repository.new(path_with_namespace, nil, self)
end
def commit(id = 'HEAD')
repository.commit(id)
end
def saved?
......@@ -492,7 +496,7 @@ class Project < ActiveRecord::Base
def execute_hooks(data, hooks_scope = :push_hooks)
hooks.send(hooks_scope).each do |hook|
hook.async_execute(data)
hook.async_execute(data, hooks_scope.to_s)
end
if group
group.hooks.send(hooks_scope).each do |hook|
......@@ -708,11 +712,21 @@ class Project < ActiveRecord::Base
end
def create_repository
if gitlab_shell.add_repository(path_with_namespace)
true
if forked?
if gitlab_shell.fork_repository(forked_from_project.path_with_namespace, self.namespace.path)
ensure_satellite_exists
true
else
errors.add(:base, 'Failed to fork repository')
false
end
else
errors.add(:base, 'Failed to create repository')
false
if gitlab_shell.add_repository(path_with_namespace)
true
else
errors.add(:base, 'Failed to create repository')
false
end
end
end
......
......@@ -20,7 +20,7 @@
class HipchatService < Service
MAX_COMMITS = 3
prop_accessor :token, :room, :server
prop_accessor :token, :room, :server, :notify, :color
validates :token, presence: true, if: :activated?
def title
......@@ -39,6 +39,8 @@ class HipchatService < Service
[
{ type: 'text', name: 'token', placeholder: 'Room token' },
{ type: 'text', name: 'room', placeholder: 'Room name or ID' },
{ type: 'checkbox', name: 'notify' },
{ type: 'select', name: 'color', choices: ['yellow', 'red', 'green', 'purple', 'gray', 'random'] },
{ type: 'text', name: 'server',
placeholder: 'Leave blank for default. https://hipchat.example.com' }
]
......@@ -52,7 +54,7 @@ class HipchatService < Service
return unless supported_events.include?(data[:object_kind])
message = create_message(data)
return unless message.present?
gate[room].send('GitLab', message)
gate[room].send('GitLab', message, message_options)
end
private
......@@ -63,6 +65,10 @@ class HipchatService < Service
@gate ||= HipChat::Client.new(token, options)
end
def message_options
{ notify: notify.present? && notify == '1', color: color || 'yellow' }
end
def create_message(data)
object_kind = data[:object_kind]
......
......@@ -112,7 +112,7 @@ class ProjectWiki
end
def repository
Repository.new(path_with_namespace, default_branch)
Repository.new(path_with_namespace, default_branch, @project)
end
def default_branch
......
......@@ -18,6 +18,6 @@ class ProtectedBranch < ActiveRecord::Base
validates :project, presence: true
def commit
project.repository.commit(self.name)
project.commit(self.name)
end
end
class Repository
include Gitlab::ShellAdapter
attr_accessor :raw_repository, :path_with_namespace
attr_accessor :raw_repository, :path_with_namespace, :project
def initialize(path_with_namespace, default_branch = nil)
def initialize(path_with_namespace, default_branch = nil, project = nil)
@path_with_namespace = path_with_namespace
@raw_repository = Gitlab::Git::Repository.new(path_to_repo) if path_with_namespace
@project = project
rescue Gitlab::Git::Repository::NoRepository
nil
end
......@@ -28,7 +29,7 @@ class Repository
def commit(id = 'HEAD')
return nil unless raw_repository
commit = Gitlab::Git::Commit.find(raw_repository, id)
commit = Commit.new(commit) if commit
commit = Commit.new(commit, @project) if commit
commit
rescue Rugged::OdbError
nil
......@@ -42,13 +43,13 @@ class Repository
limit: limit,
offset: offset,
)
commits = Commit.decorate(commits) if commits.present?
commits = Commit.decorate(commits, @project) if commits.present?
commits
end
def commits_between(from, to)
commits = Gitlab::Git::Commit.between(raw_repository, from, to)
commits = Commit.decorate(commits) if commits.present?
commits = Commit.decorate(commits, @project) if commits.present?
commits
end
......
......@@ -19,6 +19,7 @@ class Snippet < ActiveRecord::Base
include Sortable
include Linguist::BlobHelper
include Gitlab::VisibilityLevel
include Participable
default_value_for :visibility_level, Snippet::PRIVATE
......@@ -47,6 +48,8 @@ class Snippet < ActiveRecord::Base
scope :expired, -> { where(["expires_at IS NOT NULL AND expires_at < ?", Time.current]) }
scope :non_expired, -> { where(["expires_at IS NULL OR expires_at > ?", Time.current]) }
participant :author, :notes
def self.content_types
[
".rb", ".py", ".pl", ".scala", ".c", ".cpp", ".java",
......@@ -87,18 +90,6 @@ class Snippet < ActiveRecord::Base
visibility_level
end
def participants(current_user = self.author)
users = []
users << author
notes.each do |note|
users << note.author
users.push *note.mentioned_users(current_user)
end
users.uniq
end
class << self
def search(query)
where('(title LIKE :query OR file_name LIKE :query)', query: "%#{query}%")
......
......@@ -38,7 +38,7 @@ class CreateTagService < BaseService
end
def create_push_data(project, user, tag)
commits = [project.repository.commit(tag.target)].compact
commits = [project.commit(tag.target)].compact
Gitlab::PushDataBuilder.
build(project, user, Gitlab::Git::BLANK_SHA, tag.target, "#{Gitlab::Git::TAG_REF_PREFIX}#{tag.name}", commits, tag.message)
end
......
......@@ -70,7 +70,7 @@ class GitPushService
# Close issues if these commits were pushed to the project's default branch and the commit message matches the
# closing regex. Exclude any mentioned Issues from cross-referencing even if the commits are being pushed to
# a different branch.
issues_to_close = commit.closes_issues(project, user)
issues_to_close = commit.closes_issues(user)
# Load commit author only if needed.
# For push with 1k commits it prevents 900+ requests in database
......@@ -98,7 +98,7 @@ class GitPushService
author ||= commit_user(commit)
refs.each do |r|
Note.create_cross_reference_note(r, commit, author, project)
Note.create_cross_reference_note(r, commit, author)
end
end
end
......
......@@ -25,7 +25,7 @@ class GitTagPushService
tag_name = Gitlab::Git.ref_name(ref)
tag = project.repository.find_tag(tag_name)
if tag && tag.target == newrev
commit = project.repository.commit(tag.target)
commit = project.commit(tag.target)
commits = [commit].compact
message = tag.message
end
......
......@@ -29,7 +29,7 @@ module MergeRequests
# At this point we decide if merge request can be created
# If we have at least one commit to merge -> creation allowed
if commits.present?
merge_request.compare_commits = Commit.decorate(commits)
merge_request.compare_commits = Commit.decorate(commits, merge_request.source_project)
merge_request.can_be_created = true
merge_request.compare_failed = false
......
......@@ -15,7 +15,7 @@ module Notes
# Create a cross-reference note if this Note contains GFM that names an
# issue, merge request, or commit.
note.references.each do |mentioned|
Note.create_cross_reference_note(mentioned, note.noteable, note.author, note.project)
Note.create_cross_reference_note(mentioned, note.noteable, note.author)
end
execute_hooks(note)
......
......@@ -13,8 +13,7 @@ module Notes
# Create a cross-reference note if this Note contains GFM that
# names an issue, merge request, or commit.
note.references.each do |mentioned|
Note.create_cross_reference_note(mentioned, note.noteable,
note.author, note.project)
Note.create_cross_reference_note(mentioned, note.noteable, note.author)
end
end
end
......
......@@ -129,9 +129,7 @@ class NotificationService
# Add all users participating in the thread (author, assignee, comment authors)
participants =
if target.is_a?(Commit)
target.participants(note.project, note.author)
elsif target.respond_to?(:participants)
if target.respond_to?(:participants)
target.participants(note.author)
else
note.mentioned_users
......
......@@ -5,6 +5,8 @@ module Projects
end
def execute
forked_from_project_id = params.delete(:forked_from_project_id)
@project = Project.new(params)
# Make sure that the user is allowed to use the specified visibility
......@@ -45,10 +47,14 @@ module Projects
@project.creator = current_user
if forked_from_project_id
@project.build_forked_project_link(forked_from_project_id: forked_from_project_id)
end
Project.transaction do
@project.save
unless @project.import?
if @project.persisted? && !@project.import?
unless @project.create_repository
raise 'Failed to create repository'
end
......
module Projects
class ForkService < BaseService
include Gitlab::ShellAdapter
def execute
@from_project = @project
project_params = {
visibility_level: @from_project.visibility_level,
description: @from_project.description,
new_params = {
forked_from_project_id: @project.id,
visibility_level: @project.visibility_level,
description: @project.description,
name: @project.name,
path: @project.path,
namespace_id: @params[:namespace].try(:id) || current_user.namespace.id
}
project = Project.new(project_params)
project.name = @from_project.name
project.path = @from_project.path
project.creator = @current_user
if @from_project.avatar.present? && @from_project.avatar.image?
project.avatar = @from_project.avatar
end
if namespace = @params[:namespace]
project.namespace = namespace
else
project.namespace = @current_user.namespace
if @project.avatar.present? && @project.avatar.image?
new_params[:avatar] = @project.avatar
end
unless @current_user.can?(:create_projects, project.namespace)
project.errors.add(:namespace, 'insufficient access rights')
return project
end
# If the project cannot save, we do not want to trigger the project destroy
# as this can have the side effect of deleting a repo attached to an existing
# project with the same name and namespace
if project.valid?
begin
Project.transaction do
#First save the DB entries as they can be rolled back if the repo fork fails
project.build_forked_project_link(forked_to_project_id: project.id, forked_from_project_id: @from_project.id)
if project.save
project.team << [@current_user, :master, @current_user]
end
#Now fork the repo
unless gitlab_shell.fork_repository(@from_project.path_with_namespace, project.namespace.path)
raise 'forking failed in gitlab-shell'
end
project.ensure_satellite_exists
end
new_project = CreateService.new(current_user, new_params).execute
if @from_project.gitlab_ci?
ForkRegistrationWorker.perform_async(@from_project.id, project.id, @current_user.private_token)
end
rescue => ex
project.errors.add(:base, 'Fork transaction failed.')
project.destroy
if new_project.persisted?
if @project.gitlab_ci?
ForkRegistrationWorker.perform_async(@project.id, new_project.id, @current_user.private_token)
end
else
project.errors.add(:base, 'Invalid fork destination')
end
project
new_project
end
end
end
......@@ -13,21 +13,19 @@ module Projects
end
def participants_in(type, id)
users =
target =
case type
when "Issue"
issue = project.issues.find_by_iid(id)
issue.participants(current_user) if issue
project.issues.find_by_iid(id)
when "MergeRequest"
merge_request = project.merge_requests.find_by_iid(id)
merge_request.participants(current_user) if merge_request
project.merge_requests.find_by_iid(id)
when "Commit"
commit = project.repository.commit(id)
commit.participants(project, current_user) if commit
project.commit(id)
end
return [] unless target
return [] unless users
users = target.participants(current_user)
sorted(users)
end
......
......@@ -7,12 +7,12 @@ class SystemHooksService
def execute_hooks(data)
SystemHook.all.each do |sh|
async_execute_hook sh, data
async_execute_hook(sh, data, 'system_hooks')
end
end
def async_execute_hook(hook, data)
Sidekiq::Client.enqueue(SystemHookWorker, hook.id, data)
def async_execute_hook(hook, data, hook_name)
Sidekiq::Client.enqueue(SystemHookWorker, hook.id, data, hook_name)
end
def build_event_data(model, event)
......
class TestHookService
def execute(hook, current_user)
data = Gitlab::PushDataBuilder.build_sample(project(hook), current_user)
hook.execute(data)
hook.execute(data, 'push_hooks')
end
private
......
......@@ -42,6 +42,14 @@
= f.label :default_branch_protection, class: 'control-label col-sm-2'
.col-sm-10
= f.select :default_branch_protection, options_for_select(Gitlab::Access.protection_options, @application_setting.default_branch_protection), {}, class: 'form-control'
.form-group
= f.label :default_project_visibility, class: 'control-label col-sm-2'
.col-sm-10
= render('shared/visibility_radios', model_method: :default_project_visibility, form: f, selected_level: @application_setting.default_project_visibility, form_model: 'Project')
.form-group
= f.label :default_snippet_visibility, class: 'control-label col-sm-2'
.col-sm-10
= render('shared/visibility_radios', model_method: :default_snippet_visibility, form: f, selected_level: @application_setting.default_snippet_visibility, form_model: 'Snippet')
.form-group
= f.label :restricted_visibility_levels, class: 'control-label col-sm-2'
.col-sm-10
......
= render "events/event_last_push", event: @last_push
= render 'shared/event_filter'
.hidden-xs
= render "events/event_last_push", event: @last_push
- if current_user
%ul.nav.nav-pills.event_filter.pull-right
%li.pull-right
= link_to dashboard_path(:atom, { private_token: current_user.private_token }), class: 'rss-btn' do
%i.fa.fa-rss
Activity Feed
= render 'shared/event_filter'
%hr
.content_list
= spinner
xml.instruct!
xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
xml.title "#{current_user.name} issues"
xml.link href: issues_dashboard_url(:atom, private_token: current_user.private_token), rel: "self", type: "application/atom+xml"
xml.link href: issues_dashboard_url(private_token: current_user.private_token), rel: "alternate", type: "text/html"
xml.id issues_dashboard_url(private_token: current_user.private_token)
xml.link href: issues_dashboard_url(format: :atom, private_token: current_user.private_token), rel: "self", type: "application/atom+xml"
xml.link href: issues_dashboard_url, rel: "alternate", type: "text/html"
xml.id issues_dashboard_url
xml.updated @issues.first.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") if @issues.any?
@issues.each do |issue|
......
= content_for :meta_tags do
- if current_user
= auto_discovery_link_tag(:atom, issues_dashboard_url(format: :atom, private_token: current_user.private_token), title: "#{current_user.name} issues")
%h3.page-title
Issues
......@@ -6,5 +10,11 @@
%hr
.append-bottom-20
.pull-right
- if current_user
.hidden-xs.pull-left
= link_to issues_dashboard_url(format: :atom, private_token: current_user.private_token), class: 'btn' do
%i.fa.fa-rss
= render 'shared/issuable_filter'
= render 'shared/issues'
xml.instruct!
xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
xml.title "Dashboard feed#{" - #{current_user.name}" if current_user.name.present?}"
xml.link href: dashboard_url(:atom), rel: "self", type: "application/atom+xml"
xml.title "Activity"
xml.link href: dashboard_url(format: :atom, private_token: current_user.private_token), rel: "self", type: "application/atom+xml"
xml.link href: dashboard_url, rel: "alternate", type: "text/html"
xml.id projects_url
xml.id dashboard_url
xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any?
@events.each do |event|
......
= content_for :meta_tags do
- if current_user
= auto_discovery_link_tag(:atom, dashboard_url(format: :atom, private_token: current_user.private_token), title: "All activity")
- if @projects.any?
.dashboard.row
%section.activities.col-md-8
......
......@@ -18,7 +18,7 @@
%a.twitter-share-button{ |
href: "https://twitter.com/share", |
"data-url" => event.project.web_url, |
"data-text" => "I just #{event.project.imported? ? "imported" : "created"} a new project in GitLab! GitLab is version control on your server.", |
"data-text" => "I just #{event.action_name} a new project on GitLab! GitLab is version control on your server.", |
"data-size" => "medium", |
"data-related" => "gitlab", |
"data-hashtags" => "gitlab", |
......
%ul.sidebar-subnav
= nav_link(path: 'groups#edit') do
= link_to edit_group_path(@group), title: 'Group' do
%i.fa.fa-pencil-square-o
= link_to edit_group_path(@group), title: 'Group', data: {placement: 'right'} do
= icon('pencil-square-o')
%span
Group
= nav_link(path: 'groups#projects') do
= link_to projects_group_path(@group), title: 'Projects' do
%i.fa.fa-folder
= link_to projects_group_path(@group), title: 'Projects', data: {placement: 'right'} do
= icon('folder')
%span
Projects
- if ldap_enabled?
......
xml.instruct!
xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
xml.title "#{@user.name} issues"
xml.link :href => issues_dashboard_url(:atom, :private_token => @user.private_token), :rel => "self", :type => "application/atom+xml"
xml.link :href => issues_dashboard_url(:private_token => @user.private_token), :rel => "alternate", :type => "text/html"
xml.id issues_dashboard_url(:private_token => @user.private_token)
xml.link href: issues_dashboard_url(format: :atom, private_token: @user.private_token), rel: "self", type: "application/atom+xml"
xml.link href: issues_dashboard_url, rel: "alternate", type: "text/html"
xml.id issues_dashboard_url
xml.updated @issues.first.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") if @issues.any?
@issues.each do |issue|
......
= content_for :meta_tags do
- if current_user
= auto_discovery_link_tag(:atom, issues_group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} issues")
%h3.page-title
Issues
......@@ -10,5 +14,11 @@
%hr
.append-bottom-20
.pull-right
- if current_user
.hidden-xs.pull-left
= link_to issues_group_url(@group, format: :atom, private_token: current_user.private_token), class: 'btn' do
%i.fa.fa-rss
= render 'shared/issuable_filter'
= render 'shared/issues'
xml.instruct!
xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
xml.title "Group feed - #{@group.name}"
xml.link href: group_path(@group, :atom), rel: "self", type: "application/atom+xml"
xml.link href: group_path(@group), rel: "alternate", type: "text/html"
xml.id projects_url
xml.title "#{@group.name} activity"
xml.link href: group_url(@group, format: :atom, private_token: current_user.private_token), rel: "self", type: "application/atom+xml"
xml.link href: group_url(@group), rel: "alternate", type: "text/html"
xml.id group_url(@group)
xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any?
@events.each do |event|
......
= content_for :meta_tags do
- if current_user
= auto_discovery_link_tag(:atom, group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} activity")
.dashboard
.header-with-avatar.clearfix
= image_tag group_icon(@group), class: "avatar group-avatar s90"
......@@ -11,9 +15,20 @@
%hr
.row
%section.activities.col-md-8
- if current_user
= render "events/event_last_push", event: @last_push
= render 'shared/event_filter'
.hidden-xs
- if current_user
= render "events/event_last_push", event: @last_push
- if current_user
%ul.nav.nav-pills.event_filter.pull-right
%li
= link_to group_path(@group, { format: :atom, private_token: current_user.private_token }), title: "Feed", class: 'rss-btn' do
%i.fa.fa-rss
Activity Feed
= render 'shared/event_filter'
%hr
.content_list
= spinner
%aside.side.col-md-4
......
......@@ -5,6 +5,7 @@
%title
= "#{title} | " if defined?(title)
GitLab
= favicon_link_tag 'favicon.ico'
= stylesheet_link_tag "application", :media => "all"
= stylesheet_link_tag "print", :media => "print"
......@@ -14,16 +15,8 @@
%meta{name: 'viewport', content: 'width=device-width, initial-scale=1, maximum-scale=1'}
%meta{name: 'theme-color', content: '#474D57'}
= yield(:meta_tags)
= render 'layouts/google_analytics' if extra_config.has_key?('google_analytics_id')
= render 'layouts/piwik' if extra_config.has_key?('piwik_url') && extra_config.has_key?('piwik_site_id')
= render 'layouts/bootlint' if Rails.env.development?
-# Atom feed
- if current_user
- if controller_name == 'projects' && action_name == 'index'
= auto_discovery_link_tag :atom, projects_url(:atom, private_token: current_user.private_token), title: "Dashboard feed"
- if @project && !@project.new_record?
- if current_controller?(:tree, :commits)
= auto_discovery_link_tag(:atom, namespace_project_commits_url(@project.namespace, @project, @ref, format: :atom, private_token: current_user.private_token), title: "Recent commits to #{@project.name}:#{@ref}")
- if current_controller?(:issues)
= auto_discovery_link_tag(:atom, namespace_project_issues_url(@project.namespace, @project, :atom, private_token: current_user.private_token), title: "#{@project.name} issues")
......@@ -2,48 +2,47 @@
.navbar-inner
.container
%div.app_logo
= link_to root_path, class: "home has_bottom_tooltip", title: "Dashboard" do
= link_to root_path, class: 'home', title: 'Dashboard', id: 'js-shortcuts-home', data: {toggle: 'tooltip', placement: 'bottom'} do
= brand_header_logo
%h3 GitLab
%h1.title= title
%button.navbar-toggle{"data-target" => ".navbar-collapse", "data-toggle" => "collapse", type: "button"}
%button.navbar-toggle{type: 'button', data: {target: '.navbar-collapse', toggle: 'collapse'}}
%span.sr-only Toggle navigation
%i.fa.fa-bars
= icon('bars')
.navbar-collapse.collapse
%ul.nav.navbar-nav
%li.hidden-sm.hidden-xs
= render "layouts/search"
= render 'layouts/search'
%li.visible-sm.visible-xs
= link_to search_path, title: "Search", class: 'has_bottom_tooltip', 'data-original-title' => 'Search area' do
%i.fa.fa-search
= link_to search_path, title: 'Search', data: {toggle: 'tooltip', placement: 'bottom'} do
= icon('search')
%li
= link_to help_path, title: 'Help', class: 'has_bottom_tooltip',
'data-original-title' => 'Help' do
%i.fa.fa-question-circle
= link_to help_path, title: 'Help', data: {toggle: 'tooltip', placement: 'bottom'} do
= icon('question-circle')
%li
= link_to explore_root_path, title: "Explore", class: 'has_bottom_tooltip', 'data-original-title' => 'Public area' do
%i.fa.fa-globe
= link_to explore_root_path, title: 'Explore', data: {toggle: 'tooltip', placement: 'bottom'} do
= icon('globe')
%li
= link_to user_snippets_path(current_user), title: "Your snippets", class: 'has_bottom_tooltip', 'data-original-title' => 'Your snippets' do
%i.fa.fa-clipboard
= link_to user_snippets_path(current_user), title: 'Your snippets', data: {toggle: 'tooltip', placement: 'bottom'} do
= icon('clipboard')
- if current_user.is_admin?
%li
= link_to admin_root_path, title: "Admin area", class: 'has_bottom_tooltip', 'data-original-title' => 'Admin area' do
%i.fa.fa-cogs
= link_to admin_root_path, title: 'Admin area', data: {toggle: 'tooltip', placement: 'bottom'} do
= icon('cogs')
- if current_user.can_create_project?
%li
= link_to new_project_path, title: "New project", class: 'has_bottom_tooltip', 'data-original-title' => 'New project' do
%i.fa.fa-plus
= link_to new_project_path, title: 'New project', data: {toggle: 'tooltip', placement: 'bottom'} do
= icon('plus')
%li
= link_to profile_path, title: "Profile settings", class: 'has_bottom_tooltip', 'data-original-title' => 'Profile settings"' do
%i.fa.fa-user
= link_to profile_path, title: 'Profile settings', data: {toggle: 'tooltip', placement: 'bottom'} do
= icon('user')
%li
= link_to destroy_user_session_path, class: "logout", method: :delete, title: "Sign out", class: 'has_bottom_tooltip', 'data-original-title' => 'Sign out' do
%i.fa.fa-sign-out
= link_to destroy_user_session_path, class: 'logout', method: :delete, title: 'Sign out', data: {toggle: 'tooltip', placement: 'bottom'} do
= icon('sign-out')
%li.hidden-xs
= link_to current_user, class: "profile-pic has_bottom_tooltip", id: 'profile-pic', 'data-original-title' => 'Your profile' do
= link_to current_user, class: 'profile-pic', id: 'profile-pic', data: {toggle: 'tooltip', placement: 'bottom'} do
= image_tag avatar_icon(current_user.email, 60), alt: 'User activity'
= render 'shared/outdated_browser'
......@@ -15,7 +15,3 @@
= yield
= yield :embedded_scripts
:coffeescript
$('.page-sidebar-collapsed .nav-sidebar a').tooltip placement: "right"
......@@ -5,37 +5,37 @@
%span
Overview
= nav_link(controller: :projects) do
= link_to admin_namespaces_projects_path, title: 'Projects' do
= link_to admin_namespaces_projects_path, title: 'Projects', data: {placement: 'right'} do
= icon('cube fw')
%span
Projects
= nav_link(controller: :users) do
= link_to admin_users_path, title: 'Users' do
= link_to admin_users_path, title: 'Users', data: {placement: 'right'} do
= icon('user fw')
%span
Users
= nav_link(controller: :groups) do
= link_to admin_groups_path, title: 'Groups' do
= link_to admin_groups_path, title: 'Groups', data: {placement: 'right'} do
= icon('group fw')
%span
Groups
= nav_link(controller: :deploy_keys) do
= link_to admin_deploy_keys_path, title: 'Deploy Keys' do
= link_to admin_deploy_keys_path, title: 'Deploy Keys', data: {placement: 'right'} do
= icon('key fw')
%span
Deploy Keys
= nav_link(controller: :logs) do
= link_to admin_logs_path, title: 'Logs' do
= link_to admin_logs_path, title: 'Logs', data: {placement: 'right'} do
= icon('file-text fw')
%span
Logs
= nav_link(controller: :broadcast_messages) do
= link_to admin_broadcast_messages_path, title: 'Broadcast Messages' do
= link_to admin_broadcast_messages_path, title: 'Broadcast Messages', data: {placement: 'right'} do
= icon('bullhorn fw')
%span
Messages
= nav_link(controller: :hooks) do
= link_to admin_hooks_path, title: 'Hooks' do
= link_to admin_hooks_path, title: 'Hooks', data: {placement: 'right'} do
= icon('external-link fw')
%span
Hooks
......@@ -45,7 +45,7 @@
%span
Git Hooks
= nav_link(controller: :background_jobs) do
= link_to admin_background_jobs_path, title: 'Background Jobs' do
= link_to admin_background_jobs_path, title: 'Background Jobs', data: {placement: 'right'} do
= icon('cog fw')
%span
Background Jobs
......@@ -56,19 +56,19 @@
Appearance
= nav_link(controller: :applications) do
= link_to admin_applications_path, title: 'Applications' do
= link_to admin_applications_path, title: 'Applications', data: {placement: 'right'} do
= icon('cloud fw')
%span
Applications
= nav_link(controller: :services) do
= link_to admin_application_settings_services_path, title: 'Service Templates' do
= link_to admin_application_settings_services_path, title: 'Service Templates', data: {placement: 'right'} do
= icon('copy fw')
%span
Service Templates
= nav_link(controller: :application_settings, html_options: { class: 'separate-item'}) do
= link_to admin_application_settings_path, title: 'Settings' do
= link_to admin_application_settings_path, title: 'Settings', data: {placement: 'right'} do
= icon('cogs fw')
%span
Settings
%ul.nav.nav-sidebar
= nav_link(path: 'dashboard#show', html_options: {class: 'home'}) do
= link_to root_path, title: 'Home', class: 'shortcuts-activity' do
= link_to root_path, title: 'Home', class: 'shortcuts-activity', data: {placement: 'right'} do
= icon('dashboard fw')
%span
Your Projects
= nav_link(path: 'projects#starred') do
= link_to starred_dashboard_projects_path, title: 'Starred Projects' do
= link_to starred_dashboard_projects_path, title: 'Starred Projects', data: {placement: 'right'} do
= icon('star fw')
%span
Starred Projects
= nav_link(controller: :groups) do
= link_to dashboard_groups_path, title: 'Groups' do
= link_to dashboard_groups_path, title: 'Groups', data: {placement: 'right'} do
= icon('group fw')
%span
Groups
= nav_link(controller: :milestones) do
= link_to dashboard_milestones_path, title: 'Milestones' do
= link_to dashboard_milestones_path, title: 'Milestones', data: {placement: 'right'} do
= icon('clock-o fw')
%span
Milestones
= nav_link(path: 'dashboard#issues') do
= link_to assigned_issues_dashboard_path, title: 'Issues', class: 'shortcuts-issues' do
= link_to assigned_issues_dashboard_path, title: 'Issues', class: 'shortcuts-issues', data: {placement: 'right'} do
= icon('exclamation-circle fw')
%span
Issues
%span.count= current_user.assigned_issues.opened.count
= nav_link(path: 'dashboard#merge_requests') do
= link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'shortcuts-merge_requests' do
= link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'shortcuts-merge_requests', data: {placement: 'right'} do
= icon('tasks fw')
%span
Merge Requests
%span.count= current_user.assigned_merge_requests.opened.count
= nav_link(controller: :help) do
= link_to help_path, title: 'Help' do
= link_to help_path, title: 'Help', data: {placement: 'right'} do
= icon('question-circle fw')
%span
Help
%ul.nav.nav-sidebar
= nav_link(path: 'projects#trending') do
= link_to explore_root_path do
= link_to explore_root_path, title: 'Trending Projects', data: {placement: 'right'} do
= icon('comments fw')
%span Trending Projects
= nav_link(path: 'projects#starred') do
= link_to starred_explore_projects_path do
= link_to starred_explore_projects_path, title: 'Most-starred Projects', data: {placement: 'right'} do
= icon('star fw')
%span Most Starred Projects
%span Most-starred Projects
= nav_link(path: 'projects#index') do
= link_to explore_projects_path do
= link_to explore_projects_path, title: 'All Projects', data: {placement: 'right'} do
= icon('bookmark fw')
%span All Projects
= nav_link(controller: :groups) do
= link_to explore_groups_path do
= link_to explore_groups_path, title: 'All Groups', data: {placement: 'right'} do
= icon('group fw')
%span All Groups
%ul.nav.nav-sidebar
= nav_link(path: 'groups#show', html_options: {class: 'home'}) do
= link_to group_path(@group), title: "Home" do
= link_to group_path(@group), title: 'Home', data: {placement: 'right'} do
= icon('dashboard fw')
%span
Activity
- if current_user
= nav_link(controller: [:group, :milestones]) do
= link_to group_milestones_path(@group), title: 'Milestones' do
= link_to group_milestones_path(@group), title: 'Milestones', data: {placement: 'right'} do
= icon('clock-o fw')
%span
Milestones
= nav_link(path: 'groups#issues') do
= link_to issues_group_path(@group), title: 'Issues' do
= link_to issues_group_path(@group), title: 'Issues', data: {placement: 'right'} do
= icon('exclamation-circle fw')
%span
Issues
- if current_user
%span.count= Issue.opened.of_group(@group).count
= nav_link(path: 'groups#merge_requests') do
= link_to merge_requests_group_path(@group), title: 'Merge Requests' do
= link_to merge_requests_group_path(@group), title: 'Merge Requests', data: {placement: 'right'} do
= icon('tasks fw')
%span
Merge Requests
- if current_user
%span.count= MergeRequest.opened.of_group(@group).count
= nav_link(controller: [:group_members]) do
= link_to group_group_members_path(@group), title: 'Members' do
= link_to group_group_members_path(@group), title: 'Members', data: {placement: 'right'} do
= icon('users fw')
%span
Members
- if can?(current_user, :admin_group, @group)
= nav_link(html_options: { class: "#{"active" if group_settings_page?} separate-item" }) do
= link_to edit_group_path(@group), title: 'Settings', class: "tab no-highlight" do
= link_to edit_group_path(@group), title: 'Settings', class: 'tab no-highlight', data: {placement: 'right'} do
= icon ('cogs fw')
%span
Settings
......
%ul.nav.nav-sidebar
= nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
= link_to profile_path, title: "Profile" do
= link_to profile_path, title: 'Profile', data: {placement: 'right'} do
= icon('user fw')
%span
Profile
= nav_link(controller: :accounts) do
= link_to profile_account_path, title: 'Account' do
= link_to profile_account_path, title: 'Account', data: {placement: 'right'} do
= icon('gear fw')
%span
Account
= nav_link(path: ['profiles#applications', 'applications#edit', 'applications#show', 'applications#new']) do
= link_to applications_profile_path, title: 'Applications' do
= link_to applications_profile_path, title: 'Applications', data: {placement: 'right'} do
= icon('cloud fw')
%span
Applications
= nav_link(controller: :emails) do
= link_to profile_emails_path, title: 'Emails' do
= link_to profile_emails_path, title: 'Emails', data: {placement: 'right'} do
= icon('envelope-o fw')
%span
Emails
%span.count= current_user.emails.count + 1
- unless current_user.ldap_user?
= nav_link(controller: :passwords) do
= link_to edit_profile_password_path, title: 'Password' do
= link_to edit_profile_password_path, title: 'Password', data: {placement: 'right'} do
= icon('lock fw')
%span
Password
= nav_link(controller: :notifications) do
= link_to profile_notifications_path, title: 'Notifications' do
= link_to profile_notifications_path, title: 'Notifications', data: {placement: 'right'} do
= icon('inbox fw')
%span
Notifications
= nav_link(controller: :keys) do
= link_to profile_keys_path, title: 'SSH Keys' do
= link_to profile_keys_path, title: 'SSH Keys', data: {placement: 'right'} do
= icon('key fw')
%span
SSH Keys
%span.count= current_user.keys.count
= nav_link(path: 'profiles#design') do
= link_to design_profile_path, title: 'Design' do
= link_to design_profile_path, title: 'Design', data: {placement: 'right'} do
= icon('image fw')
%span
Design
= nav_link(path: 'profiles#history') do
= link_to history_profile_path, title: 'History' do
= link_to history_profile_path, title: 'History', data: {placement: 'right'} do
= icon('history fw')
%span
History
%ul.project-navigation.nav.nav-sidebar
- if @project_settings_nav
= nav_link do
= link_to project_path(@project), title: 'Back to project', class: "" do
= link_to project_path(@project), title: 'Back to project', data: {placement: 'right'} do
= icon('caret-square-o-left fw')
%span
Back to project
......@@ -11,49 +11,49 @@
= render 'projects/settings_nav'
- else
= nav_link(path: 'projects#show', html_options: {class: "home"}) do
= link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do
= nav_link(path: 'projects#show', html_options: {class: 'home'}) do
= link_to project_path(@project), title: 'Project', class: 'shortcuts-project', data: {placement: 'right'} do
= icon('dashboard fw')
%span
Project
- if project_nav_tab? :files
= nav_link(controller: %w(tree blob blame edit_tree new_tree)) do
= link_to namespace_project_tree_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Files', class: 'shortcuts-tree' do
= link_to namespace_project_tree_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Files', class: 'shortcuts-tree', data: {placement: 'right'} do
= icon('files-o fw')
%span
Files
- if project_nav_tab? :commits
= nav_link(controller: %w(commit commits compare repositories tags branches)) do
= link_to namespace_project_commits_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Commits', class: 'shortcuts-commits' do
= link_to namespace_project_commits_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Commits', class: 'shortcuts-commits', data: {placement: 'right'} do
= icon('history fw')
%span
Commits
- if project_nav_tab? :network
= nav_link(controller: %w(network)) do
= link_to namespace_project_network_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Network', class: 'shortcuts-network' do
= link_to namespace_project_network_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Network', class: 'shortcuts-network', data: {placement: 'right'} do
= icon('code-fork fw')
%span
Network
- if project_nav_tab? :graphs
= nav_link(controller: %w(graphs)) do
= link_to namespace_project_graph_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Graphs', class: 'shortcuts-graphs' do
= link_to namespace_project_graph_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Graphs', class: 'shortcuts-graphs', data: {placement: 'right'} do
= icon('area-chart fw')
%span
Graphs
- if project_nav_tab? :milestones
= nav_link(controller: :milestones) do
= link_to namespace_project_milestones_path(@project.namespace, @project), title: 'Milestones' do
= link_to namespace_project_milestones_path(@project.namespace, @project), title: 'Milestones', data: {placement: 'right'} do
= icon('clock-o fw')
%span
Milestones
- if project_nav_tab? :issues
= nav_link(controller: :issues) do
= link_to url_for_project_issues(@project, only_path: true), title: 'Issues', class: 'shortcuts-issues' do
= link_to url_for_project_issues(@project, only_path: true), title: 'Issues', class: 'shortcuts-issues', data: {placement: 'right'} do
= icon('exclamation-circle fw')
%span
Issues
......@@ -62,7 +62,7 @@
- if project_nav_tab? :merge_requests
= nav_link(controller: :merge_requests) do
= link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do
= link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests', class: 'shortcuts-merge_requests', data: {placement: 'right'} do
= icon('tasks fw')
%span
Merge Requests
......@@ -70,28 +70,28 @@
- if project_nav_tab? :labels
= nav_link(controller: :labels) do
= link_to namespace_project_labels_path(@project.namespace, @project), title: 'Labels' do
= link_to namespace_project_labels_path(@project.namespace, @project), title: 'Labels', data: {placement: 'right'} do
= icon('tags fw')
%span
Labels
- if project_nav_tab? :wiki
= nav_link(controller: :wikis) do
= link_to get_project_wiki_path(@project), title: 'Wiki', class: 'shortcuts-wiki' do
= link_to get_project_wiki_path(@project), title: 'Wiki', class: 'shortcuts-wiki', data: {placement: 'right'} do
= icon('book fw')
%span
Wiki
- if project_nav_tab? :snippets
= nav_link(controller: :snippets) do
= link_to namespace_project_snippets_path(@project.namespace, @project), title: 'Snippets', class: 'shortcuts-snippets' do
= link_to namespace_project_snippets_path(@project.namespace, @project), title: 'Snippets', class: 'shortcuts-snippets', data: {placement: 'right'} do
= icon('file-text-o fw')
%span
Snippets
- if project_nav_tab? :settings
= nav_link(html_options: {class: "#{project_tab_class} separate-item"}) do
= link_to edit_project_path(@project), title: 'Settings', class: "stat-tab tab no-highlight" do
= link_to edit_project_path(@project), title: 'Settings', class: 'stat-tab tab no-highlight', data: {placement: 'right'} do
= icon('cogs fw')
%span
Settings
%ul.nav.nav-sidebar
= nav_link(path: user_snippets_path(current_user), html_options: {class: 'home'}) do
= link_to user_snippets_path(current_user), title: 'Your snippets' do
= link_to user_snippets_path(current_user), title: 'Your snippets', data: {placement: 'right'} do
= icon('dashboard fw')
%span
Your Snippets
= nav_link(path: snippets_path) do
= link_to snippets_path, title: 'Discover snippets' do
= link_to snippets_path, title: 'Discover snippets', data: {placement: 'right'} do
= icon('globe fw')
%span
Discover Snippets
// Remove body class for any previous theme, re-add current one
$('body').removeClass('ui_basic ui_mars ui_modern ui_gray ui_color light_theme dark_theme')
$('body').removeClass('<%= Gitlab::Theme.body_classes %>')
$('body').addClass('<%= app_theme %> <%= theme_type %>')
// Re-render the header to reflect the new theme
$('header').html('<%= escape_javascript(render("layouts/head_panel", title: "Profile")) %>')
// Re-initialize header tooltips
$('.has_bottom_tooltip').tooltip({placement: 'bottom'})
%ul.project-settings-nav.sidebar-subnav
= nav_link(path: 'projects#edit') do
= link_to edit_project_path(@project), title: 'Project', class: "stat-tab tab " do
%i.fa.fa-pencil-square-o
= link_to edit_project_path(@project), title: 'Project', class: 'stat-tab tab', data: {placement: 'right'} do
= icon('pencil-square-o')
%span
Project
= nav_link(controller: [:project_members, :teams]) do
= link_to namespace_project_project_members_path(@project.namespace, @project), title: 'Members', class: "team-tab tab" do
%i.fa.fa-users
= link_to namespace_project_project_members_path(@project.namespace, @project), title: 'Members', class: 'team-tab tab', data: {placement: 'right'} do
= icon('users')
%span
Members
= nav_link(controller: :group_links) do
......@@ -15,13 +15,13 @@
%span
Groups
= nav_link(controller: :deploy_keys) do
= link_to namespace_project_deploy_keys_path(@project.namespace, @project), title: 'Deploy Keys' do
%i.fa.fa-key
= link_to namespace_project_deploy_keys_path(@project.namespace, @project), title: 'Deploy Keys', data: {placement: 'right'} do
= icon('key')
%span
Deploy Keys
= nav_link(controller: :hooks) do
= link_to namespace_project_hooks_path(@project.namespace, @project), title: 'Web Hooks' do
%i.fa.fa-link
= link_to namespace_project_hooks_path(@project.namespace, @project), title: 'Web Hooks', data: {placement: 'right'} do
= icon('link')
%span
Web Hooks
= nav_link(controller: :git_hooks) do
......@@ -30,13 +30,13 @@
%span
Git Hooks
= nav_link(controller: :services) do
= link_to namespace_project_services_path(@project.namespace, @project), title: 'Services' do
%i.fa.fa-cogs
= link_to namespace_project_services_path(@project.namespace, @project), title: 'Services', data: {placement: 'right'} do
= icon('cogs')
%span
Services
= nav_link(controller: :protected_branches) do
= link_to namespace_project_protected_branches_path(@project.namespace, @project), title: 'Protected Branches' do
%i.fa.fa-lock
= link_to namespace_project_protected_branches_path(@project.namespace, @project), title: 'Protected Branches', data: {placement: 'right'} do
= icon('lock')
%span
Protected branches
= nav_link(controller: :audit_events) do
......
.form-group.project-visibility-level-holder
= f.label :visibility_level, class: 'control-label' do
Visibility Level
= link_to "(?)", help_page_path("public_access", "public_access")
.col-sm-10
- if can_change_visibility_level
- Gitlab::VisibilityLevel.values.each do |level|
.radio
- restricted = restricted_visibility_levels.include?(level)
= label :project_visibility_level, level do
= f.radio_button :visibility_level, level, checked: (visibility_level == level), disabled: restricted
= visibility_level_icon(level)
.option-title
= visibility_level_label(level)
.option-descr
= visibility_level_description(level)
- unless restricted_visibility_levels.empty?
.col-sm-10
%span.info
Some visibility level settings have been restricted by the administrator.
- else
.col-sm-10
%span.info
= visibility_level_icon(visibility_level)
%strong
= visibility_level_label(visibility_level)
.light= visibility_level_description(visibility_level)
......@@ -12,7 +12,7 @@
.file-content.blame.highlight
%table
- @blame.each do |commit, lines, since|
- commit = Commit.new(commit)
- commit = Commit.new(commit, @project)
%tr
%td.blame-commit
%span.commit
......
......@@ -12,7 +12,7 @@
- if @note_counts
- note_count = @note_counts.fetch(commit.id, 0)
- else
- notes = commit.notes(project)
- notes = commit.notes
- note_count = notes.user.count
- if note_count > 0
......
......@@ -3,9 +3,9 @@
Commits (#{@commits.count})
- if @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE
%ul.well-list
- Commit.decorate(@commits.first(MergeRequestDiff::COMMITS_SAFE_SIZE)).each do |commit|
- Commit.decorate(@commits.first(MergeRequestDiff::COMMITS_SAFE_SIZE), @project).each do |commit|
= render "projects/commits/inline_commit", commit: commit, project: @project
%li.warning-row.unstyled
other #{@commits.size - MergeRequestDiff::COMMITS_SAFE_SIZE} commits hidden to prevent performance issues.
- else
%ul.well-list= render Commit.decorate(@commits), project: @project
%ul.well-list= render Commit.decorate(@commits, @project), project: @project
xml.instruct!
xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
xml.title "Recent commits to #{@project.name}:#{@ref}"
xml.link :href => namespace_project_commits_url(@project.namespace, @project, @ref, format: :atom), :rel => "self", :type => "application/atom+xml"
xml.link :href => namespace_project_commits_url(@project.namespace, @project, @ref), :rel => "alternate", :type => "text/html"
xml.title "#{@project.name}:#{@ref} commits"
xml.link href: namespace_project_commits_url(@project.namespace, @project, @ref, format: :atom, private_token: current_user.private_token), rel: "self", type: "application/atom+xml"
xml.link href: namespace_project_commits_url(@project.namespace, @project, @ref), rel: "alternate", type: "text/html"
xml.id namespace_project_commits_url(@project.namespace, @project, @ref)
xml.updated @commits.first.committed_date.strftime("%Y-%m-%dT%H:%M:%SZ") if @commits.any?
@commits.each do |commit|
xml.entry do
xml.id namespace_project_commit_url(@project.namespace, @project, :id => commit.id)
xml.link :href => namespace_project_commit_url(@project.namespace, @project, :id => commit.id)
xml.title truncate(commit.title, :length => 80)
xml.id namespace_project_commit_url(@project.namespace, @project, id: commit.id)
xml.link href: namespace_project_commit_url(@project.namespace, @project, id: commit.id)
xml.title truncate(commit.title, length: 80)
xml.updated commit.committed_date.strftime("%Y-%m-%dT%H:%M:%SZ")
xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(commit.author_email)
xml.media :thumbnail, width: "40", height: "40", url: avatar_icon(commit.author_email)
xml.author do |author|
xml.name commit.author_name
xml.email commit.author_email
......
= content_for :meta_tags do
- if current_user
= auto_discovery_link_tag(:atom, namespace_project_commits_url(@project.namespace, @project, @ref, format: :atom, private_token: current_user.private_token), title: "#{@project.name}:#{@ref} commits")
= render "head"
.tree-ref-holder
......
......@@ -29,7 +29,7 @@
.col-sm-10= f.select(:default_branch, @repository.branch_names, {}, {class: 'select2 select-wide'})
= render "visibility_level", f: f, visibility_level: @project.visibility_level, can_change_visibility_level: can?(current_user, :change_visibility_level, @project)
= render 'shared/visibility_level', f: f, visibility_level: @project.visibility_level, can_change_visibility_level: can?(current_user, :change_visibility_level, @project), form_model: @project
.form-group
= f.label :tag_list, "Tags", class: 'control-label'
......
......@@ -6,24 +6,34 @@
.issue-title
%span.str-truncated
= link_to_gfm issue.title, issue_path(issue), class: "row_title"
.issue-labels
- issue.labels.each do |label|
= link_to namespace_project_issues_path(issue.project.namespace, issue.project, label_name: label.name) do
= render_colored_label(label)
.pull-right.light
- if issue.closed?
%span
CLOSED
- if issue.assignee
= link_to_member(@project, issue.assignee, name: false)
- note_count = issue.notes.user.count
- if note_count > 0
&nbsp;
%span
%i.fa.fa-comments
= note_count
- else
&nbsp;
%span.issue-no-comments
%i.fa.fa-comments
= 0
.issue-info
= link_to "##{issue.iid}", issue_path(issue), class: "light"
- if issue.assignee
assigned to #{link_to_member(@project, issue.assignee)}
= "##{issue.iid} opened #{time_ago_with_tooltip(issue.created_at, 'bottom')} by #{link_to_member(@project, issue.author, avatar: false)}".html_safe
- if issue.votes_count > 0
= render 'votes/votes_inline', votable: issue
- if issue.milestone
&nbsp;
%span
%i.fa.fa-clock-o
= issue.milestone.title
......@@ -33,20 +43,3 @@
.pull-right.issue-updated-at
%small updated #{time_ago_with_tooltip(issue.updated_at, 'bottom', 'issue_update_ago')}
.issue-labels
- issue.labels.each do |label|
= link_to namespace_project_issues_path(issue.project.namespace, issue.project, label_name: label.name) do
= render_colored_label(label)
.issue-actions
- if can? current_user, :modify_issue, issue
- if issue.closed?
= link_to 'Reopen', issue_path(issue, issue: {state_event: :reopen }, status_only: true), method: :put, class: "btn btn-sm btn-grouped reopen_issue btn-reopen", remote: true
- else
= link_to 'Close', issue_path(issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn btn-sm btn-grouped close_issue btn-close", remote: true
= link_to edit_namespace_project_issue_path(issue.project.namespace, issue.project, issue), class: "btn btn-sm edit-issue-link btn-grouped" do
%i.fa.fa-pencil-square-o
Edit
xml.instruct!
xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
xml.title "#{@project.name} issues"
xml.link :href => namespace_project_issues_url(@project.namespace, @project, :atom), :rel => "self", :type => "application/atom+xml"
xml.link :href => namespace_project_issues_url(@project.namespace, @project), :rel => "alternate", :type => "text/html"
xml.link href: namespace_project_issues_url(@project.namespace, @project, format: :atom, private_token: current_user.private_token), rel: "self", type: "application/atom+xml"
xml.link href: namespace_project_issues_url(@project.namespace, @project), rel: "alternate", type: "text/html"
xml.id namespace_project_issues_url(@project.namespace, @project)
xml.updated @issues.first.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") if @issues.any?
......
= content_for :meta_tags do
- if current_user
= auto_discovery_link_tag(:atom, namespace_project_issues_url(@project.namespace, @project, :atom, private_token: current_user.private_token), title: "#{@project.name} issues")
.append-bottom-10
.pull-right
.pull-left
......
......@@ -2,6 +2,10 @@
.merge-request-title
%span.str-truncated
= link_to_gfm merge_request.title, merge_request_path(merge_request), class: "row_title"
.merge-request-labels
- merge_request.labels.each do |label|
= link_to namespace_project_merge_requests_path(merge_request.project.namespace, merge_request.project, label_name: label.name) do
= render_colored_label(label)
.pull-right.light
- if merge_request.merged?
%span
......@@ -17,20 +21,26 @@
%i.fa.fa-code-fork
%span= merge_request.target_branch
- note_count = merge_request.mr_and_commit_notes.user.count
- if merge_request.assignee
&nbsp;
= link_to_member(merge_request.source_project, merge_request.assignee, name: false)
- if note_count > 0
&nbsp;
%span
%i.fa.fa-comments
= note_count
- else
&nbsp;
%span.merge-request-no-comments
%i.fa.fa-comments
= 0
.merge-request-info
= link_to "##{merge_request.iid}", merge_request_path(merge_request), class: "light"
- if merge_request.assignee
assigned to #{link_to_member(merge_request.source_project, merge_request.assignee)}
- else
Unassigned
= "##{merge_request.iid} opened #{time_ago_with_tooltip(merge_request.created_at, 'bottom')} by #{link_to_member(@project, merge_request.author, avatar: false)}".html_safe
- if merge_request.votes_count > 0
= render 'votes/votes_inline', votable: merge_request
- if merge_request.milestone_id?
&nbsp;
%span
%i.fa.fa-clock-o
= merge_request.milestone.title
......@@ -38,11 +48,5 @@
%span.task-status
= merge_request.task_status
.pull-right.hidden-xs
%small updated #{time_ago_with_tooltip(merge_request.updated_at, 'bottom', 'merge_request_updated_ago')}
.merge-request-labels
- merge_request.labels.each do |label|
= link_to namespace_project_merge_requests_path(merge_request.project.namespace, merge_request.project, label_name: label.name) do
= render_colored_label(label)
......@@ -93,7 +93,7 @@
%span.light (optional)
.col-sm-10
= f.text_area :description, placeholder: "Awesome project", class: "form-control", rows: 3, maxlength: 250, tabindex: 3
= render "visibility_level", f: f, visibility_level: gitlab_config.default_projects_features.visibility_level, can_change_visibility_level: true
= render 'shared/visibility_level', f: f, visibility_level: default_project_visibility, can_change_visibility_level: true, form_model: @project
.form-actions
= f.submit 'Create project', class: "btn btn-create project-submit", tabindex: 4
......
xml.instruct!
xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
xml.title "#{@project.name} activity"
xml.link href: namespace_project_url(@project.namespace, @project, format: :atom, private_token: current_user.private_token), rel: "self", type: "application/atom+xml"
xml.link href: namespace_project_url(@project.namespace, @project), rel: "alternate", type: "text/html"
xml.id namespace_project_url(@project.namespace, @project)
xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any?
@events.each do |event|
event_to_atom(xml, event)
end
end
= content_for :meta_tags do
- if current_user
= auto_discovery_link_tag(:atom, namespace_project_path(@project.namespace, @project, format: :atom, private_token: current_user.private_token), title: "#{@project.name} activity")
- if current_user && can?(current_user, :download_code, @project)
= render 'shared/no_ssh'
= render 'shared/no_password'
= render "home_panel"
%ul.nav.nav-tabs
%li.active
= link_to '#tab-activity', 'data-toggle' => 'tab' do
......@@ -13,11 +16,11 @@
= link_to '#tab-readme', 'data-toggle' => 'tab' do
Readme
- if @repository.changelog
%li
%li.hidden-xs
= link_to changelog_url(@project) do
Changelog
- if @repository.contribution_guide
%li
%li.hidden-xs
= link_to contribution_guide_url(@project) do
Contribution guide
- if @repository.license
......@@ -38,8 +41,18 @@
= link_to '#aside', class: 'show-aside' do
%i.fa.fa-angle-left
%section.col-md-9
= render "events/event_last_push", event: @last_push
= render 'shared/event_filter'
.hidden-xs
= render "events/event_last_push", event: @last_push
- if current_user
%ul.nav.nav-pills.event_filter.pull-right
%li
= link_to namespace_project_path(@project.namespace, @project, format: :atom, private_token: current_user.private_token), title: "Feed", class: 'rss-btn' do
%i.fa.fa-rss
Activity Feed
= render 'shared/event_filter'
%hr
.content_list
= spinner
%aside.col-md-3.project-side
......
%h3.page-title
Edit snippet
%hr
= render "shared/snippets/form", url: namespace_project_snippet_path(@project.namespace, @project, @snippet)
= render "shared/snippets/form", url: namespace_project_snippet_path(@project.namespace, @project, @snippet), visibility_level: @snippet.visibility_level
%h3.page-title
New snippet
%hr
= render "shared/snippets/form", url: namespace_project_snippets_path(@project.namespace, @project, @snippet)
= render "shared/snippets/form", url: namespace_project_snippets_path(@project.namespace, @project, @snippet), visibility_level: default_snippet_visibility
= content_for :meta_tags do
- if current_user
= auto_discovery_link_tag(:atom, namespace_project_commits_url(@project.namespace, @project, @ref, format: :atom, private_token: current_user.private_token), title: "#{@project.name}:#{@ref} commits")
.tree-ref-holder
= render 'shared/ref_switcher', destination: 'tree', path: @path
......
......@@ -3,17 +3,3 @@
= event_filter_link EventFilter.merged, 'Merge events'
= event_filter_link EventFilter.comments, 'Comments'
= event_filter_link EventFilter.team, 'Team'
- if current_user
- if current_controller?(:dashboard)
%li.pull-right
= link_to dashboard_path(:atom, { private_token: current_user.private_token }), class: 'rss-btn' do
%i.fa.fa-rss
News Feed
- if current_controller?(:groups)
%li.pull-right
= link_to group_path(@group, { format: :atom, private_token: current_user.private_token }), title: "Feed", class: 'rss-btn' do
%i.fa.fa-rss
News Feed
%hr
......@@ -4,15 +4,15 @@
%li{class: ("active" if params[:state] == 'opened')}
= link_to page_filter_path(state: 'opened') do
%i.fa.fa-exclamation-circle
Open
#{state_filters_text_for(:opened, @project)}
%li{class: ("active" if params[:state] == 'closed')}
= link_to page_filter_path(state: 'closed') do
%i.fa.fa-check-circle
Closed
#{state_filters_text_for(:closed, @project)}
%li{class: ("active" if params[:state] == 'all')}
= link_to page_filter_path(state: 'all') do
%i.fa.fa-compass
All
#{state_filters_text_for(:all, @project)}
.issues-details-filters
= form_tag page_filter_path(without: [:assignee_id, :author_id, :milestone_id, :label_name]), method: :get, class: 'filter-form' do
......
......@@ -4,24 +4,11 @@
= link_to "(?)", help_page_path("public_access", "public_access")
.col-sm-10
- if can_change_visibility_level
- Gitlab::VisibilityLevel.values.each do |level|
.radio
- restricted = restricted_visibility_levels.include?(level)
= f.radio_button :visibility_level, level, disabled: restricted
= label "#{dom_class(@snippet)}_visibility_level", level do
= visibility_level_icon(level)
.option-title
= visibility_level_label(level)
.option-descr
= snippet_visibility_level_description(level)
- unless restricted_visibility_levels.empty?
.col-sm-10
%span.info
Some visibility level settings have been restricted by the administrator.
= render('shared/visibility_radios', model_method: :visibility_level, form: f, selected_level: visibility_level, form_model: form_model)
- else
.col-sm-10
%span.info
= visibility_level_icon(visibility_level)
%strong
= visibility_level_label(visibility_level)
.light= visibility_level_description(visibility_level)
.light= visibility_level_description(visibility_level, form_model)
- Gitlab::VisibilityLevel.values.each do |level|
.radio
- restricted = restricted_visibility_levels.include?(level)
= label model_method, level do
= form.radio_button model_method, level, checked: (selected_level == level), disabled: restricted
= visibility_level_icon(level)
.option-title
= visibility_level_label(level)
.option-descr
= visibility_level_description(level, form_model)
- unless restricted_visibility_levels.empty?
.col-sm-10
%span.info
Some visibility level settings have been restricted by the administrator.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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