Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/images/no_project_icon.pngbin0 -> 3387 bytes
-rw-r--r--app/assets/javascripts/project.js.coffee10
-rw-r--r--app/assets/stylesheets/generic/avatar.scss13
-rw-r--r--app/assets/stylesheets/sections/dashboard.scss6
-rw-r--r--app/controllers/projects/avatars_controller.rb29
-rw-r--r--app/helpers/application_helper.rb25
-rw-r--r--app/models/project.rb25
-rw-r--r--app/services/projects/fork_service.rb3
-rw-r--r--app/views/dashboard/_project.html.haml2
-rw-r--r--app/views/dashboard/projects.html.haml2
-rw-r--r--app/views/projects/_home_panel.html.haml1
-rw-r--r--app/views/projects/edit.html.haml28
-rw-r--r--config/routes.rb2
-rw-r--r--db/migrate/20140125162722_add_avatar_to_projects.rb5
-rw-r--r--db/schema.rb1
-rw-r--r--features/project/project.feature13
-rw-r--r--features/steps/project/project.rb47
-rw-r--r--spec/helpers/application_helper_spec.rb22
-rw-r--r--spec/models/project_spec.rb15
-rw-r--r--spec/routing/project_routing_spec.rb7
20 files changed, 252 insertions, 4 deletions
diff --git a/app/assets/images/no_project_icon.png b/app/assets/images/no_project_icon.png
new file mode 100644
index 00000000000..8e9529c67ec
--- /dev/null
+++ b/app/assets/images/no_project_icon.png
Binary files differ
diff --git a/app/assets/javascripts/project.js.coffee b/app/assets/javascripts/project.js.coffee
index 5a9cc66c8f0..8588a9d27cd 100644
--- a/app/assets/javascripts/project.js.coffee
+++ b/app/assets/javascripts/project.js.coffee
@@ -18,3 +18,13 @@ class @Project
$.cookie('hide_no_ssh_message', 'false', { path: path })
$(@).parents('.no-ssh-key-message').hide()
e.preventDefault()
+
+ # avatar
+ $('.js-choose-project-avatar-button').bind "click", ->
+ form = $(this).closest("form")
+ form.find(".js-project-avatar-input").click()
+
+ $('.js-project-avatar-input').bind "change", ->
+ form = $(this).closest("form")
+ filename = $(this).val().replace(/^.*[\\\/]/, '')
+ form.find(".js-avatar-filename").text(filename)
diff --git a/app/assets/stylesheets/generic/avatar.scss b/app/assets/stylesheets/generic/avatar.scss
index 80514615559..f04848ae6dc 100644
--- a/app/assets/stylesheets/generic/avatar.scss
+++ b/app/assets/stylesheets/generic/avatar.scss
@@ -23,3 +23,16 @@
&.s90 { width: 90px; height: 90px; margin-right: 15px; }
&.s160 { width: 160px; height: 160px; margin-right: 20px; }
}
+
+.identicon {
+ text-align: center;
+ vertical-align: top;
+
+ &.s16 { font-size: 12px; line-height: 1.33; }
+ &.s24 { font-size: 18px; line-height: 1.33; }
+ &.s26 { font-size: 20px; line-height: 1.33; }
+ &.s32 { font-size: 24px; line-height: 1.33; }
+ &.s60 { font-size: 45px; line-height: 1.33; }
+ &.s90 { font-size: 68px; line-height: 1.33; }
+ &.s160 { font-size: 120px; line-height: 1.33; }
+} \ No newline at end of file
diff --git a/app/assets/stylesheets/sections/dashboard.scss b/app/assets/stylesheets/sections/dashboard.scss
index 824f136d300..3135056db58 100644
--- a/app/assets/stylesheets/sections/dashboard.scss
+++ b/app/assets/stylesheets/sections/dashboard.scss
@@ -75,6 +75,9 @@
}
}
}
+.project-avatar {
+ float: left;
+}
.project-description {
overflow: hidden;
@@ -92,6 +95,9 @@
}
}
+.dash-project-avatar {
+ float: left;
+}
.dash-project-access-icon {
float: left;
margin-right: 3px;
diff --git a/app/controllers/projects/avatars_controller.rb b/app/controllers/projects/avatars_controller.rb
new file mode 100644
index 00000000000..a482b90880d
--- /dev/null
+++ b/app/controllers/projects/avatars_controller.rb
@@ -0,0 +1,29 @@
+class Projects::AvatarsController < Projects::ApplicationController
+ layout 'project'
+
+ before_filter :project
+
+ def show
+ @blob = @project.repository.blob_at_branch('master', @project.avatar_in_git)
+ if @blob
+ headers['X-Content-Type-Options'] = 'nosniff'
+ send_data(
+ @blob.data,
+ type: @blob.mime_type,
+ disposition: 'inline',
+ filename: @blob.name
+ )
+ else
+ not_found!
+ end
+ end
+
+ def destroy
+ @project.remove_avatar!
+
+ @project.save
+ @project.reset_events_cache
+
+ redirect_to edit_project_path(@project)
+ end
+end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index f65e04af205..772400d55ec 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -50,6 +50,31 @@ module ApplicationHelper
args.any? { |v| v.to_s.downcase == action_name }
end
+ def project_icon(project_id, options = {})
+ project = Project.find_with_namespace(project_id)
+ if project.avatar.present?
+ image_tag project.avatar.url, options
+ elsif options[:only_uploaded]
+ image_tag '/assets/no_project_icon.png', options
+ elsif project.avatar_in_git
+ image_tag project_avatar_path(project), options
+ else # generated icon
+ project_identicon(project, options)
+ end
+ end
+
+ def project_identicon(project, options = {})
+ options[:class] ||= ''
+ options[:class] << ' identicon'
+ bg_color = Digest::MD5.hexdigest(project.name)[0, 6]
+ brightness = bg_color[0, 2].hex + bg_color[2, 2].hex + bg_color[4, 2].hex
+ text_color = (brightness > 375) ? '#000' : '#fff'
+ content_tag(:div, class: options[:class],
+ style: "background-color: ##{ bg_color }; color: #{ text_color }") do
+ project.name[0, 1].upcase
+ end
+ end
+
def group_icon(group_path)
group = Group.find_by(path: group_path)
if group && group.avatar.present?
diff --git a/app/models/project.rb b/app/models/project.rb
index f102c477404..7160e704aa1 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -26,6 +26,7 @@
# star_count :integer default(0), not null
# import_type :string(255)
# import_source :string(255)
+# avatar :string(255)
#
class Project < ActiveRecord::Base
@@ -119,6 +120,11 @@ class Project < ActiveRecord::Base
if: :import?
validates :star_count, numericality: { greater_than_or_equal_to: 0 }
validate :check_limit, on: :create
+ validate :avatar_type,
+ if: ->(project) { project.avatar && project.avatar_changed? }
+ validates :avatar, file_size: { maximum: 100.kilobytes.to_i }
+
+ mount_uploader :avatar, AttachmentUploader
# Scopes
scope :without_user, ->(user) { where("projects.id NOT IN (:ids)", ids: user.authorized_projects.map(&:id) ) }
@@ -338,6 +344,24 @@ class Project < ActiveRecord::Base
@ci_service ||= ci_services.select(&:activated?).first
end
+ def avatar_type
+ unless avatar.image?
+ errors.add :avatar, 'only images allowed'
+ end
+ end
+
+ def avatar_in_git
+ @avatar_file ||= 'logo.png' if repository.blob_at_branch('master', 'logo.png')
+ @avatar_file ||= 'logo.jpg' if repository.blob_at_branch('master', 'logo.jpg')
+ @avatar_file ||= 'logo.gif' if repository.blob_at_branch('master', 'logo.gif')
+ @avatar_file
+ end
+
+ # For compatibility with old code
+ def code
+ path
+ end
+
def items_for(entity)
case entity
when 'issue' then
@@ -529,6 +553,7 @@ class Project < ActiveRecord::Base
# Since we do cache @event we need to reset cache in special cases:
# * when project was moved
# * when project was renamed
+ # * when the project avatar changes
# Events cache stored like events/23-20130109142513.
# The cache key includes updated_at timestamp.
# Thus it will automatically generate a new fragment
diff --git a/app/services/projects/fork_service.rb b/app/services/projects/fork_service.rb
index 4930660055a..8bb0fcf9474 100644
--- a/app/services/projects/fork_service.rb
+++ b/app/services/projects/fork_service.rb
@@ -14,6 +14,9 @@ module Projects
project.name = @from_project.name
project.path = @from_project.path
project.creator = @current_user
+ if @from_project.avatar && @from_project.avatar.image?
+ project.avatar = @from_project.avatar
+ end
if namespace = @params[:namespace]
project.namespace = namespace
diff --git a/app/views/dashboard/_project.html.haml b/app/views/dashboard/_project.html.haml
index 89ed5102754..7f19fb5a81c 100644
--- a/app/views/dashboard/_project.html.haml
+++ b/app/views/dashboard/_project.html.haml
@@ -1,4 +1,6 @@
= link_to project_path(project), class: dom_class(project) do
+ .dash-project-avatar
+ = project_icon(project.to_param, alt: '', class: 'avatar s24')
.dash-project-access-icon
= visibility_level_icon(project.visibility_level)
%span.str-truncated
diff --git a/app/views/dashboard/projects.html.haml b/app/views/dashboard/projects.html.haml
index 944441669e7..f60bcc72e1d 100644
--- a/app/views/dashboard/projects.html.haml
+++ b/app/views/dashboard/projects.html.haml
@@ -11,6 +11,8 @@
- @projects.each do |project|
%li.my-project-row
%h4.project-title
+ .project-avatar
+ = project_icon(project.to_param, alt: '', class: 'avatar s60')
.project-access-icon
= visibility_level_icon(project.visibility_level)
= link_to project_path(project), class: dom_class(project) do
diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml
index 30d063c7a36..05910c6038c 100644
--- a/app/views/projects/_home_panel.html.haml
+++ b/app/views/projects/_home_panel.html.haml
@@ -2,6 +2,7 @@
.project-home-panel{:class => ("empty-project" if empty_repo)}
.project-home-row
.project-home-desc
+ = project_icon(@project.to_param, alt: '', class: 'avatar s32')
- if @project.description.present?
= escaped_autolink(@project.description)
- if can?(current_user, :admin_project, @project)
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index f2bb56b5664..fc6499de3e2 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -7,7 +7,8 @@
%p.light Some settings, such as "Transfer Project", are hidden inside the danger area below.
%hr
.panel-body
- = form_for @project, remote: true, html: { class: "edit_project form-horizontal" } do |f|
+ = form_for @project, remote: true, html: { multipart: true, class: "edit_project form-horizontal" }, authenticity_token: true do |f|
+
%fieldset
.form-group.project_name_holder
= f.label :name, class: 'control-label' do
@@ -80,6 +81,31 @@
= f.check_box :snippets_enabled
%span.descr Share code pastes with others out of git repository
+ %fieldset.features
+ %legend
+ Project avatar:
+ .form-group
+ .col-sm-2
+ .col-sm-10
+ = project_icon(@project.to_param, alt: '', class: 'avatar s160', only_uploaded: true)
+ %p.light
+ - if @project.avatar_in_git
+ Project avatar in repository: #{ @project.avatar_in_git }
+ %p.light
+ - if @project.avatar?
+ You can change your project avatar here
+ - else
+ You can upload an project avatar here
+ %a.choose-btn.btn.btn-small.js-choose-project-avatar-button
+ %i.icon-paper-clip
+ %span Choose File ...
+ &nbsp;
+ %span.file_name.js-avatar-filename File name...
+ = f.file_field :avatar, class: "js-project-avatar-input hidden"
+ .light The maximum file size allowed is 100KB.
+ - if @project.avatar?
+ %hr
+ = link_to 'Remove avatar', project_avatar_path(@project), data: { confirm: "Project avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-small remove-avatar"
.form-actions
= f.submit 'Save changes', class: "btn btn-save"
diff --git a/config/routes.rb b/config/routes.rb
index ef3c5aedfcb..32378665c94 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -353,6 +353,8 @@ Gitlab::Application.routes.draw do
delete :delete_attachment
end
end
+
+ resource :avatar, only: [:show, :destroy]
end
end
diff --git a/db/migrate/20140125162722_add_avatar_to_projects.rb b/db/migrate/20140125162722_add_avatar_to_projects.rb
new file mode 100644
index 00000000000..9523ac722f2
--- /dev/null
+++ b/db/migrate/20140125162722_add_avatar_to_projects.rb
@@ -0,0 +1,5 @@
+class AddAvatarToProjects < ActiveRecord::Migration
+ def change
+ add_column :projects, :avatar, :string
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index b453164d712..29466f048eb 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -327,6 +327,7 @@ ActiveRecord::Schema.define(version: 20150116234544) do
t.integer "star_count", default: 0, null: false
t.string "import_type"
t.string "import_source"
+ t.string "avatar"
end
add_index "projects", ["creator_id"], name: "index_projects_on_creator_id", using: :btree
diff --git a/features/project/project.feature b/features/project/project.feature
index 7bb24e013a9..3e1fd54bee8 100644
--- a/features/project/project.feature
+++ b/features/project/project.feature
@@ -5,6 +5,19 @@ Feature: Project
And project "Shop" has push event
And I visit project "Shop" page
+ Scenario: I edit the project avatar
+ Given I visit edit project "Shop" page
+ When I change the project avatar
+ And I should see new project avatar
+ And I should see the "Remove avatar" button
+
+ Scenario: I remove the project avatar
+ Given I visit edit project "Shop" page
+ And I have an project avatar
+ When I remove my project avatar
+ Then I should see the default project avatar
+ And I should not see the "Remove avatar" button
+
@javascript
Scenario: I should see project activity
When I visit project "Shop" page
diff --git a/features/steps/project/project.rb b/features/steps/project/project.rb
index 5e7312d90ff..455539d7471 100644
--- a/features/steps/project/project.rb
+++ b/features/steps/project/project.rb
@@ -17,12 +17,53 @@ class Spinach::Features::Project < Spinach::FeatureSteps
end
step 'change project path settings' do
- fill_in "project_path", with: "new-path"
- click_button "Rename"
+ fill_in 'project_path', with: 'new-path'
+ click_button 'Rename'
end
step 'I should see project with new path settings' do
- project.path.should == "new-path"
+ project.path.should == 'new-path'
+ end
+
+ step 'I change the project avatar' do
+ attach_file(
+ :project_avatar,
+ File.join(Rails.root, 'public', 'gitlab_logo.png')
+ )
+ click_button 'Save changes'
+ @project.reload
+ end
+
+ step 'I should see new project avatar' do
+ @project.avatar.should be_instance_of AttachmentUploader
+ url = @project.avatar.url
+ url.should == "/uploads/project/avatar/#{ @project.id }/gitlab_logo.png"
+ end
+
+ step 'I should see the "Remove avatar" button' do
+ page.should have_link('Remove avatar')
+ end
+
+ step 'I have an project avatar' do
+ attach_file(
+ :project_avatar,
+ File.join(Rails.root, 'public', 'gitlab_logo.png')
+ )
+ click_button 'Save changes'
+ @project.reload
+ end
+
+ step 'I remove my project avatar' do
+ click_link 'Remove avatar'
+ @project.reload
+ end
+
+ step 'I should see the default project avatar' do
+ @project.avatar?.should be_false
+ end
+
+ step 'I should not see the "Remove avatar" button' do
+ page.should_not have_link('Remove avatar')
end
step 'I should see project "Shop" version' do
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index 1738f3443cf..ed50bf7c75d 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -56,6 +56,28 @@ describe ApplicationHelper do
end
end
+ describe 'project_icon' do
+ avatar_file_path = File.join(Rails.root, 'public', 'gitlab_logo.png')
+
+ it 'should return an url for the avatar' do
+ project = create(:project)
+ project.avatar = File.open(avatar_file_path)
+ project.save!
+ project_icon(project.to_param).to_s.should ==
+ "/uploads/project/avatar/#{ project.id }/gitlab_logo.png"
+ end
+
+ it "should give uploaded icon when present" do
+ project = create(:project)
+ project.save!
+
+ Project.any_instance.stub(:avatar_in_git).and_return(true)
+
+ project_icon(project.to_param).to_s.should match(
+ image_tag(project_avatar_path(project)))
+ end
+ end
+
describe "avatar_icon" do
avatar_file_path = File.join(Rails.root, 'public', 'gitlab_logo.png')
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 2a278176371..87d26f98b44 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -26,6 +26,7 @@
# star_count :integer default(0), not null
# import_type :string(255)
# import_source :string(255)
+# avatar :string(255)
#
require 'spec_helper'
@@ -310,4 +311,18 @@ describe Project do
expect(project.star_count).to eq(0)
end
end
+
+ describe :avatar_type do
+ let(:project) { create(:project) }
+
+ it 'should be true if avatar is image' do
+ project.update_attribute(:avatar, 'uploads/avatar.png')
+ project.avatar_type.should be_true
+ end
+
+ it 'should be false if avatar is html page' do
+ project.update_attribute(:avatar, 'uploads/avatar.html')
+ project.avatar_type.should == ['only images allowed']
+ end
+ end
end
diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb
index f149f3f62a9..67705c6cb42 100644
--- a/spec/routing/project_routing_spec.rb
+++ b/spec/routing/project_routing_spec.rb
@@ -489,4 +489,11 @@ describe Projects::ForksController, "routing" do
it "to #create" do
post("/gitlab/gitlabhq/fork").should route_to("projects/forks#create", project_id: 'gitlab/gitlabhq')
end
+
+# project_avatar DELETE /project/avatar(.:format) projects/avatars#destroy
+describe Projects::AvatarsController, 'routing' do
+ it 'to #destroy' do
+ delete('/gitlab/gitlabhq/avatar').should route_to(
+ 'projects/avatars#destroy', project_id: 'gitlab/gitlabhq')
+ end
end