summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>2013-06-04 18:36:22 +0300
committerDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>2013-06-04 18:36:22 +0300
commitbcc4e4dc7ed0740e92a61fc82c3c669f8f2d8d30 (patch)
tree1df3e2d68cd524af4dce107b2e4227778bb3945d
parent211e435ade337c968fab11c52427c172adcec99a (diff)
parente0af7cefb4c92b474d14116b40927d70c13e78cc (diff)
downloadgitlab-ce-bcc4e4dc7ed0740e92a61fc82c3c669f8f2d8d30.tar.gz
Merge branch 'gist' of https://github.com/Andrew8xx8/gitlabhq into Andrew8xx8-gist
Conflicts: Gemfile.lock app/models/ability.rb app/models/project.rb app/views/snippets/_form.html.haml db/schema.rb features/steps/shared/paths.rb spec/factories.rb spec/models/project_spec.rb
-rw-r--r--app/controllers/projects/application_controller.rb11
-rw-r--r--app/controllers/projects/snippets_controller.rb91
-rw-r--r--app/controllers/projects/teams_controller.rb7
-rw-r--r--app/controllers/snippets_controller.rb64
-rw-r--r--app/helpers/tab_helper.rb2
-rw-r--r--app/models/ability.rb14
-rw-r--r--app/models/event.rb4
-rw-r--r--app/models/note.rb6
-rw-r--r--app/models/personal_snippet.rb18
-rw-r--r--app/models/project.rb2
-rw-r--r--app/models/project_snippet.rb27
-rw-r--r--app/models/snippet.rb14
-rw-r--r--app/models/user.rb1
-rw-r--r--app/views/events/event/_note.html.haml4
-rw-r--r--app/views/layouts/snippets.html.haml22
-rw-r--r--app/views/projects/snippets/_blob.html.haml12
-rw-r--r--app/views/projects/snippets/_form.html.haml41
-rw-r--r--app/views/projects/snippets/_snippet.html.haml13
-rw-r--r--app/views/projects/snippets/edit.html.haml1
-rw-r--r--app/views/projects/snippets/index.html.haml19
-rw-r--r--app/views/projects/snippets/new.html.haml1
-rw-r--r--app/views/projects/snippets/show.html.haml9
-rw-r--r--app/views/snippets/_blob.html.haml2
-rw-r--r--app/views/snippets/_form.html.haml9
-rw-r--r--app/views/snippets/_snippet.html.haml15
-rw-r--r--app/views/snippets/_snippets.html.haml15
-rw-r--r--app/views/snippets/edit.html.haml2
-rw-r--r--app/views/snippets/index.html.haml24
-rw-r--r--app/views/snippets/new.html.haml2
-rw-r--r--app/views/snippets/show.html.haml10
-rw-r--r--app/views/snippets/user_index.html.haml20
-rw-r--r--config/routes.rb25
-rw-r--r--db/migrate/20130323174317_add_private_to_snippets.rb5
-rw-r--r--db/migrate/20130324151736_add_type_to_snippets.rb5
-rw-r--r--db/migrate/20130324172327_change_project_id_to_null_in_snipepts.rb9
-rw-r--r--db/migrate/20130324203535_add_type_value_for_snippets.rb8
-rw-r--r--db/schema.rb10
-rw-r--r--features/project/snippets.feature35
-rw-r--r--features/snippets/discover_snippets.feature10
-rw-r--r--features/snippets/snippets.feature28
-rw-r--r--features/snippets/user_snippets.feature22
-rw-r--r--features/steps/project/project_snippets.rb100
-rw-r--r--features/steps/shared/paths.rb16
-rw-r--r--features/steps/shared/snippet.rb21
-rw-r--r--features/steps/snippets/discover_snippets.rb17
-rw-r--r--features/steps/snippets/snippets.rb65
-rw-r--r--features/steps/snippets/user_snippets.rb41
-rw-r--r--lib/api/projects.rb6
-rw-r--r--spec/factories.rb16
-rw-r--r--spec/features/snippets_spec.rb99
-rw-r--r--spec/helpers/gitlab_markdown_helper_spec.rb39
-rw-r--r--spec/models/project_snippet_spec.rb30
-rw-r--r--spec/models/project_spec.rb2
-rw-r--r--spec/models/snippet_spec.rb3
-rw-r--r--spec/models/user_spec.rb1
-rw-r--r--spec/requests/api/notes_spec.rb2
-rw-r--r--spec/requests/api/projects_spec.rb2
-rw-r--r--spec/routing/project_routing_spec.rb32
-rw-r--r--spec/routing/routing_spec.rb45
59 files changed, 967 insertions, 209 deletions
diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb
index 7e4776d2d75..86e4a7cbd6b 100644
--- a/app/controllers/projects/application_controller.rb
+++ b/app/controllers/projects/application_controller.rb
@@ -1,11 +1,4 @@
class Projects::ApplicationController < ApplicationController
-
- before_filter :authorize_admin_team_member!
-
- protected
-
- def user_team
- @team ||= UserTeam.find_by_path(params[:id])
- end
-
+ before_filter :project
+ before_filter :repository
end
diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb
new file mode 100644
index 00000000000..1165fa1c583
--- /dev/null
+++ b/app/controllers/projects/snippets_controller.rb
@@ -0,0 +1,91 @@
+class Projects::SnippetsController < Projects::ApplicationController
+ before_filter :module_enabled
+ before_filter :snippet, only: [:show, :edit, :destroy, :update, :raw]
+
+ # Allow read any snippet
+ before_filter :authorize_read_project_snippet!
+
+ # Allow write(create) snippet
+ before_filter :authorize_write_project_snippet!, only: [:new, :create]
+
+ # Allow modify snippet
+ before_filter :authorize_modify_project_snippet!, only: [:edit, :update]
+
+ # Allow destroy snippet
+ before_filter :authorize_admin_project_snippet!, only: [:destroy]
+
+ layout 'project_resource'
+
+ respond_to :html
+
+ def index
+ @snippets = @project.snippets.fresh.non_expired
+ end
+
+ def new
+ @snippet = @project.snippets.build
+ end
+
+ def create
+ @snippet = @project.snippets.build(params[:project_snippet])
+ @snippet.author = current_user
+
+ if @snippet.save
+ redirect_to project_snippet_path(@project, @snippet)
+ else
+ respond_with(@snippet)
+ end
+ end
+
+ def edit
+ end
+
+ def update
+ if @snippet.update_attributes(params[:project_snippet])
+ redirect_to project_snippet_path(@project, @snippet)
+ else
+ respond_with(@snippet)
+ end
+ end
+
+ def show
+ @note = @project.notes.new(noteable: @snippet)
+ @target_type = :snippet
+ @target_id = @snippet.id
+ end
+
+ def destroy
+ return access_denied! unless can?(current_user, :admin_project_snippet, @snippet)
+
+ @snippet.destroy
+
+ redirect_to project_snippets_path(@project)
+ end
+
+ def raw
+ send_data(
+ @snippet.content,
+ type: "text/plain",
+ disposition: 'inline',
+ filename: @snippet.file_name
+ )
+ end
+
+ protected
+
+ def snippet
+ @snippet ||= @project.snippets.find(params[:id])
+ end
+
+ def authorize_modify_project_snippet!
+ return render_404 unless can?(current_user, :modify_project_snippet, @snippet)
+ end
+
+ def authorize_admin_project_snippet!
+ return render_404 unless can?(current_user, :admin_project_snippet, @snippet)
+ end
+
+ def module_enabled
+ return render_404 unless @project.snippets_enabled
+ end
+end
diff --git a/app/controllers/projects/teams_controller.rb b/app/controllers/projects/teams_controller.rb
index 17e7367364a..c7d51b84fc4 100644
--- a/app/controllers/projects/teams_controller.rb
+++ b/app/controllers/projects/teams_controller.rb
@@ -1,5 +1,7 @@
class Projects::TeamsController < Projects::ApplicationController
+ before_filter :authorize_admin_team_member!
+
def available
@teams = current_user.is_admin? ? UserTeam.scoped : current_user.user_teams
@teams = @teams.without_project(project)
@@ -24,4 +26,9 @@ class Projects::TeamsController < Projects::ApplicationController
redirect_to project_team_index_path(project)
end
+ protected
+
+ def user_team
+ @team ||= UserTeam.find_by_path(params[:id])
+ end
end
diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb
index a2e22a670a3..70525beea15 100644
--- a/app/controllers/snippets_controller.rb
+++ b/app/controllers/snippets_controller.rb
@@ -1,13 +1,6 @@
-class SnippetsController < ProjectResourceController
- before_filter :module_enabled
+class SnippetsController < ApplicationController
before_filter :snippet, only: [:show, :edit, :destroy, :update, :raw]
- # Allow read any snippet
- before_filter :authorize_read_snippet!
-
- # Allow write(create) snippet
- before_filter :authorize_write_snippet!, only: [:new, :create]
-
# Allow modify snippet
before_filter :authorize_modify_snippet!, only: [:edit, :update]
@@ -17,22 +10,38 @@ class SnippetsController < ProjectResourceController
respond_to :html
def index
- @snippets = @project.snippets.fresh.non_expired
+ @snippets = Snippet.public.fresh.non_expired.page(params[:page]).per(20)
+ end
+
+ def user_index
+ @user = User.find_by_username(params[:username])
+
+ @snippets = @current_user.snippets.fresh.non_expired
+
+ @snippets = case params[:scope]
+ when 'public' then
+ @snippets.public
+ when 'private' then
+ @snippets.private
+ else
+ @snippets
+ end
+
+ @snippets = @snippets.page(params[:page]).per(20)
end
def new
- @snippet = @project.snippets.new
+ @snippet = PersonalSnippet.new
end
def create
- @snippet = @project.snippets.new(params[:snippet])
+ @snippet = PersonalSnippet.new(params[:personal_snippet])
@snippet.author = current_user
- @snippet.save
- if @snippet.valid?
- redirect_to [@project, @snippet]
+ if @snippet.save
+ redirect_to snippet_path(@snippet)
else
- respond_with(@snippet)
+ respond_with @snippet
end
end
@@ -40,27 +49,22 @@ class SnippetsController < ProjectResourceController
end
def update
- @snippet.update_attributes(params[:snippet])
-
- if @snippet.valid?
- redirect_to [@project, @snippet]
+ if @snippet.update_attributes(params[:personal_snippet])
+ redirect_to snippet_path(@snippet)
else
- respond_with(@snippet)
+ respond_with @snippet
end
end
def show
- @note = @project.notes.new(noteable: @snippet)
- @target_type = :snippet
- @target_id = @snippet.id
end
def destroy
- return access_denied! unless can?(current_user, :admin_snippet, @snippet)
+ return access_denied! unless can?(current_user, :admin_personal_snippet, @snippet)
@snippet.destroy
- redirect_to project_snippets_path(@project)
+ redirect_to snippets_path
end
def raw
@@ -75,18 +79,14 @@ class SnippetsController < ProjectResourceController
protected
def snippet
- @snippet ||= @project.snippets.find(params[:id])
+ @snippet ||= PersonalSnippet.find(params[:id])
end
def authorize_modify_snippet!
- return render_404 unless can?(current_user, :modify_snippet, @snippet)
+ return render_404 unless can?(current_user, :modify_personal_snippet, @snippet)
end
def authorize_admin_snippet!
- return render_404 unless can?(current_user, :admin_snippet, @snippet)
- end
-
- def module_enabled
- return render_404 unless @project.snippets_enabled
+ return render_404 unless can?(current_user, :admin_personal_snippet, @snippet)
end
end
diff --git a/app/helpers/tab_helper.rb b/app/helpers/tab_helper.rb
index d2be4b1a7e6..19aba0f5f6d 100644
--- a/app/helpers/tab_helper.rb
+++ b/app/helpers/tab_helper.rb
@@ -73,7 +73,7 @@ module TabHelper
end
def project_tab_class
- return "active" if current_page?(controller: "projects", action: :edit, id: @project)
+ return "active" if current_page?(controller: "/projects", action: :edit, id: @project)
if ['services', 'hooks', 'deploy_keys', 'team_members'].include? controller.controller_name
"active"
diff --git a/app/models/ability.rb b/app/models/ability.rb
index 8f0a6141b75..24b6ad182b4 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -7,7 +7,8 @@ class Ability
when "Project" then project_abilities(user, subject)
when "Issue" then issue_abilities(user, subject)
when "Note" then note_abilities(user, subject)
- when "Snippet" then snippet_abilities(user, subject)
+ when "ProjectSnippet" then project_snippet_abilities(user, subject)
+ when "PersonalSnippet" then personal_snippet_abilities(user, subject)
when "MergeRequest" then merge_request_abilities(user, subject)
when "Group", "Namespace" then group_abilities(user, subject)
when "UserTeam" then user_team_abilities(user, subject)
@@ -54,7 +55,7 @@ class Ability
:read_wiki,
:read_issue,
:read_milestone,
- :read_snippet,
+ :read_project_snippet,
:read_team_member,
:read_merge_request,
:read_note,
@@ -67,8 +68,8 @@ class Ability
def project_report_rules
project_guest_rules + [
:download_code,
- :write_snippet,
:fork_project
+ :write_project_snippet
]
end
@@ -84,11 +85,11 @@ class Ability
project_dev_rules + [
:push_code_to_protected_branches,
:modify_issue,
- :modify_snippet,
+ :modify_project_snippet,
:modify_merge_request,
:admin_issue,
:admin_milestone,
- :admin_snippet,
+ :admin_project_snippet,
:admin_team_member,
:admin_merge_request,
:admin_note,
@@ -135,8 +136,7 @@ class Ability
rules.flatten
end
-
- [:issue, :note, :snippet, :merge_request].each do |name|
+ [:issue, :note, :project_snippet, :personal_snippet, :merge_request].each do |name|
define_method "#{name}_abilities" do |user, subject|
if subject.author == user
[
diff --git a/app/models/event.rb b/app/models/event.rb
index 97f96ac8f9e..4b75087dc2a 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -241,6 +241,10 @@ class Event < ActiveRecord::Base
target.noteable_type == "Commit"
end
+ def note_project_snippet?
+ target.noteable_type == "Snippet"
+ end
+
def note_target
target.noteable
end
diff --git a/app/models/note.rb b/app/models/note.rb
index 7b7e6e99df4..9a3481faaaa 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -159,4 +159,10 @@ class Note < ActiveRecord::Base
"wall"
end
end
+
+ # FIXME: Hack for polymorphic associations with STI
+ # For more information wisit http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#label-Polymorphic+Associations
+ def noteable_type=(sType)
+ super(sType.to_s.classify.constantize.base_class.to_s)
+ end
end
diff --git a/app/models/personal_snippet.rb b/app/models/personal_snippet.rb
new file mode 100644
index 00000000000..d581c6092aa
--- /dev/null
+++ b/app/models/personal_snippet.rb
@@ -0,0 +1,18 @@
+# == Schema Information
+#
+# Table name: snippets
+#
+# id :integer not null, primary key
+# title :string(255)
+# content :text
+# author_id :integer not null
+# project_id :integer not null
+# created_at :datetime not null
+# updated_at :datetime not null
+# file_name :string(255)
+# expires_at :datetime
+# type :string(255)
+# private :boolean
+
+class PersonalSnippet < Snippet
+end
diff --git a/app/models/project.rb b/app/models/project.rb
index 9147aed3d40..8cb290f6601 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -57,7 +57,7 @@ class Project < ActiveRecord::Base
has_many :milestones, dependent: :destroy
has_many :users_projects, dependent: :destroy
has_many :notes, dependent: :destroy
- has_many :snippets, dependent: :destroy
+ has_many :snippets, dependent: :destroy, class_name: "ProjectSnippet"
has_many :hooks, dependent: :destroy, class_name: "ProjectHook"
has_many :protected_branches, dependent: :destroy
has_many :user_team_project_relationships, dependent: :destroy
diff --git a/app/models/project_snippet.rb b/app/models/project_snippet.rb
new file mode 100644
index 00000000000..a86f2e7a32f
--- /dev/null
+++ b/app/models/project_snippet.rb
@@ -0,0 +1,27 @@
+# == Schema Information
+#
+# Table name: snippets
+#
+# id :integer not null, primary key
+# title :string(255)
+# content :text
+# author_id :integer not null
+# project_id :integer not null
+# created_at :datetime not null
+# updated_at :datetime not null
+# file_name :string(255)
+# expires_at :datetime
+# type :string(255)
+# private :boolean
+
+class ProjectSnippet < Snippet
+ belongs_to :project
+ belongs_to :author, class_name: "User"
+
+ validates :project, presence: true
+
+ # Scopes
+ scope :fresh, -> { order("created_at DESC") }
+ scope :non_expired, -> { where(["expires_at IS NULL OR expires_at > ?", Time.current]) }
+ scope :expired, -> { where(["expires_at IS NOT NULL AND expires_at < ?", Time.current]) }
+end
diff --git a/app/models/snippet.rb b/app/models/snippet.rb
index c4ee35e0556..1b37ffe8339 100644
--- a/app/models/snippet.rb
+++ b/app/models/snippet.rb
@@ -11,29 +11,31 @@
# updated_at :datetime not null
# file_name :string(255)
# expires_at :datetime
-#
+# type :string(255)
+# private :boolean
class Snippet < ActiveRecord::Base
include Linguist::BlobHelper
- attr_accessible :title, :content, :file_name, :expires_at
+ attr_accessible :title, :content, :file_name, :expires_at, :private
- belongs_to :project
belongs_to :author, class_name: "User"
+
has_many :notes, as: :noteable, dependent: :destroy
delegate :name, :email, to: :author, prefix: true, allow_nil: true
validates :author, presence: true
- validates :project, presence: true
validates :title, presence: true, length: { within: 0..255 }
validates :file_name, presence: true, length: { within: 0..255 }
validates :content, presence: true
# Scopes
- scope :fresh, -> { order("created_at DESC") }
- scope :non_expired, -> { where(["expires_at IS NULL OR expires_at > ?", Time.current]) }
+ scope :public, -> { where(private: false) }
+ scope :private, -> { where(private: true) }
+ scope :fresh, -> { order("created_at DESC") }
scope :expired, -> { where(["expires_at IS NOT NULL AND expires_at < ?", Time.current]) }
+ scope :non_expired, -> { where(["expires_at IS NULL OR expires_at > ?", Time.current]) }
def self.content_types
[
diff --git a/app/models/user.rb b/app/models/user.rb
index 82a49c8dcca..0aed0ada757 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -78,6 +78,7 @@ class User < ActiveRecord::Base
has_many :team_projects, through: :user_team_project_relationships
# Projects
+ has_many :snippets, dependent: :destroy, foreign_key: :author_id, class_name: "Snippet"
has_many :users_projects, dependent: :destroy
has_many :issues, dependent: :destroy, foreign_key: :author_id
has_many :notes, dependent: :destroy, foreign_key: :author_id
diff --git a/app/views/events/event/_note.html.haml b/app/views/events/event/_note.html.haml
index 8bcfa95ff62..81b8ff9bf24 100644
--- a/app/views/events/event/_note.html.haml
+++ b/app/views/events/event/_note.html.haml
@@ -5,6 +5,10 @@
- if event.note_commit?
= event.note_target_type
= link_to event.note_short_commit_id, project_commit_path(event.project, event.note_commit_id), class: "commit_short_id"
+ - if event.note_project_snippet?
+ = link_to project_snippet_path(event.project, event.note_target) do
+ %strong
+ #{event.note_target_type} ##{truncate event.note_target_id}
- else
= link_to [event.project, event.note_target] do
%strong
diff --git a/app/views/layouts/snippets.html.haml b/app/views/layouts/snippets.html.haml
new file mode 100644
index 00000000000..73b502e9132
--- /dev/null
+++ b/app/views/layouts/snippets.html.haml
@@ -0,0 +1,22 @@
+!!! 5
+%html{ lang: "en"}
+ = render "layouts/head", title: "Snipepts"
+ %body{class: "#{app_theme} application"}
+ = render "layouts/head_panel", title: "Snippets"
+ = render "layouts/flash"
+ .container
+ %ul.main_menu
+ = nav_link(path: 'dashboard#show', html_options: {class: 'home'}) do
+ = link_to root_path, title: "Back to dashboard" do
+ %i.icon-arrow-left
+ = nav_link(path: 'snippet#new') do
+ = link_to new_snippet_path do
+ New snippet
+ = nav_link(path: 'snippets#user_index') do
+ = link_to user_snippets_path(@current_user) do
+ My snippets
+ = nav_link(path: 'snippets#index') do
+ = link_to snippets_path do
+ Discover snippets
+
+ .content= yield
diff --git a/app/views/projects/snippets/_blob.html.haml b/app/views/projects/snippets/_blob.html.haml
new file mode 100644
index 00000000000..017a33b34f3
--- /dev/null
+++ b/app/views/projects/snippets/_blob.html.haml
@@ -0,0 +1,12 @@
+.file_holder
+ .file_title
+ %i.icon-file
+ %strong= @snippet.file_name
+ %span.options
+ = link_to "raw", raw_project_snippet_path(@project, @snippet), class: "btn btn-tiny", target: "_blank"
+ .file_content.code
+ - unless @snippet.content.empty?
+ %div{class: user_color_scheme_class}
+ = raw @snippet.colorize(formatter: :gitlab)
+ - else
+ %p.nothing_here_message Empty file
diff --git a/app/views/projects/snippets/_form.html.haml b/app/views/projects/snippets/_form.html.haml
new file mode 100644
index 00000000000..99a8761daef
--- /dev/null
+++ b/app/views/projects/snippets/_form.html.haml
@@ -0,0 +1,41 @@
+%h3.page_title
+ = @snippet.new_record? ? "New Snippet" : "Edit Snippet ##{@snippet.id}"
+%hr
+.snippet-form-holder
+ = form_for [@project, @snippet], as: :project_snippet, url: url do |f|
+ -if @snippet.errors.any?
+ .alert.alert-error
+ %ul
+ - @snippet.errors.full_messages.each do |msg|
+ %li= msg
+
+ .clearfix
+ = f.label :title
+ .input= f.text_field :title, placeholder: "Example Snippet", class: 'input-xlarge', required: true
+ .clearfix
+ = f.label "Lifetime"
+ .input= f.select :expires_at, lifetime_select_options, {}, {class: 'chosen span2'}
+ .clearfix
+ .file-editor
+ = f.label :file_name, "File"
+ .input
+ .file_holder.snippet
+ .file_title
+ = f.text_field :file_name, placeholder: "example.rb", class: 'snippet-file-name', required: true
+ .file_content.code
+ %pre#editor= @snippet.content
+ = f.hidden_field :content, class: 'snippet-file-content'
+
+ .form-actions
+ = f.submit 'Save', class: "btn-save btn"
+ = link_to "Cancel", project_snippets_path(@project), class: " btn"
+ - unless @snippet.new_record?
+ .pull-right= link_to 'Destroy', project_snippet_path(@project, @snippet), confirm: 'Are you sure?', method: :delete, class: "btn pull-right danger delete-snippet", id: "destroy_snippet_#{@snippet.id}"
+
+
+:javascript
+ var editor = ace.edit("editor");
+ $(".snippet-form-holder form").submit(function(){
+ $(".snippet-file-content").val(editor.getValue());
+ });
+
diff --git a/app/views/projects/snippets/_snippet.html.haml b/app/views/projects/snippets/_snippet.html.haml
new file mode 100644
index 00000000000..a576500c15d
--- /dev/null
+++ b/app/views/projects/snippets/_snippet.html.haml
@@ -0,0 +1,13 @@
+%tr
+ %td
+ = image_tag gravatar_icon(snippet.author_email), class: "avatar s24"
+ %a{href: project_snippet_path(snippet.project, snippet)}
+ %strong= truncate(snippet.title, length: 60)
+ %td
+ = snippet.file_name
+ %td
+ %span.cgray
+ - if snippet.expires_at
+ = snippet.expires_at.to_date.to_s(:short)
+ - else
+ Never
diff --git a/app/views/projects/snippets/edit.html.haml b/app/views/projects/snippets/edit.html.haml
new file mode 100644
index 00000000000..e28b7d4937e
--- /dev/null
+++ b/app/views/projects/snippets/edit.html.haml
@@ -0,0 +1 @@
+= render "projects/snippets/form", url: project_snippet_path(@project, @snippet)
diff --git a/app/views/projects/snippets/index.html.haml b/app/views/projects/snippets/index.html.haml
new file mode 100644
index 00000000000..5971e3ffaac
--- /dev/null
+++ b/app/views/projects/snippets/index.html.haml
@@ -0,0 +1,19 @@
+%h3.page_title
+ Snippets
+ %small share code pastes with others out of git repository
+
+ - if can? current_user, :write_project_snippet, @project
+ = link_to new_project_snippet_path(@project), class: "btn btn-small add_new pull-right", title: "New Snippet" do
+ Add new snippet
+%br
+%table
+ %thead
+ %tr
+ %th Title
+ %th File Name
+ %th Expires At
+ = render partial: "projects/snippets/snippet", collection: @snippets
+ - if @snippets.empty?
+ %tr
+ %td{colspan: 3}
+ %h3.nothing_here_message Nothing here.
diff --git a/app/views/projects/snippets/new.html.haml b/app/views/projects/snippets/new.html.haml
new file mode 100644
index 00000000000..460af34f676
--- /dev/null
+++ b/app/views/projects/snippets/new.html.haml
@@ -0,0 +1 @@
+= render "projects/snippets/form", url: project_snippets_path(@project, @snippet)
diff --git a/app/views/projects/snippets/show.html.haml b/app/views/projects/snippets/show.html.haml
new file mode 100644
index 00000000000..db5a721dc45
--- /dev/null
+++ b/app/views/projects/snippets/show.html.haml
@@ -0,0 +1,9 @@
+%h3.page_title
+ = @snippet.title
+ %small= @snippet.file_name
+ - if can?(current_user, :admin_project_snippet, @project) || @snippet.author == current_user
+ = link_to "Edit", edit_project_snippet_path(@project, @snippet), class: "btn btn-small pull-right", title: 'Edit Snippet'
+
+%br
+%div= render 'projects/snippets/blob'
+%div#notes= render "notes/notes_with_form"
diff --git a/app/views/snippets/_blob.html.haml b/app/views/snippets/_blob.html.haml
index 017a33b34f3..56e62f0d6b3 100644
--- a/app/views/snippets/_blob.html.haml
+++ b/app/views/snippets/_blob.html.haml
@@ -3,7 +3,7 @@
%i.icon-file
%strong= @snippet.file_name
%span.options
- = link_to "raw", raw_project_snippet_path(@project, @snippet), class: "btn btn-tiny", target: "_blank"
+ = link_to "raw", raw_snippet_path(@snippet), class: "btn btn-tiny", target: "_blank"
.file_content.code
- unless @snippet.content.empty?
%div{class: user_color_scheme_class}
diff --git a/app/views/snippets/_form.html.haml b/app/views/snippets/_form.html.haml
index 993a20058c6..95e9e0357bc 100644
--- a/app/views/snippets/_form.html.haml
+++ b/app/views/snippets/_form.html.haml
@@ -2,7 +2,7 @@
= @snippet.new_record? ? "New Snippet" : "Edit Snippet ##{@snippet.id}"
%hr
.snippet-form-holder
- = form_for [@project, @snippet] do |f|
+ = form_for @snippet, as: :personal_snippet, url: url do |f|
-if @snippet.errors.any?
.alert.alert-error
%ul
@@ -13,6 +13,9 @@
= f.label :title
.input= f.text_field :title, placeholder: "Example Snippet", class: 'input-xlarge', required: true
.clearfix
+ = f.label "Private?"
+ .input= f.check_box :private, {class: ''}
+ .clearfix
= f.label "Lifetime"
.input= f.select :expires_at, lifetime_select_options, {}, {class: 'chosen span2'}
.clearfix
@@ -28,9 +31,9 @@
.form-actions
= f.submit 'Save', class: "btn-save btn"
- = link_to "Cancel", project_snippets_path(@project), class: " btn"
+ = link_to "Cancel", snippets_path(@project), class: " btn"
- unless @snippet.new_record?
- .pull-right= link_to 'Destroy', [@project, @snippet], confirm: 'Removed snippet cannot be restored! Are you sure?', method: :delete, class: "btn pull-right danger delete-snippet", id: "destroy_snippet_#{@snippet.id}"
+ .pull-right= link_to 'Destroy', snippet_path(@snippet), confirm: 'Removed snippet cannot be restored! Are you sure?', method: :delete, class: "btn pull-right danger delete-snippet", id: "destroy_snippet_#{@snippet.id}"
:javascript
diff --git a/app/views/snippets/_snippet.html.haml b/app/views/snippets/_snippet.html.haml
index a576500c15d..77d9d211d8d 100644
--- a/app/views/snippets/_snippet.html.haml
+++ b/app/views/snippets/_snippet.html.haml
@@ -1,8 +1,16 @@
%tr
%td
+ - if snippet.private?
+ %i.icon-lock
+ - else
+ %i.icon-globe
= image_tag gravatar_icon(snippet.author_email), class: "avatar s24"
- %a{href: project_snippet_path(snippet.project, snippet)}
- %strong= truncate(snippet.title, length: 60)
+ - if snippet.project_id?
+ %a{href: project_snippet_path(snippet.project, snippet)}
+ %strong= truncate(snippet.title, length: 60)
+ - else
+ %a{href: snippet_path(snippet)}
+ %strong= truncate(snippet.title, length: 60)
%td
= snippet.file_name
%td
@@ -11,3 +19,6 @@
= snippet.expires_at.to_date.to_s(:short)
- else
Never
+ %td
+ - if snippet.project_id?
+ = link_to snippet.project.name, project_path(snippet.project)
diff --git a/app/views/snippets/_snippets.html.haml b/app/views/snippets/_snippets.html.haml
new file mode 100644
index 00000000000..192cb6aa94a
--- /dev/null
+++ b/app/views/snippets/_snippets.html.haml
@@ -0,0 +1,15 @@
+%table
+ %thead
+ %tr
+ %th Title
+ %th File Name
+ %th Expires At
+ %th Project
+
+ = render partial: 'snippet', collection: @snippets
+ - if @snippets.empty?
+ %tr
+ %td{colspan: 4}
+ %h3.nothing_here_message Nothing here.
+
+= paginate @snippets
diff --git a/app/views/snippets/edit.html.haml b/app/views/snippets/edit.html.haml
index f81c0b8bc64..1b88a85faf1 100644
--- a/app/views/snippets/edit.html.haml
+++ b/app/views/snippets/edit.html.haml
@@ -1 +1 @@
-= render "snippets/form"
+= render "snippets/form", url: snippet_path(@snippet)
diff --git a/app/views/snippets/index.html.haml b/app/views/snippets/index.html.haml
index bacf23d8f8d..97f7b39877e 100644
--- a/app/views/snippets/index.html.haml
+++ b/app/views/snippets/index.html.haml
@@ -1,19 +1,11 @@
%h3.page_title
- Snippets
+ Public snippets
%small share code pastes with others out of git repository
+ = link_to new_snippet_path, class: "btn btn-small add_new pull-right", title: "New Snippet" do
+ Add new snippet
+
+%hr
+.row
+ .span12
+ = render 'snippets'
- - if can? current_user, :write_snippet, @project
- = link_to new_project_snippet_path(@project), class: "btn btn-small add_new pull-right", title: "New Snippet" do
- Add new snippet
-%br
-%table
- %thead
- %tr
- %th Title
- %th File Name
- %th Expires At
- = render @snippets
- - if @snippets.empty?
- %tr
- %td{colspan: 3}
- %h3.nothing_here_message Nothing here.
diff --git a/app/views/snippets/new.html.haml b/app/views/snippets/new.html.haml
index f81c0b8bc64..90e0a1f79da 100644
--- a/app/views/snippets/new.html.haml
+++ b/app/views/snippets/new.html.haml
@@ -1 +1 @@
-= render "snippets/form"
+= render "snippets/form", url: snippets_path(@snippet)
diff --git a/app/views/snippets/show.html.haml b/app/views/snippets/show.html.haml
index 12534edf8ba..18348fb1067 100644
--- a/app/views/snippets/show.html.haml
+++ b/app/views/snippets/show.html.haml
@@ -1,9 +1,13 @@
%h3.page_title
+ - if @snippet.private?
+ %i.icon-lock
+ - else
+ %i.icon-globe
+
= @snippet.title
%small= @snippet.file_name
- - if can?(current_user, :admin_snippet, @project) || @snippet.author == current_user
- = link_to "Edit", edit_project_snippet_path(@project, @snippet), class: "btn btn-small pull-right", title: 'Edit Snippet'
+ - if @snippet.author == current_user
+ = link_to "Edit", edit_snippet_path(@snippet), class: "btn btn-small pull-right", title: 'Edit Snippet'
%br
%div= render 'blob'
-%div#notes= render "notes/notes_with_form"
diff --git a/app/views/snippets/user_index.html.haml b/app/views/snippets/user_index.html.haml
new file mode 100644
index 00000000000..2f2cce26af4
--- /dev/null
+++ b/app/views/snippets/user_index.html.haml
@@ -0,0 +1,20 @@
+%h3.page_title
+ Snippets by
+ = @user.name
+ %small share code pastes with others out of git repository
+ = link_to new_snippet_path, class: "btn btn-small add_new pull-right", title: "New Snippet" do
+ Add new snippet
+
+%hr
+.row
+ .span3
+ %ul.nav.nav-pills.nav-stacked
+ = nav_tab :scope, nil do
+ = link_to "All", user_snippets_path(@user)
+ = nav_tab :scope, 'private' do
+ = link_to "Private", user_snippets_path(@user, scope: 'private')
+ = nav_tab :scope, 'public' do
+ = link_to "Public", user_snippets_path(@user, scope: 'public')
+
+ .span9
+ = render 'snippets'
diff --git a/config/routes.rb b/config/routes.rb
index 3609ac01891..c802c60382d 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -39,6 +39,16 @@ Gitlab::Application.routes.draw do
get 'help/workflow' => 'help#workflow'
#
+ # Global snippets
+ #
+ resources :snippets do
+ member do
+ get "raw"
+ end
+ end
+ get "/s/:username" => "snippets#user_index", as: :user_snippets, constraints: { username: /.*/ }
+
+ #
# Public namespace
#
namespace :public do
@@ -182,6 +192,14 @@ Gitlab::Application.routes.draw do
resources :graph, only: [:show], constraints: {id: /(?:[^.]|\.(?!json$))+/, format: /json/}
match "/compare/:from...:to" => "compare#show", as: "compare", via: [:get, :post], constraints: {from: /.+/, to: /.+/}
+ scope module: :projects do
+ resources :snippets do
+ member do
+ get "raw"
+ end
+ end
+ end
+
resources :wikis, only: [:show, :edit, :destroy, :create] do
collection do
get :pages
@@ -255,19 +273,12 @@ Gitlab::Application.routes.draw do
end
end
- resources :snippets do
- member do
- get "raw"
- end
- end
-
resources :hooks, only: [:index, :create, :destroy] do
member do
get :test
end
end
-
resources :team, controller: 'team_members', only: [:index]
resources :milestones, except: [:destroy]
diff --git a/db/migrate/20130323174317_add_private_to_snippets.rb b/db/migrate/20130323174317_add_private_to_snippets.rb
new file mode 100644
index 00000000000..92f3a5c7011
--- /dev/null
+++ b/db/migrate/20130323174317_add_private_to_snippets.rb
@@ -0,0 +1,5 @@
+class AddPrivateToSnippets < ActiveRecord::Migration
+ def change
+ add_column :snippets, :private, :boolean, null: false, default: true
+ end
+end
diff --git a/db/migrate/20130324151736_add_type_to_snippets.rb b/db/migrate/20130324151736_add_type_to_snippets.rb
new file mode 100644
index 00000000000..276aab2ca15
--- /dev/null
+++ b/db/migrate/20130324151736_add_type_to_snippets.rb
@@ -0,0 +1,5 @@
+class AddTypeToSnippets < ActiveRecord::Migration
+ def change
+ add_column :snippets, :type, :string
+ end
+end
diff --git a/db/migrate/20130324172327_change_project_id_to_null_in_snipepts.rb b/db/migrate/20130324172327_change_project_id_to_null_in_snipepts.rb
new file mode 100644
index 00000000000..4c992bac4d1
--- /dev/null
+++ b/db/migrate/20130324172327_change_project_id_to_null_in_snipepts.rb
@@ -0,0 +1,9 @@
+class ChangeProjectIdToNullInSnipepts < ActiveRecord::Migration
+ def up
+ change_column :snippets, :project_id, :integer, :null => true
+ end
+
+ def down
+ change_column :snippets, :project_id, :integer, :null => false
+ end
+end
diff --git a/db/migrate/20130324203535_add_type_value_for_snippets.rb b/db/migrate/20130324203535_add_type_value_for_snippets.rb
new file mode 100644
index 00000000000..8c05dd2cc71
--- /dev/null
+++ b/db/migrate/20130324203535_add_type_value_for_snippets.rb
@@ -0,0 +1,8 @@
+class AddTypeValueForSnippets < ActiveRecord::Migration
+ def up
+ Snippet.where("project_id IS NOT NULL").update_all(type: 'ProjectSnippet')
+ end
+
+ def down
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 6a16caf59d8..21e553dd612 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -203,12 +203,14 @@ ActiveRecord::Schema.define(:version => 20130522141856) do
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", :null => false
- t.datetime "updated_at", :null => false
+ t.integer "author_id", :null => false
+ t.integer "project_id"
+ t.datetime "created_at", :null => false
+ t.datetime "updated_at", :null => false
t.string "file_name"
t.datetime "expires_at"
+ t.boolean "private", :default => true, :null => false
+ t.string "type"
end
add_index "snippets", ["created_at"], :name => "index_snippets_on_created_at"
diff --git a/features/project/snippets.feature b/features/project/snippets.feature
new file mode 100644
index 00000000000..a26c8dc8474
--- /dev/null
+++ b/features/project/snippets.feature
@@ -0,0 +1,35 @@
+Feature: Project Snippets
+ Background:
+ Given I sign in as a user
+ And I own project "Shop"
+ And project "Shop" have "Snippet one" snippet
+ And project "Shop" have no "Snippet two" snippet
+ And I visit project "Shop" snippets page
+
+ Scenario: I should see snippets
+ Given I visit project "Shop" snippets page
+ Then I should see "Snippet one" in snippets
+ And I should not see "Snippet two" in snippets
+
+ Scenario: I create new project snippet
+ Given I click link "New Snippet"
+ And I submit new snippet "Snippet three"
+ Then I should see snippet "Snippet three"
+
+ @javascript
+ Scenario: I comment on a snippet "Snippet one"
+ Given I visit snippet page "Snippet one"
+ And I leave a comment like "Good snippet!"
+ Then I should see comment "Good snippet!"
+
+ Scenario: I update "Snippet one"
+ Given I visit snippet page "Snippet one"
+ And I click link "Edit"
+ And I submit new title "Snippet new title"
+ Then I should see "Snippet new title"
+
+ Scenario: I destroy "Snippet one"
+ Given I visit snippet page "Snippet one"
+ And I click link "Edit"
+ And I click link "Destroy"
+ Then I should not see "Snippet one" in snippets
diff --git a/features/snippets/discover_snippets.feature b/features/snippets/discover_snippets.feature
new file mode 100644
index 00000000000..d6fd2cd7808
--- /dev/null
+++ b/features/snippets/discover_snippets.feature
@@ -0,0 +1,10 @@
+Feature: Discover Snippets
+ Background:
+ Given I sign in as a user
+ And I have public "Personal snippet one" snippet
+ And I have private "Personal snippet private" snippet
+
+ Scenario: I should see snippets
+ Given I visit snippets page
+ Then I should see "Personal snippet one" in snippets
+ And I should not see "Personal snippet private" in snippets
diff --git a/features/snippets/snippets.feature b/features/snippets/snippets.feature
new file mode 100644
index 00000000000..1119defa17d
--- /dev/null
+++ b/features/snippets/snippets.feature
@@ -0,0 +1,28 @@
+Feature: Snippets Feature
+ Background:
+ Given I sign in as a user
+ And I have public "Personal snippet one" snippet
+ And I have private "Personal snippet private" snippet
+
+ Scenario: I create new snippet
+ Given I visit new snippet page
+ And I submit new snippet "Personal snippet three"
+ Then I should see snippet "Personal snippet three"
+
+ Scenario: I update "Personal snippet one"
+ Given I visit snippet page "Personal snippet one"
+ And I click link "Edit"
+ And I submit new title "Personal snippet new title"
+ Then I should see "Personal snippet new title"
+
+ Scenario: Set "Personal snippet one" public
+ Given I visit snippet page "Personal snippet one"
+ And I click link "Edit"
+ And I uncheck "Private" checkbox
+ Then I should see "Personal snippet one" public
+
+ Scenario: I destroy "Personal snippet one"
+ Given I visit snippet page "Personal snippet one"
+ And I click link "Edit"
+ And I click link "Destroy"
+ Then I should not see "Personal snippet one" in snippets
diff --git a/features/snippets/user_snippets.feature b/features/snippets/user_snippets.feature
new file mode 100644
index 00000000000..4c8a91501c4
--- /dev/null
+++ b/features/snippets/user_snippets.feature
@@ -0,0 +1,22 @@
+Feature: User Snippets
+ Background:
+ Given I sign in as a user
+ And I have public "Personal snippet one" snippet
+ And I have private "Personal snippet private" snippet
+
+ Scenario: I should see all my snippets
+ Given I visit my snippets page
+ Then I should see "Personal snippet one" in snippets
+ And I should see "Personal snippet private" in snippets
+
+ Scenario: I can see only my private snippets
+ Given I visit my snippets page
+ And I click "Private" filter
+ Then I should not see "Personal snippet one" in snippets
+ And I should see "Personal snippet private" in snippets
+
+ Scenario: I can see only my public snippets
+ Given I visit my snippets page
+ And I click "Public" filter
+ Then I should see "Personal snippet one" in snippets
+ And I should not see "Personal snippet private" in snippets
diff --git a/features/steps/project/project_snippets.rb b/features/steps/project/project_snippets.rb
new file mode 100644
index 00000000000..c8580d6fd30
--- /dev/null
+++ b/features/steps/project/project_snippets.rb
@@ -0,0 +1,100 @@
+class ProjectSnippets < Spinach::FeatureSteps
+ include SharedAuthentication
+ include SharedProject
+ include SharedNote
+ include SharedPaths
+
+ And 'project "Shop" have "Snippet one" snippet' do
+ create(:project_snippet,
+ title: "Snippet one",
+ content: "Test content",
+ file_name: "snippet.rb",
+ project: project,
+ author: project.users.first)
+ end
+
+ And 'project "Shop" have no "Snippet two" snippet' do
+ create(:snippet,
+ title: "Snippet two",
+ content: "Test content",
+ file_name: "snippet.rb",
+ author: project.users.first)
+ end
+
+ Given 'I click link "New Snippet"' do
+ click_link "Add new snippet"
+ end
+
+ Given 'I click link "Snippet one"' do
+ click_link "Snippet one"
+ end
+
+ Then 'I should see "Snippet one" in snippets' do
+ page.should have_content "Snippet one"
+ end
+
+ And 'I should not see "Snippet two" in snippets' do
+ page.should_not have_content "Snippet two"
+ end
+
+ And 'I should not see "Snippet one" in snippets' do
+ page.should_not have_content "Snippet one"
+ end
+
+ And 'I click link "Edit"' do
+ within ".page_title" do
+ click_link "Edit"
+ end
+ end
+
+ And 'I click link "Destroy"' do
+ click_link "Destroy"
+ end
+
+ And 'I submit new snippet "Snippet three"' do
+ fill_in "project_snippet_title", :with => "Snippet three"
+ select "forever", :from => "project_snippet_expires_at"
+ fill_in "project_snippet_file_name", :with => "my_snippet.rb"
+ within('.file-editor') do
+ find(:xpath, "//input[@id='project_snippet_content']").set 'Content of snippet three'
+ end
+ click_button "Save"
+ end
+
+ Then 'I should see snippet "Snippet three"' do
+ page.should have_content "Snippet three"
+ page.should have_content "Content of snippet three"
+ end
+
+ And 'I submit new title "Snippet new title"' do
+ fill_in "project_snippet_title", :with => "Snippet new title"
+ click_button "Save"
+ end
+
+ Then 'I should see "Snippet new title"' do
+ page.should have_content "Snippet new title"
+ end
+
+ And 'I leave a comment like "Good snippet!"' do
+ within('.js-main-target-form') do
+ fill_in "note_note", with: "Good snippet!"
+ click_button "Add Comment"
+ end
+ end
+
+ Then 'I should see comment "Good snippet!"' do
+ page.should have_content "Good snippet!"
+ end
+
+ And 'I visit snippet page "Snippet one"' do
+ visit project_snippet_path(project, project_snippet)
+ end
+
+ def project
+ @project ||= Project.find_by_name!("Shop")
+ end
+
+ def project_snippet
+ @project_snippet ||= ProjectSnippet.find_by_title!("Snippet One")
+ end
+end
diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb
index 628a179ae9d..3641e788662 100644
--- a/features/steps/shared/paths.rb
+++ b/features/steps/shared/paths.rb
@@ -275,6 +275,22 @@ module SharedPaths
visit public_root_path
end
+ # ----------------------------------------
+ # Snippets
+ # ----------------------------------------
+
+ Given 'I visit project "Shop" snippets page' do
+ visit project_snippets_path(project)
+ end
+
+ Given 'I visit snippets page' do
+ visit snippets_path
+ end
+
+ Given 'I visit new snippet page' do
+ visit new_snippet_path
+ end
+
def root_ref
@project.repository.root_ref
end
diff --git a/features/steps/shared/snippet.rb b/features/steps/shared/snippet.rb
new file mode 100644
index 00000000000..543e43196a5
--- /dev/null
+++ b/features/steps/shared/snippet.rb
@@ -0,0 +1,21 @@
+module SharedSnippet
+ include Spinach::DSL
+
+ And 'I have public "Personal snippet one" snippet' do
+ create(:personal_snippet,
+ title: "Personal snippet one",
+ content: "Test content",
+ file_name: "snippet.rb",
+ private: false,
+ author: current_user)
+ end
+
+ And 'I have private "Personal snippet private" snippet' do
+ create(:personal_snippet,
+ title: "Personal snippet private",
+ content: "Provate content",
+ file_name: "private_snippet.rb",
+ private: true,
+ author: current_user)
+ end
+end
diff --git a/features/steps/snippets/discover_snippets.rb b/features/steps/snippets/discover_snippets.rb
new file mode 100644
index 00000000000..3afe019adf6
--- /dev/null
+++ b/features/steps/snippets/discover_snippets.rb
@@ -0,0 +1,17 @@
+class DiscoverSnippets < Spinach::FeatureSteps
+ include SharedAuthentication
+ include SharedPaths
+ include SharedSnippet
+
+ Then 'I should see "Personal snippet one" in snippets' do
+ page.should have_content "Personal snippet one"
+ end
+
+ And 'I should not see "Personal snippet private" in snippets' do
+ page.should_not have_content "Personal snippet private"
+ end
+
+ def snippet
+ @snippet ||= PersonalSnippet.find_by_title!("Personal snippet one")
+ end
+end
diff --git a/features/steps/snippets/snippets.rb b/features/steps/snippets/snippets.rb
new file mode 100644
index 00000000000..b185f605728
--- /dev/null
+++ b/features/steps/snippets/snippets.rb
@@ -0,0 +1,65 @@
+class SnippetsFeature < Spinach::FeatureSteps
+ include SharedAuthentication
+ include SharedPaths
+ include SharedProject
+ include SharedSnippet
+
+ Given 'I click link "Personal snippet one"' do
+ click_link "Personal snippet one"
+ end
+
+ And 'I should not see "Personal snippet one" in snippets' do
+ page.should_not have_content "Personal snippet one"
+ end
+
+ And 'I click link "Edit"' do
+ within ".page_title" do
+ click_link "Edit"
+ end
+ end
+
+ And 'I click link "Destroy"' do
+ click_link "Destroy"
+ end
+
+ And 'I submit new snippet "Personal snippet three"' do
+ fill_in "personal_snippet_title", :with => "Personal snippet three"
+ select "forever", :from => "personal_snippet_expires_at"
+ fill_in "personal_snippet_file_name", :with => "my_snippet.rb"
+ within('.file-editor') do
+ find(:xpath, "//input[@id='personal_snippet_content']").set 'Content of snippet three'
+ end
+ click_button "Save"
+ end
+
+ Then 'I should see snippet "Personal snippet three"' do
+ page.should have_content "Personal snippet three"
+ page.should have_content "Content of snippet three"
+ end
+
+ And 'I submit new title "Personal snippet new title"' do
+ fill_in "personal_snippet_title", :with => "Personal snippet new title"
+ click_button "Save"
+ end
+
+ Then 'I should see "Personal snippet new title"' do
+ page.should have_content "Personal snippet new title"
+ end
+
+ And 'I uncheck "Private" checkbox' do
+ find(:xpath, "//input[@id='personal_snippet_private']").set true
+ click_button "Save"
+ end
+
+ Then 'I should see "Personal snippet one" public' do
+ page.should have_no_xpath("//i[@class='public-snippet']")
+ end
+
+ And 'I visit snippet page "Personal snippet one"' do
+ visit snippet_path(snippet)
+ end
+
+ def snippet
+ @snippet ||= PersonalSnippet.find_by_title!("Personal snippet one")
+ end
+end
diff --git a/features/steps/snippets/user_snippets.rb b/features/steps/snippets/user_snippets.rb
new file mode 100644
index 00000000000..15d6da6db3d
--- /dev/null
+++ b/features/steps/snippets/user_snippets.rb
@@ -0,0 +1,41 @@
+class UserSnippets < Spinach::FeatureSteps
+ include SharedAuthentication
+ include SharedPaths
+ include SharedSnippet
+
+ Given 'I visit my snippets page' do
+ visit user_snippets_path(current_user)
+ end
+
+ Then 'I should see "Personal snippet one" in snippets' do
+ page.should have_content "Personal snippet one"
+ end
+
+ And 'I should see "Personal snippet private" in snippets' do
+ page.should have_content "Personal snippet private"
+ end
+
+ Then 'I should not see "Personal snippet one" in snippets' do
+ page.should_not have_content "Personal snippet one"
+ end
+
+ And 'I should not see "Personal snippet private" in snippets' do
+ page.should_not have_content "Personal snippet private"
+ end
+
+ Given 'I click "Public" filter' do
+ within('.nav-stacked') do
+ click_link "Public"
+ end
+ end
+
+ Given 'I click "Private" filter' do
+ within('.nav-stacked') do
+ click_link "Private"
+ end
+ end
+
+ def snippet
+ @snippet ||= PersonalSnippet.find_by_title!("Personal snippet one")
+ end
+end
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 3711057ecaa..8b20a2fcb14 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -328,7 +328,7 @@ module API
# Example Request:
# POST /projects/:id/snippets
post ":id/snippets" do
- authorize! :write_snippet, user_project
+ authorize! :write_project_snippet, user_project
required_attributes! [:title, :file_name, :code]
attrs = attributes_for_keys [:title, :file_name]
@@ -357,7 +357,7 @@ module API
# PUT /projects/:id/snippets/:snippet_id
put ":id/snippets/:snippet_id" do
@snippet = user_project.snippets.find(params[:snippet_id])
- authorize! :modify_snippet, @snippet
+ authorize! :modify_project_snippet, @snippet
attrs = attributes_for_keys [:title, :file_name]
attrs[:expires_at] = params[:lifetime] if params[:lifetime].present?
@@ -380,7 +380,7 @@ module API
delete ":id/snippets/:snippet_id" do
begin
@snippet = user_project.snippets.find(params[:snippet_id])
- authorize! :modify_snippet, user_project
+ authorize! :modify_project_snippet, @snippet
@snippet.destroy
rescue
end
diff --git a/spec/factories.rb b/spec/factories.rb
index f9e25382b61..b596f80fa9e 100644
--- a/spec/factories.rb
+++ b/spec/factories.rb
@@ -197,7 +197,7 @@ FactoryGirl.define do
url
end
- factory :snippet do
+ factory :project_snippet do
project
author
title
@@ -205,6 +205,20 @@ FactoryGirl.define do
file_name
end
+ factory :personal_snippet do
+ author
+ title
+ content
+ file_name
+ end
+
+ factory :snippet do
+ author
+ title
+ content
+ file_name
+ end
+
factory :protected_branch do
name
project
diff --git a/spec/features/snippets_spec.rb b/spec/features/snippets_spec.rb
deleted file mode 100644
index 1a0f6eaeef4..00000000000
--- a/spec/features/snippets_spec.rb
+++ /dev/null
@@ -1,99 +0,0 @@
-require 'spec_helper'
-
-describe "Snippets" do
- let(:project) { create(:project) }
-
- before do
- login_as :user
- project.team << [@user, :developer]
- end
-
- describe "GET /snippets" do
- before do
- @snippet = create(:snippet,
- author: @user,
- project: project)
-
- visit project_snippets_path(project)
- end
-
- subject { page }
-
- it { should have_content(@snippet.title[0..10]) }
- it { should have_content(@snippet.project.name) }
-
- describe "Destroy" do
- before do
- # admin access to remove snippet
- @user.users_projects.destroy_all
- project.team << [@user, :master]
- visit edit_project_snippet_path(project, @snippet)
- 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", js: true do
- before do
- fill_in "snippet_title", with: "login function"
- fill_in "snippet_file_name", with: "test.rb"
- page.execute_script("editor.insert('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 = create(:snippet,
- author: @user,
- project: project)
- visit project_snippet_path(project, @snippet)
- click_link "Edit Snippet"
- 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"
- 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
diff --git a/spec/helpers/gitlab_markdown_helper_spec.rb b/spec/helpers/gitlab_markdown_helper_spec.rb
index 23b18fbf0eb..0f206f47234 100644
--- a/spec/helpers/gitlab_markdown_helper_spec.rb
+++ b/spec/helpers/gitlab_markdown_helper_spec.rb
@@ -10,7 +10,7 @@ describe GitlabMarkdownHelper do
let(:commit) { project.repository.commit }
let(:issue) { create(:issue, project: project) }
let(:merge_request) { create(:merge_request, project: project) }
- let(:snippet) { create(:snippet, project: project) }
+ let(:snippet) { create(:project_snippet, project: project) }
let(:member) { project.users_projects.where(user_id: user).first }
before do
@@ -190,8 +190,43 @@ describe GitlabMarkdownHelper do
describe "referencing a snippet" do
let(:object) { snippet }
let(:reference) { "$#{snippet.id}" }
+ let(:actual) { "Reference to #{reference}" }
+ let(:expected) { project_snippet_path(project, object) }
+
+ it "should link using a valid id" do
+ gfm(actual).should match(expected)
+ end
+
+ it "should link with adjacent text" do
+ # Wrap the reference in parenthesis
+ gfm(actual.gsub(reference, "(#{reference})")).should match(expected)
+
+ # Append some text to the end of the reference
+ gfm(actual.gsub(reference, "#{reference}, right?")).should match(expected)
+ end
+
+ it "should keep whitespace intact" do
+ actual = "Referenced #{reference} already."
+ expected = /Referenced <a.+>[^\s]+<\/a> already/
+ gfm(actual).should match(expected)
+ end
+
+ it "should not link with an invalid id" do
+ # Modify the reference string so it's still parsed, but is invalid
+ reference.gsub!(/^(.)(\d+)$/, '\1' + ('\2' * 2))
+ gfm(actual).should == actual
+ end
+
+ it "should include a title attribute" do
+ title = "Snippet: #{object.title}"
+ gfm(actual).should match(/title="#{title}"/)
+ end
+
+ it "should include standard gfm classes" do
+ css = object.class.to_s.underscore
+ gfm(actual).should match(/class="\s?gfm gfm-snippet\s?"/)
+ end
- include_examples 'referenced object'
end
describe "referencing multiple objects" do
diff --git a/spec/models/project_snippet_spec.rb b/spec/models/project_snippet_spec.rb
new file mode 100644
index 00000000000..716fd81c91b
--- /dev/null
+++ b/spec/models/project_snippet_spec.rb
@@ -0,0 +1,30 @@
+# == Schema Information
+#
+# Table name: snippets
+#
+# id :integer not null, primary key
+# title :string(255)
+# content :text
+# author_id :integer not null
+# project_id :integer not null
+# created_at :datetime not null
+# updated_at :datetime not null
+# file_name :string(255)
+# expires_at :datetime
+#
+
+require 'spec_helper'
+
+describe ProjectSnippet do
+ describe "Associations" do
+ it { should belong_to(:project) }
+ end
+
+ describe "Mass assignment" do
+ it { should_not allow_mass_assignment_of(:project_id) }
+ end
+
+ describe "Validation" do
+ it { should validate_presence_of(:project) }
+ end
+end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 04b4ce1763e..2e3870b1b65 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -36,7 +36,7 @@ describe Project do
it { should have_many(:milestones).dependent(:destroy) }
it { should have_many(:users_projects).dependent(:destroy) }
it { should have_many(:notes).dependent(:destroy) }
- it { should have_many(:snippets).dependent(:destroy) }
+ it { should have_many(:snippets).class_name('ProjectSnippet').dependent(:destroy) }
it { should have_many(:deploy_keys_projects).dependent(:destroy) }
it { should have_many(:deploy_keys) }
it { should have_many(:hooks).dependent(:destroy) }
diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb
index e4d1934829f..52355c38f0c 100644
--- a/spec/models/snippet_spec.rb
+++ b/spec/models/snippet_spec.rb
@@ -17,19 +17,16 @@ require 'spec_helper'
describe Snippet do
describe "Associations" do
- it { should belong_to(:project) }
it { should belong_to(:author).class_name('User') }
it { should have_many(:notes).dependent(:destroy) }
end
describe "Mass assignment" do
it { should_not allow_mass_assignment_of(:author_id) }
- it { should_not allow_mass_assignment_of(:project_id) }
end
describe "Validation" do
it { should validate_presence_of(:author) }
- it { should validate_presence_of(:project) }
it { should validate_presence_of(:title) }
it { should ensure_length_of(:title).is_within(0..255) }
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 7559c4cc3a1..9673854da53 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -41,6 +41,7 @@ require 'spec_helper'
describe User do
describe "Associations" do
it { should have_one(:namespace) }
+ it { should have_many(:snippets).class_name('Snippet').dependent(:destroy) }
it { should have_many(:users_projects).dependent(:destroy) }
it { should have_many(:groups) }
it { should have_many(:keys).dependent(:destroy) }
diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb
index 78d55a7b4ed..11296aea73e 100644
--- a/spec/requests/api/notes_spec.rb
+++ b/spec/requests/api/notes_spec.rb
@@ -7,7 +7,7 @@ describe API::API do
let!(:project) { create(:project, namespace: user.namespace ) }
let!(:issue) { create(:issue, project: project, author: user) }
let!(:merge_request) { create(:merge_request, project: project, author: user) }
- let!(:snippet) { create(:snippet, project: project, author: user) }
+ let!(:snippet) { create(:project_snippet, project: project, author: user) }
let!(:issue_note) { create(:note, noteable: issue, project: project, author: user) }
let!(:merge_request_note) { create(:note, noteable: merge_request, project: project, author: user) }
let!(:snippet_note) { create(:note, noteable: snippet, project: project, author: user) }
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index de0631d5b70..031b1412b0c 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -10,7 +10,7 @@ describe API::API do
let(:admin) { create(:admin) }
let!(:project) { create(:project_with_code, creator_id: user.id) }
let!(:hook) { create(:project_hook, project: project, url: "http://example.com") }
- let!(:snippet) { create(:snippet, author: user, project: project, title: 'example') }
+ let!(:snippet) { create(:project_snippet, author: user, project: project, title: 'example') }
let!(:users_project) { create(:users_project, user: user, project: project, project_access: UsersProject::MASTER) }
let!(:users_project2) { create(:users_project, user: user3, project: project, project_access: UsersProject::DEVELOPER) }
diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb
index f20a1ca51a4..94f9480a4d0 100644
--- a/spec/routing/project_routing_spec.rb
+++ b/spec/routing/project_routing_spec.rb
@@ -258,13 +258,37 @@ end
# project_snippet GET /:project_id/snippets/:id(.:format) snippets#show
# PUT /:project_id/snippets/:id(.:format) snippets#update
# DELETE /:project_id/snippets/:id(.:format) snippets#destroy
-describe SnippetsController, "routing" do
+describe Project::SnippetsController, "routing" do
it "to #raw" do
- get("/gitlabhq/snippets/1/raw").should route_to('snippets#raw', project_id: 'gitlabhq', id: '1')
+ get("/gitlabhq/snippets/1/raw").should route_to('projects/snippets#raw', project_id: 'gitlabhq', id: '1')
end
- it_behaves_like "RESTful project resources" do
- let(:controller) { 'snippets' }
+ it "to #index" do
+ get("/gitlabhq/snippets").should route_to("projects/snippets#index", project_id: 'gitlabhq')
+ end
+
+ it "to #create" do
+ post("/gitlabhq/snippets").should route_to("projects/snippets#create", project_id: 'gitlabhq')
+ end
+
+ it "to #new" do
+ get("/gitlabhq/snippets/new").should route_to("projects/snippets#new", project_id: 'gitlabhq')
+ end
+
+ it "to #edit" do
+ get("/gitlabhq/snippets/1/edit").should route_to("projects/snippets#edit", project_id: 'gitlabhq', id: '1')
+ end
+
+ it "to #show" do
+ get("/gitlabhq/snippets/1").should route_to("projects/snippets#show", project_id: 'gitlabhq', id: '1')
+ end
+
+ it "to #update" do
+ put("/gitlabhq/snippets/1").should route_to("projects/snippets#update", project_id: 'gitlabhq', id: '1')
+ end
+
+ it "to #destroy" do
+ delete("/gitlabhq/snippets/1").should route_to("projects/snippets#destroy", project_id: 'gitlabhq', id: '1')
end
end
diff --git a/spec/routing/routing_spec.rb b/spec/routing/routing_spec.rb
index b6135b4ca81..aa3952f74b6 100644
--- a/spec/routing/routing_spec.rb
+++ b/spec/routing/routing_spec.rb
@@ -19,6 +19,51 @@ describe "Mounted Apps", "routing" do
end
end
+# snippets GET /snippets(.:format) snippets#index
+# POST /snippets(.:format) snippets#create
+# new_snippet GET /snippets/new(.:format) snippets#new
+# edit_snippet GET /snippets/:id/edit(.:format) snippets#edit
+# snippet GET /snippets/:id(.:format) snippets#show
+# PUT /snippets/:id(.:format) snippets#update
+# DELETE /snippets/:id(.:format) snippets#destroy
+describe SnippetsController, "routing" do
+ it "to #user_index" do
+ get("/s/User").should route_to('snippets#user_index', username: 'User')
+ end
+
+ it "to #raw" do
+ get("/snippets/1/raw").should route_to('snippets#raw', id: '1')
+ end
+
+ it "to #index" do
+ get("/snippets").should route_to('snippets#index')
+ end
+
+ it "to #create" do
+ post("/snippets").should route_to('snippets#create')
+ end
+
+ it "to #new" do
+ get("/snippets/new").should route_to('snippets#new')
+ end
+
+ it "to #edit" do
+ get("/snippets/1/edit").should route_to('snippets#edit', id: '1')
+ end
+
+ it "to #show" do
+ get("/snippets/1").should route_to('snippets#show', id: '1')
+ end
+
+ it "to #update" do
+ put("/snippets/1").should route_to('snippets#update', id: '1')
+ end
+
+ it "to #destroy" do
+ delete("/snippets/1").should route_to('snippets#destroy', id: '1')
+ end
+end
+
# help GET /help(.:format) help#index
# help_permissions GET /help/permissions(.:format) help#permissions
# help_workflow GET /help/workflow(.:format) help#workflow