Commit 9265de3d authored by gitlabhq's avatar gitlabhq

snippets are ready

parent 526ad466
$(document).ready(function(){
$("#snippets-table .snippet").live('click', function(e){
if(e.target.nodeName != "A" && e.target.nodeName != "INPUT") {
location.href = $(this).attr("url");
e.stopPropagation();
return false;
}
});
});
...@@ -22,8 +22,8 @@ td.linenos{ ...@@ -22,8 +22,8 @@ td.linenos{
.highlight{ .highlight{
background:none; background:none;
padding:10px 0px 0px 0; padding:10px 0px 0px 10px;
margin-left:10px; margin-left:0px;
} }
.highlight pre{ .highlight pre{
} }
...@@ -43,7 +43,7 @@ td.linenos { ...@@ -43,7 +43,7 @@ td.linenos {
} }
td.code .highlight { td.code .highlight {
overflow-x: scroll; overflow: auto;
} }
table.highlighttable pre{ table.highlighttable pre{
padding:0; padding:0;
......
...@@ -310,6 +310,7 @@ input.ssh_project_url { ...@@ -310,6 +310,7 @@ input.ssh_project_url {
} }
#projects-list .project, #projects-list .project,
#snippets-table .snippet,
#issues-table .issue{ #issues-table .issue{
cursor:pointer; cursor:pointer;
...@@ -360,6 +361,8 @@ input.ssh_project_url { ...@@ -360,6 +361,8 @@ input.ssh_project_url {
.user_new, .user_new,
.edit_user, .edit_user,
.new_project, .new_project,
.new_snippet,
.edit_snippet,
.edit_project { .edit_project {
input[type='text'], input[type='text'],
input[type='email'], input[type='email'],
......
// Place all the styles related to the Snippets controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
...@@ -41,6 +41,8 @@ class NotesController < ApplicationController ...@@ -41,6 +41,8 @@ class NotesController < ApplicationController
Notify.note_commit_email(u, @note).deliver Notify.note_commit_email(u, @note).deliver
when "Issue" then when "Issue" then
Notify.note_issue_email(u, @note).deliver Notify.note_issue_email(u, @note).deliver
when "Snippet"
true
else else
Notify.note_wall_email(u, @note).deliver Notify.note_wall_email(u, @note).deliver
end end
......
class SnippetsController < ApplicationController
before_filter :authenticate_user!
before_filter :project
# Authorize
before_filter :add_project_abilities
before_filter :authorize_read_snippet!
before_filter :authorize_write_snippet!, :only => [:new, :create, :close, :edit, :update, :sort]
respond_to :html
def index
@snippets = @project.snippets
end
def new
@snippet = @project.snippets.new
end
def create
@snippet = @project.snippets.new(params[:snippet])
@snippet.author = current_user
@snippet.save
if @snippet.valid?
redirect_to [@project, @snippet]
else
respond_with(@snippet)
end
end
def edit
@snippet = @project.snippets.find(params[:id])
end
def update
@snippet = @project.snippets.find(params[:id])
@snippet.update_attributes(params[:snippet])
if @snippet.valid?
redirect_to [@project, @snippet]
else
respond_with(@snippet)
end
end
def show
@snippet = @project.snippets.find(params[:id])
@notes = @snippet.notes
@note = @project.notes.new(:noteable => @snippet)
end
def destroy
@snippet = @project.snippets.find(params[:id])
authorize_admin_snippet! unless @snippet.author == current_user
@snippet.destroy
respond_to do |format|
format.js { render :nothing => true }
end
end
end
...@@ -53,7 +53,7 @@ module ApplicationHelper ...@@ -53,7 +53,7 @@ module ApplicationHelper
[projects, default_nav, project_nav].flatten.to_json [projects, default_nav, project_nav].flatten.to_json
end end
def handle_file_type(file_name, mime_type) def handle_file_type(file_name, mime_type = nil)
if file_name =~ /(\.rb|\.ru|\.rake|Rakefile|\.gemspec|\.rbx|Gemfile)$/ if file_name =~ /(\.rb|\.ru|\.rake|Rakefile|\.gemspec|\.rbx|Gemfile)$/
:ruby :ruby
elsif file_name =~ /\.py$/ elsif file_name =~ /\.py$/
......
module SnippetsHelper
end
...@@ -12,6 +12,7 @@ class Ability ...@@ -12,6 +12,7 @@ class Ability
rules << [ rules << [
:read_project, :read_project,
:read_issue, :read_issue,
:read_snippet,
:read_team_member, :read_team_member,
:read_note :read_note
] if project.readers.include?(user) ] if project.readers.include?(user)
...@@ -19,12 +20,14 @@ class Ability ...@@ -19,12 +20,14 @@ class Ability
rules << [ rules << [
:write_project, :write_project,
:write_issue, :write_issue,
:write_snippet,
:write_note :write_note
] if project.writers.include?(user) ] if project.writers.include?(user)
rules << [ rules << [
:admin_project, :admin_project,
:admin_issue, :admin_issue,
:admin_snippet,
:admin_team_member, :admin_team_member,
:admin_note :admin_note
] if project.admins.include?(user) ] if project.admins.include?(user)
......
...@@ -7,6 +7,7 @@ class Project < ActiveRecord::Base ...@@ -7,6 +7,7 @@ class Project < ActiveRecord::Base
has_many :users_projects, :dependent => :destroy has_many :users_projects, :dependent => :destroy
has_many :users, :through => :users_projects has_many :users, :through => :users_projects
has_many :notes, :dependent => :destroy has_many :notes, :dependent => :destroy
has_many :snippets, :dependent => :destroy
validates :name, validates :name,
:uniqueness => true, :uniqueness => true,
......
class Snippet < ActiveRecord::Base
belongs_to :project
belongs_to :author, :class_name => "User"
has_many :notes, :as => :noteable
attr_protected :author, :author_id, :project, :project_id
validates_presence_of :project_id
validates_presence_of :author_id
validates :title,
:presence => true,
:length => { :within => 0..255 }
validates :file_name,
:presence => true,
:length => { :within => 0..255 }
validates :content,
:presence => true,
:length => { :within => 0..10000 }
def self.content_types
[
".rb", ".py", ".pl", ".scala", ".c", ".cpp", ".java",
".haml", ".html", ".sass", ".scss", ".xml", ".php", ".erb",
".js", ".sh", ".coffee", ".yml", ".md"
]
end
end
= form_for(@project, :remote => true) do |f| = form_for(@project, :remote => true) do |f|
%div.form_content %div.form_content
- if @project.new_record? - unless @project.new_record?
%h1 New Project
- else
%h1 Edit Project %h1 Edit Project
- if @project.errors.any? - if @project.errors.any?
#error_explanation #error_explanation
......
...@@ -18,6 +18,11 @@ ...@@ -18,6 +18,11 @@
Wall Wall
- if @project.common_notes.count > 0 - if @project.common_notes.count > 0
%span{ :class => "top_menu_count" }= @project.common_notes.count %span{ :class => "top_menu_count" }= @project.common_notes.count
%span
= link_to project_snippets_path(@project), :class => (controller.controller_name == "snippets") ? "current" : nil do
Snippets
- if @project.snippets.count > 0
%span{ :class => "top_menu_count" }= @project.snippets.count
- if @commit - if @commit
%span= link_to truncate(commit_name(@project,@commit), :length => 15), project_commit_path(@project, :id => @commit.id), :class => current_page?(:controller => "commits", :action => "show", :project_id => @project, :id => @commit.id) ? "current" : nil %span= link_to truncate(commit_name(@project,@commit), :length => 15), project_commit_path(@project, :id => @commit.id), :class => current_page?(:controller => "commits", :action => "show", :project_id => @project, :id => @commit.id) ? "current" : nil
......
%div
= form_for [@project, @snippet] do |f|
-if @snippet.errors.any?
%ul
- @snippet.errors.full_messages.each do |msg|
%li= msg
%table.round-borders
%tr
%td= f.label :title
%td= f.text_field :title, :placeholder => "Example Snippet"
%tr
%td= f.label :file_name
%td= f.text_field :file_name, :placeholder => "example.rb"
%tr
%td{:colspan => 2}
= f.label :content, "Code"
%br
= f.text_area :content, :style => "height:240px;width:932px;"
.actions.prepend-top
= f.submit 'Save', :class => "lbutton vm"
%tr{ :id => dom_id(snippet), :class => "snippet", :url => project_snippet_path(@project, snippet) }
%td
= image_tag gravatar_icon(snippet.author.email), :class => "left", :width => 40, :style => "padding:0 5px;"
= truncate snippet.author.name, :lenght => 20
%td= html_escape snippet.title
%td= html_escape snippet.file_name
%td
- if can?(current_user, :admin_snippet, @project) || snippet.author == current_user
= link_to 'Edit', edit_project_snippet_path(@project, snippet), :class => "lbutton positive"
- if can?(current_user, :admin_snippet, @project) || snippet.author == current_user
= link_to 'Destroy', [@project, snippet], :confirm => 'Are you sure?', :method => :delete, :remote => true, :class => "lbutton delete-snippet negative", :id => "destroy_snippet_#{snippet.id}"
= render "snippets/form"
%div
- if can? current_user, :write_snippet, @project
.left= link_to 'New Snippet', new_project_snippet_path(@project), :class => "lbutton vm"
%table.round-borders#snippets-table
%tr
%th Author
%th Title
%th File name
%th
= render @snippets
:javascript
$('.delete-snippet').live('ajax:success', function() {
$(this).closest('tr').fadeOut(); });
= render "snippets/form"
%h2
= "Snippet ##{@snippet.id} - #{@snippet.title}"
.view_file
.view_file_header
%strong
= @snippet.file_name
%br/
.view_file_content
- ft = handle_file_type(@snippet.file_name)
:erb
<%= raw Albino.colorize(@snippet.content, ft, :html, 'utf-8', "linenos=True") %>
- if can?(current_user, :admin_snippet, @project) || @snippet.author == current_user
= link_to 'Edit', edit_project_snippet_path(@project, @snippet), :class => "lbutton positive"
- if can?(current_user, :admin_snippet, @project) || @snippet.author == current_user
= link_to 'Destroy', [@project, @snippet], :confirm => 'Are you sure?', :method => :delete, :class => "lbutton delete-snippet negative", :id => "destroy_snippet_#{@snippet.id}"
%br
.snippet_notes= render "notes/notes"
.clear
...@@ -38,6 +38,8 @@ Gitlab::Application.routes.draw do ...@@ -38,6 +38,8 @@ Gitlab::Application.routes.draw do
} }
end end
resources :snippets
resources :commits resources :commits
resources :team_members resources :team_members
resources :issues do resources :issues do
......
class CreateSnippets < ActiveRecord::Migration
def change
create_table :snippets do |t|
t.string :title
t.text :content
t.integer :author_id, :null => false
t.integer :project_id, :null => false
t.timestamps
end
end
end
class AddContentTypeToSnippets < ActiveRecord::Migration
def change
add_column :snippets, :content_type, :string, :null => false, :default => "txt"
end
end
class AddFileNameToSnippets < ActiveRecord::Migration
def change
add_column :snippets, :file_name, :string
remove_column :snippets, :content_type
end
end
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended to check this file into your version control system. # It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 20111015154310) do ActiveRecord::Schema.define(:version => 20111016195506) do
create_table "issues", :force => true do |t| create_table "issues", :force => true do |t|
t.string "title" t.string "title"
...@@ -56,6 +56,16 @@ ActiveRecord::Schema.define(:version => 20111015154310) do ...@@ -56,6 +56,16 @@ ActiveRecord::Schema.define(:version => 20111015154310) do
t.integer "owner_id" t.integer "owner_id"
end end
create_table "snippets", :force => true do |t|
t.string "title"
t.text "content"
t.integer "author_id", :null => false
t.integer "project_id", :null => false
t.datetime "created_at"
t.datetime "updated_at"
t.string "file_name"
end
create_table "users", :force => true do |t| create_table "users", :force => true do |t|
t.string "email", :default => "", :null => false t.string "email", :default => "", :null => false
t.string "encrypted_password", :limit => 128, :default => "", :null => false t.string "encrypted_password", :limit => 128, :default => "", :null => false
......
...@@ -35,6 +35,12 @@ Factory.add(:issue, Issue) do |obj| ...@@ -35,6 +35,12 @@ Factory.add(:issue, Issue) do |obj|
obj.content = Faker::Lorem.sentences obj.content = Faker::Lorem.sentences
end end
Factory.add(:snippet, Snippet) do |obj|
obj.title = Faker::Lorem.sentence
obj.file_name = Faker::Lorem.sentence
obj.content = Faker::Lorem.sentences
end
Factory.add(:note, Note) do |obj| Factory.add(:note, Note) do |obj|
obj.note = Faker::Lorem.sentence obj.note = Faker::Lorem.sentence
end end
......
require 'spec_helper'
describe Snippet do
describe "Associations" do
it { should belong_to(:project) }
it { should belong_to(:author) }
end
describe "Validation" do
it { should validate_presence_of(:title) }
it { should validate_presence_of(:author_id) }
it { should validate_presence_of(:project_id) }
it { should validate_presence_of(:file_name) }
it { should validate_presence_of(:content) }
end
end
...@@ -107,5 +107,14 @@ describe "Projects" do ...@@ -107,5 +107,14 @@ describe "Projects" do
it { project_issues_path(@project).should be_denied_for :user } it { project_issues_path(@project).should be_denied_for :user }
it { project_issues_path(@project).should be_denied_for :visitor } it { project_issues_path(@project).should be_denied_for :visitor }
end end
describe "GET /project_code/snippets" do
it { project_snippets_path(@project).should be_allowed_for @u1 }
it { project_snippets_path(@project).should be_allowed_for @u3 }
it { project_snippets_path(@project).should be_denied_for :admin }
it { project_snippets_path(@project).should be_denied_for @u2 }
it { project_snippets_path(@project).should be_denied_for :user }
it { project_snippets_path(@project).should be_denied_for :visitor }
end
end end
end end
require 'spec_helper'
describe "Snippets" do
let(:project) { Factory :project }
before do
login_as :user
project.add_access(@user, :read, :write)
end
describe "GET /snippets" do
before do
@snippet = Factory :snippet,
:author => @user,
:project => project
visit project_snippets_path(project)
end
subject { page }
it { should have_content(@snippet.title) }
it { should have_content(@snippet.project.name) }
it { should have_content(@snippet.author.name) }
describe "Destroy" do
before do
# admin access to remove snippet
@user.users_projects.destroy_all
project.add_access(@user, :read, :write, :admin)
visit project_snippets_path(project)
end
it "should remove entry" do
expect {
click_link "destroy_snippet_#{@snippet.id}"
}.to change { Snippet.count }.by(-1)
end
end
end
describe "New snippet" do
before do
visit project_snippets_path(project)
click_link "New Snippet"
end
it "should open new snippet popup" do
page.current_path.should == new_project_snippet_path(project)
end
describe "fill in" do
before do
fill_in "snippet_title", :with => "login function"
fill_in "snippet_file_name", :with => "test.rb"
fill_in "snippet_content", :with => "def login; end"
end
it { expect { click_button "Save" }.to change {Snippet.count}.by(1) }
it "should add new snippet to table" do
click_button "Save"
page.current_path.should == project_snippet_path(project, Snippet.last)
page.should have_content "login function"
page.should have_content "test.rb"
end
end
end
describe "Edit snippet" do
before do
@snippet = Factory :snippet,
:author => @user,
:project => project
visit project_snippets_path(project)
click_link "Edit"
end
it "should open edit page" do
page.current_path.should == edit_project_snippet_path(project, @snippet)
end
describe "fill in" do
before do
fill_in "snippet_title", :with => "login function"
fill_in "snippet_file_name", :with => "test.rb"
fill_in "snippet_content", :with => "def login; end"
end
it { expect { click_button "Save" }.to_not change {Snippet.count} }
it "should update snippet fields" do
click_button "Save"
page.current_path.should == project_snippet_path(project, @snippet)
page.should have_content "login function"
page.should have_content "test.rb"
end
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment