Commit 9e9ce95d authored by Vinnie Okada's avatar Vinnie Okada

Merge branch 'master' into rails-4.1.9

Conflicts:
	app/views/dashboard/_project.html.haml
	app/views/events/event/_common.html.haml
	app/views/explore/projects/_project.html.haml
	app/views/groups/_projects.html.haml
	app/views/projects/_home_panel.html.haml
	app/views/projects/_issues_nav.html.haml
	app/views/projects/issues/_discussion.html.haml
	app/views/projects/issues/_issues.html.haml
	app/views/projects/issues/show.html.haml
	app/views/projects/merge_requests/_discussion.html.haml
	app/views/projects/merge_requests/_show.html.haml
	app/views/projects/milestones/index.html.haml
	app/views/projects/notes/_edit_form.html.haml
	app/views/shared/_issuable_filter.html.haml
parents 76aad9b7 de040ffa
v 7.9.0 (unreleased)
- Fix broken access control for note attachments (Hannes Rosenögger)
v 7.8.0 (unreleased)
- Replace highlight.js with rouge-fork rugments (Stefan Tatschner)
- Make project search case insensitive (Hannes Rosenögger)
......@@ -10,7 +13,7 @@ v 7.8.0 (unreleased)
- View note image attachments in new tab when clicked instead of downloading them
- Improve sorting logic in UI and API. Explicitly define what sorting method is used by default
- Allow more variations for commit messages closing issues (Julien Bianchi and Hannes Rosenögger)
- Fix overflow at sidebar when have several itens
- Fix overflow at sidebar when have several items
- Add notes for label changes in issue and merge requests
- Show tags in commit view (Hannes Rosenögger)
- Only count a user's vote once on a merge request or issue (Michael Clarke)
......@@ -54,6 +57,13 @@ v 7.8.0 (unreleased)
- Link head panel titles to relevant root page.
- Allow users that signed up via OAuth to set their password in order to use Git over HTTP(S).
- Upgrade Rails gem to version 4.1.9.
- Show users button to share their newly created public or internal projects on twitter
- Add quick help links to the GitLab pricing and feature comparison pages.
- Fix duplicate authorized applications in user profile and incorrect application client count in admin area.
- Make sure Markdown previews always use the same styling as the eventual destination.
- Remove deprecated Group#owner_id from API
- Show projects user contributed to on user page. Show stars near project on user page.
- Improve database performance for GitLab
v 7.7.2
- Update GitLab Shell to version 2.4.2 that fixes a bug when developers can push to protected branch
......
......@@ -47,6 +47,9 @@ GEM
astrolabe (1.3.0)
parser (>= 2.2.0.pre.3, < 3.0)
attr_required (1.0.0)
autoprefixer-rails (5.1.6)
execjs
json
awesome_print (1.2.0)
axiom-types (0.0.5)
descendants_tracker (~> 0.0.1)
......@@ -57,8 +60,9 @@ GEM
erubis (>= 2.6.6)
binding_of_caller (0.7.2)
debug_inspector (>= 0.0.1)
bootstrap-sass (3.0.3.0)
sass (~> 3.2)
bootstrap-sass (3.3.3)
autoprefixer-rails (>= 5.0.0.1)
sass (>= 3.2.19)
browser (0.7.2)
builder (3.2.2)
cal-heatmap-rails (0.0.1)
......
......@@ -71,7 +71,7 @@ Thanks for the issue report. Please reformat your issue to conform to the issue
### Feature requests
Thank you for your interest in improving GitLab. We don't use the issue tracker for feature requests. Things that are wrong but are not a regression compared to older versions of GitLab are considered feature requests and not issues. Please use the [feature request forum](http://feedback.gitlab.com/) for this purpose or create a merge request implementing this feature. Have a look at the \[contribution guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md) for more information.
Thank you for your interest in improving GitLab. We don't use the issue tracker for feature requests. Things that are wrong but are not a regression compared to older versions of GitLab are considered feature requests and not issues. Please use the \[feature request forum\]\(http://feedback.gitlab.com/) for this purpose or create a merge request implementing this feature. Have a look at the \[contribution guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md) for more information.
### Issue report for old version
......
......@@ -60,7 +60,7 @@ Please see the [requirements documentation](doc/install/requirements.md) for sys
## Installation
The recommended way to install GitLab is using the provided [Omnibus packages](https://about.gitlab.com/downloads/). Compared to a manual installation, this is faster and less error prone. Just select your operating system, download the respective package (Debian or RPM) and install it using the system's package manager.
The recommended way to install GitLab is using the provided [Omnibus packages](https://about.gitlab.com/downloads/). Compared to an installation from source, this is faster and less error prone. Just select your operating system, download the respective package (Debian or RPM) and install it using the system's package manager.
There are various other options to install GitLab, please refer to the [installation page on the GitLab website](https://about.gitlab.com/installation/) for more information.
......@@ -76,7 +76,7 @@ Since 2011 a minor or major version of GitLab is released on the 22nd of every m
## Upgrading
For updating the Omnibus installation please see the [update documentation](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/update.md). For manual installations there is an [upgrader script](doc/update/upgrader.md) and there are [upgrade guides](doc/update) detailing all necessary commands to migrate to the next version.
For updating the Omnibus installation please see the [update documentation](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/update.md). For installations from source there is an [upgrader script](doc/update/upgrader.md) and there are [upgrade guides](doc/update) detailing all necessary commands to migrate to the next version.
## Install a development environment
......
7.8.0.pre
7.9.0.pre
......@@ -15,3 +15,9 @@ class @Issue
"issue"
updateTaskState
)
$('.issuable-affix').affix offset:
top: ->
@top = $('.issue-details').outerHeight(true) + 25
bottom: ->
@bottom = $('.footer').outerHeight(true)
......@@ -15,7 +15,7 @@
$(this).html totalIssues + 1
else
$(this).html totalIssues - 1
$("body").on "click", ".issues-filters .dropdown-menu a", ->
$("body").on "click", ".issues-other-filters .dropdown-menu a", ->
$('.issues-list').block(
message: null,
overlayCSS:
......@@ -77,9 +77,9 @@
ids.push $(value).attr("data-id")
$("#update_issues_ids").val ids
$(".issues-filters").hide()
$(".issues-other-filters").hide()
$(".issues_bulk_update").show()
else
$("#update_issues_ids").val []
$(".issues_bulk_update").hide()
$(".issues-filters").show()
$(".issues-other-filters").show()
......@@ -20,6 +20,12 @@ class @MergeRequest
if $("a.btn-close").length
$("li.task-list-item input:checkbox").prop("disabled", false)
$('.issuable-affix').affix offset:
top: ->
@top = $('.merge-request-details').outerHeight(true) + 70
bottom: ->
@bottom = $('.footer').outerHeight(true)
# Local jQuery finder
$: (selector) ->
this.$el.find(selector)
......
......@@ -77,7 +77,7 @@ class @Notes
$(document).off "click", ".js-discussion-reply-button"
$(document).off "click", ".js-add-diff-note-button"
$(document).off "visibilitychange"
$(document).off "keypress", @notes_forms
$(document).off "keydown", @notes_forms
$(document).off "keyup", ".js-note-text"
$(document).off "click", ".js-note-target-reopen"
$(document).off "click", ".js-note-target-close"
......@@ -272,7 +272,7 @@ class @Notes
note_li = $(".note-row-" + note.id)
note_li.replaceWith(note.html)
note_li.find('.note-edit-form').hide()
note_li.find('.note-text').show()
note_li.find('.note-body > .note-text').show()
###
Called in response to clicking the edit note link
......@@ -284,7 +284,7 @@ class @Notes
showEditForm: (e) ->
e.preventDefault()
note = $(this).closest(".note")
note.find(".note-text").hide()
note.find(".note-body > .note-text").hide()
note.find(".note-header").hide()
base_form = note.find(".note-edit-form")
form = base_form.clone().insertAfter(base_form)
......@@ -311,7 +311,7 @@ class @Notes
cancelEdit: (e) ->
e.preventDefault()
note = $(this).closest(".note")
note.find(".note-text").show()
note.find(".note-body > .note-text").show()
note.find(".note-header").show()
note.find(".current-note-edit-form").remove()
......@@ -345,7 +345,7 @@ class @Notes
removeAttachment: ->
note = $(this).closest(".note")
note.find(".note-attachment").remove()
note.find(".note-text").show()
note.find(".note-body > .note-text").show()
note.find(".js-note-attachment-delete").hide()
note.find(".note-edit-form").hide()
......
......@@ -17,6 +17,10 @@ h3.page-title {
font-size: 22px;
}
h4.page-title {
margin-top: 0px;
}
h6 {
color: #888;
text-transform: uppercase;
......@@ -131,4 +135,4 @@ textarea.js-gfm-input {
.strikethrough {
text-decoration: line-through;
}
\ No newline at end of file
}
......@@ -12,6 +12,7 @@
border-left: 1px solid #666;
}
// highlight line via anchor
pre.hll {
background-color: #fff !important;
}
......
......@@ -12,6 +12,11 @@
border-left: 1px solid #555;
}
// highlight line via anchor
pre.hll {
background-color: #49483e !important;
}
.hll { background-color: #49483e }
.c { color: #75715e } /* Comment */
.err { color: #960050; background-color: #1e0010 } /* Error */
......
......@@ -12,6 +12,11 @@
border-left: 1px solid #113b46;
}
// highlight line via anchor
pre.hll {
background-color: #073642 !important;
}
/* Solarized Dark
For use with Jekyll and Pygments
......
......@@ -12,6 +12,11 @@
border-left: 1px solid #c5d0d4;
}
// highlight line via anchor
pre.hll {
background-color: #eee8d5 !important;
}
/* Solarized Light
For use with Jekyll and Pygments
......
......@@ -12,6 +12,11 @@
border-left: 1px solid #bbb;
}
// highlight line via anchor
pre.hll {
background-color: #f8eec7 !important;
}
.hll { background-color: #f8f8f8 }
.c { color: #999988; font-style: italic; }
.err { color: #a61717; background-color: #e3d2d2; }
......
......@@ -120,6 +120,15 @@
}
}
.dash-new-group {
background: $bg_success;
border: 1px solid $border_success;
a {
color: #FFF;
}
}
.dash-list .str-truncated {
max-width: 72%;
}
......@@ -64,6 +64,10 @@
.md {
font-size: 13px;
iframe.twitter-share-button {
vertical-align: bottom;
}
}
pre {
......
@media (max-width: $screen-sm-max) {
.issuable-affix {
margin-top: 20px;
}
}
@media (max-width: $screen-md-max) {
.issuable-affix {
position: static;
}
}
@media (min-width: $screen-md-max) {
.issuable-affix {
&.affix-top {
position: static;
}
&.affix {
position: fixed;
top: 70px;
width: 220px;
}
}
}
......@@ -94,8 +94,15 @@
}
}
.issue-show-labels .color-label {
padding: 6px 10px;
.issue-show-labels {
a {
margin-right: 5px;
margin-bottom: 5px;
display: inline-block;
.color-label {
padding: 6px 10px;
}
}
}
form.edit-issue {
......
......@@ -24,6 +24,7 @@
.accept-control {
display: inline-block;
margin: 0;
margin-left: 20px;
padding: 10px 0;
line-height: 20px;
......@@ -31,6 +32,7 @@
.remove_source_checkbox {
margin: 0;
font-weight: bold;
}
}
}
......@@ -185,6 +187,13 @@
}
}
.merge-request-show-labels .label {
padding: 6px 10px;
.merge-request-show-labels {
a {
margin-right: 5px;
margin-bottom: 5px;
display: inline-block;
.color-label {
padding: 6px 10px;
}
}
}
......@@ -40,12 +40,16 @@
.nav-sidebar li {
&.active a {
color: #111;
background: #EEE;
color: #333;
background: #FFF;
font-weight: bold;
border: 1px solid #EEE;
border-right: 1px solid transparent;
border-left: 3px solid $style_color;
&.no-highlight {
background: none;
border: none;
}
i {
......@@ -65,7 +69,7 @@
color: #555;
display: block;
text-decoration: none;
padding: 6px 15px;
padding: 8px 15px;
font-size: 13px;
line-height: 20px;
text-shadow: 0 1px 2px #FFF;
......@@ -133,7 +137,7 @@
li a {
padding-left: 18px;
font-size: 14px;
padding: 10px 15px;
padding: 8px 15px;
text-align: center;
& > span {
......
......@@ -26,6 +26,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:signup_enabled,
:signin_enabled,
:gravatar_enabled,
:twitter_sharing_enabled,
:sign_in_text,
:home_page_url
)
......
......@@ -12,11 +12,7 @@ class DashboardController < ApplicationController
@groups = current_user.authorized_groups.order_name_asc
@has_authorized_projects = @projects.count > 0
@projects_count = @projects.count
@projects = @projects.limit(@projects_limit)
@events = Event.in_projects(current_user.authorized_projects.pluck(:id))
@events = @event_filter.apply_filter(@events)
@events = @events.limit(20).offset(params[:offset] || 0)
@projects = @projects.includes(:namespace).limit(@projects_limit)
@last_push = current_user.recent_push
......@@ -24,8 +20,16 @@ class DashboardController < ApplicationController
respond_to do |format|
format.html
format.json { pager_json("events/_events", @events.count) }
format.atom { render layout: false }
format.json do
load_events
pager_json("events/_events", @events.count)
end
format.atom do
load_events
render layout: false
end
end
end
......@@ -74,4 +78,10 @@ class DashboardController < ApplicationController
def load_projects
@projects = current_user.authorized_projects.sorted_by_activity.non_archived
end
def load_events
@events = Event.in_projects(current_user.authorized_projects.pluck(:id))
@events = @event_filter.apply_filter(@events).with_associations
@events = @events.limit(20).offset(params[:offset] || 0)
end
end
......@@ -18,7 +18,7 @@ class Explore::ProjectsController < ApplicationController
def starred
@starred_projects = ProjectsFinder.new.execute(current_user)
@starred_projects = @starred_projects.order('star_count DESC')
@starred_projects = @starred_projects.reorder('star_count DESC')
@starred_projects = @starred_projects.page(params[:page]).per(10)
end
end
......@@ -10,11 +10,11 @@ class GroupsController < ApplicationController
# Load group projects
before_filter :load_projects, except: [:new, :create, :projects, :edit, :update]
before_filter :event_filter, only: :show
before_filter :set_title, only: [:new, :create]
layout :determine_layout
before_filter :set_title, only: [:new, :create]
def new
@group = Group.new
end
......@@ -32,15 +32,21 @@ class GroupsController < ApplicationController
end
def show
@events = Event.in_projects(project_ids)
@events = event_filter.apply_filter(@events)
@events = @events.limit(20).offset(params[:offset] || 0)
@last_push = current_user.recent_push if current_user
@projects = @projects.includes(:namespace)
respond_to do |format|
format.html
format.json { pager_json("events/_events", @events.count) }
format.atom { render layout: false }
format.json do
load_events
pager_json("events/_events", @events.count)
end
format.atom do
load_events
render layout: false
end
end
end
......@@ -149,4 +155,10 @@ class GroupsController < ApplicationController
def group_params
params.require(:group).permit(:name, :description, :path, :avatar)
end
def load_events
@events = Event.in_projects(project_ids)
@events = event_filter.apply_filter(@events).with_associations
@events = @events.limit(20).offset(params[:offset] || 0)
end
end
......@@ -16,6 +16,7 @@ class ProfilesController < ApplicationController
def applications
@applications = current_user.oauth_applications
@authorized_tokens = current_user.oauth_authorized_tokens
@authorized_apps = @authorized_tokens.map(&:application).uniq
end
def update
......
......@@ -27,7 +27,7 @@ class Projects::TagsController < Projects::ApplicationController
tag = @repository.find_tag(params[:id])
if tag && @repository.rm_tag(tag.name)
Event.create_ref_event(@project, current_user, tag, 'rm', 'refs/tags')
EventCreateService.new.push_ref(@project, current_user, tag, 'rm', 'refs/tags')
end
respond_to do |format|
......
......@@ -5,9 +5,10 @@ class ProjectsController < ApplicationController
# Authorize
before_filter :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer, :archive, :unarchive]
before_filter :set_title, only: [:new, :create]
before_filter :event_filter, only: :show
layout 'navless', only: [:new, :create, :fork]
before_filter :set_title, only: [:new, :create]
def new
@project = Project.new
......@@ -65,9 +66,6 @@ class ProjectsController < ApplicationController
end
limit = (params[:limit] || 20).to_i
@events = @project.events.recent
@events = event_filter.apply_filter(@events)
@events = @events.limit(limit).offset(params[:offset] || 0)
@show_star = !(current_user && current_user.starred?(@project))
......@@ -85,7 +83,12 @@ class ProjectsController < ApplicationController
end
end
format.json { pager_json('events/_events', @events.count) }
format.json do
@events = @project.events.recent
@events = event_filter.apply_filter(@events).with_associations
@events = @events.limit(limit).offset(params[:offset] || 0)
pager_json('events/_events', @events.count)
end
end
end
......
......@@ -4,19 +4,20 @@ class UsersController < ApplicationController
layout :determine_layout
def show
# Projects user can view
visible_projects = ProjectsFinder.new.execute(current_user)
authorized_projects_ids = visible_projects.pluck(:id)
@contributed_projects = Project.
where(id: authorized_projects_ids & @user.contributed_projects_ids).
in_group_namespace.includes(:namespace)
@projects = @user.personal_projects.
where(id: authorized_projects_ids)
where(id: authorized_projects_ids).includes(:namespace)
# Collect only groups common for both users
@groups = @user.groups & GroupsFinder.new.execute(current_user)
# Get user activity feed for projects common for both users
@events = @user.recent_events.
where(project_id: authorized_projects_ids).limit(30)
where(project_id: authorized_projects_ids).
with_associations.limit(30)
@title = @user.name
@title_url = user_path(@user)
......@@ -28,8 +29,8 @@ class UsersController < ApplicationController
end
def calendar
visible_projects = ProjectsFinder.new.execute(current_user)
calendar = Gitlab::CommitsCalendar.new(visible_projects, @user)
projects = Project.where(id: authorized_projects_ids & @user.contributed_projects_ids)
calendar = Gitlab::CommitsCalendar.new(projects, @user)
@timestamps = calendar.timestamps
@starting_year = calendar.starting_year
@starting_month = calendar.starting_month
......@@ -54,4 +55,10 @@ class UsersController < ApplicationController
return authenticate_user!
end
end
def authorized_projects_ids
# Projects user can view
@authorized_projects_ids ||=
ProjectsFinder.new.execute(current_user).pluck(:id)
end
end
......@@ -8,7 +8,7 @@ class TrendingProjectsFinder
# for period of time - ex. month
projects.joins(:notes).where('notes.created_at > ?', start_date).
select("projects.*, count(notes.id) as ncount").
group("projects.id").order("ncount DESC")
group("projects.id").reorder("ncount DESC")
end
private
......
......@@ -51,7 +51,13 @@ module ApplicationHelper
end
def project_icon(project_id, options = {})
project = Project.find_with_namespace(project_id)
project =
if project_id.is_a?(Project)
project = project_id
else
Project.find_with_namespace(project_id)
end
if project.avatar.present?
image_tag project.avatar.url, options
elsif project.avatar_in_git
......
......@@ -3,6 +3,10 @@ module ApplicationSettingsHelper
current_application_settings.gravatar_enabled?
end
def twitter_sharing_enabled?
current_application_settings.twitter_sharing_enabled?
end
def signup_enabled?
current_application_settings.signup_enabled?
end
......
......@@ -17,7 +17,7 @@ module BlobHelper
end
def no_highlight_files
%w(credits changelog copying copyright license authors)
%w(credits changelog news copying copyright license authors)
end
def edit_blob_link(project, ref, path, options = {})
......
......@@ -10,11 +10,15 @@ module EventsHelper
end
def event_action_name(event)
target = if event.target_type
event.target_type.titleize.downcase
else
'project'
end
target = if event.target_type
if event.note?
event.note_target_type
else
event.target_type.titleize.downcase
end
else
'project'
end
[event.action_name, target].join(" ")
end
......@@ -42,21 +46,30 @@ module EventsHelper
end
def event_feed_title(event)
if event.issue?
"#{event.author_name} #{event.action_name} issue ##{event.target_iid}: #{event.issue_title} at #{event.project_name}"
elsif event.merge_request?
"#{event.author_name} #{event.action_name} MR ##{event.target_iid}: #{event.merge_request_title} at #{event.project_name}"
elsif event.push?
"#{event.author_name} #{event.push_action_name} #{event.ref_type} #{event.ref_name} at #{event.project_name}"
elsif event.membership_changed?
"#{event.author_name} #{event.action_name} #{event.project_name}"
elsif event.note? && event.note_commit?
"#{event.author_name} commented on #{event.note_target_type} #{event.note_short_commit_id} at #{event.project_name}"
elsif event.note?
"#{event.author_name} commented on #{event.note_target_type} ##{truncate event.note_target_iid} at #{event.project_name}"
else
""
words = []
words << event.author_name
words << event_action_name(event)
if event.push?
words << event.ref_type
words << event.ref_name
words << "at"
elsif event.commented?
if event.note_commit?
words << event.note_short_commit_id
else
words << "##{truncate event.note_target_iid}"
end
words << "at"
elsif event.target
words << "##{event.target_iid}:"
words << event.target.title if event.target.respond_to?(:title)
words << "at"
end
words << event.project_name
words.join(" ")
end
def event_feed_url(event)
......@@ -106,8 +119,6 @@ module EventsHelper
render "events/event_push", event: event
elsif event.merge_request?
render "events/event_merge_request", merge_request: event.merge_request
elsif event.push?
render "events/event_push", event: event
elsif event.note?
render "events/event_note", note: event.note
end
......
......@@ -110,7 +110,7 @@ module GitlabMarkdownHelper
end
def link_to_ignore?(link)
if link =~ /\#\w+/
if link =~ /\A\#\w+/
# ignore anchors like <a href="#my-header">
true
else
......@@ -122,10 +122,11 @@ module GitlabMarkdownHelper
["http://","https://", "ftp://", "mailto:"]
end
def rebuild_path(path)
path.gsub!(/(#.*)/, "")
def rebuild_path(file_path)
file_path = file_path.dup
file_path.gsub!(/(#.*)/, "")
id = $1 || ""
file_path = relative_file_path(path)
file_path = relative_file_path(file_path)
file_path = sanitize_slashes(file_path)
[
......
......@@ -8,6 +8,7 @@
# signup_enabled :boolean
# signin_enabled :boolean
# gravatar_enabled :boolean
# twitter_sharing_enabled :boolean
# sign_in_text :text
# created_at :datetime
# updated_at :datetime
......@@ -30,6 +31,7 @@ class ApplicationSetting < ActiveRecord::Base
default_branch_protection: Settings.gitlab['default_branch_protection'],
signup_enabled: Settings.gitlab['signup_enabled'],
signin_enabled: Settings.gitlab['signin_enabled'],
twitter_sharing_enabled: Settings.gitlab['twitter_sharing_enabled'],
gravatar_enabled: Settings.gravatar['enabled'],
sign_in_text: Settings.extra['sign_in_text'],
)
......
......@@ -47,31 +47,9 @@ class Event < ActiveRecord::Base
scope :recent, -> { order("created_at DESC") }
scope :code_push, -> { where(action: PUSHED) }
scope :in_projects, ->(project_ids) { where(project_id: project_ids).recent }
scope :with_associations, -> { includes(project: :namespace) }
class << self
def create_ref_event(project, user, ref, action = 'add', prefix = 'refs/heads')
commit = project.repository.commit(ref.target)
if action.to_s == 'add'
before = '00000000'
after = commit.id
else
before = commit.id
after = '00000000'
end
Event.create(
project: project,
action: Event::PUSHED,
data: {
ref: "#{prefix}/#{ref.name}",
before: before,
after: after
},
author_id: user.id
)
end
def reset_event_cache_for(target)
Event.where(target_id: target.id, target_type: target.class.to_s).
order('id DESC').limit(100).
......@@ -84,6 +62,8 @@ class Event < ActiveRecord::Base
true
elsif membership_changed?
true
elsif created_project?
true
else
(issue? || merge_request? || note? || milestone?) && target
end
......@@ -98,25 +78,51 @@ class Event < ActiveRecord::Base
end
def target_title
if target && target.respond_to?(:title)
target.title
end
target.title if target && target.respond_to?(:title)
end
def created?
action == CREATED
end
def push?
action == self.class::PUSHED && valid_push?
action == PUSHED && valid_push?
end
def merged?
action == self.class::MERGED
action == MERGED
end
def closed?
action == self.class::CLOSED
action == CLOSED
end
def reopened?
action == self.class::REOPENED
action == REOPENED
end
def joined?
action == JOINED
end
def left?
action == LEFT
end
def commented?
action == COMMENTED
end
def membership_changed?
joined? || left?
end
def created_project?
created? && !target
end
def created_target?
created? && target
end
def milestone?
......@@ -135,32 +141,32 @@ class Event < ActiveRecord::Base
target_type == "MergeRequest"
end
def joined?
action == JOINED
end
def left?
action == LEFT
end
def membership_changed?
joined? || left?
def milestone
target if milestone?
end
def issue
target if target_type == "Issue"
target if issue?
end
def merge_request
target if target_type == "MergeRequest"
target if merge_request?
end
def note
target if target_type == "Note"
target if note?
end
def action_name
if closed?
if push?
if new_ref?
"pushed new"
elsif rm_ref?
"deleted"
else
"pushed to"
end
elsif closed?
"closed"
elsif merged?
"accepted"
......@@ -168,6 +174,10 @@ class Event < ActiveRecord::Base
'joined'
elsif left?
'left'
elsif commented?
"commented on"
elsif created_project?
"created"
else
"opened"
end
......@@ -236,16 +246,6 @@ class Event < ActiveRecord::Base
tag? ? "tag" : "branch"
end
def push_action_name
if new_ref?
"pushed new"
elsif rm_ref?
"deleted"
else
"pushed to"
end
end
def push_with_commits?
md_ref? && commits.any? && commit_from && commit_to
end
......
......@@ -114,13 +114,11 @@ class ProjectMember < Member
end
def post_create_hook
Event.create(
project_id: self.project.id,
action: Event::JOINED,
author_id: self.user.id
)
notification_service.new_team_member(self) unless owner?
unless owner?
event_service.join_project(self.project, self.user)
notification_service.new_team_member(self)
end
system_hook_service.execute_hooks_for(self, :create)
end
......@@ -129,15 +127,14 @@ class ProjectMember < Member
end
def post_destroy_hook
Event.create(
project_id: self.project.id,
action: Event::LEFT,
author_id: self.user.id
)
event_service.leave_project(self.project, self.user)
system_hook_service.execute_hooks_for(self, :destroy)
end
def event_service
EventCreateService.new
end
def notification_service
NotificationService.new
end
......
......@@ -55,14 +55,13 @@ class User < ActiveRecord::Base
include Gitlab::ConfigHelper
include TokenAuthenticatable
extend Gitlab::ConfigHelper
extend Gitlab::CurrentSettings
include Gitlab::CurrentSettings
default_value_for :admin, false
default_value_for :can_create_group, gitlab_config.default_can_create_group
default_value_for :can_create_team, false
default_value_for :hide_no_ssh_key, false
default_value_for :hide_no_password, false
default_value_for :projects_limit, current_application_settings.default_projects_limit
default_value_for :theme_id, gitlab_config.default_theme
devise :database_authenticatable, :lockable, :async,
......@@ -141,6 +140,7 @@ class User < ActiveRecord::Base
before_save :ensure_authentication_token
after_save :ensure_namespace_correct
after_initialize :set_projects_limit
after_create :post_create_hook
after_destroy :post_destroy_hook
......@@ -255,7 +255,7 @@ class User < ActiveRecord::Base
counter = 0
base = username
while User.by_login(username).present? || Namespace.by_path(username).present?
counter += 1
counter += 1
username = "#{base}#{counter}"
end
......@@ -459,10 +459,17 @@ class User < ActiveRecord::Base
def set_notification_email
if self.notification_email.blank? || !self.all_emails.include?(self.notification_email)
self.notification_email = self.email
self.notification_email = self.email
end
end
def set_projects_limit
connection_default_value_defined = new_record? && !projects_limit_changed?
return unless self.projects_limit.nil? || connection_default_value_defined
self.projects_limit = current_application_settings.default_projects_limit
end
def requires_ldap_check?
if !Gitlab.config.ldap.enabled
false
......@@ -559,7 +566,7 @@ class User < ActiveRecord::Base
def post_create_hook
log_info("User \"#{self.name}\" (#{self.email}) was created")
notification_service.new_user(self, @reset_token)
notification_service.new_user(self, @reset_token) if self.created_by_id
system_hook_service.execute_hooks_for(self, :create)
end
......@@ -607,4 +614,13 @@ class User < ActiveRecord::Base
def oauth_authorized_tokens
Doorkeeper::AccessToken.where(resource_owner_id: self.id, revoked_at: nil)
end
def contributed_projects_ids
Event.where(author_id: self).
where("created_at > ?", Time.now - 1.year).
code_push.
reorder(project_id: :desc).
select('DISTINCT(project_id)').
map(&:project_id)
end
end
......@@ -17,7 +17,7 @@ class CreateBranchService < BaseService
new_branch = repository.find_branch(branch_name)
if new_branch
Event.create_ref_event(project, current_user, new_branch, 'add')
EventCreateService.new.push_ref(project, current_user, new_branch, 'add')
return success(new_branch)
else
return error('Invalid reference name')
......
......@@ -26,7 +26,7 @@ class CreateTagService < BaseService
project.gitlab_ci_service.async_execute(push_data)
end
Event.create_ref_event(project, current_user, new_tag, 'add', 'refs/tags')
EventCreateService.new.push_ref(project, current_user, new_tag, 'add', 'refs/tags')
success(new_tag)
else
error('Invalid reference name')
......
......@@ -25,7 +25,7 @@ class DeleteBranchService < BaseService
end
if repository.rm_branch(branch_name)
Event.create_ref_event(project, current_user, branch, 'rm')
EventCreateService.new.push_ref(project, current_user, branch, 'rm')
success('Branch was removed')
else
return error('Failed to remove branch')
......
......@@ -7,58 +7,98 @@
#
class EventCreateService
def open_issue(issue, current_user)
create_event(issue, current_user, Event::CREATED)
create_record_event(issue, current_user, Event::CREATED)
end
def close_issue(issue, current_user)
create_event(issue, current_user, Event::CLOSED)
create_record_event(issue, current_user, Event::CLOSED)
end
def reopen_issue(issue, current_user)
create_event(issue, current_user, Event::REOPENED)
create_record_event(issue, current_user, Event::REOPENED)
end
def open_mr(merge_request, current_user)
create_event(merge_request, current_user, Event::CREATED)
create_record_event(merge_request, current_user, Event::CREATED)
end
def close_mr(merge_request, current_user)
create_event(merge_request, current_user, Event::CLOSED)
create_record_event(merge_request, current_user, Event::CLOSED)
end
def reopen_mr(merge_request, current_user)
create_event(merge_request, current_user, Event::REOPENED)
create_record_event(merge_request, current_user, Event::REOPENED)
end
def merge_mr(merge_request, current_user)
create_event(merge_request, current_user, Event::MERGED)
create_record_event(merge_request, current_user, Event::MERGED)
end
def open_milestone(milestone, current_user)
create_event(milestone, current_user, Event::CREATED)
create_record_event(milestone, current_user, Event::CREATED)
end
def close_milestone(milestone, current_user)
create_event(milestone, current_user, Event::CLOSED)
create_record_event(milestone, current_user, Event::CLOSED)
end
def reopen_milestone(milestone, current_user)
create_event(milestone, current_user, Event::REOPENED)
create_record_event(milestone, current_user, Event::REOPENED)
end
def leave_note(note, current_user)
create_event(note, current_user, Event::COMMENTED)
create_record_event(note, current_user, Event::COMMENTED)
end
def join_project(project, current_user)
create_event(project, current_user, Event::JOINED)
end
def leave_project(project, current_user)
create_event(project, current_user, Event::LEFT)
end
def create_project(project, current_user)
create_event(project, current_user, Event::CREATED)
end
def push_ref(project, current_user, ref, action = 'add', prefix = 'refs/heads')
commit = project.repository.commit(ref.target)
if action.to_s == 'add'
before = '00000000'
after = commit.id
else
before = commit.id
after = '00000000'
end
data = {
ref: "#{prefix}/#{ref.name}",
before: before,
after: after
}
push(project, current_user, data)
end
def push(project, current_user, push_data)
create_event(project, current_user, Event::PUSHED, data: push_data)
end
private
def create_event(record, current_user, status)
Event.create(
project: record.project,
target_id: record.id,
target_type: record.class.name,
def create_record_event(record, current_user, status)
create_event(record.project, current_user, status, target_id: record.id, target_type: record.class.name)
end
def create_event(project, current_user, status, attributes = {})
attributes.reverse_merge!(
project: project,
action: status,
author_id: current_user.id
)
Event.create(attributes)
end
end
......@@ -52,7 +52,7 @@ class GitPushService
end
@push_data = post_receive_data(oldrev, newrev, ref)
create_push_event(@push_data)
EventCreateService.new.push(project, user, @push_data)
project.execute_hooks(@push_data.dup, :push_hooks)
project.execute_services(@push_data.dup)
end
......@@ -60,15 +60,6 @@ class GitPushService
protected
def create_push_event(push_data)
Event.create!(
project: project,
action: Event::PUSHED,
data: push_data,
author_id: push_data[:user_id]
)
end
# Extract any GFM references from the pushed commit messages. If the configured issue-closing regex is matched,
# close the referenced Issue. Create cross-reference Notes corresponding to any other referenced Mentionables.
def process_commit_messages(ref)
......
......@@ -5,7 +5,7 @@ class GitTagPushService
@project, @user = project, user
@push_data = create_push_data(oldrev, newrev, ref)
create_push_event
EventCreateService.new.push(project, user, @push_data)
project.repository.expire_cache
project.execute_hooks(@push_data.dup, :tag_push_hooks)
......@@ -22,13 +22,4 @@ class GitTagPushService
Gitlab::PushDataBuilder.
build(project, user, oldrev, newrev, ref, [])
end
def create_push_event
Event.create!(
project: project,
action: Event::PUSHED,
data: push_data,
author_id: push_data[:user_id]
)
end
end
......@@ -52,13 +52,7 @@ module Projects
end
end
if @project.persisted?
if @project.wiki_enabled?
@project.create_wiki
end
after_create_actions
end
after_create_actions if @project.persisted?
@project
rescue => ex
......@@ -79,6 +73,10 @@ module Projects
def after_create_actions
log_info("#{@project.owner.name} created a new project \"#{@project.name_with_namespace}\"")
@project.create_wiki if @project.wiki_enabled?
event_service.create_project(@project, current_user)
system_hook_service.execute_hooks_for(@project, :create)
unless @project.group
......
......@@ -19,6 +19,11 @@
= f.label :gravatar_enabled, class: 'control-label'
.col-sm-10
= f.check_box :gravatar_enabled, class: 'checkbox'
.form-group
= f.label :twitter_sharing_enabled, "Twitter enabled", class: 'control-label'
.col-sm-10
= f.check_box :twitter_sharing_enabled, class: 'checkbox'
%span.help-block Show users button to share their newly created public or internal projects on twitter
%fieldset
%legend Misc
.form-group
......
......@@ -17,6 +17,6 @@
%tr{:id => "application_#{application.id}"}
%td= link_to application.name, admin_application_path(application)
%td= application.redirect_uri
%td= application.access_tokens.count
%td= application.access_tokens.map(&:resource_owner_id).uniq.count
%td= link_to 'Edit', edit_admin_application_path(application), class: 'btn btn-link'
%td= render 'delete_form', application: application
......@@ -13,15 +13,13 @@
.form-group
%strong Activity
.checkbox
= label_tag :with_push, 'Not empty'
= check_box_tag :with_push, 1, params[:with_push]
&nbsp;
%span.light Projects with push events
= label_tag :with_push do
= check_box_tag :with_push, 1, params[:with_push]
%span Projects with push events
.checkbox
= label_tag :abandoned, 'Abandoned'
= check_box_tag :abandoned, 1, params[:abandoned]
&nbsp;
%span.light No activity over 6 month
= label_tag :abandoned do
= check_box_tag :abandoned, 1, params[:abandoned]
%span No activity over 6 month
%fieldset
%strong Visibility level:
......
= render "events/event_last_push", event: @last_push
= render 'shared/event_filter'
- if @events.any?
.content_list
- else
.nothing-here-block Projects activity will be displayed here
.content_list
= spinner
......@@ -3,7 +3,7 @@
.input-group
= search_field_tag :filter_group, nil, placeholder: 'Filter by name', class: 'dash-filter form-control'
- if current_user.can_create_group?
.input-group-addon
.input-group-addon.dash-new-group
= link_to new_group_path, class: "" do
%strong New group
%ul.well-list.dash-list
......
= link_to namespace_project_path(project.namespace, project), class: dom_class(project) do
.dash-project-avatar
= project_icon("#{project.namespace.to_param}/#{project.to_param}", alt: '', class: 'avatar project-avatar s40')
= project_icon(project, alt: '', class: 'avatar project-avatar s40')
.dash-project-access-icon
= visibility_level_icon(project.visibility_level)
%span.str-truncated
......
......@@ -2,11 +2,11 @@
= f.text_field :login, class: "form-control top", placeholder: "Username or Email", autofocus: "autofocus"
= f.password_field :password, class: "form-control bottom", placeholder: "Password"
- if devise_mapping.rememberable?
.remember-me
%label.checkbox.remember_me{for: "user_remember_me"}
.remember-me.checkbox
%label{for: "user_remember_me"}
= f.check_box :remember_me
%span Remember me
.pull-right
= link_to "Forgot your password?", new_password_path(resource_name)
.pull-right
= link_to "Forgot your password?", new_password_path(resource_name)
%div
= f.submit "Sign in", class: "btn btn-save"
......@@ -3,12 +3,14 @@
.event-item-timestamp
#{time_ago_with_tooltip(event.created_at)}
= cache event do
= cache [event, current_user] do
= image_tag avatar_icon(event.author_email, 24), class: "avatar s24", alt:''
- if event.push?
= render "events/event/push", event: event
- elsif event.note?
- elsif event.commented?
= render "events/event/note", event: event
- elsif event.created_project?
= render "events/event/created_project", event: event
- else
= render "events/event/common", event: event
\ No newline at end of file
.event-title
%span.author_name= link_to_author event
%span.event_label{class: event.action_name}= event_action_name(event)
%span.event_label{class: event.action_name}
= event_action_name(event)
- if event.target
%strong= link_to "##{event.target_iid}", [event.project.namespace.becomes(Namespace), event.project, event.target]
- else
%strong= gfm event.target_title
at
at
- if event.project
= link_to_project event.project
- else
= event.project_name
- if event.target.respond_to?(:title)
.event-body
.event-note
......
.event-title
%span.author_name= link_to_author event
%span.event_label{class: event.action_name}
= event_action_name(event)
- if event.project
= link_to_project event.project
- else
= event.project_name
- if current_user == event.author && !event.project.private? && twitter_sharing_enabled?
.event-body
.event-note
.md
%p
Congratulations! Why not share your accomplishment with the world?
%a.twitter-share-button{ |
href: "https://twitter.com/share", |
"data-url" => event.project.web_url, |
"data-text" => "I just created a new project in GitLab! GitLab is version control on your server.", |
"data-size" => "medium", |
"data-related" => "gitlab", |
"data-hashtags" => "gitlab", |
"data-count" => "none"}
Tweet
%script{src: "//platform.twitter.com/widgets.js"}
\ No newline at end of file
.event-title
%span.author_name= link_to_author event
%span.event_label commented on #{event_note_title_html(event)} at
%span.event_label
= event.action_name
= event_note_title_html(event)
at
- if event.project
= link_to_project event.project
- else
......
.event-title
%span.author_name= link_to_author event
%span.event_label.pushed #{event.push_action_name} #{event.ref_type}
%span.event_label.pushed #{event.action_name} #{event.ref_type}
- if event.rm_ref?
%strong= event.ref_name
- else
......
......@@ -3,11 +3,9 @@
.project-access-icon
= visibility_level_icon(project.visibility_level)
= link_to project.name_with_namespace, [project.namespace.becomes(Namespace), project]
- if current_page?(starred_explore_projects_path)
%strong.pull-right
%i.fa.fa-star
= pluralize project.star_count, 'star'
%span.pull-right
%i.fa.fa-star
= project.star_count
.project-info
- if project.description.present?
......
......@@ -13,7 +13,7 @@
%li.project-row
= link_to namespace_project_path(project.namespace, project), class: dom_class(project) do
.dash-project-avatar
= project_icon("#{project.namespace.to_param}/#{project.to_param}", alt: '', class: 'avatar s40')
= project_icon(project, alt: '', class: 'avatar s40')
.dash-project-access-icon
= visibility_level_icon(project.visibility_level)
%span.str-truncated
......
......@@ -13,10 +13,7 @@
- if current_user
= render "events/event_last_push", event: @last_push
= render 'shared/event_filter'
- if @events.any?
.content_list
- else
.nothing-here-block Project activity will be displayed here
.content_list
= spinner
%aside.side.col-md-4
= render "projects", projects: @projects
......@@ -42,3 +42,9 @@
%li
Use
= link_to 'shortcuts', '#', onclick: 'Shortcuts.showHelp(event)'
%li
Get a support
= link_to 'subscription', 'https://about.gitlab.com/pricing/'
%li
= link_to 'Compare', 'https://about.gitlab.com/features/#compare'
GitLab editions
......@@ -49,8 +49,14 @@
%span
Graphs
= nav_link(controller: :milestones) do
= link_to project_milestones_path(@project), title: 'Milestones' do
%i.fa.fa-clock-o
%span
Milestones
- if project_nav_tab? :issues
= nav_link(controller: %w(issues milestones labels)) do
= nav_link(controller: :issues) do
= link_to url_for_project_issues, title: 'Issues', class: 'shortcuts-issues' do
%i.fa.fa-exclamation-circle
%span
......@@ -66,6 +72,12 @@
Merge Requests
%span.count.merge_counter= @project.merge_requests.opened.count
= nav_link(controller: :labels) do
= link_to project_labels_path(@project), title: 'Labels' do
%i.fa.fa-tags
%span
Labels
- if project_nav_tab? :wiki
= nav_link(controller: :wikis) do
= link_to namespace_project_wiki_path(@project.namespace, @project, :home), title: 'Wiki', class: 'shortcuts-wiki' do
......
......@@ -36,12 +36,12 @@
%th Scope
%th
%tbody
- @authorized_tokens.each do |token|
- application = token.application
%tr{:id => "application_#{application.id}"}
%td= application.name
- @authorized_apps.each do |app|
- token = app.authorized_tokens.order('created_at desc').first
%tr{:id => "application_#{app.id}"}
%td= app.name
%td= token.created_at
%td= token.scopes
%td= render 'doorkeeper/authorized_applications/delete_form', application: application
%td= render 'doorkeeper/authorized_applications/delete_form', application: app
- else
%p.light You dont have any authorized applications
- empty_repo = @project.empty_repo?
.project-home-panel{:class => ("empty-project" if empty_repo)}
.project-identicon-holder
= project_icon("#{@project.namespace.to_param}/#{@project.to_param}", alt: '', class: 'avatar project-avatar')
= project_icon(@project, alt: '', class: 'avatar project-avatar')
.project-home-row
.project-home-desc
- if @project.description.present?
......
......@@ -15,7 +15,7 @@
= f.label :description, 'Description', class: 'control-label'
.col-sm-10
= render layout: 'projects/md_preview' do
= render layout: 'projects/md_preview', locals: { preview_class: "wiki" } do
= render 'projects/zen', f: f, attr: :description,
classes: 'description form-control'
.col-sm-12.hint
......
%ul.nav.nav-tabs
- if project_nav_tab? :issues
= nav_link(controller: :issues) do
= link_to namespace_project_issues_path(@project.namespace, @project), class: "tab" do
%i.fa.fa-exclamation-circle
Issues
- if project_nav_tab? :merge_requests
= nav_link(controller: :merge_requests) do
= link_to namespace_project_merge_requests_path(@project.namespace, @project), class: "tab" do
%i.fa.fa-tasks
Merge Requests
= nav_link(controller: :milestones) do
= link_to namespace_project_milestones_path(@project.namespace, @project), class: "tab" do
%i.fa.fa-clock-o
Milestones
= nav_link(controller: :labels) do
= link_to namespace_project_labels_path(@project.namespace, @project), class: "tab" do
%i.fa.fa-tags
Labels
- if current_controller?(:issues)
- if current_user
%li.hidden-xs
= link_to namespace_project_issues_path(@project.namespace, @project, :atom, { private_token: current_user.private_token }) do
%i.fa.fa-rss
%li.pull-right
.pull-right
.pull-left
= form_tag namespace_project_issues_path(@project.namespace, @project), method: :get, id: "issue_search_form", class: 'pull-left issue-search-form' do
.append-right-10.hidden-xs.hidden-sm
= search_field_tag :issue_search, params[:issue_search], { placeholder: 'Filter by title or description', class: 'form-control issue_search search-text-input input-mn-300' }
= hidden_field_tag :state, params['state']
= hidden_field_tag :scope, params['scope']
= hidden_field_tag :assignee_id, params['assignee_id']
= hidden_field_tag :milestone_id, params['milestone_id']
= hidden_field_tag :label_id, params['label_id']
- if can? current_user, :write_issue, @project
= link_to new_namespace_project_issue_path(@project.namespace, @project, issue: { assignee_id: params[:assignee_id], milestone_id: params[:milestone_id]}), class: "btn btn-new pull-left", title: "New Issue", id: "new_issue_link" do
%i.fa.fa-plus
New Issue
- if current_controller?(:merge_requests)
%li.pull-right
.pull-right
- if can? current_user, :write_merge_request, @project
= link_to new_namespace_project_merge_request_path(@project.namespace, @project), class: "btn btn-new pull-left", title: "New Merge Request" do
%i.fa.fa-plus
New Merge Request
......@@ -10,4 +10,4 @@
.md-write-holder
= yield
.md-preview-holder.hide
.js-md-preview
.js-md-preview{class: (preview_class if defined?(preview_class))}
......@@ -7,8 +7,8 @@
- Gitlab::VisibilityLevel.values.each do |level|
.radio
- restricted = restricted_visibility_levels.include?(level)
= f.radio_button :visibility_level, level, checked: (visibility_level == level), disabled: restricted
= 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)
......
......@@ -15,5 +15,5 @@
%p
To preserve performance only
%strong #{allowed_diff_size} of #{diffs.size}
files displayed.
files are displayed.
......@@ -14,24 +14,24 @@
.voting_notes#notes= render "projects/notes/notes_with_form"
.col-md-3
%div
.clearfix
%span.slead.has_tooltip{:"data-original-title" => 'Cross-project reference'}
= cross_project_reference(@project, @issue)
%hr
.context
%cite.cgray
= render partial: 'issue_context', locals: { issue: @issue }
%hr
.clearfix
.votes-holder
%h6 Votes
#votes= render 'votes/votes_block', votable: @issue
- if @issue.labels.any?
.issuable-affix
.clearfix
%span.slead.has_tooltip{:"data-original-title" => 'Cross-project reference'}
= cross_project_reference(@project, @issue)
%hr
%h6 Labels
.issue-show-labels
- @issue.labels.each do |label|
= link_to namespace_project_issues_path(@project.namespace, @project, label_name: label.name) do
%p= render_colored_label(label)
.context
%cite.cgray
= render partial: 'issue_context', locals: { issue: @issue }
%hr
.clearfix
.votes-holder
%h6 Votes
#votes= render 'votes/votes_block', votable: @issue
- if @issue.labels.any?
%hr
%h6 Labels
.issue-show-labels
- @issue.labels.each do |label|
= link_to namespace_project_issues_path(@project.namespace, @project, label_name: label.name) do
= render_colored_label(label)
.append-bottom-10
.check-all-holder
= check_box_tag "check_all_issues", nil, false, class: "check_all_issues left", disabled: !can?(current_user, :modify_issue, @project)
= 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[status]', options_for_select([['Open', 'open'], ['Closed', 'closed']]), prompt: "Status")
= 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 :status, params[:status]
= button_tag "Update issues", class: "btn update_selected_issues btn-save"
.panel.panel-default
%ul.well-list.issues-list
= render @issues
......
= render "projects/issues_nav"
.append-bottom-10
.pull-right
.pull-left
- if current_user
.hidden-xs.pull-left
= link_to project_issues_path(@project, :atom, { private_token: current_user.private_token }), class: 'btn append-right-10' do
%i.fa.fa-rss
= form_tag project_issues_path(@project), method: :get, id: "issue_search_form", class: 'pull-left issue-search-form' do
.append-right-10.hidden-xs.hidden-sm
= search_field_tag :issue_search, params[:issue_search], { placeholder: 'Filter by title or description', class: 'form-control issue_search search-text-input input-mn-300' }
= hidden_field_tag :state, params['state']
= hidden_field_tag :scope, params['scope']
= hidden_field_tag :assignee_id, params['assignee_id']
= hidden_field_tag :milestone_id, params['milestone_id']
= hidden_field_tag :label_id, params['label_id']
- if can? current_user, :write_issue, @project
= link_to new_project_issue_path(@project, issue: { assignee_id: params[:assignee_id], milestone_id: params[:milestone_id]}), class: "btn btn-new pull-left", title: "New Issue", id: "new_issue_link" do
%i.fa.fa-plus
New Issue
= render 'shared/issuable_filter'
.clearfix
.issues_bulk_update.hide
= form_tag bulk_update_project_issues_path(@project), method: :post do
= select_tag('update[status]', options_for_select([['Open', 'open'], ['Closed', 'closed']]), prompt: "Status")
= 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 :status, params[:status]
= button_tag "Update issues", class: "btn update_selected_issues btn-save"
.issues-holder
= render "issues"
%h4.page-title
.issue-box{ class: issue_box_class(@issue) }
- if @issue.closed?
Closed
- else
Open
Issue ##{@issue.iid}
%small.creator
&middot; created by #{link_to_member(@project, @issue.author)} #{issue_timestamp(@issue)}
.issue
.issue-details
%h4.page-title
.issue-box{ class: issue_box_class(@issue) }
- if @issue.closed?
Closed
- else
Open
Issue ##{@issue.iid}
%small.creator
&middot; created by #{link_to_member(@project, @issue.author)} #{issue_timestamp(@issue)}
.pull-right
- if can?(current_user, :write_issue, @project)
= link_to new_namespace_project_issue_path(@project.namespace, @project), class: "btn btn-grouped new-issue-link", title: "New Issue", id: "new_issue_link" do
%i.fa.fa-plus
New Issue
- if can?(current_user, :modify_issue, @issue)
- if @issue.closed?
= link_to 'Reopen', namespace_project_issue_path(@project.namespace, @project, @issue, issue: {state_event: :reopen }, status_only: true), method: :put, class: "btn btn-grouped btn-reopen"
- else
= link_to 'Close', namespace_project_issue_path(@project.namespace, @project, @issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn btn-grouped btn-close", title: "Close Issue"
.pull-right
- if can?(current_user, :write_issue, @project)
= link_to new_namespace_project_issue_path(@project.namespace, @project), class: "btn btn-grouped new-issue-link", title: "New Issue", id: "new_issue_link" do
%i.fa.fa-plus
New Issue
- if can?(current_user, :modify_issue, @issue)
- if @issue.closed?
= link_to 'Reopen', namespace_project_issue_path(@project.namespace, @project, @issue, issue: {state_event: :reopen }, status_only: true), method: :put, class: "btn btn-grouped btn-reopen"
- else
= link_to 'Close', namespace_project_issue_path(@project.namespace, @project, @issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn btn-grouped btn-close", title: "Close Issue"
= link_to edit_namespace_project_issue_path(@project.namespace, @project, @issue), class: "btn btn-grouped issuable-edit" do
%i.fa.fa-pencil-square-o
Edit
= link_to edit_namespace_project_issue_path(@project.namespace, @project, @issue), class: "btn btn-grouped issuable-edit" do
%i.fa.fa-pencil-square-o
Edit
%hr
%h3.issue-title
= gfm escape_once(@issue.title)
%div
- if @issue.description.present?
.description
.wiki
= preserve do
= markdown(@issue.description, parse_tasks: true)
%hr
%h3.issue-title
= gfm escape_once(@issue.title)
%div
- if @issue.description.present?
.description
.wiki
= preserve do
= markdown(@issue.description, parse_tasks: true)
%hr
= render "projects/issues/discussion"
%hr
.issue-discussion
= render "projects/issues/discussion"
= render "projects/issues_nav"
- if can? current_user, :admin_label, @project
= link_to new_namespace_project_label_path(@project.namespace, @project), class: "pull-right btn btn-new" do
New label
......
......@@ -10,22 +10,23 @@
= render "projects/merge_requests/show/participants"
= render "projects/notes/notes_with_form"
.col-md-3
.clearfix
%span.slead.has_tooltip{:"data-original-title" => 'Cross-project reference'}
= cross_project_reference(@project, @merge_request)
%hr
.context
%cite.cgray
= render partial: 'projects/merge_requests/show/context', locals: { merge_request: @merge_request }
%hr
.votes-holder
%h6 Votes
#votes= render 'votes/votes_block', votable: @merge_request
- if @merge_request.labels.any?
.issuable-affix
.clearfix
%span.slead.has_tooltip{:"data-original-title" => 'Cross-project reference'}
= cross_project_reference(@project, @merge_request)
%hr
.context
%cite.cgray
= render partial: 'projects/merge_requests/show/context', locals: { merge_request: @merge_request }
%hr
%h6 Labels
.merge-request-show-labels
- @merge_request.labels.each do |label|
= link_to namespace_project_merge_requests_path(@project.namespace, @project, label_name: label.name) do
%p= render_colored_label(label)
.votes-holder
%h6 Votes
#votes= render 'votes/votes_block', votable: @merge_request
- if @merge_request.labels.any?
%hr
%h6 Labels
.merge-request-show-labels
- @merge_request.labels.each do |label|
= link_to namespace_project_merge_requests_path(@project.namespace, @project, label_name: label.name) do
= render_colored_label(label)
......@@ -18,7 +18,7 @@
- if merge_request.assignee
assigned to #{link_to_member(merge_request.source_project, merge_request.assignee)}
- else
Work In Progress
Unassigned
- if merge_request.votes_count > 0
= render 'votes/votes_inline', votable: merge_request
- if merge_request.notes.any?
......
......@@ -19,7 +19,7 @@
.form-group.issuable-description
= f.label :description, 'Description', class: 'control-label'
.col-sm-10
= render layout: 'projects/md_preview' do
= render layout: 'projects/md_preview', locals: { preview_class: "wiki" } do
= render 'projects/zen', f: f, attr: :description, classes: 'description form-control'
.col-sm-12-hint
......
.merge-request{'data-url' => namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}
= render "projects/merge_requests/show/mr_title"
%hr
= render "projects/merge_requests/show/mr_box"
%hr
.append-bottom-20
.slead
%span From
- if @merge_request.for_fork?
%strong.label-branch<
- if @merge_request.source_project
= link_to @merge_request.source_project_namespace, namespace_project_path(@merge_request.source_project.namespace, @merge_request.source_project)
- else
\ #{@merge_request.source_project_namespace}
\:#{@merge_request.source_branch}
%span into
%strong.label-branch #{@merge_request.target_project_namespace}:#{@merge_request.target_branch}
- else
%strong.label-branch #{@merge_request.source_branch}
%span into
%strong.label-branch #{@merge_request.target_branch}
- if @merge_request.open?
%span.pull-right
.btn-group
%a.btn.dropdown-toggle{ data: {toggle: :dropdown} }
%i.fa.fa-download
Download as
%span.caret
%ul.dropdown-menu
%li= link_to "Email Patches", namespace_project_merge_request_path(@project.namespace, @project, @merge_request, format: :patch)
%li= link_to "Plain Diff", namespace_project_merge_request_path(@project.namespace, @project, @merge_request, format: :diff)
.merge-request-details
= render "projects/merge_requests/show/mr_title"
%hr
= render "projects/merge_requests/show/mr_box"
%hr
.append-bottom-20
.slead
%span From
- if @merge_request.for_fork?
%strong.label-branch<
- if @merge_request.source_project
= link_to @merge_request.source_project_namespace, namespace_project_path(@merge_request.source_project.namespace, @merge_request.source_project)
- else
\ #{@merge_request.source_project_namespace}
\:#{@merge_request.source_branch}
%span into
%strong.label-branch #{@merge_request.target_project_namespace}:#{@merge_request.target_branch}
- else
%strong.label-branch #{@merge_request.source_branch}
%span into
%strong.label-branch #{@merge_request.target_branch}
- if @merge_request.open?
%span.pull-right
.btn-group
%a.btn.dropdown-toggle{ data: {toggle: :dropdown} }
%i.fa.fa-download
Download as
%span.caret
%ul.dropdown-menu
%li= link_to "Email Patches", namespace_project_merge_request_path(@project.namespace, @project, @merge_request, format: :patch)
%li= link_to "Plain Diff", namespace_project_merge_request_path(@project.namespace, @project, @merge_request, format: :diff)
= render "projects/merge_requests/show/how_to_merge"
= render "projects/merge_requests/show/state_widget"
= render "projects/merge_requests/show/how_to_merge"
= render "projects/merge_requests/show/state_widget"
- if @commits.present?
%ul.nav.nav-tabs.merge-request-tabs
......
= render "projects/issues_nav"
.merge-requests-holder
.append-bottom-10
.pull-right
- if can? current_user, :write_merge_request, @project
= link_to new_project_merge_request_path(@project), class: "btn btn-new pull-left", title: "New Merge Request" do
%i.fa.fa-plus
New Merge Request
= render 'shared/issuable_filter'
.panel.panel-default
%ul.well-list.mr-list
......
......@@ -17,7 +17,7 @@
.accept-action
= f.submit "Accept Merge Request", class: "btn btn-create accept_merge_request"
- if can_remove_branch?(@merge_request.source_project, @merge_request.source_branch) && !@merge_request.for_fork?
.accept-control
.accept-control.checkbox
= label_tag :should_remove_source_branch, class: "remove_source_checkbox" do
= check_box_tag :should_remove_source_branch
Remove source-branch
......
......@@ -21,7 +21,7 @@
.form-group.milestone-description
= f.label :description, "Description", class: "control-label"
.col-sm-10
= render layout: 'projects/md_preview' do
= render layout: 'projects/md_preview', locals: { preview_class: "wiki" } do
= render 'projects/zen', f: f, attr: :description, classes: 'description form-control'
.hint
.pull-left Milestones are parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"), target: '_blank'}.
......
= render "projects/issues_nav"
.milestones_content
%h3.page-title
Milestones
- if can? current_user, :admin_milestone, @project
= link_to new_namespace_project_milestone_path(@project.namespace, @project), class: "pull-right btn btn-new", title: "New Milestone" do
%i.fa.fa-plus
New Milestone
.pull-right
- if can? current_user, :admin_milestone, @project
= link_to new_namespace_project_milestone_path(@project.namespace, @project), class: "pull-right btn btn-new", title: "New Milestone" do
%i.fa.fa-plus
New Milestone
= render 'shared/milestones_filter'
.milestones
......
= render "projects/issues_nav"
%h4.page-title
.issue-box{ class: issue_box_class(@milestone) }
- if @milestone.closed?
......
......@@ -52,7 +52,7 @@
%i.fa.fa-github
Import projects from GitHub
= render 'github_import_modal'
.project-import.form-group
.col-sm-2
.col-sm-10
......@@ -60,7 +60,7 @@
= link_to status_import_gitlab_path do
%i.fa.fa-heart
Import projects from GitLab.com
- else
- elsif request.host != 'gitlab.com'
= link_to '#', class: 'how_to_import_link light' do
%i.fa.fa-heart
Import projects from GitLab.com
......@@ -99,4 +99,4 @@
e.preventDefault()
import_modal = $(this).parent().find(".modal").show()
$('.modal-header .close').bind 'click', ->
$(".modal").hide()
\ No newline at end of file
$(".modal").hide()
.note-edit-form
= form_for note, url: namespace_project_note_path(@project.namespace, @project, note), method: :put, remote: true, authenticity_token: true do |f|
= render layout: 'projects/md_preview' do
= render layout: 'projects/md_preview', locals: { preview_class: "note-text" } do
= render 'projects/zen', f: f, attr: :note,
classes: 'note_text js-note-text'
......
......@@ -5,7 +5,7 @@
= f.hidden_field :noteable_id
= f.hidden_field :noteable_type
= render layout: 'projects/md_preview' do
= render layout: 'projects/md_preview', locals: { preview_class: "note-text" } do
= render 'projects/zen', f: f, attr: :note,
classes: 'note_text js-note-text'
......
......@@ -22,7 +22,7 @@
.form-group.wiki-content
= f.label :content, class: 'control-label'
.col-sm-10
= render layout: 'projects/md_preview' do
= render layout: 'projects/md_preview', locals: { preview_class: "wiki" } do
= render 'projects/zen', f: f, attr: :content, classes: 'description form-control'
.col-sm-12.hint
.pull-left Wiki content is parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"), target: '_blank'}
......
......@@ -7,4 +7,5 @@
= link_to "#L#{i}", id: "L#{i}", rel: "#L#{i}" do
%i.fa.fa-link
= i
= highlight(blob.name, blob.data)
:preserve
#{highlight(blob.name, blob.data)}
.issues-filters
.pull-left.append-right-20
%ul.nav.nav-pills.nav-compact
.issues-state-filters
%ul.nav.nav-tabs
%li{class: ("active" if params[:state] == 'opened')}
= link_to page_filter_path(state: 'opened') do
%i.fa.fa-exclamation-circle
......@@ -14,99 +14,106 @@
%i.fa.fa-compass
All
.dropdown.inline.assignee-filter
%a.dropdown-toggle.btn{href: '#', "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
.dropdown.inline.prepend-left-10.author-filter
%a.dropdown-toggle.btn{href: '#', "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
.dropdown.inline.prepend-left-10.milestone-filter
%a.dropdown-toggle.btn{href: '#', "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
%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
%a.dropdown-toggle.btn{href: '#', "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
- if @project
.dropdown.inline.prepend-left-10.labels-filter
%a.dropdown-toggle.btn{href: '#', "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
.dropdown.inline.prepend-left-10.author-filter
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%i.fa.fa-user
%span.light author:
- if @author.present?
%strong= @author.name
- elsif params[:author_id] == "0"
Unassigned
- else
Any
- if @project.labels.any?
- @project.labels.each do |label|
%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(label_name: label.name) do
= render_colored_label(label)
- else
= link_to page_filter_path(author_id: user.id) do
= image_tag avatar_icon(user.email), class: "avatar s16", alt: ''
= user.name
.dropdown.inline.prepend-left-10.milestone-filter
%a.dropdown-toggle.btn{href: '#', "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 generate_namespace_project_labels_path(@project.namespace, @project, redirect: request.original_url), method: :post do
%i.fa.fa-plus-circle
Create default labels
= 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
- if @project
.dropdown.inline.prepend-left-10.labels-filter
%a.dropdown-toggle.btn{href: '#', "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
.pull-right
= render 'shared/sort_dropdown'
.pull-right
= render 'shared/sort_dropdown'
.milestones-filters.append-bottom-10
%ul.nav.nav-pills.nav-compact
%ul.nav.nav-tabs
%li{class: ("active" if params[:state].blank? || params[:state] == 'opened')}
= link_to milestones_filter_path(state: 'opened') do
%i.fa.fa-exclamation-circle
......
.panel.panel-default
.panel-heading Personal projects
%ul.well-list
- projects.each do |project|
%li
= link_to_project project
- if @contributed_projects.present?
.panel.panel-default
.panel-heading Projects contributed to
%ul.well-list
- @contributed_projects.sort_by(&:star_count).reverse.each do |project|
%li
= link_to_project project
%span.pull-right.light
%i.fa.fa-star
= project.star_count
- if @projects.present?
.panel.panel-default
.panel-heading Personal projects
%ul.well-list
- @projects.sort_by(&:star_count).reverse.each do |project|
%li
= link_to_project project
%span.pull-right.light
%i.fa.fa-star
= project.star_count
%h4 Calendar
%h4 Commits calendar
#cal-heatmap.calendar
:javascript
new calendar(
......
......@@ -35,9 +35,7 @@
= render @events
.col-md-4
= render 'profile', user: @user
- if @projects.present?
= render 'projects', projects: @projects
= render 'projects'
:coffeescript
$ ->
......
......@@ -112,6 +112,7 @@ end
Settings.gitlab['time_zone'] ||= nil
Settings.gitlab['signup_enabled'] ||= true if Settings.gitlab['signup_enabled'].nil?
Settings.gitlab['signin_enabled'] ||= true if Settings.gitlab['signin_enabled'].nil?
Settings.gitlab['twitter_sharing_enabled'] ||= true if Settings.gitlab['twitter_sharing_enabled'].nil?
Settings.gitlab['restricted_visibility_levels'] = Settings.send(:verify_constant_array, Gitlab::VisibilityLevel, Settings.gitlab['restricted_visibility_levels'], [])
Settings.gitlab['username_changing_enabled'] = true if Settings.gitlab['username_changing_enabled'].nil?
Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing)) +(?:(?:issues? +)?#\d+(?:(?:, *| +and +)?))+)' if Settings.gitlab['issue_closing_pattern'].nil?
......
......@@ -3,6 +3,7 @@ Gitlab::Seeder.quiet do
s.id = 1
s.name = 'Administrator'
s.email = 'admin@example.com'
s.notification_email = 'admin@example.com'
s.username = 'root'
s.password = '5iveL!fe'
s.admin = true
......
class AddTwitterSharingEnabledToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :twitter_sharing_enabled, :boolean, default: true
end
end
......@@ -10,10 +10,11 @@
- [SSH](ssh/README.md) Setup your ssh keys and deploy keys for secure access to your projects.
- [Web hooks](web_hooks/web_hooks.md) Let GitLab notify you when new code has been pushed to your project.
- [Workflow](workflow/README.md) Using GitLab functionality and importing projects from GitHub and SVN.
- [GitLab as OAuth2 authentication service provider](integration/oauth_provider.md). It allows you to login to other applications from GitLab.
## Administrator documentation
- [Install](install/README.md) Requirements, directory structures and manual installation.
- [Install](install/README.md) Requirements, directory structures and installation from source.
- [Integration](integration/README.md) How to integrate with systems such as JIRA, Redmine, LDAP and Twitter.
- [Raketasks](raketasks/README.md) Backups, maintenance, automatic web hook setup and the importing of projects.
- [Custom git hooks](hooks/custom_hooks.md) Custom git hooks (on the filesystem) for when web hooks aren't enough.
......
......@@ -22,6 +22,7 @@
## Clients
Find API Clients for GitLab [on our website](https://about.gitlab.com/applications/#api-clients).
You can use [GitLab as an OAuth2 client](oauth2.md) to make API calls.
## Introduction
......@@ -67,7 +68,7 @@ curl https://localhost:3000/api/v3/user?access_token=OAUTH-TOKEN
curl -H "Authorization: Bearer OAUTH-TOKEN" https://localhost:3000/api/v3/user
```
Read more about [OAuth2 in GitLab](oauth2.md).
Read more about [GitLab as an OAuth2 client](oauth2.md).
## Status codes
......
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