Skip to content

Commit

Permalink
Fixed bug hugopl#3 - Add project owners
Browse files Browse the repository at this point in the history
Allow to specify which users are the owner of the project
Only owners can edit project information
  • Loading branch information
Renato Araujo Oliveira Filho committed Apr 19, 2017
1 parent f5a2db0 commit 6be3d6d
Show file tree
Hide file tree
Showing 10 changed files with 114 additions and 45 deletions.
59 changes: 32 additions & 27 deletions app/assets/javascripts/projects.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,35 @@
window.projects = function() {
var tag = $('#project_users');
if (!tag.length)
return;
var tags = $('#project_owners, #project_members');
tags.each(function(index) {
var value = tags[index]
var field = value.dataset.field
var users = value.dataset.users.split('|');
var myself = value.dataset.myself;

var before_add = function(event, ui) {
return users.indexOf(ui.tagLabel) !== -1;
};

var before_remove = function(event, ui) {
var its_me = ui.tagLabel === myself;
if (its_me)
alert('You need to participate on your own project.');
return !its_me;
};

$(tags[index]).tagit({
fieldName: 'project['+field+'][]',
availableTags: users,
autocomplete: {
delay: 0,
minLength: 1
},
allowDuplicates: false,
removeConfirmation: true,
beforeTagAdded: before_add,
beforeTagRemoved: before_remove,
placeholderText: 'Type the ' + field + ' names'
});
})

var users = tag[0].dataset.users.split('|');
var myself = tag[0].dataset.myself;
var before_add = function(event, ui) {
return users.indexOf(ui.tagLabel) !== -1;
};
var before_remove = function(event, ui) {
var its_me = ui.tagLabel === myself;
if (its_me)
alert('You need to participate on your own project.');
return !its_me;
};
tag.tagit({
fieldName: 'project[users][]',
availableTags: users,
autocomplete: {
delay: 0,
minLength: 1
},
allowDuplicates: false,
removeConfirmation: true,
beforeTagAdded: before_add,
beforeTagRemoved: before_remove,
placeholderText: 'Type the user names'
});
};
18 changes: 14 additions & 4 deletions app/controllers/projects_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ def edit
end

def update
return if !project.owner?(current_user)

params[:project].delete(:jira_password) if (params[:project] || {})[:jira_password].blank?
project.update_attributes(project_params)
set_project_users
Expand All @@ -51,10 +53,18 @@ def destroy
private

def set_project_users
names = params[:project][:users].is_a?(Array) ? params[:project][:users] : []
users = User.where(name: names) << current_user
users.uniq!
@project.users = users
names = params[:project][:members].is_a?(Array) ? params[:project][:members] : []
members = User.where(name: names).to_a
members.uniq!

names = params[:project][:owners].is_a?(Array) ? params[:project][:owners] : []
owners = User.where(name: names).to_a
owners.uniq!

# add current user as owner if it is empty to keep the project editable by someone
owners << current_user if owners.empty?

@project.update_users!(owners, members)
end

def project_params
Expand Down
32 changes: 31 additions & 1 deletion app/models/project.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
class Project < ActiveRecord::Base
has_and_belongs_to_many :users
has_many :projects_users, class_name: "ProjectsUsers",
foreign_key: "project_id",
dependent: :destroy
has_many :users, through: :projects_users

has_many :owners, -> { where(owner: true) }, class_name: 'ProjectsUsers'
has_many :members, -> { where(owner: false) }, class_name: 'ProjectsUsers'

has_many :merge_requests, dependent: :destroy

validates :name, presence: true
Expand All @@ -17,8 +24,31 @@ def configuration_hash
Digest::MD5.hexdigest(linter)
end

def update_users!(owners, members)
raise ActiveRecord::RecordNotSaved, 'Project must have a owner.' if owners.empty?

# if the user is on the owner list remove it from member list
members.delete_if { |m| owners.include?(m) }

# remove previous users
projects_users.clear()

set_users(members, false)
set_users(owners, true)
end

def owner?(who)
return who.nil? ? false : owners.exists?(user_id: who.id)
end

private

def set_users(users, is_owner)
users.each do |user|
ProjectsUsers.create(project_id: id, user_id: user.id, owner: is_owner)
end
end

def validate_repository
is_valid = URI.regexp =~ repository && /\A[^ ;&|]+\z/ =~ repository
errors.add(:repository, 'is not a valid URI') unless is_valid
Expand Down
4 changes: 4 additions & 0 deletions app/models/projects_users.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
class ProjectsUsers < ActiveRecord::Base
belongs_to :user
belongs_to :project
end
6 changes: 5 additions & 1 deletion app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ class User < ActiveRecord::Base
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_and_belongs_to_many :projects

has_many :projects_users, class_name: "ProjectsUsers",
foreign_key: "user_id",
dependent: :destroy
has_many :projects, through: :projects_users

before_create :generate_api_token
has_many :comments
Expand Down
14 changes: 10 additions & 4 deletions app/views/projects/_form.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,18 @@
%i (a script in the project directory that will run before any MR creation or update)
%br/
= f.text_field :linter, placeholder: 'Command run by CLI before create/update a MR'

%label.required Owners
%br/
%ul#project_owners{ 'data-field' => 'owners', 'data-myself' => current_user.name.to_s, 'data-users' => User.all_names.join('|').to_s }
- @project.owners.each do |owner|
%li= @project.users.find(owner.user_id).name

%label.required Users
%label.required Members
%br/
%ul#project_users{ 'data-myself' => current_user.name.to_s, 'data-users' => User.all_names.join('|').to_s }
- @project.users.each do |user|
%li= user.name
%ul#project_members{ 'data-field' => 'members', 'data-users' => User.all_names.join('|').to_s }
- @project.members.each do |member|
%li= @project.users.find(member.user_id).name

%h1 Jira integration

Expand Down
14 changes: 8 additions & 6 deletions app/views/projects/edit.html.haml
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
%h1 Edit Project

= link_to 'Destroy project', @project, method: :delete,
data: { confirm: 'Are you sure?' },
id: 'project_destroy',
class: 'reject button'
= render partial: 'form', locals: { action: 'update' }
- if @project.owner?(current_user)
= link_to 'Destroy project', @project, method: :delete,
data: { confirm: 'Are you sure?' },
id: 'project_destroy',
class: 'reject button'
= render partial: 'form', locals: { action: 'update' }
- else
%strong= 'Only project onwers can edit the project'
4 changes: 3 additions & 1 deletion app/views/projects/show.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@

%p= @project.users.pluck(:name).to_sentence
%p
Didn't like it!? So #{link_to 'edit your project', edit_project_path(@project)}.
- if @project.owner?(current_user)
Didn't like it!? So #{link_to 'edit your project', edit_project_path(@project)}.


%h2 Statistics

Expand Down
5 changes: 5 additions & 0 deletions db/migrate/20170417142909_add_owner_to_projects_users.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddOwnerToProjectsUsers < ActiveRecord::Migration
def change
add_column :projects_users, :owner, :boolean, :default => true
end
end
3 changes: 2 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 20170315005553) do
ActiveRecord::Schema.define(version: 20170417142909) do

# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
Expand Down Expand Up @@ -87,6 +87,7 @@
create_table "projects_users", id: false, force: :cascade do |t|
t.integer "project_id", null: false
t.integer "user_id", null: false
t.boolean "owner", default: true
end

add_index "projects_users", ["project_id", "user_id"], name: "index_projects_users_on_project_id_and_user_id", unique: true, using: :btree
Expand Down

0 comments on commit 6be3d6d

Please sign in to comment.