Commit e6edaa3b502090f461b58c439ea476da2d37f039

Authored by Jeremy Anderson
2 parents 0301ba33 3caf0aa8

Merge remote-tracking branch 'upstream/master'

Showing 64 changed files with 763 additions and 198 deletions   Show diff stats
Gemfile
... ... @@ -14,7 +14,7 @@ gem "devise", "~> 2.1.0"
14 14 gem "grit", :git => "https://github.com/gitlabhq/grit.git", :ref => "7f35cb98ff17d534a07e3ce6ec3d580f67402837"
15 15 gem "gitolite", :git => "https://github.com/gitlabhq/gitolite-client.git", :ref => "9b715ca8bab6529f6c92204a25f84d12f25a6eb0"
16 16 gem "pygments.rb", :git => "https://github.com/gitlabhq/pygments.rb.git", :ref => "2cada028da5054616634a1d9ca6941b65b3ce188"
17   -gem "omniauth-ldap", :git => "https://github.com/gitlabhq/omniauth-ldap.git", :ref => "7edf27d0281e09561838122982c16b7e62181f44"
  17 +gem "omniauth-ldap", :git => "https://github.com/gitlabhq/omniauth-ldap.git", :ref => "f038dd852d7bd473a557e385d5d7c2fd5dc1dc2e"
18 18 gem 'yaml_db', :git => "https://github.com/gitlabhq/yaml_db.git"
19 19 gem 'grack', :git => "https://github.com/gitlabhq/grack.git"
20 20 gem "linguist", "~> 1.0.0", :git => "https://github.com/gitlabhq/linguist.git"
... ...
Gemfile.lock
... ... @@ -42,8 +42,8 @@ GIT
42 42  
43 43 GIT
44 44 remote: https://github.com/gitlabhq/omniauth-ldap.git
45   - revision: 7edf27d0281e09561838122982c16b7e62181f44
46   - ref: 7edf27d0281e09561838122982c16b7e62181f44
  45 + revision: f038dd852d7bd473a557e385d5d7c2fd5dc1dc2e
  46 + ref: f038dd852d7bd473a557e385d5d7c2fd5dc1dc2e
