Commit e1f77b9be071fac9f57e85b2f3853d2f333aeaab
Exists in
master
and in
4 other branches
Merge branch 'link-to-issue-tracker' of https://github.com/Undev/gitlabhq into U…
…ndev-link-to-issue-tracker Conflicts: Gemfile.lock
Showing
20 changed files
with
299 additions
and
14 deletions
Show diff stats
Gemfile
| @@ -46,6 +46,9 @@ gem "grape-entity", "~> 0.2.0" | @@ -46,6 +46,9 @@ gem "grape-entity", "~> 0.2.0" | ||
| 46 | # based on human-friendly examples | 46 | # based on human-friendly examples |
| 47 | gem "stamp" | 47 | gem "stamp" |
| 48 | 48 | ||
| 49 | +# Enumeration fields | ||
| 50 | +gem 'enumerize' | ||
| 51 | + | ||
| 49 | # Pagination | 52 | # Pagination |
| 50 | gem "kaminari", "~> 0.14.1" | 53 | gem "kaminari", "~> 0.14.1" |
| 51 | 54 | ||
| @@ -113,6 +116,7 @@ group :assets do | @@ -113,6 +116,7 @@ group :assets do | ||
| 113 | gem 'bootstrap-sass', "2.2.1.1" | 116 | gem 'bootstrap-sass', "2.2.1.1" |
| 114 | gem "font-awesome-sass-rails", "~> 3.0.0" | 117 | gem "font-awesome-sass-rails", "~> 3.0.0" |
| 115 | gem "gemoji", "~> 1.2.1", require: 'emoji/railtie' | 118 | gem "gemoji", "~> 1.2.1", require: 'emoji/railtie' |
| 119 | + gem "gon" | ||
| 116 | end | 120 | end |
| 117 | 121 | ||
| 118 | group :development do | 122 | group :development do |
Gemfile.lock
| @@ -146,6 +146,8 @@ GEM | @@ -146,6 +146,8 @@ GEM | ||
| 146 | email_spec (1.4.0) | 146 | email_spec (1.4.0) |
| 147 | launchy (~> 2.1) | 147 | launchy (~> 2.1) |
| 148 | mail (~> 2.2) | 148 | mail (~> 2.2) |
| 149 | + enumerize (0.5.1) | ||
| 150 | + activesupport (>= 3.2) | ||
| 149 | erubis (2.7.0) | 151 | erubis (2.7.0) |
| 150 | escape_utils (0.2.4) | 152 | escape_utils (0.2.4) |
| 151 | eventmachine (1.0.0) | 153 | eventmachine (1.0.0) |
| @@ -184,10 +186,13 @@ GEM | @@ -184,10 +186,13 @@ GEM | ||
| 184 | pyu-ruby-sasl (~> 0.0.3.1) | 186 | pyu-ruby-sasl (~> 0.0.3.1) |
| 185 | rubyntlm (~> 0.1.1) | 187 | rubyntlm (~> 0.1.1) |
| 186 | gitlab_yaml_db (1.0.0) | 188 | gitlab_yaml_db (1.0.0) |
| 189 | + gon (4.0.2) | ||
| 187 | grape (0.3.1) | 190 | grape (0.3.1) |
| 191 | + actionpack (>= 2.3.0) | ||
| 188 | activesupport | 192 | activesupport |
| 189 | grape-entity (~> 0.2.0) | 193 | grape-entity (~> 0.2.0) |
| 190 | hashie (~> 1.2) | 194 | hashie (~> 1.2) |
| 195 | + json | ||
| 191 | multi_json (>= 1.3.2) | 196 | multi_json (>= 1.3.2) |
| 192 | multi_xml | 197 | multi_xml |
| 193 | rack | 198 | rack |
| @@ -473,6 +478,7 @@ DEPENDENCIES | @@ -473,6 +478,7 @@ DEPENDENCIES | ||
| 473 | devise (~> 2.1.0) | 478 | devise (~> 2.1.0) |
| 474 | draper (~> 0.18.0) | 479 | draper (~> 0.18.0) |
| 475 | email_spec | 480 | email_spec |
| 481 | + enumerize | ||
| 476 | factory_girl_rails | 482 | factory_girl_rails |
| 477 | ffaker | 483 | ffaker |
| 478 | font-awesome-sass-rails (~> 3.0.0) | 484 | font-awesome-sass-rails (~> 3.0.0) |
| @@ -484,6 +490,7 @@ DEPENDENCIES | @@ -484,6 +490,7 @@ DEPENDENCIES | ||
| 484 | gitlab_meta (= 5.0) | 490 | gitlab_meta (= 5.0) |
| 485 | gitlab_omniauth-ldap (= 1.0.2) | 491 | gitlab_omniauth-ldap (= 1.0.2) |
| 486 | gitlab_yaml_db (= 1.0.0) | 492 | gitlab_yaml_db (= 1.0.0) |
| 493 | + gon | ||
| 487 | grack! | 494 | grack! |
| 488 | grape (~> 0.3.1) | 495 | grape (~> 0.3.1) |
| 489 | grape-entity (~> 0.2.0) | 496 | grape-entity (~> 0.2.0) |
app/assets/javascripts/projects.js.coffee
| @@ -18,3 +18,18 @@ $ -> | @@ -18,3 +18,18 @@ $ -> | ||
| 18 | # Ref switcher | 18 | # Ref switcher |
| 19 | $('.project-refs-select').on 'change', -> | 19 | $('.project-refs-select').on 'change', -> |
| 20 | $(@).parents('form').submit() | 20 | $(@).parents('form').submit() |
| 21 | + | ||
| 22 | + $('#project_issues_enabled').change -> | ||
| 23 | + if ($(this).is(':checked') == true) | ||
| 24 | + $('#project_issues_tracker').removeAttr('disabled') | ||
| 25 | + else | ||
| 26 | + $('#project_issues_tracker').attr('disabled', 'disabled') | ||
| 27 | + | ||
| 28 | + $('#project_issues_tracker').change() | ||
| 29 | + | ||
| 30 | + $('#project_issues_tracker').change -> | ||
| 31 | + if ($(this).val() == gon.default_issues_tracker || $(this).is(':disabled')) | ||
| 32 | + $('#project_issues_tracker_id').attr('disabled', 'disabled') | ||
| 33 | + else | ||
| 34 | + $('#project_issues_tracker_id').removeAttr('disabled') | ||
| 35 | + |
app/controllers/application_controller.rb
| @@ -5,6 +5,7 @@ class ApplicationController < ActionController::Base | @@ -5,6 +5,7 @@ class ApplicationController < ActionController::Base | ||
| 5 | before_filter :add_abilities | 5 | before_filter :add_abilities |
| 6 | before_filter :dev_tools if Rails.env == 'development' | 6 | before_filter :dev_tools if Rails.env == 'development' |
| 7 | before_filter :default_headers | 7 | before_filter :default_headers |
| 8 | + before_filter :add_gon_variables | ||
| 8 | 9 | ||
| 9 | protect_from_forgery | 10 | protect_from_forgery |
| 10 | 11 | ||
| @@ -148,4 +149,8 @@ class ApplicationController < ActionController::Base | @@ -148,4 +149,8 @@ class ApplicationController < ActionController::Base | ||
| 148 | headers['X-Frame-Options'] = 'DENY' | 149 | headers['X-Frame-Options'] = 'DENY' |
| 149 | headers['X-XSS-Protection'] = '1; mode=block' | 150 | headers['X-XSS-Protection'] = '1; mode=block' |
| 150 | end | 151 | end |
| 152 | + | ||
| 153 | + def add_gon_variables | ||
| 154 | + gon.default_issues_tracker = Project.issues_tracker.default_value | ||
| 155 | + end | ||
| 151 | end | 156 | end |
app/helpers/issues_helper.rb
| @@ -40,4 +40,39 @@ module IssuesHelper | @@ -40,4 +40,39 @@ module IssuesHelper | ||
| 40 | def issues_active_milestones | 40 | def issues_active_milestones |
| 41 | @project.milestones.active.order("id desc").all | 41 | @project.milestones.active.order("id desc").all |
| 42 | end | 42 | end |
| 43 | + | ||
| 44 | + def url_for_project_issues | ||
| 45 | + return "" if @project.nil? | ||
| 46 | + | ||
| 47 | + if @project.used_default_issues_tracker? | ||
| 48 | + project_issues_filter_path(@project) | ||
| 49 | + else | ||
| 50 | + url = Settings[:issues_tracker][@project.issues_tracker]["project_url"] | ||
| 51 | + url.gsub(':project_id', @project.id.to_s) | ||
| 52 | + .gsub(':issues_tracker_id', @project.issues_tracker_id.to_s) | ||
| 53 | + end | ||
| 54 | + end | ||
| 55 | + | ||
| 56 | + def url_for_issue(issue_id) | ||
| 57 | + return "" if @project.nil? | ||
| 58 | + | ||
| 59 | + if @project.used_default_issues_tracker? | ||
| 60 | + url = project_issue_url project_id: @project, id: issue_id | ||
| 61 | + else | ||
| 62 | + url = Settings[:issues_tracker][@project.issues_tracker]["issues_url"] | ||
| 63 | + url.gsub(':id', issue_id.to_s) | ||
| 64 | + .gsub(':project_id', @project.id.to_s) | ||
| 65 | + .gsub(':issues_tracker_id', @project.issues_tracker_id.to_s) | ||
| 66 | + end | ||
| 67 | + end | ||
| 68 | + | ||
| 69 | + def title_for_issue(issue_id) | ||
| 70 | + return "" if @project.nil? | ||
| 71 | + | ||
| 72 | + if @project.used_default_issues_tracker? && issue = @project.issues.where(id: issue_id).first | ||
| 73 | + issue.title | ||
| 74 | + else | ||
| 75 | + "" | ||
| 76 | + end | ||
| 77 | + end | ||
| 43 | end | 78 | end |
app/models/project.rb
| @@ -11,6 +11,7 @@ | @@ -11,6 +11,7 @@ | ||
| 11 | # creator_id :integer | 11 | # creator_id :integer |
| 12 | # default_branch :string(255) | 12 | # default_branch :string(255) |
| 13 | # issues_enabled :boolean default(TRUE), not null | 13 | # issues_enabled :boolean default(TRUE), not null |
| 14 | +# issues_tracker :string not null | ||
| 14 | # wall_enabled :boolean default(TRUE), not null | 15 | # wall_enabled :boolean default(TRUE), not null |
| 15 | # merge_requests_enabled :boolean default(TRUE), not null | 16 | # merge_requests_enabled :boolean default(TRUE), not null |
| 16 | # wiki_enabled :boolean default(TRUE), not null | 17 | # wiki_enabled :boolean default(TRUE), not null |
| @@ -22,11 +23,12 @@ require "grit" | @@ -22,11 +23,12 @@ require "grit" | ||
| 22 | 23 | ||
| 23 | class Project < ActiveRecord::Base | 24 | class Project < ActiveRecord::Base |
| 24 | include Gitolited | 25 | include Gitolited |
| 26 | + extend Enumerize | ||
| 25 | 27 | ||
| 26 | class TransferError < StandardError; end | 28 | class TransferError < StandardError; end |
| 27 | 29 | ||
| 28 | - attr_accessible :name, :path, :description, :default_branch, | ||
| 29 | - :issues_enabled, :wall_enabled, :merge_requests_enabled, | 30 | + attr_accessible :name, :path, :description, :default_branch, :issues_tracker, |
| 31 | + :issues_enabled, :wall_enabled, :merge_requests_enabled, :issues_tracker_id, | ||
| 30 | :wiki_enabled, :public, :import_url, as: [:default, :admin] | 32 | :wiki_enabled, :public, :import_url, as: [:default, :admin] |
| 31 | 33 | ||
| 32 | attr_accessible :namespace_id, :creator_id, as: :admin | 34 | attr_accessible :namespace_id, :creator_id, as: :admin |
| @@ -72,6 +74,7 @@ class Project < ActiveRecord::Base | @@ -72,6 +74,7 @@ class Project < ActiveRecord::Base | ||
| 72 | message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" } | 74 | message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" } |
| 73 | validates :issues_enabled, :wall_enabled, :merge_requests_enabled, | 75 | validates :issues_enabled, :wall_enabled, :merge_requests_enabled, |
| 74 | :wiki_enabled, inclusion: { in: [true, false] } | 76 | :wiki_enabled, inclusion: { in: [true, false] } |
| 77 | + validates :issues_tracker_id, length: { within: 0..255 } | ||
| 75 | 78 | ||
| 76 | validates_uniqueness_of :name, scope: :namespace_id | 79 | validates_uniqueness_of :name, scope: :namespace_id |
| 77 | validates_uniqueness_of :path, scope: :namespace_id | 80 | validates_uniqueness_of :path, scope: :namespace_id |
| @@ -93,6 +96,8 @@ class Project < ActiveRecord::Base | @@ -93,6 +96,8 @@ class Project < ActiveRecord::Base | ||
| 93 | scope :joined, ->(user) { where("namespace_id != ?", user.namespace_id) } | 96 | scope :joined, ->(user) { where("namespace_id != ?", user.namespace_id) } |
| 94 | scope :public_only, -> { where(public: true) } | 97 | scope :public_only, -> { where(public: true) } |
| 95 | 98 | ||
| 99 | + enumerize :issues_tracker, :in => (Gitlab.config.issues_tracker.keys).append(:gitlab), :default => :gitlab | ||
| 100 | + | ||
| 96 | class << self | 101 | class << self |
| 97 | def abandoned | 102 | def abandoned |
| 98 | project_ids = Event.select('max(created_at) as latest_date, project_id'). | 103 | project_ids = Event.select('max(created_at) as latest_date, project_id'). |
| @@ -201,6 +206,22 @@ class Project < ActiveRecord::Base | @@ -201,6 +206,22 @@ class Project < ActiveRecord::Base | ||
| 201 | issues.tag_counts_on(:labels) | 206 | issues.tag_counts_on(:labels) |
| 202 | end | 207 | end |
| 203 | 208 | ||
| 209 | + def issue_exists?(issue_id) | ||
| 210 | + if used_default_issues_tracker? | ||
| 211 | + self.issues.where(id: issue_id).first.present? | ||
| 212 | + else | ||
| 213 | + true | ||
| 214 | + end | ||
| 215 | + end | ||
| 216 | + | ||
| 217 | + def used_default_issues_tracker? | ||
| 218 | + self.issues_tracker == Project.issues_tracker.default_value | ||
| 219 | + end | ||
| 220 | + | ||
| 221 | + def can_have_issues_tracker_id? | ||
| 222 | + self.issues_enabled && !self.used_default_issues_tracker? | ||
| 223 | + end | ||
| 224 | + | ||
| 204 | def services | 225 | def services |
| 205 | [gitlab_ci_service].compact | 226 | [gitlab_ci_service].compact |
| 206 | end | 227 | end |
app/views/admin/projects/_form.html.haml
| @@ -31,6 +31,15 @@ | @@ -31,6 +31,15 @@ | ||
| 31 | = f.label :issues_enabled, "Issues" | 31 | = f.label :issues_enabled, "Issues" |
| 32 | .input= f.check_box :issues_enabled | 32 | .input= f.check_box :issues_enabled |
| 33 | 33 | ||
| 34 | + - if Project.issues_tracker.values.count > 1 | ||
| 35 | + .clearfix | ||
| 36 | + = f.label :issues_tracker, "Issues tracker", class: 'control-label' | ||
| 37 | + .input= f.select(:issues_tracker, Project.issues_tracker.values, {}, { disabled: !@project.issues_enabled }) | ||
| 38 | + | ||
| 39 | + .clearfix | ||
| 40 | + = f.label :issues_tracker_id, "Project name or id in issues tracker", class: 'control-label' | ||
| 41 | + .input= f.text_field :issues_tracker_id, class: "xxlarge", disabled: !@project.can_have_issues_tracker_id? | ||
| 42 | + | ||
| 34 | .clearfix | 43 | .clearfix |
| 35 | = f.label :merge_requests_enabled, "Merge Requests" | 44 | = f.label :merge_requests_enabled, "Merge Requests" |
| 36 | .input= f.check_box :merge_requests_enabled | 45 | .input= f.check_box :merge_requests_enabled |
app/views/layouts/_head.html.haml
| @@ -7,6 +7,7 @@ | @@ -7,6 +7,7 @@ | ||
| 7 | = stylesheet_link_tag "application" | 7 | = stylesheet_link_tag "application" |
| 8 | = javascript_include_tag "application" | 8 | = javascript_include_tag "application" |
| 9 | = csrf_meta_tags | 9 | = csrf_meta_tags |
| 10 | + = include_gon | ||
| 10 | 11 | ||
| 11 | -# Atom feed | 12 | -# Atom feed |
| 12 | - if current_user | 13 | - if current_user |
app/views/layouts/project_resource.html.haml
| @@ -22,11 +22,12 @@ | @@ -22,11 +22,12 @@ | ||
| 22 | = nav_link(controller: %w(graph)) do | 22 | = nav_link(controller: %w(graph)) do |
| 23 | = link_to "Network", project_graph_path(@project, @ref || @repository.root_ref) | 23 | = link_to "Network", project_graph_path(@project, @ref || @repository.root_ref) |
| 24 | 24 | ||
| 25 | - - if @project.issues_enabled | 25 | + - if @project.issues_enabled |
| 26 | = nav_link(controller: %w(issues milestones labels)) do | 26 | = nav_link(controller: %w(issues milestones labels)) do |
| 27 | - = link_to project_issues_filter_path(@project) do | 27 | + = link_to url_for_project_issues do |
| 28 | Issues | 28 | Issues |
| 29 | - %span.count.issue_counter= @project.issues.opened.count | 29 | + - if @project.used_default_issues_tracker? |
| 30 | + %span.count.issue_counter= @project.issues.opened.count | ||
| 30 | 31 | ||
| 31 | - if @project.repo_exists? && @project.merge_requests_enabled | 32 | - if @project.repo_exists? && @project.merge_requests_enabled |
| 32 | = nav_link(controller: :merge_requests) do | 33 | = nav_link(controller: :merge_requests) do |
app/views/projects/_form.html.haml
| @@ -24,6 +24,15 @@ | @@ -24,6 +24,15 @@ | ||
| 24 | = f.check_box :issues_enabled | 24 | = f.check_box :issues_enabled |
| 25 | %span.descr Lightweight issue tracking system for this project | 25 | %span.descr Lightweight issue tracking system for this project |
| 26 | 26 | ||
| 27 | + - if Project.issues_tracker.values.count > 1 | ||
| 28 | + .control-group | ||
| 29 | + = f.label :issues_tracker, "Issues tracker", class: 'control-label' | ||
| 30 | + .input= f.select(:issues_tracker, Project.issues_tracker.values, {}, { disabled: !@project.issues_enabled }) | ||
| 31 | + | ||
| 32 | + .clearfix | ||
| 33 | + = f.label :issues_tracker_id, "Project name or id in issues tracker", class: 'control-label' | ||
| 34 | + .input= f.text_field :issues_tracker_id, class: "xxlarge", disabled: !@project.can_have_issues_tracker_id? | ||
| 35 | + | ||
| 27 | .control-group | 36 | .control-group |
| 28 | = f.label :merge_requests_enabled, "Merge Requests", class: 'control-label' | 37 | = f.label :merge_requests_enabled, "Merge Requests", class: 'control-label' |
| 29 | .controls | 38 | .controls |
config/gitlab.yml.example
| @@ -37,6 +37,22 @@ production: &base | @@ -37,6 +37,22 @@ production: &base | ||
| 37 | # signup_enabled: true # default: false - Account passwords are not sent via the email if signup is enabled. | 37 | # signup_enabled: true # default: false - Account passwords are not sent via the email if signup is enabled. |
| 38 | # username_changing_enabled: false # default: true - User can change her username/namespace | 38 | # username_changing_enabled: false # default: true - User can change her username/namespace |
| 39 | 39 | ||
| 40 | + | ||
| 41 | + ## External issues trackers | ||
| 42 | + issues_tracker: | ||
| 43 | + redmine: | ||
| 44 | + ## If not nil, link 'Issues' on project page will be replaced tp this | ||
| 45 | + ## Use placeholders: | ||
| 46 | + ## :project_id - Gitlab project identifier | ||
| 47 | + ## :issues_tracker_id - Project Name or Id in external issue tracker | ||
| 48 | + project_url: "http://redmine.sample/projects/:issues_tracker_id" | ||
| 49 | + ## If not nil, links from /#\d/ entities from commit messages will replaced to this | ||
| 50 | + ## Use placeholders: | ||
| 51 | + ## :project_id - Gitlab project identifier | ||
| 52 | + ## :issues_tracker_id - Project Name or Id in external issue tracker | ||
| 53 | + ## :id - Issue id (from commit messages) | ||
| 54 | + issues_url: "http://redmine.sample/issues/:id" | ||
| 55 | + | ||
| 40 | ## Gravatar | 56 | ## Gravatar |
| 41 | gravatar: | 57 | gravatar: |
| 42 | enabled: true # Use user avatar images from Gravatar.com (default: true) | 58 | enabled: true # Use user avatar images from Gravatar.com (default: true) |
| @@ -133,6 +149,10 @@ development: | @@ -133,6 +149,10 @@ development: | ||
| 133 | 149 | ||
| 134 | test: | 150 | test: |
| 135 | <<: *base | 151 | <<: *base |
| 152 | + issues_tracker: | ||
| 153 | + redmine: | ||
| 154 | + project_url: "http://redmine/projects/:issues_tracker_id" | ||
| 155 | + issues_url: "http://redmine/:project_id/:issues_tracker_id/:id" | ||
| 136 | 156 | ||
| 137 | staging: | 157 | staging: |
| 138 | <<: *base | 158 | <<: *base |
config/initializers/1_settings.rb
| @@ -42,6 +42,8 @@ Settings['omniauth'] ||= Settingslogic.new({}) | @@ -42,6 +42,8 @@ Settings['omniauth'] ||= Settingslogic.new({}) | ||
| 42 | Settings.omniauth['enabled'] = false if Settings.omniauth['enabled'].nil? | 42 | Settings.omniauth['enabled'] = false if Settings.omniauth['enabled'].nil? |
| 43 | Settings.omniauth['providers'] ||= [] | 43 | Settings.omniauth['providers'] ||= [] |
| 44 | 44 | ||
| 45 | +Settings['issues_tracker'] ||= {} | ||
| 46 | + | ||
| 45 | # | 47 | # |
| 46 | # GitLab | 48 | # GitLab |
| 47 | # | 49 | # |
db/migrate/20130123114545_add_issues_tracker_to_project.rb
0 → 100644
db/migrate/20130211085435_add_issues_tracker_id_to_project.rb
0 → 100644
db/schema.rb
| @@ -106,11 +106,11 @@ ActiveRecord::Schema.define(:version => 20130220133245) do | @@ -106,11 +106,11 @@ ActiveRecord::Schema.define(:version => 20130220133245) do | ||
| 106 | add_index "milestones", ["project_id"], :name => "index_milestones_on_project_id" | 106 | add_index "milestones", ["project_id"], :name => "index_milestones_on_project_id" |
| 107 | 107 | ||
| 108 | create_table "namespaces", :force => true do |t| | 108 | create_table "namespaces", :force => true do |t| |
| 109 | - t.string "name", :null => false | ||
| 110 | - t.string "path", :null => false | ||
| 111 | - t.integer "owner_id", :null => false | ||
| 112 | - t.datetime "created_at", :null => false | ||
| 113 | - t.datetime "updated_at", :null => false | 109 | + t.string "name", :null => false |
| 110 | + t.string "path", :null => false | ||
| 111 | + t.integer "owner_id", :null => false | ||
| 112 | + t.datetime "created_at", :null => false | ||
| 113 | + t.datetime "updated_at", :null => false | ||
| 114 | t.string "type" | 114 | t.string "type" |
| 115 | end | 115 | end |
| 116 | 116 | ||
| @@ -152,6 +152,8 @@ ActiveRecord::Schema.define(:version => 20130220133245) do | @@ -152,6 +152,8 @@ ActiveRecord::Schema.define(:version => 20130220133245) do | ||
| 152 | t.boolean "wiki_enabled", :default => true, :null => false | 152 | t.boolean "wiki_enabled", :default => true, :null => false |
| 153 | t.integer "namespace_id" | 153 | t.integer "namespace_id" |
| 154 | t.boolean "public", :default => false, :null => false | 154 | t.boolean "public", :default => false, :null => false |
| 155 | + t.string "issues_tracker", :default => "gitlab", :null => false | ||
| 156 | + t.string "issues_tracker_id" | ||
| 155 | end | 157 | end |
| 156 | 158 | ||
| 157 | add_index "projects", ["creator_id"], :name => "index_projects_on_owner_id" | 159 | add_index "projects", ["creator_id"], :name => "index_projects_on_owner_id" |
| @@ -230,8 +232,8 @@ ActiveRecord::Schema.define(:version => 20130220133245) do | @@ -230,8 +232,8 @@ ActiveRecord::Schema.define(:version => 20130220133245) do | ||
| 230 | t.string "name" | 232 | t.string "name" |
| 231 | t.string "path" | 233 | t.string "path" |
| 232 | t.integer "owner_id" | 234 | t.integer "owner_id" |
| 233 | - t.datetime "created_at", :null => false | ||
| 234 | - t.datetime "updated_at", :null => false | 235 | + t.datetime "created_at", :null => false |
| 236 | + t.datetime "updated_at", :null => false | ||
| 235 | end | 237 | end |
| 236 | 238 | ||
| 237 | create_table "users", :force => true do |t| | 239 | create_table "users", :force => true do |t| |
lib/gitlab/markdown.rb
| @@ -25,6 +25,8 @@ module Gitlab | @@ -25,6 +25,8 @@ module Gitlab | ||
| 25 | # >> gfm(":trollface:") | 25 | # >> gfm(":trollface:") |
| 26 | # => "<img alt=\":trollface:\" class=\"emoji\" src=\"/images/trollface.png" title=\":trollface:\" /> | 26 | # => "<img alt=\":trollface:\" class=\"emoji\" src=\"/images/trollface.png" title=\":trollface:\" /> |
| 27 | module Markdown | 27 | module Markdown |
| 28 | + include IssuesHelper | ||
| 29 | + | ||
| 28 | attr_reader :html_options | 30 | attr_reader :html_options |
| 29 | 31 | ||
| 30 | # Public: Parse the provided text with GitLab-Flavored Markdown | 32 | # Public: Parse the provided text with GitLab-Flavored Markdown |
| @@ -163,8 +165,11 @@ module Gitlab | @@ -163,8 +165,11 @@ module Gitlab | ||
| 163 | end | 165 | end |
| 164 | 166 | ||
| 165 | def reference_issue(identifier) | 167 | def reference_issue(identifier) |
| 166 | - if issue = @project.issues.where(id: identifier).first | ||
| 167 | - link_to("##{identifier}", project_issue_url(@project, issue), html_options.merge(title: "Issue: #{issue.title}", class: "gfm gfm-issue #{html_options[:class]}")) | 168 | + if @project.issue_exists? identifier |
| 169 | + url = url_for_issue(identifier) | ||
| 170 | + title = title_for_issue(identifier) | ||
| 171 | + | ||
| 172 | + link_to("##{identifier}", url, html_options.merge(title: "Issue: #{title}", class: "gfm gfm-issue #{html_options[:class]}")) | ||
| 168 | end | 173 | end |
| 169 | end | 174 | end |
| 170 | 175 |
spec/factories.rb
| @@ -29,6 +29,11 @@ FactoryGirl.define do | @@ -29,6 +29,11 @@ FactoryGirl.define do | ||
| 29 | creator | 29 | creator |
| 30 | end | 30 | end |
| 31 | 31 | ||
| 32 | + factory :redmine_project, parent: :project do | ||
| 33 | + issues_tracker { "redmine" } | ||
| 34 | + issues_tracker_id { "project_name_in_redmine" } | ||
| 35 | + end | ||
| 36 | + | ||
| 32 | factory :group do | 37 | factory :group do |
| 33 | sequence(:name) { |n| "group#{n}" } | 38 | sequence(:name) { |n| "group#{n}" } |
| 34 | path { name.downcase.gsub(/\s/, '_') } | 39 | path { name.downcase.gsub(/\s/, '_') } |
spec/helpers/gitlab_markdown_helper_spec.rb
| @@ -2,6 +2,7 @@ require "spec_helper" | @@ -2,6 +2,7 @@ require "spec_helper" | ||
| 2 | 2 | ||
| 3 | describe GitlabMarkdownHelper do | 3 | describe GitlabMarkdownHelper do |
| 4 | include ApplicationHelper | 4 | include ApplicationHelper |
| 5 | + include IssuesHelper | ||
| 5 | 6 | ||
| 6 | let!(:project) { create(:project) } | 7 | let!(:project) { create(:project) } |
| 7 | 8 |
| @@ -0,0 +1,79 @@ | @@ -0,0 +1,79 @@ | ||
| 1 | +require "spec_helper" | ||
| 2 | + | ||
| 3 | +describe IssuesHelper do | ||
| 4 | + let(:project) { create :project } | ||
| 5 | + let(:issue) { create :issue, project: project } | ||
| 6 | + let(:ext_project) { create :redmine_project } | ||
| 7 | + | ||
| 8 | + describe :title_for_issue do | ||
| 9 | + it "should return issue title if used internal tracker" do | ||
| 10 | + @project = project | ||
| 11 | + title_for_issue(issue.id).should eq issue.title | ||
| 12 | + end | ||
| 13 | + | ||
| 14 | + it "should always return empty string if used external tracker" do | ||
| 15 | + @project = ext_project | ||
| 16 | + title_for_issue(rand(100)).should eq "" | ||
| 17 | + end | ||
| 18 | + | ||
| 19 | + it "should always return empty string if project nil" do | ||
| 20 | + @project = nil | ||
| 21 | + | ||
| 22 | + title_for_issue(rand(100)).should eq "" | ||
| 23 | + end | ||
| 24 | + end | ||
| 25 | + | ||
| 26 | + describe :url_for_project_issues do | ||
| 27 | + let(:project_url) { Gitlab.config.issues_tracker.redmine.project_url} | ||
| 28 | + let(:ext_expected) do | ||
| 29 | + project_url.gsub(':project_id', ext_project.id.to_s) | ||
| 30 | + .gsub(':issues_tracker_id', ext_project.issues_tracker_id.to_s) | ||
| 31 | + end | ||
| 32 | + let(:int_expected) { polymorphic_path([project]) } | ||
| 33 | + | ||
| 34 | + it "should return internal path if used internal tracker" do | ||
| 35 | + @project = project | ||
| 36 | + url_for_project_issues.should match(int_expected) | ||
| 37 | + end | ||
| 38 | + | ||
| 39 | + it "should return path to external tracker" do | ||
| 40 | + @project = ext_project | ||
| 41 | + | ||
| 42 | + url_for_project_issues.should match(ext_expected) | ||
| 43 | + end | ||
| 44 | + | ||
| 45 | + it "should return empty string if project nil" do | ||
| 46 | + @project = nil | ||
| 47 | + | ||
| 48 | + url_for_project_issues.should eq "" | ||
| 49 | + end | ||
| 50 | + end | ||
| 51 | + | ||
| 52 | + describe :url_for_issue do | ||
| 53 | + let(:issue_id) { 3 } | ||
| 54 | + let(:issues_url) { Gitlab.config.issues_tracker.redmine.issues_url} | ||
| 55 | + let(:ext_expected) do | ||
| 56 | + issues_url.gsub(':id', issue_id.to_s) | ||
| 57 | + .gsub(':project_id', ext_project.id.to_s) | ||
| 58 | + .gsub(':issues_tracker_id', ext_project.issues_tracker_id.to_s) | ||
| 59 | + end | ||
| 60 | + let(:int_expected) { polymorphic_path([project, issue]) } | ||
| 61 | + | ||
| 62 | + it "should return internal path if used internal tracker" do | ||
| 63 | + @project = project | ||
| 64 | + url_for_issue(issue.id).should match(int_expected) | ||
| 65 | + end | ||
| 66 | + | ||
| 67 | + it "should return path to external tracker" do | ||
| 68 | + @project = ext_project | ||
| 69 | + | ||
| 70 | + url_for_issue(issue_id).should match(ext_expected) | ||
| 71 | + end | ||
| 72 | + | ||
| 73 | + it "should return empty string if project nil" do | ||
| 74 | + @project = nil | ||
| 75 | + | ||
| 76 | + url_for_issue(issue.id).should eq "" | ||
| 77 | + end | ||
| 78 | + end | ||
| 79 | +end |
spec/models/project_spec.rb
| @@ -60,6 +60,7 @@ describe Project do | @@ -60,6 +60,7 @@ describe Project do | ||
| 60 | it { should ensure_inclusion_of(:wall_enabled).in_array([true, false]) } | 60 | it { should ensure_inclusion_of(:wall_enabled).in_array([true, false]) } |
| 61 | it { should ensure_inclusion_of(:merge_requests_enabled).in_array([true, false]) } | 61 | it { should ensure_inclusion_of(:merge_requests_enabled).in_array([true, false]) } |
| 62 | it { should ensure_inclusion_of(:wiki_enabled).in_array([true, false]) } | 62 | it { should ensure_inclusion_of(:wiki_enabled).in_array([true, false]) } |
| 63 | + it { should ensure_length_of(:issues_tracker_id).is_within(0..255) } | ||
| 63 | 64 | ||
| 64 | it "should not allow new projects beyond user limits" do | 65 | it "should not allow new projects beyond user limits" do |
| 65 | project.stub(:creator).and_return(double(can_create_project?: false, projects_limit: 1)) | 66 | project.stub(:creator).and_return(double(can_create_project?: false, projects_limit: 1)) |
| @@ -190,4 +191,57 @@ describe Project do | @@ -190,4 +191,57 @@ describe Project do | ||
| 190 | Project.new(path: "empty").repository.should be_nil | 191 | Project.new(path: "empty").repository.should be_nil |
| 191 | end | 192 | end |
| 192 | end | 193 | end |
| 194 | + | ||
| 195 | + describe :issue_exists? do | ||
| 196 | + let(:project) { create(:project) } | ||
| 197 | + let(:existed_issue) { create(:issue, project: project) } | ||
| 198 | + let(:not_existed_issue) { create(:issue) } | ||
| 199 | + let(:ext_project) { create(:redmine_project) } | ||
| 200 | + | ||
| 201 | + it "should be true or if used internal tracker and issue exists" do | ||
| 202 | + project.issue_exists?(existed_issue.id).should be_true | ||
| 203 | + end | ||
| 204 | + | ||
| 205 | + it "should be false or if used internal tracker and issue not exists" do | ||
| 206 | + project.issue_exists?(not_existed_issue.id).should be_false | ||
| 207 | + end | ||
| 208 | + | ||
| 209 | + it "should always be true if used other tracker" do | ||
| 210 | + ext_project.issue_exists?(rand(100)).should be_true | ||
| 211 | + end | ||
| 212 | + end | ||
| 213 | + | ||
| 214 | + describe :used_default_issues_tracker? do | ||
| 215 | + let(:project) { create(:project) } | ||
| 216 | + let(:ext_project) { create(:redmine_project) } | ||
| 217 | + | ||
| 218 | + it "should be true if used internal tracker" do | ||
| 219 | + project.used_default_issues_tracker?.should be_true | ||
| 220 | + end | ||
| 221 | + | ||
| 222 | + it "should be false if used other tracker" do | ||
| 223 | + ext_project.used_default_issues_tracker?.should be_false | ||
| 224 | + end | ||
| 225 | + end | ||
| 226 | + | ||
| 227 | + describe :can_have_issues_tracker_id? do | ||
| 228 | + let(:project) { create(:project) } | ||
| 229 | + let(:ext_project) { create(:redmine_project) } | ||
| 230 | + | ||
| 231 | + it "should be true for projects with external issues tracker if issues enabled" do | ||
| 232 | + ext_project.can_have_issues_tracker_id?.should be_true | ||
| 233 | + end | ||
| 234 | + | ||
| 235 | + it "should be false for projects with internal issue tracker if issues enabled" do | ||
| 236 | + project.can_have_issues_tracker_id?.should be_false | ||
| 237 | + end | ||
| 238 | + | ||
| 239 | + it "should be always false if issues disbled" do | ||
| 240 | + project.issues_enabled = false | ||
| 241 | + ext_project.issues_enabled = false | ||
| 242 | + | ||
| 243 | + project.can_have_issues_tracker_id?.should be_false | ||
| 244 | + ext_project.can_have_issues_tracker_id?.should be_false | ||
| 245 | + end | ||
| 246 | + end | ||
| 193 | end | 247 | end |