#258: updated Project Collaborators
This commit is contained in:
parent
cc1bfb75c2
commit
b59c43b4bd
|
@ -1,12 +1,26 @@
|
|||
RosaABF.controller('CollaboratorsController', ['$scope', 'ApiCollaborator', function($scope, ApiCollaborator) {
|
||||
|
||||
$scope.def_role = "<%=Relation::ROLES.first%>";
|
||||
|
||||
$scope.popup = $('#add_collaborator_form .users-search-popup');
|
||||
$scope.owner = $('#owner_name').val();
|
||||
$scope.project = $('#project_name').val();
|
||||
$scope.resource = ApiCollaborator.resource;
|
||||
$scope.collaborators = [];
|
||||
|
||||
$scope.new_collaborators = [];
|
||||
|
||||
$scope.initNewCollaborator = function(c) {
|
||||
if (c) {
|
||||
c.term = c.actor_name;
|
||||
c.collaborator.role = $scope.def_role;
|
||||
} else {
|
||||
c = {collaborator: {role: $scope.def_role}};
|
||||
}
|
||||
$scope.new_collaborator = c;
|
||||
}
|
||||
$scope.initNewCollaborator();
|
||||
|
||||
$scope.getCollaborators = function() {
|
||||
$scope.collaborators = $scope.resource.query({owner: $scope.owner, project: $scope.project});
|
||||
}
|
||||
|
@ -29,17 +43,23 @@ RosaABF.controller('CollaboratorsController', ['$scope', 'ApiCollaborator', func
|
|||
}
|
||||
|
||||
$scope.search = function() {
|
||||
if ($scope.collaborator.term.length > 2) {
|
||||
if ($scope.new_collaborator.term.length > 2) {
|
||||
$scope.new_collaborators = $scope.resource.find(
|
||||
{owner: $scope.owner, project: $scope.project, term: $scope.collaborator.term});
|
||||
{owner: $scope.owner, project: $scope.project, term: $scope.new_collaborator.term});
|
||||
$scope.popup.show();
|
||||
}
|
||||
}
|
||||
|
||||
$scope.select = function(collaborator) {
|
||||
$scope.select = function(c) {
|
||||
$scope.initNewCollaborator(c);
|
||||
$scope.popup.hide();
|
||||
collaborator.term = collaborator.actor_name;
|
||||
$scope.collaborator = collaborator;
|
||||
}
|
||||
|
||||
$scope.add = function() {
|
||||
$scope.new_collaborator.$save(function() {
|
||||
$scope.collaborators.push($scope.new_collaborator);
|
||||
$scope.initNewCollaborator();
|
||||
});
|
||||
}
|
||||
|
||||
}]);
|
|
@ -1,97 +0,0 @@
|
|||
Rosa.Models.Collaborator = Backbone.Model.extend({
|
||||
paramRoot: 'collaborator',
|
||||
|
||||
defaults: {
|
||||
id: null,
|
||||
actor_id: null,
|
||||
actor_name: null,
|
||||
actor_type: null,
|
||||
avatar: null,
|
||||
actor_path: null,
|
||||
project_id: null,
|
||||
role: null,
|
||||
removed: false
|
||||
},
|
||||
|
||||
changeRole: function(r) {
|
||||
var self = this;
|
||||
this._prevState = this.get('role');
|
||||
this.save({role: r},
|
||||
{wait: true,
|
||||
success: function(model, response) {
|
||||
self.trigger('sync_success');
|
||||
},
|
||||
error: function(model, response) {
|
||||
model.set({role: model._prevState});
|
||||
self.trigger('sync_failed');
|
||||
}
|
||||
});
|
||||
return this;
|
||||
},
|
||||
setRole: function(r) {
|
||||
this.set({ role: r });
|
||||
return this;
|
||||
},
|
||||
toggleRemoved: function() {
|
||||
if (this.get('removed') === false) {
|
||||
this.set({removed: true});
|
||||
} else {
|
||||
this.set({removed: false});
|
||||
}
|
||||
return this;
|
||||
}
|
||||
});
|
||||
|
||||
Rosa.Collections.CollaboratorsCollection = Backbone.Collection.extend({
|
||||
model: Rosa.Models.Collaborator,
|
||||
|
||||
initialize: function(coll, opts) {
|
||||
if (opts === undefined || opts['url'] === undefined) {
|
||||
this.url = window.location.pathname;
|
||||
} else {
|
||||
this.url = opts['url'];
|
||||
}
|
||||
this.on('change:removed change:id add', this.sort, this);
|
||||
},
|
||||
comparator: function(m) {
|
||||
var res = ''
|
||||
if (m.get('removed') === true) {
|
||||
res = 0;
|
||||
} else if (m.isNew()) {
|
||||
res = 1;
|
||||
} else { res = 2 }
|
||||
return res + m.get('actor_name');
|
||||
},
|
||||
|
||||
removeMarked: function() {
|
||||
var marked = this.where({removed: true});
|
||||
marked.forEach(function(el) {
|
||||
el.destroy({wait: true, silent: true});
|
||||
});
|
||||
},
|
||||
|
||||
saveAndAdd: function(model) {
|
||||
|
||||
model.urlRoot = this.url;
|
||||
var self = this;
|
||||
model.save({}, {
|
||||
wait: true,
|
||||
success: function(m) {
|
||||
self.add(m.toJSON());
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
filterByName: function(term, options) {
|
||||
if (term == "") return this;
|
||||
console.log(term);
|
||||
|
||||
var pattern = new RegExp(term, "i");
|
||||
|
||||
return _(this.filter(function(data) {
|
||||
console.log(data.get("actor_name"));
|
||||
console.log(pattern.test(data.get("actor_name")));
|
||||
return pattern.test(data.get("actor_name"));
|
||||
}));
|
||||
}
|
||||
});
|
|
@ -1,15 +0,0 @@
|
|||
Rosa.Routers.CollaboratorsRouter = Backbone.Router.extend({
|
||||
routes: {},
|
||||
|
||||
initialize: function() {
|
||||
this.collaboratorsCollection = new Rosa.Collections.CollaboratorsCollection(Rosa.bootstrapedData.collaborators, { url: window.location.pathname });
|
||||
this.searchCollection = new Rosa.Collections.CollaboratorsCollection(null, { url: window.location.pathname + '/find' });
|
||||
this.tableView = new Rosa.Views.CollaboratorsView({ collection: this.collaboratorsCollection });
|
||||
this.addView = new Rosa.Views.AddCollaboratorView({ collection: this.searchCollection });
|
||||
|
||||
this.addView.on('collaborator_prepared', this.collaboratorsCollection.saveAndAdd, this.collaboratorsCollection);
|
||||
|
||||
this.tableView.render();
|
||||
this.addView.render();
|
||||
}
|
||||
});
|
|
@ -1,39 +0,0 @@
|
|||
%td
|
||||
%span#niceCheckbox1.nicecheck-main{ style: "background-position: 0px 0px; " }
|
||||
- if (removed === true) {
|
||||
%input{ type: 'checkbox', checked: 'checked' }
|
||||
- } else {
|
||||
%input{ type: 'checkbox' }
|
||||
- }
|
||||
|
||||
%td
|
||||
.img
|
||||
%img{ src: avatar, alt: 'avatar' }
|
||||
.forimg
|
||||
%a{ href: actor_path }
|
||||
= actor_name
|
||||
|
||||
- var ROLES = Rosa.bootstrapedData.ROLES;
|
||||
|
||||
- for (var i = 0; i < ROLES.length; i++) {
|
||||
%td
|
||||
.radio
|
||||
- var radio_id = id + '_' + ROLES[i];
|
||||
- var radio_type = 'role[' + id + ']';
|
||||
- if (ROLES[i] === role) {
|
||||
- if ( removed ) {
|
||||
%input.niceRadio{type: 'radio', value: ROLES[i], id: radio_id, name: radio_type, disabled: 'disabled', checked: 'checked'}
|
||||
- } else {
|
||||
%input.niceRadio{type: 'radio', value: ROLES[i], id: radio_id, name: radio_type, checked: 'checked'}
|
||||
- };
|
||||
- } else {
|
||||
- if ( removed ) {
|
||||
%input.niceRadio{type: 'radio', value: ROLES[i], id: radio_id, name: radio_type, disabled: 'disabled' }
|
||||
- } else {
|
||||
%input.niceRadio{type: 'radio', value: ROLES[i], id: radio_id, name: radio_type }
|
||||
- };
|
||||
- }
|
||||
.forradio
|
||||
%label{ for: radio_id }
|
||||
= ROLES[i]
|
||||
- }
|
|
@ -1,7 +0,0 @@
|
|||
%a
|
||||
.collaborator
|
||||
.img
|
||||
%img{width: '16px', src: avatar, alt: 'avatar'}
|
||||
.name
|
||||
= actor_name
|
||||
.both
|
|
@ -1,92 +0,0 @@
|
|||
Rosa.Views.AddCollaboratorView = Backbone.View.extend({
|
||||
result_empty: JST['backbone/templates/shared/autocomplete_result_empty'],
|
||||
|
||||
events: {
|
||||
'click #add_collaborator_button': 'addCollaborator'
|
||||
},
|
||||
|
||||
initialize: function() {
|
||||
_.bindAll(this, 'getData', 'renderAll', 'onFocus', 'selectItem', 'addCollaborator');
|
||||
|
||||
this.$el = $('#add_collaborator_form');
|
||||
this.$_search_input = this.$('#collaborator_name');
|
||||
this.$_image = this.$('.admin-search.withimage img');
|
||||
this.$_role = this.$('#role');
|
||||
|
||||
this.ac = this.$_search_input.autocomplete({
|
||||
minLength: 1,
|
||||
source: this.getData,
|
||||
focus: this.onFocus,
|
||||
select: this.selectItem
|
||||
});
|
||||
this.ac.data("autocomplete")._renderItem = this.addOne;
|
||||
this.ac.data("autocomplete")._renderMenu = this.renderAll;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return this;
|
||||
},
|
||||
|
||||
getData: function(request, response) {
|
||||
var self = this;
|
||||
var res = this.collection.fetch({
|
||||
data: {term: request.term},
|
||||
wait: true,
|
||||
success: function(collection) {
|
||||
self.$_image.hide();
|
||||
if (collection.length !== 0) {
|
||||
response(collection.models);
|
||||
} else {
|
||||
response([{result_empty: true}]);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
addOne: function(ul, item) {
|
||||
var v = new Rosa.Views.SearchedCollaboratorView({ model: item });
|
||||
return v.render().$el.appendTo(ul);
|
||||
},
|
||||
|
||||
renderAll: function( ul, items ) {
|
||||
var self = this;
|
||||
if (items[0]['result_empty'] !== undefined && items[0]['result_empty'] === true) {
|
||||
ul.removeClass('has_results').append(this.result_empty());
|
||||
} else {
|
||||
ul.addClass('has_results');
|
||||
_.each( items, function( item ) {
|
||||
self.addOne( ul, item );
|
||||
});
|
||||
var factor = (items.length > 10) ? 10 : items.length;
|
||||
ul.height(ul.children('li').first() * factor);
|
||||
}
|
||||
},
|
||||
|
||||
onFocus: function( event, ui ) {
|
||||
this.$_search_input.val(ui.item.get('actor_name'));
|
||||
return false;
|
||||
},
|
||||
|
||||
selectItem: function( event, ui ) {
|
||||
var model = ui.item;
|
||||
this.$_image.attr('src', model.get('avatar')).show();
|
||||
|
||||
this.__selected_item = model;
|
||||
return false;
|
||||
},
|
||||
|
||||
addCollaborator: function(e) {
|
||||
e.preventDefault();
|
||||
var model = this.__selected_item;
|
||||
|
||||
if ( model !== undefined ) {
|
||||
model.setRole(this.$_role.val());
|
||||
this.trigger('collaborator_prepared', model);
|
||||
this.__selected_item = undefined;
|
||||
this.$_image.hide();
|
||||
this.$_search_input.val('');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
});
|
|
@ -1,69 +0,0 @@
|
|||
Rosa.Views.CollaboratorView = Backbone.View.extend({
|
||||
template: JST['backbone/templates/collaborators/collaborator'],
|
||||
tagName: 'tr',
|
||||
className: 'regular',
|
||||
|
||||
events: {
|
||||
'change input[type="radio"]': 'changeRole',
|
||||
'change input[type="checkbox"]': 'toggleRemoved'
|
||||
},
|
||||
|
||||
initialize: function() {
|
||||
this.$el.attr('id', 'admin-table-members-row' + this.options.model.get('id') + this.options.model.get('actor_type'));
|
||||
this.model.on('change', this.render, this);
|
||||
this.model.on('destroy', this.hide, this);
|
||||
this.model.on('sync_failed', this.syncError, this);
|
||||
this.model.on('sync_success', this.syncSuccess, this);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if (this.model.get('removed')) {
|
||||
this.$el.addClass('removed');
|
||||
} else {
|
||||
this.$el.removeClass('removed');
|
||||
};
|
||||
this.$el.html(this.template(this.model.toJSON()));
|
||||
return this;
|
||||
},
|
||||
|
||||
changeRole: function(e) {
|
||||
this.$('input[type="radio"]').attr('disabled', 'disabled');
|
||||
this.model.changeRole(e.target.value);
|
||||
},
|
||||
|
||||
toggleRemoved: function(e) {
|
||||
this.model.toggleRemoved();
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
this.remove();
|
||||
},
|
||||
|
||||
syncError: function() {
|
||||
var self = this;
|
||||
this.$el.addClass('sync_error');
|
||||
this.$('td').animate({
|
||||
'background-color': '#FFFFFF'
|
||||
}, {
|
||||
duration: 800,
|
||||
easing: 'easeInCirc',
|
||||
complete: function() {
|
||||
self.$el.removeClass('sync_error');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
syncSuccess: function() {
|
||||
var self = this;
|
||||
this.$el.addClass('sync_success');
|
||||
this.$('td').animate({
|
||||
'background-color': '#FFFFFF'
|
||||
}, {
|
||||
duration: 800,
|
||||
easing: 'easeInCirc',
|
||||
complete: function() {
|
||||
self.$el.removeClass('sync_success');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
|
@ -1,57 +0,0 @@
|
|||
Rosa.Views.CollaboratorsView = Backbone.View.extend({
|
||||
initialize: function() {
|
||||
_.bindAll(this, 'deleterClick', 'processFilter', 'addOne');
|
||||
this.setupDeleter();
|
||||
this.setupFilter();
|
||||
this.$el = $('#collaborators > tbody');
|
||||
this.collection.on('reset', this.render, this);
|
||||
this.collection.on('add', this.clearFilter, this);
|
||||
},
|
||||
|
||||
addOne: function(collaborator) {
|
||||
var cView = new Rosa.Views.CollaboratorView({ model: collaborator });
|
||||
this.$el.append(cView.render().$el);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
this.clearFilter();
|
||||
this.$el.empty();
|
||||
this.collection.forEach(this.addOne, this);
|
||||
this._$deleter.show();
|
||||
return this;
|
||||
},
|
||||
|
||||
renderList: function(list) {
|
||||
this.$el.empty();
|
||||
|
||||
list.each(this.addOne);
|
||||
return this;
|
||||
},
|
||||
|
||||
setupDeleter: function() {
|
||||
this._$deleter = $('#collaborators_deleter');
|
||||
this._$deleter.on('click.deleter', this.deleterClick);
|
||||
this._$deleter.attr('title', 'Remove selected rows');
|
||||
},
|
||||
|
||||
deleterClick: function() {
|
||||
this.collection.removeMarked();
|
||||
},
|
||||
|
||||
setupFilter: function() {
|
||||
this._$filter = $('#collaborators thead input[type="text"]');
|
||||
this._$filter.on('keyup', this.processFilter);
|
||||
this.clearFilter();
|
||||
},
|
||||
|
||||
clearFilter: function() {
|
||||
this._$filter.val('');
|
||||
},
|
||||
|
||||
processFilter: function() {
|
||||
var term = this._$filter.val();
|
||||
var list = this.collection.filterByName(term, {excludeRemoved: true});
|
||||
console.log(list);
|
||||
this.renderList(list);
|
||||
}
|
||||
});
|
|
@ -1,12 +0,0 @@
|
|||
Rosa.Views.SearchedCollaboratorView = Backbone.View.extend({
|
||||
template: JST['backbone/templates/collaborators/searched_collaborator'],
|
||||
tagName: 'li',
|
||||
className: 'item',
|
||||
|
||||
render: function() {
|
||||
this.$el.empty();
|
||||
this.$el.data( "item.autocomplete", this.model )
|
||||
.append(this.template(this.model.toJSON()));
|
||||
return this;
|
||||
}
|
||||
})
|
|
@ -894,12 +894,11 @@ ul.ui-autocomplete.has_results {
|
|||
float: left;
|
||||
padding-top: 6px;
|
||||
}
|
||||
#add_collaborator_form div.img {
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
#add_collaborator_form div.img img {
|
||||
#collaborators {
|
||||
.img, img {
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
#add_collaborator_form .admin-role .lineForm {
|
||||
|
|
|
@ -14,12 +14,6 @@ class Projects::CollaboratorsController < Projects::BaseController
|
|||
respond_with @collaborators
|
||||
end
|
||||
|
||||
def show
|
||||
end
|
||||
|
||||
def new
|
||||
end
|
||||
|
||||
def find
|
||||
users = User.not_member_of(@project)
|
||||
groups = Group.not_member_of(@project)
|
||||
|
@ -33,9 +27,6 @@ class Projects::CollaboratorsController < Projects::BaseController
|
|||
end
|
||||
end
|
||||
|
||||
def edit
|
||||
end
|
||||
|
||||
def create
|
||||
@collaborator = Collaborator.new(params[:collaborator])
|
||||
@collaborator.project = @project
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
json.id collaborator.id
|
||||
|
||||
json.actor_id collaborator.actor_id
|
||||
json.actor_name collaborator.actor_name
|
||||
json.actor_type collaborator.actor_type
|
||||
json.(collaborator, :id, :actor_name)
|
||||
json.collaborator do # attr_accessible for AngularJS
|
||||
json.(collaborator, :role, :actor_id, :actor_type, :project_id)
|
||||
end
|
||||
json.project do
|
||||
json.(collaborator.project, :name, :owner_uname)
|
||||
end
|
||||
json.avatar avatar_url(collaborator.actor)
|
||||
json.actor_path participant_path(collaborator.actor)
|
||||
|
||||
json.project_id collaborator.project_id
|
||||
json.role collaborator.role
|
||||
|
|
|
@ -1,12 +1,3 @@
|
|||
json.array!(collaborators) do |cb|
|
||||
json.(cb, :id, :actor_id, :actor_name, :actor_type, :project_id)
|
||||
json.collaborator do # attr_accessible for AngularJS
|
||||
json.(cb, :role)
|
||||
end
|
||||
json.project do
|
||||
json.(cb.project, :name, :owner_uname)
|
||||
end
|
||||
json.avatar avatar_url(cb.actor)
|
||||
json.actor_path participant_path(cb.actor)
|
||||
|
||||
json.array!(collaborators) do |collaborator|
|
||||
json.partial! 'projects/collaborators/collaborator', :collaborator => collaborator
|
||||
end
|
||||
|
|
|
@ -5,15 +5,15 @@
|
|||
%a{:name => 'users'}
|
||||
%h3= t("layout.users.list_header")
|
||||
|
||||
%div{'ng-controller' => 'CollaboratorsController'}
|
||||
#collaborators{'ng-controller' => 'CollaboratorsController'}
|
||||
= hidden_field_tag :owner_name, @project.try(:owner).try(:uname)
|
||||
= hidden_field_tag :project_name, @project.try(:name)
|
||||
|
||||
#add_collaborator_form
|
||||
.admin-search.withimage
|
||||
.img
|
||||
%img{:alt => 'avatar', 'ng-src' => '{{collaborator.avatar}}', 'ng-show' => 'collaborator.avatar'}
|
||||
= text_field_tag :collaborator_name, nil, 'ng-model' => 'collaborator.term', 'ng-keyup' => 'search()'
|
||||
%img{:alt => 'avatar', 'ng-src' => '{{new_collaborator.avatar}}', 'ng-show' => 'new_collaborator.avatar'}
|
||||
= text_field_tag :collaborator_name, nil, 'ng-model' => 'new_collaborator.term', 'ng-keyup' => 'search()'
|
||||
|
||||
.users-search-popup
|
||||
.header
|
||||
|
@ -27,19 +27,16 @@
|
|||
.both
|
||||
|
||||
.nothing{'ng-hide' => 'new_collaborators.length > 0'}= t('layout.issues.nothing_to_show')
|
||||
|
||||
|
||||
//= autocomplete_field_tag :collaborator_name, params[:user_uname], find_project_collaborators_path(@project)
|
||||
.both
|
||||
.admin-role
|
||||
.lineForm
|
||||
= select_tag 'role', options_for_collaborators_roles_select
|
||||
= select_tag 'role', options_for_collaborators_roles_select, 'ng-model' => 'new_collaborator.collaborator.role'
|
||||
.admin-add
|
||||
%a{:id => 'add_collaborator_button', :class => 'button', :rel => 'nofollow', :href => 'javascript:void(0)'}
|
||||
%a.button{:rel => 'nofollow', :href => '', 'ng-click' => 'add()'}
|
||||
= t('layout.add')
|
||||
.both
|
||||
|
||||
%table#collaborators.tablesorter{:cellpadding => "0", :cellspacing => "0"}
|
||||
%table.tablesorter{:cellpadding => "0", :cellspacing => "0"}
|
||||
%thead
|
||||
%tr
|
||||
%th.centered
|
||||
|
|
Loading…
Reference in New Issue