47 47 specs:
48 48 omniauth-ldap (1.0.2)
49 49 net-ldap (~> 0.2.2)
... ...
app/assets/javascripts/note.js
... ... @@ -33,7 +33,7 @@ init:
33 33 })
34 34  
35 35 $("#note_note").live("focus", function(){
36   - $(this).css("height", "100px");
  36 + $(this).css("height", "80px");
37 37 $('.note_advanced_opts').show();
38 38 });
39 39  
... ...
app/assets/javascripts/pager.js
... ... @@ -8,7 +8,6 @@ var Pager = {
8 8 this.limit=limit;
9 9 this.offset=limit;
10 10 this.initLoadMore();
11   - $('.loading').show();
12 11 },
13 12  
14 13 getOld:
... ...
app/assets/stylesheets/common.scss
... ... @@ -337,6 +337,15 @@ p.time {
337 337 padding: 15px 5px;
338 338 &:last-child { border:none }
339 339 .wll:hover { background:none }
  340 +
  341 + .event_commits {
  342 + margin-top: 5px;
  343 +
  344 + li.commit {
  345 + padding:5px;
  346 + border:none;
  347 + }
  348 + }
340 349 }
341 350  
342 351 .ico {
... ...
app/assets/stylesheets/gitlab_bootstrap.scss
... ... @@ -18,7 +18,8 @@ a {
18 18 }
19 19  
20 20 &.lined {
21   - text-decoration:underlined;
  21 + text-decoration:underline;
  22 + &:hover { text-decoration:underline; }
22 23 }
23 24  
24 25 &.gray {
... ... @@ -74,10 +75,6 @@ h5 {
74 75 font-size:14px;
75 76 }
76 77  
77   -code {
78   - background:#FCEEC1;
79   - color:$style_color;
80   -}
81 78  
82 79 table {
83 80 width:100%;
... ... @@ -381,7 +378,6 @@ form {
381 378 min-height: 20px;
382 379 border-bottom: 1px solid #eee;
383 380 border-bottom: 1px solid rgba(0, 0, 0, 0.05);
384   - cursor:pointer;
385 381 &.smoke {
386 382 background-color:#f5f5f5;
387 383 }
... ... @@ -516,7 +512,8 @@ form {
516 512 .row_title {
517 513 font-weight:bold;
518 514 color:#444;
519   - &:hover {
  515 + &:hover {
  516 + color:#444;
520 517 text-decoration:underline;
521 518 }
522 519 }
... ...
app/assets/stylesheets/notes.scss
... ... @@ -14,7 +14,8 @@
14 14 border-bottom:1px solid #aaa;
15 15 }
16 16  
17   -.issue_notes {
  17 +.issue_notes,
  18 +.wiki_notes {
18 19 .note_content {
19 20 float:left;
20 21 width:400px;
... ... @@ -23,8 +24,8 @@
23 24  
24 25 /* Note textare */
25 26 #note_note {
26   - height:100px;
27   - width:97%;
  27 + height:80px;
  28 + width:99%;
28 29 font-size:14px;
29 30 }
30 31  
... ... @@ -99,8 +100,25 @@ tr.line_notes_row {
99 100 td {
100 101 border-bottom:1px solid #ddd;
101 102 }
102   - .actions {
  103 + .note_actions {
103 104 margin:0;
  105 + padding-top: 10px;
  106 +
  107 + .buttons {
  108 + float:left;
  109 + width:300px;
  110 + }
  111 + .options {
  112 + .labels {
  113 + float:left;
  114 + padding-left:10px;
  115 + label {
  116 + padding: 6px 0;
  117 + margin: 0;
  118 + width:120px;
  119 + }
  120 + }
  121 + }
104 122 }
105 123 }
106 124  
... ...
app/assets/stylesheets/sections/commits.scss
... ... @@ -194,4 +194,16 @@
194 194 float:right;
195 195 @extend .cgray;
196 196 }
  197 +
  198 + code {
  199 + background:#FCEEC1;
  200 + color:$style_color;
  201 + }
  202 +
  203 + .commit_short_id {
  204 + float:left;
  205 + @extend .lined;
  206 + min-width:65px;
  207 + font-family: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace;
  208 + }
197 209 }
... ...
app/assets/stylesheets/sections/issues.scss
... ... @@ -30,13 +30,14 @@
30 30 .issue {
31 31 padding:7px 10px;
32 32  
  33 + p {
  34 + padding-top:0;
  35 + padding-bottom:2px;
  36 + }
  37 +
33 38 img.avatar {
34 39 width:32px;
35 40 margin-top:4px;
36 41 }
37   - p.row_title {
38   - padding:0px;
39   - padding-bottom:2px;
40   - }
41 42 }
42 43 }
... ...
app/contexts/notes_load.rb
... ... @@ -17,6 +17,8 @@ class NotesLoad < BaseContext
17 17 then project.issues.find(target_id).notes.inc_author.order("created_at DESC").limit(20)
18 18 when "merge_request"
19 19 then project.merge_requests.find(target_id).notes.inc_author.order("created_at DESC").limit(20)
  20 + when "wiki"
  21 + then project.wikis.reverse.map {|w| w.notes.fresh }.flatten[0..20]
20 22 end
21 23  
22 24 @notes = if last_id
... ...
app/controllers/commits_controller.rb
... ... @@ -17,6 +17,7 @@ class CommitsController < ApplicationController
17 17 @limit, @offset = (params[:limit] || 40), (params[:offset] || 0)
18 18  
19 19 @commits = @project.commits(@ref, params[:path], @limit, @offset)
  20 + @commits = CommitDecorator.decorate(@commits)
20 21  
21 22 respond_to do |format|
22 23 format.html # index.html.erb
... ... @@ -51,6 +52,8 @@ class CommitsController < ApplicationController
51 52 @commit = result[:commit]
52 53 @diffs = result[:diffs]
53 54 @line_notes = []
  55 +
  56 + @commits = CommitDecorator.decorate(@commits)
54 57 end
55 58  
56 59 def patch
... ...
app/controllers/merge_requests_controller.rb
... ... @@ -143,5 +143,6 @@ class MergeRequestsController < ApplicationController
143 143 # Get commits from repository
144 144 # or from cache if already merged
145 145 @commits = @merge_request.commits
  146 + @commits = CommitDecorator.decorate(@commits)
146 147 end
147 148 end
... ...
app/controllers/omniauth_callbacks_controller.rb
... ... @@ -3,13 +3,10 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
3 3 # Extend the standard message generation to accept our custom exception
4 4 def failure_message
5 5 exception = env["omniauth.error"]
6   - if exception.class == OmniAuth::Error
7   - error = exception.message
8   - else
9   - error = exception.error_reason if exception.respond_to?(:error_reason)
10   - error ||= exception.error if exception.respond_to?(:error)
11   - error ||= env["omniauth.error.type"].to_s
12   - end
  6 + error = exception.error_reason if exception.respond_to?(:error_reason)
  7 + error ||= exception.error if exception.respond_to?(:error)
  8 + error ||= exception.message if exception.respond_to?(:message)
  9 + error ||= env["omniauth.error.type"].to_s
13 10 error.to_s.humanize if error
14 11 end
15 12  
... ...
app/controllers/refs_controller.rb
... ... @@ -51,7 +51,8 @@ class RefsController < ApplicationController
51 51 @logs = contents.map do |content|
52 52 file = params[:path] ? File.join(params[:path], content.name) : content.name
53 53 last_commit = @project.commits(@commit.id, file, 1).last
54   - {
  54 + last_commit = CommitDecorator.decorate(last_commit)
  55 + {
55 56 :file_name => content.name,
56 57 :commit => last_commit
57 58 }
... ...
app/controllers/wikis_controller.rb
... ... @@ -13,16 +13,16 @@ class WikisController < ApplicationController
13 13 @wiki = @project.wikis.where(:slug => params[:id]).order("created_at").last
14 14 end
15 15  
16   - unless @wiki
17   - return render_404 unless can?(current_user, :write_wiki, @project)
18   - end
  16 + @note = @project.notes.new(:noteable => @wiki)
19 17  
20   - respond_to do |format|
21   - if @wiki
22   - format.html
23   - else
  18 + if @wiki
  19 + render 'show'
  20 + else
  21 + if can?(current_user, :write_wiki, @project)
24 22 @wiki = @project.wikis.new(:slug => params[:id])
25   - format.html { render "edit" }
  23 + render 'edit'
  24 + else
  25 + render 'empty'
26 26 end
27 27 end
28 28 end
... ...
app/mailers/notify.rb
... ... @@ -46,6 +46,13 @@ class Notify < ActionMailer::Base
46 46 mail(:to => recipient.email, :subject => "gitlab | note for issue #{@issue.id} | #{@note.project_name} ")
47 47 end
48 48  
  49 + def note_wiki_email(recipient_id, note_id)
  50 + recipient = User.find(recipient_id)
  51 + @note = Note.find(note_id)
  52 + @wiki = @note.noteable
  53 + mail(:to => recipient.email, :subject => "gitlab | note for wiki | #{@note.project_name}")
  54 + end
  55 +
49 56 def new_merge_request_email(merge_request_id)
50 57 @merge_request = MergeRequest.find(merge_request_id)
51 58 mail(:to => @merge_request.assignee_email, :subject => "gitlab | new merge request | #{@merge_request.title} ")
... ...
app/models/commit.rb
... ... @@ -114,6 +114,10 @@ class Commit
114 114 @head = head
115 115 end
116 116  
  117 + def short_id(length = 10)
  118 + id.to_s[0..length]
  119 + end
  120 +
117 121 def safe_message
118 122 utf8 message
119 123 end
... ... @@ -150,4 +154,8 @@ class Commit
150 154 def prev_commit_id
151 155 prev_commit.try :id
152 156 end
  157 +
  158 + def parents_count
  159 + parents && parents.count || 0
  160 + end
153 161 end
... ...
app/models/wiki.rb
1 1 class Wiki < ActiveRecord::Base
2 2 belongs_to :project
3 3 belongs_to :user
  4 + has_many :notes, :as => :noteable, :dependent => :destroy
4 5  
5 6 validates :content, :title, :user_id, :presence => true
6 7 validates :title, :length => 1..250
... ...
app/observers/mailer_observer.rb
... ... @@ -34,6 +34,7 @@ class MailerObserver &lt; ActiveRecord::Observer
34 34 case note.noteable_type
35 35 when "Commit"; Notify.note_commit_email(u.id, note.id).deliver
36 36 when "Issue"; Notify.note_issue_email(u.id, note.id).deliver
  37 + when "Wiki"; Notify.note_wiki_email(u.id, note.id).deliver
37 38 when "MergeRequest"; Notify.note_merge_request_email(u.id, note.id).deliver
38 39 when "Snippet"; true
39 40 else
... ...
app/views/commits/_commit.html.haml
... ... @@ -2,16 +2,15 @@
2 2 .browse_code_link_holder
3 3 %p
4 4 %strong= link_to "Browse Code »", tree_project_ref_path(@project, commit.id), :class => "right"
5   - = link_to project_commit_path(@project, :id => commit.id) do
6   - %p
7   - %code.left= commit.id.to_s[0..10]
8   - %strong.cgray= commit.author_name
9   - &ndash;
10   - = image_tag gravatar_icon(commit.author_email), :class => "avatar", :width => 16
11   - %span.row_title= truncate(commit.safe_message, :length => 50)
  5 + %p
  6 + = link_to commit.short_id(8), project_commit_path(@project, :id => commit.id), :class => "commit_short_id"
  7 + %strong.cgray= commit.author_name
  8 + &ndash;
  9 + = image_tag gravatar_icon(commit.author_email), :class => "avatar", :width => 16
  10 + = link_to truncate(commit.title, :length => 50), project_commit_path(@project, :id => commit.id), :class => "row_title"
12 11  
13   - %span.committed_ago
14   - = time_ago_in_words(commit.committed_date)
15   - ago
16   - &nbsp;
  12 + %span.committed_ago
  13 + = time_ago_in_words(commit.committed_date)
  14 + ago
  15 + &nbsp;
17 16  
... ...
app/views/commits/_commit_box.html.haml
1   -.commit-box{class: @commit.parents.count > 1 ? "merge-commit" : ""}
  1 +.commit-box{class: @commit.parents_count > 1 ? "merge-commit" : ""}
2 2 .commit-head
3 3 .right
4 4 - if @notes_count > 0
... ...
app/views/commits/compare.html.haml
... ... @@ -24,8 +24,9 @@
24 24  
25 25  
26 26 - unless @commits.empty?
27   - %h4 Commits (#{@commits.count})
28   - %ul.unstyled= render @commits
  27 + %div.ui-box
  28 + %h5.small Commits (#{@commits.count})
  29 + %ul.unstyled= render @commits
29 30  
30 31 - unless @diffs.empty?
31 32 %h4 Diff
... ...
app/views/commits/index.atom.builder
... ... @@ -10,14 +10,14 @@ xml.feed &quot;xmlns&quot; =&gt; &quot;http://www.w3.org/2005/Atom&quot;, &quot;xmlns:media&quot; =&gt; &quot;http://sear
10 10 xml.entry do
11 11 xml.id project_commit_url(@project, :id => commit.id)
12 12 xml.link :href => project_commit_url(@project, :id => commit.id)
13   - xml.title truncate(commit.safe_message, :length => 80)
  13 + xml.title truncate(commit.title, :length => 80)
14 14 xml.updated commit.committed_date.strftime("%Y-%m-%dT%H:%M:%SZ")
15 15 xml.media :thumbnail, :width => "40", :height => "40", :url => gravatar_icon(commit.author_email)
16 16 xml.author do |author|
17 17 xml.name commit.author_name
18 18 xml.email commit.author_email
19 19 end
20   - xml.summary commit.safe_message
  20 + xml.summary commit.description
21 21 end
22 22 end
23 23 end
... ...
app/views/dashboard/merge_requests.html.haml
1 1 %h3.page_title
2 2 Merge Requests
3   - %small (authored or assigned to you)
  3 + %small (authored by or assigned to you)
4 4 %small.right #{@merge_requests.total_count} merge requests
5 5  
6 6 %br
... ...
app/views/errors/gitolite.html.haml
1 1 .alert-message.block-message.error
2 2 %h3 Gitolite Error
3   - %hr
4 3 %h4 Application cant get access to your gitolite system.
5   - %ol
6   - %li
7   - %p
8   - Check 'config/gitlab.yml' for correct settings.
9   - %li
10   - %p
11   - Make sure web server user has access to gitolite.
12   - %a{:href => "https://github.com/gitlabhq/gitlabhq/wiki/Gitolite"} Setup tutorial
13   - %li
14   - %p
15   - Try:
  4 +
  5 +
  6 +
  7 +
  8 +%h4 Tips for Administrator:
  9 +
  10 +%ul
  11 + %li
  12 + %p
  13 + Check git logs in admin area
  14 + %li
  15 + %p
  16 + Check config/gitlab.yml for correct settings.
  17 + %li
  18 + %p
  19 + Diagnostic tool:
16 20 %pre
17   - = preserve do
18   - sudo chmod -R 770 /home/git/repositories/
19   - sudo chown -R git:git /home/git/repositories/
  21 + bundle exec rake gitlab:app:status RAILS_ENV=production
  22 + %li
  23 + %p
  24 + Permissions:
  25 + %pre
  26 + = preserve do
  27 + sudo chmod -R 770 /home/git/repositories/
  28 + sudo chown -R git:git /home/git/repositories/
  29 + sudo chown gitlab:gitlab /home/git/repositories/**/hooks/post-receive
  30 +
... ...
app/views/events/_commit.html.haml
  1 +- commit = CommitDecorator.decorate(commit)
1 2 %li.wll.commit
2   - = link_to project_commit_path(project, :id => commit.id) do
3   - %p
4   - %code.left= commit.id.to_s[0..10]
5   - %strong.cgray= commit.author_name
6   - &ndash;
7   - = image_tag gravatar_icon(commit.author_email), :class => "avatar", :width => 16
8   - %span.row_title= truncate(commit.safe_message, :length => 50) rescue "--broken encoding"
  3 + %p
  4 + = link_to commit.short_id(8), project_commit_path(project, :id => commit.id), :class => "commit_short_id"
  5 + %strong.cdark= commit.author_name
  6 + &ndash;
  7 + = image_tag gravatar_icon(commit.author_email), :class => "avatar", :width => 16
  8 + = truncate(commit.title, :length => 50) rescue "--broken encoding"
9 9  
... ...
app/views/help/api.html.haml
1 1 %h3 API
2 2 .back_link
3   - = link_to help_path do
  3 + = link_to help_path do
4 4 &larr; to index
5 5 %hr
6 6  
7 7 %ol
8   - %li
  8 + %li
9 9 %a{:href => "#README"} README
10   - %li
  10 + %li
11 11 %a{:href => "#projects"} Projects
12   - %li
  12 + %li
13 13 %a{:href => "#users"} Users
  14 + %li
  15 + %a{:href => "#issues"} Issues
14 16  
15 17 .file_holder#README
16 18 .file_title
... ... @@ -39,3 +41,13 @@
39 41 .file_content.wiki
40 42 = preserve do
41 43 = markdown File.read(Rails.root.join("doc", "api", "users.md"))
  44 +
  45 +%br
  46 +
  47 +.file_holder#issues
  48 + .file_title
  49 + %i.icon-file
  50 + Issues
  51 + .file_content.wiki
  52 + = preserve do
  53 + = markdown File.read(Rails.root.join("doc", "api", "issues.md"))
... ...
app/views/help/permissions.html.haml
... ... @@ -38,7 +38,6 @@
38 38 %li Push to non-protected branches
39 39 %li Remove non-protected branches
40 40 %li Add tags
41   - %li Create new merge request
42 41 %li Write a wiki
43 42  
44 43 .ui-box.span3
... ... @@ -55,7 +54,6 @@
55 54 %li Push to non-protected branches
56 55 %li Remove non-protected branches
57 56 %li Add tags
58   - %li Create new merge request
59 57 %li Write a wiki
60 58 %li Add new team members
61 59 %li Push to protected branches
... ...
app/views/issues/_show.html.haml
... ... @@ -24,8 +24,7 @@
24 24 - else
25 25 = image_tag "no_avatar.png", :class => "avatar"
26 26  
27   - = link_to project_issue_path(issue.project, issue) do
28   - %p.row_title= truncate(issue.title, :length => 100)
  27 + %p= link_to truncate(issue.title, :length => 100), project_issue_path(issue.project, issue), :class => "row_title"
29 28  
30 29 %span.update-author
31 30 %small.cdark= "##{issue.id}"
... ...
app/views/issues/show.html.haml
... ... @@ -46,9 +46,7 @@
46 46 - if @issue.milestone
47 47 - milestone = @issue.milestone
48 48 %cite.cgray and attached to milestone
49   - = link_to project_milestone_path(milestone.project, milestone) do
50   - %strong
51   - = truncate(milestone.title, :length => 20)
  49 + %strong= link_to truncate(milestone.title, :length => 20), project_milestone_path(milestone.project, milestone)
52 50  
53 51 .right
54 52 - @issue.labels.each do |label|
... ...
app/views/merge_requests/_merge_request.html.haml
... ... @@ -16,8 +16,7 @@
16 16 = merge_request.target_branch
17 17 = image_tag gravatar_icon(merge_request.author_email), :class => "avatar"
18 18  
19   - = link_to project_merge_request_path(merge_request.project, merge_request) do
20   - %p.row_title= truncate(merge_request.title, :length => 80)
  19 + %p= link_to truncate(merge_request.title, :length => 80), project_merge_request_path(merge_request.project, merge_request), :class => "row_title"
21 20  
22 21 %span.update-author
23 22 %small.cdark= "##{merge_request.id}"
... ...
app/views/milestones/_milestone.html.haml
... ... @@ -6,14 +6,13 @@
6 6 = link_to 'Browse Issues', project_issues_path(milestone.project, :milestone_id => milestone.id), :class => "btn small grouped"
7 7 - if can? current_user, :admin_milestone, milestone.project
8 8 = link_to 'Edit', edit_project_milestone_path(milestone.project, milestone), :class => "btn small edit-milestone-link grouped"
9   - = link_to project_milestone_path(milestone.project, milestone) do
10   - %h4.row_title
11   - = truncate(milestone.title, :length => 100)
12   - %small
13   - = milestone.expires_at
14   - %br
15   - .progress.progress-success.span3
16   - .bar{:style => "width: #{milestone.percent_complete}%;"}
  9 + %h4
  10 + = link_to truncate(milestone.title, :length => 100), project_milestone_path(milestone.project, milestone), :class => "row_title"
  11 + %small
  12 + = milestone.expires_at
  13 + %br
  14 + .progress.progress-success.span3
  15 + .bar{:style => "width: #{milestone.percent_complete}%;"}
17 16  
18 17  
19 18 &nbsp;
... ...
app/views/milestones/show.html.haml
... ... @@ -50,8 +50,8 @@
50 50 %td
51 51 = link_to [@project, issue] do
52 52 %span.badge.badge-info ##{issue.id}
53   - &ndash;
54   - = truncate issue.title, :length => 60
  53 + &ndash;
  54 + = link_to truncate(issue.title, :length => 60), [@project, issue]
55 55 %br
56 56 = paginate @issues, :theme => "gitlab"
57 57  
... ...
app/views/notes/_form.html.haml
1 1 = form_for [@project, @note], :remote => "true", :multipart => true do |f|
2   - %h3 Leave a comment
  2 + %h3.page_title Leave a comment
3 3 -if @note.errors.any?
4 4 .alert-message.block-message.error
5 5 - @note.errors.full_messages.each do |msg|
... ...
app/views/notes/_per_line_form.html.haml
... ... @@ -2,7 +2,7 @@
2 2 %tr.per_line_form
3 3 %td{:colspan => 3 }
4 4 = form_for [@project, @note], :remote => "true", :multipart => true do |f|
5   - %h3 Leave a note
  5 + %h3.page_title Leave a note
6 6 %div.span10
7 7 -if @note.errors.any?
8 8 .alert-message.block-message.error
... ... @@ -13,19 +13,21 @@
13 13 = f.hidden_field :noteable_type
14 14 = f.hidden_field :line_code
15 15 = f.text_area :note, :size => 255
16   - %h5 Notify via email:
17   - .clearfix
18   - = label_tag :notify do
19   - = check_box_tag :notify, 1, @note.noteable_type != "Commit"
20   - %span Project team
  16 + .note_actions
  17 + .buttons
  18 + = f.submit 'Add note', :class => "btn primary submit_note", :id => "submit_note"
  19 + = link_to "Cancel", "#", :class => "btn hide-button"
  20 + .options
  21 + %h6.left Notify via email:
  22 + .labels
  23 + = label_tag :notify do
  24 + = check_box_tag :notify, 1, @note.noteable_type != "Commit"
  25 + %span Project team
21 26  
22   - - if @note.notify_only_author?(current_user)
23   - = label_tag :notify_author do
24   - = check_box_tag :notify_author, 1 , @note.noteable_type == "Commit"
25   - %span Commit author
26   - .actions
27   - = f.submit 'Add note', :class => "btn primary submit_note", :id => "submit_note"
28   - = link_to "Close", "#", :class => "btn hide-button"
  27 + - if @note.notify_only_author?(current_user)
  28 + = label_tag :notify_author do
  29 + = check_box_tag :notify_author, 1 , @note.noteable_type == "Commit"
  30 + %span Commit author
29 31  
30 32 :javascript
31 33 $(function(){
... ...
app/views/notify/note_wiki_email.html.haml 0 → 100644
... ... @@ -0,0 +1,24 @@
  1 +%td.content{:align => "left", :style => "font-family: Helvetica, Arial, sans-serif; padding: 20px 0 0;", :valign => "top", :width => "600"}
  2 + %table{:border => "0", :cellpadding => "0", :cellspacing => "0", :style => "color: #717171; font: normal 11px Helvetica, Arial, sans-serif; margin: 0; padding: 0;", :width => "600"}
  3 + %tr
  4 + %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"}
  5 + %td{:align => "left", :style => "padding: 20px 0 0;"}
  6 + %h2{:style => "color:#646464 !important; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "}
  7 + New comment -
  8 + = link_to project_issue_url(@wiki.project, @wiki, :anchor => "note_#{@note.id}") do
  9 + = "Wiki ##{@wiki.title.to_s}"
  10 + %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"}
  11 + %tr
  12 + %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"}
  13 + %td{:style => "padding: 15px 0 15px;", :valign => "top"}
  14 + %p{:style => "color:#767676; font-weight: normal; margin: 0; padding: 0; line-height: 20px; font-size: 12px;font-family: Helvetica, Arial, sans-serif; "}
  15 + %a{:href => "#", :style => "color: #0eb6ce; text-decoration: none;"} #{@note.author_name}
  16 + commented on Wiki page:
  17 + %br
  18 + %table{:border => "0", :cellpadding => "0", :cellspacing => "0", :width => "558"}
  19 + %tr
  20 + %td{:valign => "top"}
  21 + %div{ :style => "background:#f5f5f5; padding:20px;border:1px solid #ddd" }
  22 + = markdown(@note.note)
  23 + %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"}
  24 +
... ...
app/views/projects/files.html.haml
... ... @@ -14,6 +14,6 @@
14 14 ago
15 15 - else
16 16 .alert-message.block-message
17   - %p All files attached to project wall, issues etc will be displayed here
  17 + %span All files attached to project wall, issues etc will be displayed here
18 18  
19 19  
... ...
app/views/refs/_tree_commit.html.haml
1 1 - if tm
2 2 %strong= link_to "[#{tm.user_name}]", project_team_member_path(@project, tm)
3   -= link_to truncate(content_commit.safe_message, :length => tm ? 30 : 50), project_commit_path(@project, content_commit.id), :class => "tree-commit-link"
  3 += link_to truncate(content_commit.title, :length => tm ? 30 : 50), project_commit_path(@project, content_commit.id), :class => "tree-commit-link"
... ...
app/views/refs/blame.html.haml
... ... @@ -25,15 +25,15 @@
25 25 %table
26 26 - @blame.each do |commit, lines|
27 27 - commit = Commit.new(commit)
  28 + - commit = CommitDecorator.decorate(commit)
28 29 %tr
29 30 %td.author
30 31 = image_tag gravatar_icon(commit.author_email, 16)
31 32 = commit.author_name
32 33 %td.blame_commit
33 34 &nbsp;
34   - = link_to project_commit_path(@project, :id => commit.id) do
35   - %code= commit.id.to_s[0..10]
36   - %span.row_title= truncate(commit.safe_message, :length => 30) rescue "--broken encoding"
  35 + %code= link_to commit.short_id, project_commit_path(@project, :id => commit.id)
  36 + = link_to truncate(commit.title, :length => 30), project_commit_path(@project, :id => commit.id), :class => "row_title" rescue "--broken encoding"
37 37 %td.lines
38 38 = preserve do
39 39 %pre
... ...
app/views/repositories/_branch.html.haml
  1 +- commit = Commit.new(branch.commit)
  2 +- commit = CommitDecorator.decorate(commit)
1 3 %tr
2 4 %td
3 5 = link_to project_commits_path(@project, :ref => branch.name) do
... ... @@ -5,14 +7,14 @@
5 7 - if branch.name == @project.root_ref
6 8 %span.label default
7 9 %td
8   - = link_to project_commit_path(@project, :id => branch.commit.id) do
9   - %code= branch.commit.id.to_s[0..10]
  10 + = link_to project_commit_path(@project, :id => commit.id) do
  11 + %code= commit.short_id
10 12  
11   - = image_tag gravatar_icon(Commit.new(branch.commit).author_email), :class => "", :width => 16
12   - = truncate(Commit.new(branch.commit).safe_message, :length => 40)
  13 + = image_tag gravatar_icon(commit.author_email), :class => "", :width => 16
  14 + = truncate(commit.title, :length => 40)
13 15 %td
14 16 %span.update-author.right
15   - = time_ago_in_words(branch.commit.committed_date)
  17 + = time_ago_in_words(commit.committed_date)
16 18 ago
17 19 %td
18 20 - if can? current_user, :download_code, @project
... ...
app/views/repositories/_feed.html.haml
1 1 - commit = update
  2 +- commit = CommitDecorator.new(commit)
2 3 %tr
3 4 %td
4 5 = link_to project_commits_path(@project, :ref => commit.head.name) do
... ... @@ -10,9 +11,9 @@
10 11 %td
11 12 %div
12 13 = link_to project_commits_path(@project, commit.id) do
13   - %code= commit.id.to_s[0..10]
  14 + %code= commit.short_id
14 15 = image_tag gravatar_icon(commit.author_email), :class => "", :width => 16
15   - = truncate(commit.safe_message, :length => 40)
  16 + = truncate(commit.title, :length => 40)
16 17 %td
17 18 %span.right.cgray
18 19 = time_ago_in_words(commit.committed_date)
... ...
app/views/repositories/tags.html.haml
... ... @@ -9,14 +9,15 @@
9 9 %th
10 10 - @tags.each do |tag|
11 11 - commit = Commit.new(tag.commit)
  12 + - commit = CommitDecorator.decorate(commit)
12 13 %tr
13 14 %td
14 15 %strong= link_to tag.name, project_commits_path(@project, :ref => tag.name), :class => ""
15 16 %td
16 17 = link_to project_commit_path(@project, commit.id) do
17   - %code= commit.id.to_s[0..10]
  18 + %code= commit.short_id
18 19 = image_tag gravatar_icon(commit.author_email), :class => "", :width => 16
19   - = truncate(commit.safe_message, :length => 40)
  20 + = truncate(commit.title, :length => 40)
20 21 %td
21 22 %span.update-author.right
22 23 = time_ago_in_words(commit.committed_date)
... ...
app/views/wikis/_form.html.haml
... ... @@ -6,19 +6,21 @@
6 6 - @wiki.errors.full_messages.each do |msg|
7 7 %li= msg
8 8  
9   - .alert-message.block-message.warning
10   - %p
11   - Wiki content is parsed with #{link_to "Markdown", "http://en.wikipedia.org/wiki/Markdown"}.
12   - %br
13   - To add link to new page you can just type
14   - %code [Link Title](page-slug)
15   - .clearfix
16   - = f.label :title
17   - .input= f.text_field :title, :class => :xxlarge
18   - = f.hidden_field :slug
19   - .clearfix
20   - = f.label :content
21   - .input= f.text_area :content, :class => :xxlarge
  9 + .main_box
  10 + .top_box_content
  11 + = f.label :title
  12 + .input= f.text_field :title, :class => 'span8'
  13 + = f.hidden_field :slug
  14 + .middle_box_content
  15 + .input
  16 + %span.cgray
  17 + Wiki content is parsed with #{link_to "Markdown", "http://en.wikipedia.org/wiki/Markdown"}.
  18 + To add link to new page you can just type
  19 + %code [Link Title](page-slug)
  20 +
  21 + .bottom_box_content
  22 + = f.label :content
  23 + .input= f.text_area :content, :class => 'span8'
22 24 .actions
23 25 = f.submit 'Save', :class => "primary btn"
24 26 = link_to "Cancel", project_wiki_path(@project, :index), :class => "btn"
... ...
app/views/wikis/edit.html.haml
1   -%h3 Editing page
  1 +%h3.page_title Editing page
2 2 %hr
3 3 = render 'form'
... ...
app/views/wikis/empty.html.haml 0 → 100644
... ... @@ -0,0 +1,4 @@
  1 +%h3.page_title Empty page
  2 +%hr
  3 +.alert-message.block-message.warning
  4 + %span You are not allowed to create wiki pages
... ...
app/views/wikis/history.html.haml
1   -%h3 Versions
2   -%table
  1 +%h3.page_title Versions
  2 +%br
  3 +%table.admin-table
3 4 %thead
4 5 %tr
5 6 %th #
... ...
app/views/wikis/show.html.haml
... ... @@ -5,13 +5,18 @@
5 5 = link_to history_project_wiki_path(@project, @wiki), :class => "btn small grouped" do
6 6 History
7 7 = link_to edit_project_wiki_path(@project, @wiki), :class => "btn small grouped" do
  8 + %i.icon-edit
8 9 Edit
9   -%hr
10   -.wiki_content
11   - = preserve do
12   - = markdown @wiki.content
  10 +%br
  11 +.file_holder
  12 + .file_content.wiki
  13 + = preserve do
  14 + = markdown @wiki.content
13 15  
14 16 %p.time Last edited by #{@wiki.user.name}, #{time_ago_in_words @wiki.created_at} ago
15 17 - if can? current_user, :admin_wiki, @project
16 18 = link_to project_wiki_path(@project, @wiki), :confirm => "Are you sure you want to delete this page?", :method => :delete do
17 19 Delete this page
  20 +
  21 +%hr
  22 +.wiki_notes#notes= render "notes/notes", :tid => @wiki.id, :tt => "wiki"
... ...
doc/api/README.md
... ... @@ -27,3 +27,4 @@ The API uses JSON to serialize data. You don&#39;t need to specify `.json` at the en
27 27  
28 28 + [Users](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/users.md)
29 29 + [Projects](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/projects.md)
  30 ++ [Issues](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/issues.md)
... ...
doc/api/issues.md 0 → 100644
... ... @@ -0,0 +1,184 @@
  1 +## List issues
  2 +
  3 +Get all issues created by authenticed user.
  4 +
  5 +```
  6 +GET /issues
  7 +```
  8 +
  9 +```json
  10 +[
  11 + {
  12 + "id": 43,
  13 + "project_id": 8,
  14 + "title": "4xx/5xx pages",
  15 + "description": "",
  16 + "labels": [ ],
  17 + "milestone": null,
  18 + "assignee": null,
  19 + "author": {
  20 + "id": 1,
  21 + "email": "john@example.com",
  22 + "name": "John Smith",
  23 + "blocked": false,
  24 + "created_at": "2012-05-23T08:00:58Z"
  25 + },
  26 + "closed": true,
  27 + "updated_at": "2012-07-02T17:53:12Z",
  28 + "created_at": "2012-07-02T17:53:12Z"
  29 + },
  30 + {
  31 + "id": 42,
  32 + "project_id": 8,
  33 + "title": "Add user settings",
  34 + "description": "",
  35 + "labels": [
  36 + "feature"
  37 + ],
  38 + "milestone": {
  39 + "id": 1,
  40 + "title": "v1.0",
  41 + "description": "",
  42 + "due_date": "2012-07-20",
  43 + "closed": false,
  44 + "updated_at": "2012-07-04T13:42:48Z",
  45 + "created_at": "2012-07-04T13:42:48Z"
  46 + },
  47 + "assignee": {
  48 + "id": 2,
  49 + "email": "jack@example.com",
  50 + "name": "Jack Smith",
  51 + "blocked": false,
  52 + "created_at": "2012-05-23T08:01:01Z"
  53 + },
  54 + "author": {
  55 + "id": 1,
  56 + "email": "john@example.com",
  57 + "name": "John Smith",
  58 + "blocked": false,
  59 + "created_at": "2012-05-23T08:00:58Z"
  60 + },
  61 + "closed": false,
  62 + "updated_at": "2012-07-12T13:43:19Z",
  63 + "created_at": "2012-06-28T12:58:06Z"
  64 + }
  65 +]
  66 +```
  67 +
  68 +## List project issues
  69 +
  70 +Get a list of project issues.
  71 +
  72 +```
  73 +GET /projects/:id/issues
  74 +```
  75 +
  76 +Parameters:
  77 +
  78 ++ `id` (required) - The ID or code name of a project
  79 +
  80 +## Single issue
  81 +
  82 +Get a project issue.
  83 +
  84 +```
  85 +GET /projects/:id/issues/:issue_id
  86 +```
  87 +
  88 +Parameters:
  89 +
  90 ++ `id` (required) - The ID or code name of a project
  91 ++ `issue_id` (required) - The ID of a project issue
  92 +
  93 +```json
  94 +{
  95 + "id": 42,
  96 + "project_id": 8,
  97 + "title": "Add user settings",
  98 + "description": "",
  99 + "labels": [
  100 + "feature"
  101 + ],
  102 + "milestone": {
  103 + "id": 1,
  104 + "title": "v1.0",
  105 + "description": "",
  106 + "due_date": "2012-07-20",
  107 + "closed": false,
  108 + "updated_at": "2012-07-04T13:42:48Z",
  109 + "created_at": "2012-07-04T13:42:48Z"
  110 + },
  111 + "assignee": {
  112 + "id": 2,
  113 + "email": "jack@example.com",
  114 + "name": "Jack Smith",
  115 + "blocked": false,
  116 + "created_at": "2012-05-23T08:01:01Z"
  117 + },
  118 + "author": {
  119 + "id": 1,
  120 + "email": "john@example.com",
  121 + "name": "John Smith",
  122 + "blocked": false,
  123 + "created_at": "2012-05-23T08:00:58Z"
  124 + },
  125 + "closed": false,
  126 + "updated_at": "2012-07-12T13:43:19Z",
  127 + "created_at": "2012-06-28T12:58:06Z"
  128 +}
  129 +```
  130 +
  131 +## New issue
  132 +
  133 +Create a new project issue.
  134 +
  135 +```
  136 +POST /projects/:id/issues
  137 +```
  138 +
  139 +Parameters:
  140 +
  141 ++ `id` (required) - The ID or code name of a project
  142 ++ `title` (required) - The title of an issue
  143 ++ `description` (optional) - The description of an issue
  144 ++ `assignee_id` (optional) - The ID of a user to assign issue
  145 ++ `milestone_id` (optional) - The ID of a milestone to assign issue
  146 ++ `labels` (optional) - Comma-separated label names for an issue
  147 +
  148 +Will return created issue with status `201 Created` on success, or `404 Not found` on fail.
  149 +
  150 +## Edit issue
  151 +
  152 +Update an existing project issue.
  153 +
  154 +```
  155 +PUT /projects/:id/issues/:issue_id
  156 +```
  157 +
  158 +Parameters:
  159 +
  160 ++ `id` (required) - The ID or code name of a project
  161 ++ `issue_id` (required) - The ID of a project's issue
  162 ++ `title` (optional) - The title of an issue
  163 ++ `description` (optional) - The description of an issue
  164 ++ `assignee_id` (optional) - The ID of a user to assign issue
  165 ++ `milestone_id` (optional) - The ID of a milestone to assign issue
  166 ++ `labels` (optional) - Comma-separated label names for an issue
  167 ++ `closed` (optional) - The state of an issue (0 = false, 1 = true)
  168 +
  169 +Will return updated issue with status `200 OK` on success, or `404 Not found` on fail.
  170 +
  171 +## Delete issue
  172 +
  173 +Delete existing project issue.
  174 +
  175 +```
  176 +DELETE /projects/:id/issues/:issue_id
  177 +```
  178 +
  179 +Parameters:
  180 +
  181 ++ `id` (required) - The ID or code name of a project
  182 ++ `issue_id` (required) - The ID of a project's issue
  183 +
  184 +Status code `200` will be returned on success.
... ...
doc/api/projects.md
1 1 ## List projects
2 2  
3   -Get a list of authenticated users' projects.
  3 +Get a list of authenticated user's projects.
4 4  
5 5 ```
6 6 GET /projects
... ... @@ -63,7 +63,7 @@ GET /projects/:id
63 63  
64 64 Parameters:
65 65  
66   -+ `id` (required) - The code name of a project
  66 ++ `id` (required) - The ID or code name of a project
67 67  
68 68 ```json
69 69 {
... ... @@ -91,7 +91,7 @@ Parameters:
91 91  
92 92 ## Project repository branches
93 93  
94   -Get a list of project repository branches.
  94 +Get a list of project repository branches sorted by name alphabetically.
95 95  
96 96 ```
97 97 GET /projects/:id/repository/branches
... ... @@ -99,7 +99,7 @@ GET /projects/:id/repository/branches
99 99  
100 100 Parameters:
101 101  
102   -+ `id` (required) - The code name of a project
  102 ++ `id` (required) - The ID or code name of a project
103 103  
104 104 ```json
105 105 [
... ... @@ -131,7 +131,7 @@ Parameters:
131 131  
132 132 ## Project repository tags
133 133  
134   -Get a list of project repository tags.
  134 +Get a list of project repository tags sorted by name in reverse alphabetical order.
135 135  
136 136 ```
137 137 GET /projects/:id/repository/tags
... ... @@ -139,7 +139,7 @@ GET /projects/:id/repository/tags
139 139  
140 140 Parameters:
141 141  
142   -+ `id` (required) - The code name of a project
  142 ++ `id` (required) - The ID or code name of a project
143 143  
144 144 ```json
145 145 [
... ... @@ -183,7 +183,7 @@ GET /projects/:id/snippets/:snippet_id
183 183  
184 184 Parameters:
185 185  
186   -+ `id` (required) - The code name of a project
  186 ++ `id` (required) - The ID or code name of a project
187 187 + `snippet_id` (required) - The ID of a project's snippet
188 188  
189 189 ```json
... ... @@ -214,7 +214,7 @@ GET /projects/:id/snippets/:snippet_id/raw
214 214  
215 215 Parameters:
216 216  
217   -+ `id` (required) - The code name of a project
  217 ++ `id` (required) - The ID or code name of a project
218 218 + `snippet_id` (required) - The ID of a project's snippet
219 219  
220 220 ## New snippet
... ... @@ -227,7 +227,7 @@ POST /projects/:id/snippets
227 227  
228 228 Parameters:
229 229  
230   -+ `id` (required) - The code name of a project
  230 ++ `id` (required) - The ID or code name of a project
231 231 + `title` (required) - The title of a snippet
232 232 + `file_name` (required) - The name of a snippet file
233 233 + `lifetime` (optional) - The expiration date of a snippet
... ... @@ -245,7 +245,7 @@ PUT /projects/:id/snippets/:snippet_id
245 245  
246 246 Parameters:
247 247  
248   -+ `id` (required) - The code name of a project
  248 ++ `id` (required) - The ID or code name of a project
249 249 + `snippet_id` (required) - The ID of a project's snippet
250 250 + `title` (optional) - The title of a snippet
251 251 + `file_name` (optional) - The name of a snippet file
... ... @@ -264,7 +264,7 @@ DELETE /projects/:id/snippets/:snippet_id
264 264  
265 265 Parameters:
266 266  
267   -+ `id` (required) - The code name of a project
  267 ++ `id` (required) - The ID or code name of a project
268 268 + `snippet_id` (required) - The ID of a project's snippet
269 269  
270 270 Status code `200` will be returned on success.
... ...
doc/installation.md
... ... @@ -119,6 +119,7 @@ Permissions:
119 119  
120 120 sudo chmod -R g+rwX /home/git/repositories/
121 121 sudo chown -R git:git /home/git/repositories/
  122 + sudo chown gitlab:gitlab /home/git/repositories/**/hooks/post-receive
122 123  
123 124 #### CHECK: Logout & login again to apply git group to your user
124 125  
... ...
features/step_definitions/project_commits_steps.rb
... ... @@ -16,11 +16,11 @@ Given /^I click atom feed link$/ do
16 16 end
17 17  
18 18 Then /^I see commits atom feed$/ do
19   - commit = @project.commit
  19 + commit = CommitDecorator.decorate(@project.commit)
20 20 page.response_headers['Content-Type'].should have_content("application/atom+xml")
21 21 page.body.should have_selector("title", :text => "Recent commits to #{@project.name}")
22 22 page.body.should have_selector("author email", :text => commit.author_email)
23   - page.body.should have_selector("entry summary", :text => commit.message)
  23 + page.body.should have_selector("entry summary", :text => commit.description)
24 24 end
25 25  
26 26 Given /^I click on commit link$/ do
... ...
lib/api.rb
... ... @@ -15,5 +15,6 @@ module Gitlab
15 15  
16 16 mount Users
17 17 mount Projects
  18 + mount Issues
18 19 end
19 20 end
... ...
lib/api/entities.rb
... ... @@ -16,11 +16,7 @@ module Gitlab
16 16 expose :issues_enabled, :merge_requests_enabled, :wall_enabled, :wiki_enabled, :created_at
17 17 end
18 18  
19   - class ProjectRepositoryBranches < Grape::Entity
20   - expose :name, :commit
21   - end
22   -
23   - class ProjectRepositoryTags < Grape::Entity
  19 + class RepoObject < Grape::Entity
24 20 expose :name, :commit
25 21 end
26 22  
... ... @@ -29,5 +25,19 @@ module Gitlab
29 25 expose :author, :using => Entities::UserBasic
30 26 expose :expires_at, :updated_at, :created_at
31 27 end
  28 +
  29 + class Milestone < Grape::Entity
  30 + expose :id, :title, :description, :due_date, :closed, :updated_at, :created_at
  31 + end
  32 +
  33 + class Issue < Grape::Entity
  34 + expose :id
  35 + expose (:project_id) {|issue| issue.project.id}
  36 + expose :title, :description
  37 + expose :label_list, :as => :labels
  38 + expose :milestone, :using => Entities::Milestone
  39 + expose :assignee, :author, :using => Entities::UserBasic
  40 + expose :closed, :updated_at, :created_at
  41 + end
32 42 end
33 43 end
... ...
lib/api/helpers.rb
... ... @@ -4,6 +4,16 @@ module Gitlab
4 4 @current_user ||= User.find_by_authentication_token(params[:private_token])
5 5 end
6 6  
  7 + def user_project
  8 + if @project ||= current_user.projects.find_by_id(params[:id]) ||
  9 + current_user.projects.find_by_code(params[:id])
  10 + else
  11 + error!({'message' => '404 Not found'}, 404)
  12 + end
  13 +
  14 + @project
  15 + end
  16 +
7 17 def authenticate!
8 18 error!({'message' => '401 Unauthorized'}, 401) unless current_user
9 19 end
... ...
lib/api/issues.rb 0 → 100644
... ... @@ -0,0 +1,111 @@
  1 +module Gitlab
  2 + # Issues API
  3 + class Issues < Grape::API
  4 + before { authenticate! }
  5 +
  6 + resource :issues do
  7 + # Get currently authenticated user's issues
  8 + #
  9 + # Example Request:
  10 + # GET /issues
  11 + get do
  12 + present current_user.issues, :with => Entities::Issue
  13 + end
  14 + end
  15 +
  16 + resource :projects do
  17 + # Get a list of project issues
  18 + #
  19 + # Parameters:
  20 + # id (required) - The ID or code name of a project
  21 + # Example Request:
  22 + # GET /projects/:id/issues
  23 + get ":id/issues" do
  24 + present user_project.issues, :with => Entities::Issue
  25 + end
  26 +
  27 + # Get a single project issue
  28 + #
  29 + # Parameters:
  30 + # id (required) - The ID or code name of a project
  31 + # issue_id (required) - The ID of a project issue
  32 + # Example Request:
  33 + # GET /projects/:id/issues/:issue_id
  34 + get ":id/issues/:issue_id" do
  35 + @issue = user_project.issues.find(params[:issue_id])
  36 + present @issue, :with => Entities::Issue
  37 + end
  38 +
  39 + # Create a new project issue
  40 + #
  41 + # Parameters:
  42 + # id (required) - The ID or code name of a project
  43 + # title (required) - The title of an issue
  44 + # description (optional) - The description of an issue
  45 + # assignee_id (optional) - The ID of a user to assign issue
  46 + # milestone_id (optional) - The ID of a milestone to assign issue
  47 + # labels (optional) - The labels of an issue
  48 + # Example Request:
  49 + # POST /projects/:id/issues
  50 + post ":id/issues" do
  51 + @issue = user_project.issues.new(
  52 + :title => params[:title],
  53 + :description => params[:description],
  54 + :assignee_id => params[:assignee_id],
  55 + :milestone_id => params[:milestone_id],
  56 + :label_list => params[:labels]
  57 + )
  58 + @issue.author = current_user
  59 +
  60 + if @issue.save
  61 + present @issue, :with => Entities::Issue
  62 + else
  63 + error!({'message' => '404 Not found'}, 404)
  64 + end
  65 + end
  66 +
  67 + # Update an existing issue
  68 + #
  69 + # Parameters:
  70 + # id (required) - The ID or code name of a project
  71 + # issue_id (required) - The ID of a project issue
  72 + # title (optional) - The title of an issue
  73 + # description (optional) - The description of an issue
  74 + # assignee_id (optional) - The ID of a user to assign issue
  75 + # milestone_id (optional) - The ID of a milestone to assign issue
  76 + # labels (optional) - The labels of an issue
  77 + # closed (optional) - The state of an issue (0 = false, 1 = true)
  78 + # Example Request:
  79 + # PUT /projects/:id/issues/:issue_id
  80 + put ":id/issues/:issue_id" do
  81 + @issue = user_project.issues.find(params[:issue_id])
  82 + parameters = {
  83 + :title => (params[:title] || @issue.title),
  84 + :description => (params[:description] || @issue.description),
  85 + :assignee_id => (params[:assignee_id] || @issue.assignee_id),
  86 + :milestone_id => (params[:milestone_id] || @issue.milestone_id),
  87 + :label_list => (params[:labels] || @issue.label_list),
  88 + :closed => (params[:closed] || @issue.closed)
  89 + }
  90 +
  91 + if @issue.update_attributes(parameters)
  92 + present @issue, :with => Entities::Issue
  93 + else
  94 + error!({'message' => '404 Not found'}, 404)
  95 + end
  96 + end
  97 +
  98 + # Delete a project issue
  99 + #
  100 + # Parameters:
  101 + # id (required) - The ID or code name of a project
  102 + # issue_id (required) - The ID of a project issue
  103 + # Example Request:
  104 + # DELETE /projects/:id/issues/:issue_id
  105 + delete ":id/issues/:issue_id" do
  106 + @issue = user_project.issues.find(params[:issue_id])
  107 + @issue.destroy
  108 + end
  109 + end
  110 + end
  111 +end
... ...
lib/api/projects.rb
... ... @@ -16,53 +16,49 @@ module Gitlab
16 16 # Get a single project
17 17 #
18 18 # Parameters:
19   - # id (required) - The code of a project
  19 + # id (required) - The ID or code name of a project
20 20 # Example Request:
21 21 # GET /projects/:id
22 22 get ":id" do
23   - @project = current_user.projects.find_by_code(params[:id])
24   - present @project, :with => Entities::Project
  23 + present user_project, :with => Entities::Project
25 24 end
26 25  
27 26 # Get a project repository branches
28 27 #
29 28 # Parameters:
30   - # id (required) - The code of a project
  29 + # id (required) - The ID or code name of a project
31 30 # Example Request:
32 31 # GET /projects/:id/repository/branches
33 32 get ":id/repository/branches" do
34   - @project = current_user.projects.find_by_code(params[:id])
35   - present @project.repo.heads.sort_by(&:name), :with => Entities::ProjectRepositoryBranches
  33 + present user_project.repo.heads.sort_by(&:name), :with => Entities::RepoObject
36 34 end
37 35  
38 36 # Get a project repository tags
39 37 #
40 38 # Parameters:
41   - # id (required) - The code of a project
  39 + # id (required) - The ID or code name of a project
42 40 # Example Request:
43 41 # GET /projects/:id/repository/tags
44 42 get ":id/repository/tags" do
45   - @project = current_user.projects.find_by_code(params[:id])
46   - present @project.repo.tags.sort_by(&:name).reverse, :with => Entities::ProjectRepositoryTags
  43 + present user_project.repo.tags.sort_by(&:name).reverse, :with => Entities::RepoObject
47 44 end
48 45  
49 46 # Get a project snippet
50 47 #
51 48 # Parameters:
52   - # id (required) - The code of a project
  49 + # id (required) - The ID or code name of a project
53 50 # snippet_id (required) - The ID of a project snippet
54 51 # Example Request:
55 52 # GET /projects/:id/snippets/:snippet_id
56 53 get ":id/snippets/:snippet_id" do
57   - @project = current_user.projects.find_by_code(params[:id])
58   - @snippet = @project.snippets.find(params[:snippet_id])
  54 + @snippet = user_project.snippets.find(params[:snippet_id])
59 55 present @snippet, :with => Entities::ProjectSnippet
60 56 end
61 57  
62 58 # Create a new project snippet
63 59 #
64 60 # Parameters:
65   - # id (required) - The code name of a project
  61 + # id (required) - The ID or code name of a project
66 62 # title (required) - The title of a snippet
67 63 # file_name (required) - The name of a snippet file
68 64 # lifetime (optional) - The expiration date of a snippet
... ... @@ -70,8 +66,7 @@ module Gitlab
70 66 # Example Request:
71 67 # POST /projects/:id/snippets
72 68 post ":id/snippets" do
73   - @project = current_user.projects.find_by_code(params[:id])
74   - @snippet = @project.snippets.new(
  69 + @snippet = user_project.snippets.new(
75 70 :title => params[:title],
76 71 :file_name => params[:file_name],
77 72 :expires_at => params[:lifetime],
... ... @@ -89,7 +84,7 @@ module Gitlab
89 84 # Update an existing project snippet
90 85 #
91 86 # Parameters:
92   - # id (required) - The code name of a project
  87 + # id (required) - The ID or code name of a project
93 88 # snippet_id (required) - The ID of a project snippet
94 89 # title (optional) - The title of a snippet
95 90 # file_name (optional) - The name of a snippet file
... ... @@ -98,8 +93,7 @@ module Gitlab
98 93 # Example Request:
99 94 # PUT /projects/:id/snippets/:snippet_id
100 95 put ":id/snippets/:snippet_id" do
101   - @project = current_user.projects.find_by_code(params[:id])
102   - @snippet = @project.snippets.find(params[:snippet_id])
  96 + @snippet = user_project.snippets.find(params[:snippet_id])
103 97 parameters = {
104 98 :title => (params[:title] || @snippet.title),
105 99 :file_name => (params[:file_name] || @snippet.file_name),
... ... @@ -117,26 +111,24 @@ module Gitlab
117 111 # Delete a project snippet
118 112 #
119 113 # Parameters:
120   - # id (required) - The code of a project
  114 + # id (required) - The ID or code name of a project
121 115 # snippet_id (required) - The ID of a project snippet
122 116 # Example Request:
123 117 # DELETE /projects/:id/snippets/:snippet_id
124 118 delete ":id/snippets/:snippet_id" do
125   - @project = current_user.projects.find_by_code(params[:id])
126   - @snippet = @project.snippets.find(params[:snippet_id])
  119 + @snippet = user_project.snippets.find(params[:snippet_id])
127 120 @snippet.destroy
128 121 end
129 122  
130 123 # Get a raw project snippet
131 124 #
132 125 # Parameters:
133   - # id (required) - The code of a project
  126 + # id (required) - The ID or code name of a project
134 127 # snippet_id (required) - The ID of a project snippet
135 128 # Example Request:
136 129 # GET /projects/:id/snippets/:snippet_id/raw
137 130 get ":id/snippets/:snippet_id/raw" do
138   - @project = current_user.projects.find_by_code(params[:id])
139   - @snippet = @project.snippets.find(params[:snippet_id])
  131 + @snippet = user_project.snippets.find(params[:snippet_id])
140 132 present @snippet.content
141 133 end
142 134 end
... ...
lib/gitlab/logger.rb
... ... @@ -10,6 +10,7 @@ module Gitlab
10 10  
11 11 def self.read_latest
12 12 path = Rails.root.join("log/githost.log")
  13 + self.build unless File.exist?(path)
13 14 logs = File.read(path).split("\n")
14 15 end
15 16  
... ...
lib/tasks/gitlab/backup.rake
... ... @@ -121,7 +121,7 @@ namespace :gitlab do
121 121 backup_path_repo = File.join(Gitlab.config.backup_path, "repositories")
122 122 FileUtils.mkdir_p(backup_path_repo) until Dir.exists?(backup_path_repo)
123 123 puts "Dumping repositories:"
124   - project = Project.all.map { |n| [n.name,n.path_to_repo] }
  124 + project = Project.all.map { |n| [n.path,n.path_to_repo] }
125 125 project << ["gitolite-admin.git", File.join(File.dirname(project.first.second), "gitolite-admin.git")]
126 126 project.each do |project|
127 127 print "- Dumping repository #{project.first}... "
... ... @@ -136,12 +136,18 @@ namespace :gitlab do
136 136 task :repo_restore => :environment do
137 137 backup_path_repo = File.join(Gitlab.config.backup_path, "repositories")
138 138 puts "Restoring repositories:"
139   - project = Project.all.map { |n| [n.name,n.path_to_repo] }
  139 + project = Project.all.map { |n| [n.path,n.path_to_repo] }
140 140 project << ["gitolite-admin.git", File.join(File.dirname(project.first.second), "gitolite-admin.git")]
141 141 project.each do |project|
142 142 print "- Restoring repository #{project.first}... "
143 143 FileUtils.rm_rf(project.second) if File.dirname(project.second) # delet old stuff
144 144 if Kernel.system("cd #{File.dirname(project.second)} > /dev/null 2>&1 && git clone --bare #{backup_path_repo}/#{project.first}.bundle #{project.first}.git > /dev/null 2>&1")
  145 + permission_commands = [
  146 + "sudo chmod -R g+rwX #{Gitlab.config.git_base_path}",
  147 + "sudo chown -R #{Gitlab.config.ssh_user}:#{Gitlab.config.ssh_user} #{Gitlab.config.git_base_path}",
  148 + "sudo chown gitlab:gitlab /home/git/repositories/**/hooks/post-receive"
  149 + ]
  150 + permission_commands.each { |command| Kernel.system(command) }
145 151 puts "[DONE]".green
146 152 else
147 153 puts "[FAILED]".red
... ...
lib/tasks/gitlab/status.rake
... ... @@ -2,7 +2,7 @@ namespace :gitlab do
2 2 namespace :app do
3 3 desc "GITLAB | Check gitlab installation status"
4 4 task :status => :environment do
5   - puts "Starting diagnostic"
  5 + puts "Starting diagnostic".yellow
6 6 git_base_path = Gitlab.config.git_base_path
7 7  
8 8 print "config/database.yml............"
... ... @@ -56,7 +56,28 @@ namespace :gitlab do
56 56 return
57 57 end
58 58  
59   - puts "\nFinished"
  59 + if Project.count > 0
  60 + puts "Validating projects repositories:".yellow
  61 + Project.find_each(:batch_size => 100) do |project|
  62 + print "#{project.name}....."
  63 + hook_file = File.join(project.path_to_repo, 'hooks','post-receive')
  64 +
  65 + unless File.exists?(hook_file)
  66 + puts "post-receive file missing".red
  67 + next
  68 + end
  69 +
  70 +
  71 + unless File.owned?(hook_file)
  72 + puts "post-receive file is not owner by gitlab".red
  73 + next
  74 + end
  75 +
  76 + puts "post-reveice file ok".green
  77 + end
  78 + end
  79 +
  80 + puts "\nFinished".blue
60 81 end
61 82 end
62 83 end
... ...
spec/api/issues_spec.rb 0 → 100644
... ... @@ -0,0 +1,71 @@
  1 +require 'spec_helper'
  2 +
  3 +describe Gitlab::API do
  4 + let(:user) { Factory :user }
  5 + let!(:project) { Factory :project, :owner => user }
  6 + let!(:issue) { Factory :issue, :author => user, :assignee => user, :project => project }
  7 + before { project.add_access(user, :read) }
  8 +
  9 + describe "GET /issues" do
  10 + it "should return authentication error" do
  11 + get "#{api_prefix}/issues"
  12 + response.status.should == 401
  13 + end
  14 +
  15 + describe "authenticated GET /issues" do
  16 + it "should return an array of issues" do
  17 + get "#{api_prefix}/issues?private_token=#{user.private_token}"
  18 + response.status.should == 200
  19 + json_response.should be_an Array
  20 + json_response.first['title'].should == issue.title
  21 + end
  22 + end
  23 + end
  24 +
  25 + describe "GET /projects/:id/issues" do
  26 + it "should return project issues" do
  27 + get "#{api_prefix}/projects/#{project.code}/issues?private_token=#{user.private_token}"
  28 + response.status.should == 200
  29 + json_response.should be_an Array
  30 + json_response.first['title'].should == issue.title
  31 + end
  32 + end
  33 +
  34 + describe "GET /projects/:id/issues/:issue_id" do
  35 + it "should return a project issue by id" do
  36 + get "#{api_prefix}/projects/#{project.code}/issues/#{issue.id}?private_token=#{user.private_token}"
  37 + response.status.should == 200
  38 + json_response['title'].should == issue.title
  39 + end
  40 + end
  41 +
  42 + describe "POST /projects/:id/issues" do
  43 + it "should create a new project issue" do
  44 + post "#{api_prefix}/projects/#{project.code}/issues?private_token=#{user.private_token}",
  45 + :title => 'new issue', :labels => 'label, label2'
  46 + response.status.should == 201
  47 + json_response['title'].should == 'new issue'
  48 + json_response['description'].should be_nil
  49 + json_response['labels'].should == ['label', 'label2']
  50 + end
  51 + end
  52 +
  53 + describe "PUT /projects/:id/issues/:issue_id" do
  54 + it "should update a project issue" do
  55 + put "#{api_prefix}/projects/#{project.code}/issues/#{issue.id}?private_token=#{user.private_token}",
  56 + :title => 'updated title', :labels => 'label2', :closed => 1
  57 + response.status.should == 200
  58 + json_response['title'].should == 'updated title'
  59 + json_response['labels'].should == ['label2']
  60 + json_response['closed'].should be_true
  61 + end
  62 + end
  63 +
  64 + describe "DELETE /projects/:id/issues/:issue_id" do
  65 + it "should delete a project issue" do
  66 + expect {
  67 + delete "#{api_prefix}/projects/#{project.code}/issues/#{issue.id}?private_token=#{user.private_token}"
  68 + }.to change { Issue.count }.by(-1)
  69 + end
  70 + end
  71 +end
... ...
spec/api/projects_spec.rb
... ... @@ -25,11 +25,23 @@ describe Gitlab::API do
25 25  
26 26 describe "GET /projects/:id" do
27 27 it "should return a project by id" do
28   - get "#{api_prefix}/projects/#{project.code}?private_token=#{user.private_token}"
  28 + get "#{api_prefix}/projects/#{project.id}?private_token=#{user.private_token}"
29 29 response.status.should == 200
30 30 json_response['name'].should == project.name
31 31 json_response['owner']['email'].should == user.email
32 32 end
  33 +
  34 + it "should return a project by code name" do
  35 + get "#{api_prefix}/projects/#{project.code}?private_token=#{user.private_token}"
  36 + response.status.should == 200
  37 + json_response['name'].should == project.name
  38 + end
  39 +
  40 + it "should return a 404 error if not found" do
  41 + get "#{api_prefix}/projects/42?private_token=#{user.private_token}"
  42 + response.status.should == 404
  43 + json_response['message'].should == '404 Not found'
  44 + end
33 45 end
34 46  
35 47 describe "GET /projects/:id/repository/branches" do
... ...
spec/requests/commits_spec.rb
... ... @@ -2,7 +2,7 @@ require &#39;spec_helper&#39;
2 2  
3 3 describe "Commits" do
4 4 let(:project) { Factory :project }
5   - let!(:commit) { project.commit }
  5 + let!(:commit) { CommitDecorator.decorate(project.commit) }
6 6 before do
7 7 login_as :user
8 8 project.add_access(@user, :read)
... ... @@ -22,8 +22,8 @@ describe &quot;Commits&quot; do
22 22 end
23 23  
24 24 it "should list commits" do
25   - page.should have_content(commit.message)
26   - page.should have_content(commit.id.to_s[0..5])
  25 + page.should have_content(commit.description)
  26 + page.should have_content(commit.short_id(8))
27 27 end
28 28  
29 29 it "should render atom feed" do
... ... @@ -32,7 +32,7 @@ describe &quot;Commits&quot; do
32 32 page.response_headers['Content-Type'].should have_content("application/atom+xml")
33 33 page.body.should have_selector("title", :text => "Recent commits to #{project.name}")
34 34 page.body.should have_selector("author email", :text => commit.author_email)
35   - page.body.should have_selector("entry summary", :text => commit.message)
  35 + page.body.should have_selector("entry summary", :text => commit.description)
36 36 end
37 37  
38 38 it "should render atom feed via private token" do
... ... @@ -42,7 +42,7 @@ describe &quot;Commits&quot; do
42 42 page.response_headers['Content-Type'].should have_content("application/atom+xml")
43 43 page.body.should have_selector("title", :text => "Recent commits to #{project.name}")
44 44 page.body.should have_selector("author email", :text => commit.author_email)
45   - page.body.should have_selector("entry summary", :text => commit.message)
  45 + page.body.should have_selector("entry summary", :text => commit.description)
46 46 end
47 47 end
48 48  
... ...
spec/requests/wikis_notes_spec.rb 0 → 100644
... ... @@ -0,0 +1,29 @@
  1 +require 'spec_helper'
  2 +
  3 +describe "Wikis" do
  4 + let(:project) { Factory :project }
  5 +
  6 + before do
  7 + login_as :user
  8 + project.add_access(@user, :read, :write)
  9 + end
  10 +
  11 + describe "add new note", :js => true do
  12 + before do
  13 + visit project_wiki_path(project, :index)
  14 +
  15 + fill_in "Title", :with => 'Test title'
  16 + fill_in "Content", :with => '[link test](test)'
  17 + click_on "Save"
  18 +
  19 + page.should have_content("Test title")
  20 +
  21 + fill_in "note_note", :with => "Comment on wiki!"
  22 + click_button "Add Comment"
  23 + end
  24 +
  25 + it "should contain the new note" do
  26 + page.should have_content("Comment on wiki!")
  27 + end
  28 + end
  29 +end
... ...