Commit 86f1f833 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets
Browse files

Merge branch 'epic/refactor-labels' into 'master'

Epic/refactor labels

Part of #1385

See merge request !1003
......@@ -94,7 +94,7 @@
}
}
.issue-show-labels .label {
.issue-show-labels .color-label {
padding: 6px 10px;
}
......
.suggest-colors {
margin-top: 5px;
a {
@include border-radius(4px);
width: 30px;
height: 30px;
display: inline-block;
margin-right: 10px;
}
}
.manage-labels-list {
.label {
padding: 9px;
font-size: 14px;
}
}
.color-label {
padding: 3px 4px;
}
......@@ -46,11 +46,11 @@ def projects
@projects = @projects.where(namespace_id: Group.find_by(name: params[:group])) if params[:group].present?
@projects = @projects.where(visibility_level: params[:visibility_level]) if params[:visibility_level].present?
@projects = @projects.includes(:namespace)
@projects = @projects.tagged_with(params[:label]) if params[:label].present?
@projects = @projects.tagged_with(params[:tag]) if params[:tag].present?
@projects = @projects.sort(@sort = params[:sort])
@projects = @projects.page(params[:page]).per(30)
@labels = current_user.authorized_projects.tags_on(:labels)
@tags = current_user.authorized_projects.tags_on(:tags)
@groups = current_user.authorized_groups
end
......
......@@ -152,7 +152,7 @@ def redirect_old
def issue_params
params.require(:issue).permit(
:title, :assignee_id, :position, :description,
:milestone_id, :label_list, :state_event
:milestone_id, :state_event, label_ids: []
)
end
end
class Projects::LabelsController < Projects::ApplicationController
before_filter :module_enabled
before_filter :label, only: [:edit, :update, :destroy]
before_filter :authorize_labels!
before_filter :authorize_admin_labels!, except: [:index]
respond_to :js, :html
def index
@labels = @project.issues_labels
@labels = @project.labels.order('title ASC').page(params[:page]).per(20)
end
def new
@label = @project.labels.new
end
def create
@label = @project.labels.create(label_params)
if @label.valid?
redirect_to project_labels_path(@project)
else
render 'new'
end
end
def edit
end
def update
if @label.update_attributes(label_params)
redirect_to project_labels_path(@project)
else
render 'edit'
end
end
def generate
......@@ -21,6 +47,12 @@ def generate
end
end
def destroy
@label.destroy
redirect_to project_labels_path(@project), notice: 'Label was removed'
end
protected
def module_enabled
......@@ -28,4 +60,16 @@ def module_enabled
return render_404
end
end
def label_params
params.require(:label).permit(:title, :color)
end
def label
@label = @project.labels.find(params[:id])
end
def authorize_admin_labels!
return render_404 unless can?(current_user, :admin_label, @project)
end
end
......@@ -242,7 +242,7 @@ def merge_request_params
params.require(:merge_request).permit(
:title, :assignee_id, :source_project_id, :source_branch,
:target_project_id, :target_branch, :milestone_id,
:state_event, :description, :label_list
:state_event, :description, label_ids: []
)
end
end
......@@ -196,7 +196,7 @@ def user_layout
def project_params
params.require(:project).permit(
:name, :path, :description, :issues_tracker, :label_list,
:name, :path, :description, :issues_tracker, :tag_list,
:issues_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id, :default_branch,
:wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id
)
......
......@@ -125,7 +125,13 @@ def by_assignee(items)
def by_label(items)
if params[:label_name].present?
items = items.tagged_with(params[:label_name])
label_names = params[:label_name].split(",")
item_ids = LabelLink.joins(:label).
where('labels.title in (?)', label_names).
where(target_type: klass.name).pluck(:target_id)
items = items.where(id: item_ids)
end
items
......
module LabelsHelper
def issue_label_names
@project.issues_labels.map(&:name)
def project_label_names
@project.labels.pluck(:title)
end
def labels_autocomplete_source
labels = @project.issues_labels
labels = labels.map{ |l| { label: l.name, value: l.name } }
labels.to_json
def render_colored_label(label)
label_color = label.color || "#428bca"
text_color = text_color_for_bg(label_color)
content_tag :span, class: 'label color-label', style: "background:#{label_color};color:#{text_color}" do
label.name
end
end
def suggested_colors
[
'#d9534f',
'#f0ad4e',
'#428bca',
'#5cb85c',
'#34495e',
'#7f8c8d',
'#8e44ad',
'#FFECDB'
]
end
def label_css_class(name)
klass = Gitlab::IssuesLabels
def text_color_for_bg(bg_color)
r, g, b = bg_color.slice(1,7).scan(/.{2}/).map(&:hex)
case name.downcase
when *klass.warning_labels
'label-warning'
when *klass.neutral_labels
'label-primary'
when *klass.positive_labels
'label-success'
when *klass.important_labels
'label-danger'
if (r + g + b) > 500
"#333"
else
'label-info'
"#FFF"
end
end
end
......@@ -142,6 +142,7 @@ def project_dev_rules
:write_wiki,
:modify_issue,
:admin_issue,
:admin_label,
:push_code
]
end
......
......@@ -13,6 +13,8 @@ module Issuable
belongs_to :assignee, class_name: "User"
belongs_to :milestone
has_many :notes, as: :noteable, dependent: :destroy
has_many :label_links, as: :target, dependent: :destroy
has_many :labels, through: :label_links
validates :author, presence: true
validates :title, presence: true, length: { within: 0..255 }
......@@ -131,4 +133,15 @@ def to_hook_data
object_attributes: self.attributes
}
end
def label_names
labels.order('title ASC').pluck(:title)
end
def add_labels_by_names(label_names)
label_names.each do |label_name|
label = project.labels.find_or_create_by(title: label_name.strip)
self.labels << label
end
end
end
......@@ -32,9 +32,6 @@ class Issue < ActiveRecord::Base
scope :of_group, ->(group) { where(project_id: group.project_ids) }
scope :of_user_team, ->(team) { where(project_id: team.project_ids, assignee_id: team.member_ids) }
acts_as_taggable_on :labels
scope :cared, ->(user) { where(assignee_id: user) }
scope :open_for, ->(user) { opened.assigned_to(user) }
......
class Label < ActiveRecord::Base
belongs_to :project
has_many :label_links, dependent: :destroy
has_many :issues, through: :label_links, source: :target, source_type: 'Issue'
validates :color, format: { with: /\A\#[0-9A-Fa-f]{6}+\Z/ }, allow_blank: true
validates :project, presence: true
# Dont allow '?', '&', and ',' for label titles
validates :title, presence: true, format: { with: /\A[^&\?,&]*\z/ }
def name
title
end
def open_issues_count
issues.opened.count
end
end
class LabelLink < ActiveRecord::Base
belongs_to :target, polymorphic: true
belongs_to :label
validates :target, presence: true
validates :label, presence: true
end
......@@ -47,9 +47,6 @@ class MergeRequest < ActiveRecord::Base
attr_accessor :can_be_created, :compare_failed,
:compare_commits, :compare_diffs
ActsAsTaggableOn.strict_case_match = true
acts_as_taggable_on :labels
state_machine :state, initial: :opened do
event :close do
transition [:reopened, :opened] => :closed
......
......@@ -41,8 +41,7 @@ class Project < ActiveRecord::Base
default_value_for :snippets_enabled, gitlab_config_features.snippets
ActsAsTaggableOn.strict_case_match = true
acts_as_taggable_on :labels, :issues_default_labels
acts_as_taggable_on :tags
attr_accessor :new_default_branch
......@@ -71,6 +70,7 @@ class Project < ActiveRecord::Base
# Merge requests from source project should be kept when source project was removed
has_many :fork_merge_requests, foreign_key: "source_project_id", class_name: MergeRequest
has_many :issues, -> { order "state DESC, created_at DESC" }, dependent: :destroy
has_many :labels, dependent: :destroy
has_many :services, dependent: :destroy
has_many :events, dependent: :destroy
has_many :milestones, dependent: :destroy
......@@ -282,13 +282,6 @@ def project_id
self.id
end
# Tags are shared by issues and merge requests
def issues_labels
@issues_labels ||= (issues_default_labels +
merge_requests.tags_on(:labels) +
issues.tags_on(:labels)).uniq.sort_by(&:name)
end
def issue_exists?(issue_id)
if used_default_issues_tracker?
self.issues.where(iid: issue_id).first.present?
......
......@@ -44,12 +44,12 @@
- if @labels.present?
- if @tags.present?
%fieldset
%legend Labels
%legend Tags
%ul.nav.nav-pills.nav-stacked.nav-small
- @labels.each do |label|
%li{ class: (label.name == params[:label]) ? 'active' : 'light' }
= link_to projects_dashboard_filter_path(scope: params[:scope], label: label.name) do
- @tags.each do |tag|
%li{ class: (tag.name == params[:tag]) ? 'active' : 'light' }
= link_to projects_dashboard_filter_path(scope: params[:scope], tag: tag.name) do
%i.icon-tag
= label.name
= tag.name
......@@ -54,10 +54,10 @@
%span.label
%i.icon-archive
Archived
- project.labels.each do |label|
- project.tags.each do |tag|
%span.label.label-info
%i.icon-tag
= label.name
= tag.name
- if project.description.present?
%p= truncate project.description, length: 100
.last-activity
......
......@@ -33,12 +33,12 @@
%fieldset.features
%legend
Labels:
Tags:
.form-group
= f.label :label_list, "Labels", class: 'control-label'
= f.label :tag_list, "Tags", class: 'control-label'
.col-sm-10
= f.text_field :label_list, maxlength: 2000, class: "form-control"
%p.hint Separate labels with commas.
= f.text_field :tag_list, maxlength: 2000, class: "form-control"
%p.hint Separate tags with commas.
%fieldset.features
%legend
......
......@@ -44,14 +44,11 @@
.col-sm-10= f.select(:milestone_id, milestone_options(@issue), { include_blank: "Select milestone" }, {class: 'select2'})
.form-group
= f.label :label_list, class: 'control-label' do
= f.label :label_ids, class: 'control-label' do
%i.icon-tag
Labels
.col-sm-10
= f.text_field :label_list, maxlength: 2000, class: "form-control"
%p.hint Separate labels with commas.
= f.collection_select :label_ids, @project.labels.all, :id, :name, { selected: @issue.label_ids }, multiple: true, class: 'select2'
.form-actions
- if @issue.new_record?
......@@ -63,35 +60,6 @@
= link_to "Cancel", cancel_path, class: 'btn btn-cancel'
:javascript
$("#issue_label_list")
.bind( "keydown", function( event ) {
if ( event.keyCode === $.ui.keyCode.TAB &&
$( this ).data( "autocomplete" ).menu.active ) {
event.preventDefault();
}
})
.bind("click", function(event) {
$(this).autocomplete("search", "");
})
.autocomplete({
minLength: 0,
source: function( request, response ) {
response( $.ui.autocomplete.filter(
#{raw labels_autocomplete_source}, extractLast( request.term ) ) );
},
focus: function() {
return false;
},
select: function(event, ui) {
var terms = split( this.value );
terms.pop();
terms.push( ui.item.value );
terms.push( "" );
this.value = terms.join( ", " );
return false;
}
});
$('.assign-to-me-link').on('click', function(e){
$('#issue_assignee_id').val("#{current_user.id}").trigger("change");
e.preventDefault();
......
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