Merge branch 'Undev-link-to-issue-tracker'
This commit is contained in:
commit
afed5cb614
20 changed files with 299 additions and 14 deletions
4
Gemfile
4
Gemfile
|
@ -46,6 +46,9 @@ gem "grape-entity", "~> 0.2.0"
|
|||
# based on human-friendly examples
|
||||
gem "stamp"
|
||||
|
||||
# Enumeration fields
|
||||
gem 'enumerize'
|
||||
|
||||
# Pagination
|
||||
gem "kaminari", "~> 0.14.1"
|
||||
|
||||
|
@ -113,6 +116,7 @@ group :assets do
|
|||
gem 'bootstrap-sass', "2.2.1.1"
|
||||
gem "font-awesome-sass-rails", "~> 3.0.0"
|
||||
gem "gemoji", "~> 1.2.1", require: 'emoji/railtie'
|
||||
gem "gon"
|
||||
end
|
||||
|
||||
group :development do
|
||||
|
|
|
@ -146,6 +146,8 @@ GEM
|
|||
email_spec (1.4.0)
|
||||
launchy (~> 2.1)
|
||||
mail (~> 2.2)
|
||||
enumerize (0.5.1)
|
||||
activesupport (>= 3.2)
|
||||
erubis (2.7.0)
|
||||
escape_utils (0.2.4)
|
||||
eventmachine (1.0.0)
|
||||
|
@ -184,10 +186,13 @@ GEM
|
|||
pyu-ruby-sasl (~> 0.0.3.1)
|
||||
rubyntlm (~> 0.1.1)
|
||||
gitlab_yaml_db (1.0.0)
|
||||
gon (4.0.2)
|
||||
grape (0.3.1)
|
||||
actionpack (>= 2.3.0)
|
||||
activesupport
|
||||
grape-entity (~> 0.2.0)
|
||||
hashie (~> 1.2)
|
||||
json
|
||||
multi_json (>= 1.3.2)
|
||||
multi_xml
|
||||
rack
|
||||
|
@ -473,6 +478,7 @@ DEPENDENCIES
|
|||
devise (~> 2.1.0)
|
||||
draper (~> 0.18.0)
|
||||
email_spec
|
||||
enumerize
|
||||
factory_girl_rails
|
||||
ffaker
|
||||
font-awesome-sass-rails (~> 3.0.0)
|
||||
|
@ -484,6 +490,7 @@ DEPENDENCIES
|
|||
gitlab_meta (= 5.0)
|
||||
gitlab_omniauth-ldap (= 1.0.2)
|
||||
gitlab_yaml_db (= 1.0.0)
|
||||
gon
|
||||
grack!
|
||||
grape (~> 0.3.1)
|
||||
grape-entity (~> 0.2.0)
|
||||
|
|
|
@ -18,3 +18,18 @@ $ ->
|
|||
# Ref switcher
|
||||
$('.project-refs-select').on 'change', ->
|
||||
$(@).parents('form').submit()
|
||||
|
||||
$('#project_issues_enabled').change ->
|
||||
if ($(this).is(':checked') == true)
|
||||
$('#project_issues_tracker').removeAttr('disabled')
|
||||
else
|
||||
$('#project_issues_tracker').attr('disabled', 'disabled')
|
||||
|
||||
$('#project_issues_tracker').change()
|
||||
|
||||
$('#project_issues_tracker').change ->
|
||||
if ($(this).val() == gon.default_issues_tracker || $(this).is(':disabled'))
|
||||
$('#project_issues_tracker_id').attr('disabled', 'disabled')
|
||||
else
|
||||
$('#project_issues_tracker_id').removeAttr('disabled')
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ class ApplicationController < ActionController::Base
|
|||
before_filter :add_abilities
|
||||
before_filter :dev_tools if Rails.env == 'development'
|
||||
before_filter :default_headers
|
||||
before_filter :add_gon_variables
|
||||
|
||||
protect_from_forgery
|
||||
|
||||
|
@ -148,4 +149,8 @@ class ApplicationController < ActionController::Base
|
|||
headers['X-Frame-Options'] = 'DENY'
|
||||
headers['X-XSS-Protection'] = '1; mode=block'
|
||||
end
|
||||
|
||||
def add_gon_variables
|
||||
gon.default_issues_tracker = Project.issues_tracker.default_value
|
||||
end
|
||||
end
|
||||
|
|
|
@ -40,4 +40,39 @@ module IssuesHelper
|
|||
def issues_active_milestones
|
||||
@project.milestones.active.order("id desc").all
|
||||
end
|
||||
|
||||
def url_for_project_issues
|
||||
return "" if @project.nil?
|
||||
|
||||
if @project.used_default_issues_tracker?
|
||||
project_issues_filter_path(@project)
|
||||
else
|
||||
url = Settings[:issues_tracker][@project.issues_tracker]["project_url"]
|
||||
url.gsub(':project_id', @project.id.to_s)
|
||||
.gsub(':issues_tracker_id', @project.issues_tracker_id.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
def url_for_issue(issue_id)
|
||||
return "" if @project.nil?
|
||||
|
||||
if @project.used_default_issues_tracker?
|
||||
url = project_issue_url project_id: @project, id: issue_id
|
||||
else
|
||||
url = Settings[:issues_tracker][@project.issues_tracker]["issues_url"]
|
||||
url.gsub(':id', issue_id.to_s)
|
||||
.gsub(':project_id', @project.id.to_s)
|
||||
.gsub(':issues_tracker_id', @project.issues_tracker_id.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
def title_for_issue(issue_id)
|
||||
return "" if @project.nil?
|
||||
|
||||
if @project.used_default_issues_tracker? && issue = @project.issues.where(id: issue_id).first
|
||||
issue.title
|
||||
else
|
||||
""
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
# creator_id :integer
|
||||
# default_branch :string(255)
|
||||
# issues_enabled :boolean default(TRUE), not null
|
||||
# issues_tracker :string not null
|
||||
# wall_enabled :boolean default(TRUE), not null
|
||||
# merge_requests_enabled :boolean default(TRUE), not null
|
||||
# wiki_enabled :boolean default(TRUE), not null
|
||||
|
@ -22,11 +23,12 @@ require "grit"
|
|||
|
||||
class Project < ActiveRecord::Base
|
||||
include Gitolited
|
||||
extend Enumerize
|
||||
|
||||
class TransferError < StandardError; end
|
||||
|
||||
attr_accessible :name, :path, :description, :default_branch,
|
||||
:issues_enabled, :wall_enabled, :merge_requests_enabled,
|
||||
attr_accessible :name, :path, :description, :default_branch, :issues_tracker,
|
||||
:issues_enabled, :wall_enabled, :merge_requests_enabled, :issues_tracker_id,
|
||||
:wiki_enabled, :public, :import_url, as: [:default, :admin]
|
||||
|
||||
attr_accessible :namespace_id, :creator_id, as: :admin
|
||||
|
@ -72,6 +74,7 @@ class Project < ActiveRecord::Base
|
|||
message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
|
||||
validates :issues_enabled, :wall_enabled, :merge_requests_enabled,
|
||||
:wiki_enabled, inclusion: { in: [true, false] }
|
||||
validates :issues_tracker_id, length: { within: 0..255 }
|
||||
|
||||
validates_uniqueness_of :name, scope: :namespace_id
|
||||
validates_uniqueness_of :path, scope: :namespace_id
|
||||
|
@ -93,6 +96,8 @@ class Project < ActiveRecord::Base
|
|||
scope :joined, ->(user) { where("namespace_id != ?", user.namespace_id) }
|
||||
scope :public_only, -> { where(public: true) }
|
||||
|
||||
enumerize :issues_tracker, :in => (Gitlab.config.issues_tracker.keys).append(:gitlab), :default => :gitlab
|
||||
|
||||
class << self
|
||||
def abandoned
|
||||
project_ids = Event.select('max(created_at) as latest_date, project_id').
|
||||
|
@ -201,6 +206,22 @@ class Project < ActiveRecord::Base
|
|||
issues.tag_counts_on(:labels)
|
||||
end
|
||||
|
||||
def issue_exists?(issue_id)
|
||||
if used_default_issues_tracker?
|
||||
self.issues.where(id: issue_id).first.present?
|
||||
else
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
def used_default_issues_tracker?
|
||||
self.issues_tracker == Project.issues_tracker.default_value
|
||||
end
|
||||
|
||||
def can_have_issues_tracker_id?
|
||||
self.issues_enabled && !self.used_default_issues_tracker?
|
||||
end
|
||||
|
||||
def services
|
||||
[gitlab_ci_service].compact
|
||||
end
|
||||
|
|
|
@ -31,6 +31,15 @@
|
|||
= f.label :issues_enabled, "Issues"
|
||||
.input= f.check_box :issues_enabled
|
||||
|
||||
- if Project.issues_tracker.values.count > 1
|
||||
.clearfix
|
||||
= f.label :issues_tracker, "Issues tracker", class: 'control-label'
|
||||
.input= f.select(:issues_tracker, Project.issues_tracker.values, {}, { disabled: !@project.issues_enabled })
|
||||
|
||||
.clearfix
|
||||
= f.label :issues_tracker_id, "Project name or id in issues tracker", class: 'control-label'
|
||||
.input= f.text_field :issues_tracker_id, class: "xxlarge", disabled: !@project.can_have_issues_tracker_id?
|
||||
|
||||
.clearfix
|
||||
= f.label :merge_requests_enabled, "Merge Requests"
|
||||
.input= f.check_box :merge_requests_enabled
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
= stylesheet_link_tag "application"
|
||||
= javascript_include_tag "application"
|
||||
= csrf_meta_tags
|
||||
= include_gon
|
||||
|
||||
-# Atom feed
|
||||
- if current_user
|
||||
|
|
|
@ -24,8 +24,9 @@
|
|||
|
||||
- if @project.issues_enabled
|
||||
= nav_link(controller: %w(issues milestones labels)) do
|
||||
= link_to project_issues_filter_path(@project) do
|
||||
= link_to url_for_project_issues do
|
||||
Issues
|
||||
- if @project.used_default_issues_tracker?
|
||||
%span.count.issue_counter= @project.issues.opened.count
|
||||
|
||||
- if @project.repo_exists? && @project.merge_requests_enabled
|
||||
|
|
|
@ -24,6 +24,15 @@
|
|||
= f.check_box :issues_enabled
|
||||
%span.descr Lightweight issue tracking system for this project
|
||||
|
||||
- if Project.issues_tracker.values.count > 1
|
||||
.control-group
|
||||
= f.label :issues_tracker, "Issues tracker", class: 'control-label'
|
||||
.input= f.select(:issues_tracker, Project.issues_tracker.values, {}, { disabled: !@project.issues_enabled })
|
||||
|
||||
.clearfix
|
||||
= f.label :issues_tracker_id, "Project name or id in issues tracker", class: 'control-label'
|
||||
.input= f.text_field :issues_tracker_id, class: "xxlarge", disabled: !@project.can_have_issues_tracker_id?
|
||||
|
||||
.control-group
|
||||
= f.label :merge_requests_enabled, "Merge Requests", class: 'control-label'
|
||||
.controls
|
||||
|
|
|
@ -37,6 +37,22 @@ production: &base
|
|||
# signup_enabled: true # default: false - Account passwords are not sent via the email if signup is enabled.
|
||||
# username_changing_enabled: false # default: true - User can change her username/namespace
|
||||
|
||||
|
||||
## External issues trackers
|
||||
issues_tracker:
|
||||
redmine:
|
||||
## If not nil, link 'Issues' on project page will be replaced tp this
|
||||
## Use placeholders:
|
||||
## :project_id - Gitlab project identifier
|
||||
## :issues_tracker_id - Project Name or Id in external issue tracker
|
||||
project_url: "http://redmine.sample/projects/:issues_tracker_id"
|
||||
## If not nil, links from /#\d/ entities from commit messages will replaced to this
|
||||
## Use placeholders:
|
||||
## :project_id - Gitlab project identifier
|
||||
## :issues_tracker_id - Project Name or Id in external issue tracker
|
||||
## :id - Issue id (from commit messages)
|
||||
issues_url: "http://redmine.sample/issues/:id"
|
||||
|
||||
## Gravatar
|
||||
gravatar:
|
||||
enabled: true # Use user avatar images from Gravatar.com (default: true)
|
||||
|
@ -133,6 +149,10 @@ development:
|
|||
|
||||
test:
|
||||
<<: *base
|
||||
issues_tracker:
|
||||
redmine:
|
||||
project_url: "http://redmine/projects/:issues_tracker_id"
|
||||
issues_url: "http://redmine/:project_id/:issues_tracker_id/:id"
|
||||
|
||||
staging:
|
||||
<<: *base
|
||||
|
|
|
@ -42,6 +42,8 @@ Settings['omniauth'] ||= Settingslogic.new({})
|
|||
Settings.omniauth['enabled'] = false if Settings.omniauth['enabled'].nil?
|
||||
Settings.omniauth['providers'] ||= []
|
||||
|
||||
Settings['issues_tracker'] ||= {}
|
||||
|
||||
#
|
||||
# GitLab
|
||||
#
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
class AddIssuesTrackerToProject < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :projects, :issues_tracker, :string, default: :gitlab, null: false
|
||||
end
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
class AddIssuesTrackerIdToProject < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :projects, :issues_tracker_id, :string
|
||||
end
|
||||
end
|
|
@ -152,6 +152,8 @@ ActiveRecord::Schema.define(:version => 20130220133245) do
|
|||
t.boolean "wiki_enabled", :default => true, :null => false
|
||||
t.integer "namespace_id"
|
||||
t.boolean "public", :default => false, :null => false
|
||||
t.string "issues_tracker", :default => "gitlab", :null => false
|
||||
t.string "issues_tracker_id"
|
||||
end
|
||||
|
||||
add_index "projects", ["creator_id"], :name => "index_projects_on_owner_id"
|
||||
|
|
|
@ -25,6 +25,8 @@ module Gitlab
|
|||
# >> gfm(":trollface:")
|
||||
# => "<img alt=\":trollface:\" class=\"emoji\" src=\"/images/trollface.png" title=\":trollface:\" />
|
||||
module Markdown
|
||||
include IssuesHelper
|
||||
|
||||
attr_reader :html_options
|
||||
|
||||
# Public: Parse the provided text with GitLab-Flavored Markdown
|
||||
|
@ -163,8 +165,11 @@ module Gitlab
|
|||
end
|
||||
|
||||
def reference_issue(identifier)
|
||||
if issue = @project.issues.where(id: identifier).first
|
||||
link_to("##{identifier}", project_issue_url(@project, issue), html_options.merge(title: "Issue: #{issue.title}", class: "gfm gfm-issue #{html_options[:class]}"))
|
||||
if @project.issue_exists? identifier
|
||||
url = url_for_issue(identifier)
|
||||
title = title_for_issue(identifier)
|
||||
|
||||
link_to("##{identifier}", url, html_options.merge(title: "Issue: #{title}", class: "gfm gfm-issue #{html_options[:class]}"))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -29,6 +29,11 @@ FactoryGirl.define do
|
|||
creator
|
||||
end
|
||||
|
||||
factory :redmine_project, parent: :project do
|
||||
issues_tracker { "redmine" }
|
||||
issues_tracker_id { "project_name_in_redmine" }
|
||||
end
|
||||
|
||||
factory :group do
|
||||
sequence(:name) { |n| "group#{n}" }
|
||||
path { name.downcase.gsub(/\s/, '_') }
|
||||
|
|
|
@ -2,6 +2,7 @@ require "spec_helper"
|
|||
|
||||
describe GitlabMarkdownHelper do
|
||||
include ApplicationHelper
|
||||
include IssuesHelper
|
||||
|
||||
let!(:project) { create(:project) }
|
||||
|
||||
|
|
79
spec/helpers/issues_helper_spec.rb
Normal file
79
spec/helpers/issues_helper_spec.rb
Normal file
|
@ -0,0 +1,79 @@
|
|||
require "spec_helper"
|
||||
|
||||
describe IssuesHelper do
|
||||
let(:project) { create :project }
|
||||
let(:issue) { create :issue, project: project }
|
||||
let(:ext_project) { create :redmine_project }
|
||||
|
||||
describe :title_for_issue do
|
||||
it "should return issue title if used internal tracker" do
|
||||
@project = project
|
||||
title_for_issue(issue.id).should eq issue.title
|
||||
end
|
||||
|
||||
it "should always return empty string if used external tracker" do
|
||||
@project = ext_project
|
||||
title_for_issue(rand(100)).should eq ""
|
||||
end
|
||||
|
||||
it "should always return empty string if project nil" do
|
||||
@project = nil
|
||||
|
||||
title_for_issue(rand(100)).should eq ""
|
||||
end
|
||||
end
|
||||
|
||||
describe :url_for_project_issues do
|
||||
let(:project_url) { Gitlab.config.issues_tracker.redmine.project_url}
|
||||
let(:ext_expected) do
|
||||
project_url.gsub(':project_id', ext_project.id.to_s)
|
||||
.gsub(':issues_tracker_id', ext_project.issues_tracker_id.to_s)
|
||||
end
|
||||
let(:int_expected) { polymorphic_path([project]) }
|
||||
|
||||
it "should return internal path if used internal tracker" do
|
||||
@project = project
|
||||
url_for_project_issues.should match(int_expected)
|
||||
end
|
||||
|
||||
it "should return path to external tracker" do
|
||||
@project = ext_project
|
||||
|
||||
url_for_project_issues.should match(ext_expected)
|
||||
end
|
||||
|
||||
it "should return empty string if project nil" do
|
||||
@project = nil
|
||||
|
||||
url_for_project_issues.should eq ""
|
||||
end
|
||||
end
|
||||
|
||||
describe :url_for_issue do
|
||||
let(:issue_id) { 3 }
|
||||
let(:issues_url) { Gitlab.config.issues_tracker.redmine.issues_url}
|
||||
let(:ext_expected) do
|
||||
issues_url.gsub(':id', issue_id.to_s)
|
||||
.gsub(':project_id', ext_project.id.to_s)
|
||||
.gsub(':issues_tracker_id', ext_project.issues_tracker_id.to_s)
|
||||
end
|
||||
let(:int_expected) { polymorphic_path([project, issue]) }
|
||||
|
||||
it "should return internal path if used internal tracker" do
|
||||
@project = project
|
||||
url_for_issue(issue.id).should match(int_expected)
|
||||
end
|
||||
|
||||
it "should return path to external tracker" do
|
||||
@project = ext_project
|
||||
|
||||
url_for_issue(issue_id).should match(ext_expected)
|
||||
end
|
||||
|
||||
it "should return empty string if project nil" do
|
||||
@project = nil
|
||||
|
||||
url_for_issue(issue.id).should eq ""
|
||||
end
|
||||
end
|
||||
end
|
|
@ -60,6 +60,7 @@ describe Project do
|
|||
it { should ensure_inclusion_of(:wall_enabled).in_array([true, false]) }
|
||||
it { should ensure_inclusion_of(:merge_requests_enabled).in_array([true, false]) }
|
||||
it { should ensure_inclusion_of(:wiki_enabled).in_array([true, false]) }
|
||||
it { should ensure_length_of(:issues_tracker_id).is_within(0..255) }
|
||||
|
||||
it "should not allow new projects beyond user limits" do
|
||||
project.stub(:creator).and_return(double(can_create_project?: false, projects_limit: 1))
|
||||
|
@ -190,4 +191,57 @@ describe Project do
|
|||
Project.new(path: "empty").repository.should be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe :issue_exists? do
|
||||
let(:project) { create(:project) }
|
||||
let(:existed_issue) { create(:issue, project: project) }
|
||||
let(:not_existed_issue) { create(:issue) }
|
||||
let(:ext_project) { create(:redmine_project) }
|
||||
|
||||
it "should be true or if used internal tracker and issue exists" do
|
||||
project.issue_exists?(existed_issue.id).should be_true
|
||||
end
|
||||
|
||||
it "should be false or if used internal tracker and issue not exists" do
|
||||
project.issue_exists?(not_existed_issue.id).should be_false
|
||||
end
|
||||
|
||||
it "should always be true if used other tracker" do
|
||||
ext_project.issue_exists?(rand(100)).should be_true
|
||||
end
|
||||
end
|
||||
|
||||
describe :used_default_issues_tracker? do
|
||||
let(:project) { create(:project) }
|
||||
let(:ext_project) { create(:redmine_project) }
|
||||
|
||||
it "should be true if used internal tracker" do
|
||||
project.used_default_issues_tracker?.should be_true
|
||||
end
|
||||
|
||||
it "should be false if used other tracker" do
|
||||
ext_project.used_default_issues_tracker?.should be_false
|
||||
end
|
||||
end
|
||||
|
||||
describe :can_have_issues_tracker_id? do
|
||||
let(:project) { create(:project) }
|
||||
let(:ext_project) { create(:redmine_project) }
|
||||
|
||||
it "should be true for projects with external issues tracker if issues enabled" do
|
||||
ext_project.can_have_issues_tracker_id?.should be_true
|
||||
end
|
||||
|
||||
it "should be false for projects with internal issue tracker if issues enabled" do
|
||||
project.can_have_issues_tracker_id?.should be_false
|
||||
end
|
||||
|
||||
it "should be always false if issues disbled" do
|
||||
project.issues_enabled = false
|
||||
ext_project.issues_enabled = false
|
||||
|
||||
project.can_have_issues_tracker_id?.should be_false
|
||||
ext_project.can_have_issues_tracker_id?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue