...@@ -56,6 +56,10 @@ v 7.8.0 (unreleased) ...@@ -56,6 +56,10 @@ v 7.8.0 (unreleased)
- Show users button to share their newly created public or internal projects on twitter - 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. - 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. - 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 v 7.7.2
- Update GitLab Shell to version 2.4.2 that fixes a bug when developers can push to protected branch - Update GitLab Shell to version 2.4.2 that fixes a bug when developers can push to protected branch
... ...
......
2.4.3 2.5.1
...@@ -15,3 +15,9 @@ class @Issue ...@@ -15,3 +15,9 @@ class @Issue
"issue" "issue"
updateTaskState updateTaskState
) )
$('.issuable-affix').affix offset:
top: ->
@top = $('.issue-details').outerHeight(true) + 25
bottom: ->
@bottom = $('.footer').outerHeight(true)
...@@ -20,6 +20,12 @@ class @MergeRequest ...@@ -20,6 +20,12 @@ class @MergeRequest
if $("a.btn-close").length if $("a.btn-close").length
$("li.task-list-item input:checkbox").prop("disabled", false) $("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 # Local jQuery finder
$: (selector) -> $: (selector) ->
this.$el.find(selector) this.$el.find(selector)
... ...
......
...@@ -272,7 +272,7 @@ class @Notes ...@@ -272,7 +272,7 @@ class @Notes
note_li = $(".note-row-" + note.id) note_li = $(".note-row-" + note.id)
note_li.replaceWith(note.html) note_li.replaceWith(note.html)
note_li.find('.note-edit-form').hide() 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 Called in response to clicking the edit note link
...@@ -284,7 +284,7 @@ class @Notes ...@@ -284,7 +284,7 @@ class @Notes
showEditForm: (e) -> showEditForm: (e) ->
e.preventDefault() e.preventDefault()
note = $(this).closest(".note") note = $(this).closest(".note")
note.find(".note-text").hide() note.find(".note-body > .note-text").hide()
note.find(".note-header").hide() note.find(".note-header").hide()
base_form = note.find(".note-edit-form") base_form = note.find(".note-edit-form")
form = base_form.clone().insertAfter(base_form) form = base_form.clone().insertAfter(base_form)
...@@ -311,7 +311,7 @@ class @Notes ...@@ -311,7 +311,7 @@ class @Notes
cancelEdit: (e) -> cancelEdit: (e) ->
e.preventDefault() e.preventDefault()
note = $(this).closest(".note") note = $(this).closest(".note")
note.find(".note-text").show() note.find(".note-body > .note-text").show()
note.find(".note-header").show() note.find(".note-header").show()
note.find(".current-note-edit-form").remove() note.find(".current-note-edit-form").remove()
...@@ -345,7 +345,7 @@ class @Notes ...@@ -345,7 +345,7 @@ class @Notes
removeAttachment: -> removeAttachment: ->
note = $(this).closest(".note") note = $(this).closest(".note")
note.find(".note-attachment").remove() 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(".js-note-attachment-delete").hide()
note.find(".note-edit-form").hide() note.find(".note-edit-form").hide()
... ...
......
@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,9 +94,16 @@ ...@@ -94,9 +94,16 @@
} }
} }
.issue-show-labels .color-label { .issue-show-labels {
a {
margin-right: 5px;
margin-bottom: 5px;
display: inline-block;
.color-label {
padding: 6px 10px; padding: 6px 10px;
} }
}
}
form.edit-issue { form.edit-issue {
margin: 0; margin: 0;
... ...
......
...@@ -185,6 +185,13 @@ ...@@ -185,6 +185,13 @@
} }
} }
.merge-request-show-labels .label { .merge-request-show-labels {
a {
margin-right: 5px;
margin-bottom: 5px;
display: inline-block;
.color-label {
padding: 6px 10px; padding: 6px 10px;
} }
}
}
...@@ -12,11 +12,7 @@ class DashboardController < ApplicationController ...@@ -12,11 +12,7 @@ class DashboardController < ApplicationController
@groups = current_user.authorized_groups.order_name_asc @groups = current_user.authorized_groups.order_name_asc
@has_authorized_projects = @projects.count > 0 @has_authorized_projects = @projects.count > 0
@projects_count = @projects.count @projects_count = @projects.count
@projects = @projects.limit(@projects_limit) @projects = @projects.includes(:namespace).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)
@last_push = current_user.recent_push @last_push = current_user.recent_push
...@@ -24,8 +20,16 @@ class DashboardController < ApplicationController ...@@ -24,8 +20,16 @@ class DashboardController < ApplicationController
respond_to do |format| respond_to do |format|
format.html 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
end end
...@@ -74,4 +78,10 @@ class DashboardController < ApplicationController ...@@ -74,4 +78,10 @@ class DashboardController < ApplicationController
def load_projects def load_projects
@projects = current_user.authorized_projects.sorted_by_activity.non_archived @projects = current_user.authorized_projects.sorted_by_activity.non_archived
end 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 end
...@@ -18,7 +18,7 @@ class Explore::ProjectsController < ApplicationController ...@@ -18,7 +18,7 @@ class Explore::ProjectsController < ApplicationController
def starred def starred
@starred_projects = ProjectsFinder.new.execute(current_user) @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) @starred_projects = @starred_projects.page(params[:page]).per(10)
end end
end end
...@@ -10,11 +10,11 @@ class GroupsController < ApplicationController ...@@ -10,11 +10,11 @@ class GroupsController < ApplicationController
# Load group projects # Load group projects
before_filter :load_projects, except: [:new, :create, :projects, :edit, :update] 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 layout :determine_layout
before_filter :set_title, only: [:new, :create]
def new def new
@group = Group.new @group = Group.new
end end
...@@ -32,15 +32,21 @@ class GroupsController < ApplicationController ...@@ -32,15 +32,21 @@ class GroupsController < ApplicationController
end end
def show 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 @last_push = current_user.recent_push if current_user
@projects = @projects.includes(:namespace)
respond_to do |format| respond_to do |format|
format.html 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
end end
...@@ -149,4 +155,10 @@ class GroupsController < ApplicationController ...@@ -149,4 +155,10 @@ class GroupsController < ApplicationController
def group_params def group_params
params.require(:group).permit(:name, :description, :path, :avatar) params.require(:group).permit(:name, :description, :path, :avatar)
end 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 end
...@@ -5,9 +5,10 @@ class ProjectsController < ApplicationController ...@@ -5,9 +5,10 @@ class ProjectsController < ApplicationController
# Authorize # Authorize
before_filter :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer, :archive, :unarchive] 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] layout 'navless', only: [:new, :create, :fork]
before_filter :set_title, only: [:new, :create]
def new def new
@project = Project.new @project = Project.new
...@@ -56,9 +57,6 @@ class ProjectsController < ApplicationController ...@@ -56,9 +57,6 @@ class ProjectsController < ApplicationController
end end
limit = (params[:limit] || 20).to_i 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)) @show_star = !(current_user && current_user.starred?(@project))
...@@ -76,7 +74,12 @@ class ProjectsController < ApplicationController ...@@ -76,7 +74,12 @@ class ProjectsController < ApplicationController
end end
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
end end
... ...
......
...@@ -8,15 +8,19 @@ class UsersController < ApplicationController ...@@ -8,15 +8,19 @@ class UsersController < ApplicationController
visible_projects = ProjectsFinder.new.execute(current_user) visible_projects = ProjectsFinder.new.execute(current_user)
authorized_projects_ids = visible_projects.pluck(:id) authorized_projects_ids = visible_projects.pluck(:id)
@contributed_projects = Project.where(id: authorized_projects_ids).
in_group_namespace.includes(:namespace)
@projects = @user.personal_projects. @projects = @user.personal_projects.
where(id: authorized_projects_ids) where(id: authorized_projects_ids).includes(:namespace)
# Collect only groups common for both users # Collect only groups common for both users
@groups = @user.groups & GroupsFinder.new.execute(current_user) @groups = @user.groups & GroupsFinder.new.execute(current_user)
# Get user activity feed for projects common for both users # Get user activity feed for projects common for both users
@events = @user.recent_events. @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 = @user.name
@title_url = user_path(@user) @title_url = user_path(@user)
... ...
......
...@@ -51,7 +51,13 @@ module ApplicationHelper ...@@ -51,7 +51,13 @@ module ApplicationHelper
end end
def project_icon(project_id, options = {}) 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? if project.avatar.present?
image_tag project.avatar.url, options image_tag project.avatar.url, options
elsif project.avatar_in_git elsif project.avatar_in_git
... ...
......
...@@ -110,7 +110,7 @@ module GitlabMarkdownHelper ...@@ -110,7 +110,7 @@ module GitlabMarkdownHelper
end end
def link_to_ignore?(link) def link_to_ignore?(link)
if link =~ /\#\w+/ if link =~ /\A\#\w+/
# ignore anchors like <a href="#my-header"> # ignore anchors like <a href="#my-header">
true true
else else
...@@ -122,10 +122,11 @@ module GitlabMarkdownHelper ...@@ -122,10 +122,11 @@ module GitlabMarkdownHelper
["http://","https://", "ftp://", "mailto:"] ["http://","https://", "ftp://", "mailto:"]
end end
def rebuild_path(path) def rebuild_path(file_path)
path.gsub!(/(#.*)/, "") file_path = file_path.dup
file_path.gsub!(/(#.*)/, "")
id = $1 || "" id = $1 || ""
file_path = relative_file_path(path) file_path = relative_file_path(file_path)
file_path = sanitize_slashes(file_path) file_path = sanitize_slashes(file_path)
[ [
... ...
......
...@@ -47,6 +47,7 @@ class Event < ActiveRecord::Base ...@@ -47,6 +47,7 @@ class Event < ActiveRecord::Base
scope :recent, -> { order("created_at DESC") } scope :recent, -> { order("created_at DESC") }
scope :code_push, -> { where(action: PUSHED) } scope :code_push, -> { where(action: PUSHED) }
scope :in_projects, ->(project_ids) { where(project_id: project_ids).recent } scope :in_projects, ->(project_ids) { where(project_id: project_ids).recent }
scope :with_associations, -> { includes(project: :namespace) }
class << self class << self
def reset_event_cache_for(target) def reset_event_cache_for(target)
... ...
......
...@@ -607,4 +607,13 @@ class User < ActiveRecord::Base ...@@ -607,4 +607,13 @@ class User < ActiveRecord::Base
def oauth_authorized_tokens def oauth_authorized_tokens
Doorkeeper::AccessToken.where(resource_owner_id: self.id, revoked_at: nil) Doorkeeper::AccessToken.where(resource_owner_id: self.id, revoked_at: nil)
end 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 end
= render "events/event_last_push", event: @last_push = render "events/event_last_push", event: @last_push
= render 'shared/event_filter' = render 'shared/event_filter'
- if @events.any?
.content_list .content_list
- else
.nothing-here-block Projects activity will be displayed here
= spinner = spinner
= link_to project_path(project), class: dom_class(project) do = link_to project_path(project), class: dom_class(project) do
.dash-project-avatar .dash-project-avatar
= project_icon(project.to_param, alt: '', class: 'avatar project-avatar s40') = project_icon(project, alt: '', class: 'avatar project-avatar s40')
.dash-project-access-icon .dash-project-access-icon
= visibility_level_icon(project.visibility_level) = visibility_level_icon(project.visibility_level)
%span.str-truncated %span.str-truncated
... ...
......
...@@ -3,11 +3,9 @@ ...@@ -3,11 +3,9 @@
.project-access-icon .project-access-icon
= visibility_level_icon(project.visibility_level) = visibility_level_icon(project.visibility_level)
= link_to project.name_with_namespace, project = link_to project.name_with_namespace, project
%span.pull-right
- if current_page?(starred_explore_projects_path)
%strong.pull-right
%i.fa.fa-star %i.fa.fa-star
= pluralize project.star_count, 'star' = project.star_count
.project-info .project-info
- if project.description.present? - if project.description.present?
... ...
......