fix hound and refactor

This commit is contained in:
mlorb 2017-09-21 10:36:09 +02:00
parent 48c8922604
commit 55772a80ab
12 changed files with 385 additions and 325 deletions

View file

@ -1,34 +1,34 @@
module ClientApi
module Users
class InvitationsController < Devise::InvitationsController
module Users
class InvitationsController < Devise::InvitationsController
before_action :check_invite_users_permission, only: :invite_users
before_action :check_invite_users_permission, only: :invite_users
def invite_users
invite_service =
ClientApi::InvitationsService.new(user: current_user,
team: @team,
role: params['user_role'],
emails: params[:emails])
invite_results = invite_service.invitation
success_response(invite_results)
end
def invite_users
invite_service = ClientApi::InvitationsService.new(user: current_user,
team: @team,
role: params['user_role'],
emails: params[:emails])
invite_results = invite_service.invitation
success_response(invite_results)
end
def success_response(invite_results)
respond_to do |format|
format.json do
render template: '/client_api/users/invite_users',
status: :ok,
locals: {invite_results: invite_results, team: @team}
def success_response(invite_results)
respond_to do |format|
format.json do
render template: '/client_api/users/invite_users',
status: :ok,
locals: { invite_results: invite_results, team: @team }
end
end
end
end
private
private
def check_invite_users_permission
@team = Team.find_by_id(params[:team_id])
render_403 if @team && !is_admin_of_team(@team)
def check_invite_users_permission
@team = Team.find_by_id(params[:team_id])
render_403 if @team && !is_admin_of_team(@team)
end
end
end
end
end

View file

@ -1,27 +1,28 @@
import React from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import { DropdownButton, MenuItem } from 'react-bootstrap';
import React from "react";
import { func } from "prop-types";
import { FormattedMessage } from "react-intl";
import { DropdownButton, MenuItem } from "react-bootstrap";
const InviteUsersButton = props =>
<DropdownButton
bsStyle={'primary'}
title={<FormattedMessage id="invite_users.dropdown_button.invite" />}
id="invite_users.submit_button"
>
<MenuItem onClick={() => props.handleClick('guest')}>
<FormattedMessage id="invite_users.dropdown_button.guest" />
</MenuItem>
<MenuItem onClick={() => props.handleClick('normal_user')}>
<FormattedMessage id="invite_users.dropdown_button.normal_user" />
</MenuItem>
<MenuItem onClick={() => props.handleClick('admin')}>
<FormattedMessage id="invite_users.dropdown_button.admin" />
</MenuItem>
</DropdownButton>;
const InviteUsersButton = props => (
<DropdownButton
bsStyle={"primary"}
title={<FormattedMessage id="invite_users.dropdown_button.invite" />}
id="invite_users.submit_button"
>
<MenuItem onClick={() => props.handleClick("guest")}>
<FormattedMessage id="invite_users.dropdown_button.guest" />
</MenuItem>
<MenuItem onClick={() => props.handleClick("normal_user")}>
<FormattedMessage id="invite_users.dropdown_button.normal_user" />
</MenuItem>
<MenuItem onClick={() => props.handleClick("admin")}>
<FormattedMessage id="invite_users.dropdown_button.admin" />
</MenuItem>
</DropdownButton>
);
InviteUsersButton.propTypes = {
handleClick: PropTypes.func.isRequired
handleClick: func.isRequired
};
export default InviteUsersButton;

View file

@ -1,38 +1,42 @@
import React from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import { FormGroup, HelpBlock } from 'react-bootstrap';
import TagsInput from 'react-tagsinput';
import React from "react";
import { string, func, arrayOf } from "prop-types";
import { FormattedMessage } from "react-intl";
import { FormGroup, HelpBlock } from "react-bootstrap";
import TagsInput from "react-tagsinput";
import { INVITE_USERS_LIMIT } from '../../../config/constants/numeric';
import { INVITE_USERS_LIMIT } from "../../../config/constants/numeric";
const InviteUsersForm = props =>
<FormGroup controlId="form-invite-user">
<p>
<FormattedMessage id="invite_users.input_text" values={{team: props.teamName}} />
</p>
<TagsInput
value={props.tags}
addKeys={[9, 13, 188]}
addOnPaste
onlyUnique
maxTags={INVITE_USERS_LIMIT}
inputProps={{
placeholder: ''
}}
onChange={props.handleChange}
/>
<HelpBlock>
<em>
<FormattedMessage id="invite_users.input_help" />
</em>
</HelpBlock>
</FormGroup>;
const InviteUsersForm = props => (
<FormGroup controlId="form-invite-user">
<p>
<FormattedMessage
id="invite_users.input_text"
values={{ team: props.teamName }}
/>
</p>
<TagsInput
value={props.tags}
addKeys={[9, 13, 188]}
addOnPaste
onlyUnique
maxTags={INVITE_USERS_LIMIT}
inputProps={{
placeholder: ""
}}
onChange={props.handleChange}
/>
<HelpBlock>
<em>
<FormattedMessage id="invite_users.input_help" />
</em>
</HelpBlock>
</FormGroup>
);
InviteUsersForm.propTypes = {
tags: PropTypes.arrayOf(PropTypes.string.isRequired).isRequired,
handleChange: PropTypes.func.isRequired,
teamName: PropTypes.string.isRequired
tags: arrayOf(string.isRequired).isRequired,
handleChange: func.isRequired,
teamName: string.isRequired
};
export default InviteUsersForm;

View file

@ -1,30 +1,38 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Alert } from 'react-bootstrap';
import { FormattedMessage } from 'react-intl';
import React from "react";
import { shape, arrayOf, string } from "prop-types";
import { Alert } from "react-bootstrap";
import { FormattedMessage } from "react-intl";
const InviteUsersResults = props =>
<div>
<h5>
<FormattedMessage id="invite_users.results_title" />
</h5>
<hr />
{props.results.invite_results.map(result =>
<Alert bsStyle={result.alert} key={result.email}>
<strong>{result.email}</strong>
&nbsp;-&nbsp;
<FormattedMessage id={`invite_users.results_msg.${result.status}`}
values={{team: props.results.team_name,
role: <FormattedMessage id={`invite_users.roles.${result.user_role}`} />,
nr: result.invite_limit
}} />
</Alert>
)}
</div>;
const InviteUsersResults = props => (
<div>
<h5>
<FormattedMessage id="invite_users.results_title" />
</h5>
<hr />
{props.results.invite_results.map(result => (
<Alert bsStyle={result.alert} key={result.email}>
<strong>{result.email}</strong>
&nbsp;-&nbsp;
<FormattedMessage
id={`invite_users.results_msg.${result.status}`}
values={{
team: props.results.team_name,
role: (
<FormattedMessage id={`invite_users.roles.${result.user_role}`} />
),
nr: result.invite_limit
}}
/>
</Alert>
))}
</div>
);
InviteUsersResults.propTypes = {
results: PropTypes.object.isRequired
results: shape({
invite_results: arrayOf.isRequired,
team_name: string.isRequired
}).isRequired
};
export default InviteUsersResults;

View file

@ -1,107 +1,115 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import { Modal, ButtonToolbar, Button } from 'react-bootstrap';
import styled from 'styled-components';
import axios from '../../config/axios';
import React, { Component } from "react";
import { bool, func, shape, number, string } from "prop-types";
import { FormattedMessage } from "react-intl";
import { Modal, ButtonToolbar, Button } from "react-bootstrap";
import styled from "styled-components";
import axios from "../../config/axios";
import { INVITE_USERS_PATH, TEAM_DETAILS_PATH } from '../../config/api_endpoints';
import InviteUsersForm from './components/InviteUsersForm';
import InviteUsersResults from './components/InviteUsersResults';
import InviteUsersButton from './components/InviteUsersButton';
import {
INVITE_USERS_PATH,
TEAM_DETAILS_PATH
} from "../../config/api_endpoints";
import InviteUsersForm from "./components/InviteUsersForm";
import InviteUsersResults from "./components/InviteUsersResults";
import InviteUsersButton from "./components/InviteUsersButton";
const StyledButtonToolbar = styled(ButtonToolbar)`
float: right;
`;
const StyledButtonToolbar = styled(ButtonToolbar)`float: right;`;
class InviteUsersModal extends Component {
constructor() {
super();
this.state = {
showInviteUsersResults: false,
inputTags: [],
inviteResults: []
};
this.handleInputChange = this.handleInputChange.bind(this);
this.inviteAs = this.inviteAs.bind(this);
this.handleCloseModal = this.handleCloseModal.bind(this)
}
constructor(props) {
super(props);
this.state = {
showInviteUsersResults: false,
inputTags: [],
inviteResults: []
};
this.handleInputChange = this.handleInputChange.bind(this);
this.inviteAs = this.inviteAs.bind(this);
this.handleCloseModal = this.handleCloseModal.bind(this);
}
handleCloseModal() {
const path = TEAM_DETAILS_PATH.replace(":team_id", this.props.team.id);
this.props.onCloseModal();
this.setState({
showInviteUsersResults: false,
inputTags: [],
inviteResults: []
});
// Update team members table
axios.get(path).then(response => {
const { users } = response.data.team_details;
this.props.updateUsersCallback(users);
})
}
handleCloseModal() {
const path = TEAM_DETAILS_PATH.replace(":team_id", this.props.team.id);
this.props.onCloseModal();
this.setState({
showInviteUsersResults: false,
inputTags: [],
inviteResults: []
});
// Update team members table
axios.get(path).then(response => {
const { users } = response.data.team_details;
this.props.updateUsersCallback(users);
});
}
handleInputChange(inputTags) {
this.setState({ inputTags });
}
handleInputChange(inputTags) {
this.setState({ inputTags });
}
inviteAs(role) {
axios
.put(INVITE_USERS_PATH, {
user_role: role,
emails: this.state.inputTags,
team_id: this.props.team.id
})
.then(({ data }) => {
this.setState({ inviteResults: data });
this.setState({ showInviteUsersResults: true });
})
.catch(error => {});
}
inviteAs(role) {
axios
.put(INVITE_USERS_PATH, {
user_role: role,
emails: this.state.inputTags,
team_id: this.props.team.id
})
.then(({ data }) => {
this.setState({ inviteResults: data });
this.setState({ showInviteUsersResults: true });
})
.catch(error => {});
}
render() {
let modalBody = null;
let inviteButton = null;
if (this.state.showInviteUsersResults) {
modalBody = <InviteUsersResults results={this.state.inviteResults} />;
inviteButton = null;
} else {
modalBody = <InviteUsersForm tags={this.state.inputTags} handleChange={this.handleInputChange} teamName={this.props.team.name} />;
inviteButton = <InviteUsersButton handleClick={this.inviteAs} />;
}
render() {
let modalBody = null;
let inviteButton = null;
if (this.state.showInviteUsersResults) {
modalBody = <InviteUsersResults results={this.state.inviteResults} />;
inviteButton = null;
} else {
modalBody = (
<InviteUsersForm
tags={this.state.inputTags}
handleChange={this.handleInputChange}
teamName={this.props.team.name}
/>
);
inviteButton = <InviteUsersButton handleClick={this.inviteAs} />;
}
return (
<Modal show={this.props.showModal} onHide={this.handleCloseModal}>
<Modal.Header closeButton>
<Modal.Title>
<FormattedMessage id="invite_users.modal_title" values={{team: this.props.team.name}} />
</Modal.Title>
</Modal.Header>
<Modal.Body>
{modalBody}
</Modal.Body>
<Modal.Footer>
<StyledButtonToolbar>
<Button onClick={this.handleCloseModal}>
<FormattedMessage id="general.cancel" />
</Button>
{inviteButton}
</StyledButtonToolbar>
</Modal.Footer>
</Modal>
);
}
return (
<Modal show={this.props.showModal} onHide={this.handleCloseModal}>
<Modal.Header closeButton>
<Modal.Title>
<FormattedMessage
id="invite_users.modal_title"
values={{ team: this.props.team.name }}
/>
</Modal.Title>
</Modal.Header>
<Modal.Body>{modalBody}</Modal.Body>
<Modal.Footer>
<StyledButtonToolbar>
<Button onClick={this.handleCloseModal}>
<FormattedMessage id="general.cancel" />
</Button>
{inviteButton}
</StyledButtonToolbar>
</Modal.Footer>
</Modal>
);
}
}
InviteUsersModal.propTypes = {
showModal: PropTypes.bool.isRequired,
onCloseModal: PropTypes.func.isRequired,
team: PropTypes.shape({
id: PropTypes.number.isRequired,
name: PropTypes.string.isRequired
}).isRequired,
updateUsersCallback: PropTypes.func.isRequired
showModal: bool.isRequired,
onCloseModal: func.isRequired,
team: shape({
id: number.isRequired,
name: string.isRequired
}).isRequired,
updateUsersCallback: func.isRequired
};
export default InviteUsersModal;

View file

@ -39,7 +39,7 @@ export const CHANGE_USER_RECENT_NOTIFICATION_EMAIL_PATH =
"/client_api/users/change_recent_notification_email";
export const CHANGE_USER_SYSTEM_MESSAGE_NOTIFICATION_EMAIL_PATH =
"/client_api/users/change_system_notification_email";
export const INVITE_USERS_PATH = '/client_api/users/invite_users';
export const INVITE_USERS_PATH = "/client_api/users/invite_users";
// info dropdown_title
export const CUSTOMER_SUPPORT_LINK = "http://scinote.net/support";

View file

@ -1,25 +1,25 @@
export default {
'en-US': {
"en-US": {
general: {
close: 'Close',
cancel: 'Cancel',
update: 'Update',
edit: 'Edit',
loading: 'Loading ...'
close: "Close",
cancel: "Cancel",
update: "Update",
edit: "Edit",
loading: "Loading ..."
},
error_messages: {
text_too_short: "is too short (minimum is {min_length} characters)",
text_too_long: "is too long (maximum is {max_length} characters)"
},
navbar: {
page_title: 'sciNote',
home_label: 'Home',
protocols_label: 'Protocols',
repositories_label: 'Repositories',
activities_label: 'Activities',
search_label: 'Search',
notifications_label: 'Notifications',
info_label: 'Info'
page_title: "sciNote",
home_label: "Home",
protocols_label: "Protocols",
repositories_label: "Repositories",
activities_label: "Activities",
search_label: "Search",
notifications_label: "Notifications",
info_label: "Info"
},
settings_page: {
all_teams: "All teams",
@ -54,8 +54,8 @@ export default {
"Assignment notifications appear whenever you get assigned to a team, project, task.",
recent_changes: "Recent changes",
recent_changes_msg:
'Recent changes notifications appear whenever there is a change on a task you are assigned to.',
system_message: 'System message',
"Recent changes notifications appear whenever there is a change on a task you are assigned to.",
system_message: "System message",
system_message_msg:
"System message notifications are specifically sent by site maintainers to notify all users about a system update.",
show_in_scinote: "Show in sciNote",
@ -64,20 +64,28 @@ export default {
yes: "Yes",
leave_team_modal: {
title: "Leave team {teamName}",
subtitle: "Are you sure you wish to leave team My projects? This action is irreversible.",
subtitle:
"Are you sure you wish to leave team My projects? This action is irreversible.",
warnings: "Leaving team has following consequences:",
warning_message_one: "you will lose access to all content belonging to the team (including projects, tasks, protocols and activities);",
warning_message_two: "all projects in the team where you were the sole <b>Owner</b> will receive a new owner from the team administrators;",
warning_message_three: "all repository protocols in the team belonging to you will be reassigned onto a new owner from team administrators.",
warning_message_one:
"you will lose access to all content belonging to the team (including projects, tasks, protocols and activities);",
warning_message_two:
"all projects in the team where you were the sole <b>Owner</b> will receive a new owner from the team administrators;",
warning_message_three:
"all repository protocols in the team belonging to you will be reassigned onto a new owner from team administrators.",
leave_team: "Leave"
},
remove_user_modal: {
title: "Remove user {user} from team {team}",
subtitle: "Are you sure you wish to remove user {user} from team {team}?",
subtitle:
"Are you sure you wish to remove user {user} from team {team}?",
warnings: "Removing user from team has following consequences:",
warning_message_one: "user will lose access to all content belonging to the team (including projects, tasks, protocols and activities);",
warning_message_two: "all projects in the team where user was the sole <b>Owner</b> will be reassigned onto you as a new owner;",
warning_message_three: "all repository protocols in the team belonging to user will be reassigned onto you.",
warning_message_one:
"user will lose access to all content belonging to the team (including projects, tasks, protocols and activities);",
warning_message_two:
"all projects in the team where user was the sole <b>Owner</b> will be reassigned onto you as a new owner;",
warning_message_three:
"all repository protocols in the team belonging to user will be reassigned onto you.",
remove_user: "Remove user"
},
update_team_description_modal: {
@ -114,61 +122,65 @@ export default {
}
},
activities: {
modal_title: 'Activities',
no_data: 'No Data',
more_activities: 'More Activities'
modal_title: "Activities",
no_data: "No Data",
more_activities: "More Activities"
},
global_team_switch: {
new_team: 'New team'
new_team: "New team"
},
notifications: {
dropdown_title: 'Notifications',
dropdown_settings_link: 'Settings',
dropdown_show_all: 'Show all notifications'
dropdown_title: "Notifications",
dropdown_settings_link: "Settings",
dropdown_show_all: "Show all notifications"
},
info_dropdown: {
customer_support: 'Customer support',
tutorials: 'Tutorials',
release_notes: 'Release notes',
premium: 'Premium',
contact_us: 'Contact us'
customer_support: "Customer support",
tutorials: "Tutorials",
release_notes: "Release notes",
premium: "Premium",
contact_us: "Contact us"
},
user_account_dropdown: {
greeting: 'Hi, {name}',
settings: 'Settings',
log_out: 'Log out'
greeting: "Hi, {name}",
settings: "Settings",
log_out: "Log out"
},
invite_users: {
modal_title: 'Invite users to team {team}',
input_text: 'Invite more people to team {team} and start using sciNote.',
input_help: 'Input one or multiple emails, confirm each email with ENTER key.',
modal_title: "Invite users to team {team}",
input_text: "Invite more people to team {team} and start using sciNote.",
input_help:
"Input one or multiple emails, confirm each email with ENTER key.",
dropdown_button: {
invite: 'Invite user/s',
guest: 'as Guest/s',
normal_user: 'as Normal user/s',
admin: 'as Administrator/s'
invite: "Invite user/s",
guest: "as Guest/s",
normal_user: "as Normal user/s",
admin: "as Administrator/s"
},
results_title: 'Invitation results:',
results_title: "Invitation results:",
roles: {
guest: 'Guest',
normal_user: 'Normal user',
admin: 'Administrator'
guest: "Guest",
normal_user: "Normal user",
admin: "Administrator"
},
results_msg: {
user_exists: 'User is already a member of sciNote.',
user_exists: "User is already a member of sciNote.",
user_exists_unconfirmed:
'User is already a member of sciNote but is not confirmed yet.',
"User is already a member of sciNote but is not confirmed yet.",
user_exists_and_in_team_unconfirmed:
'User is already a member of sciNote and team {team} as {role} but is not confirmed yet.',
"User is already a member of sciNote and team {team} as {role} but is not confirmed yet.",
user_exists_invited_to_team_unconfirmed:
'User is already a member of sciNote but is not confirmed yet - successfully invited to team {team} as {role}.',
user_exists_and_in_team: 'User is already a member of sciNote and team {team} as {role}.',
"User is already a member of sciNote but is not confirmed yet - successfully invited to team {team} as {role}.",
user_exists_and_in_team:
"User is already a member of sciNote and team {team} as {role}.",
user_exists_invited_to_team:
'User was already a member of sciNote - successfully invited to team {team} as {role}.',
user_created: 'User succesfully invited to sciNote.',
user_created_invited_to_team: 'User successfully invited to sciNote and team {team} as {role}.',
user_invalid: 'Invalid email.',
too_many_emails: 'Only invited first {nr} emails. To invite more users, fill in another invitation form.'
"User was already a member of sciNote - successfully invited to team {team} as {role}.",
user_created: "User succesfully invited to sciNote.",
user_created_invited_to_team:
"User successfully invited to sciNote and team {team} as {role}.",
user_invalid: "Invalid email.",
too_many_emails:
"Only invited first {nr} emails. To invite more users, fill in another invitation form."
}
}
}

View file

@ -10,7 +10,7 @@ import {
import { FormattedMessage } from "react-intl";
import axios from "../../../../../config/axios";
import InviteUsersModal from '../../../../../components/InviteUsersModal';
import InviteUsersModal from "../../../../../components/InviteUsersModal";
import RemoveUserModal from "./RemoveUserModal";
import DataTable from "../../../../../components/data_table";
import { UPDATE_USER_TEAM_ROLE_PATH } from "../../../../../config/api_endpoints";
@ -30,8 +30,12 @@ class TeamsMembers extends Component {
userToRemove: initalUserToRemove
};
this.memberAction = this.memberAction.bind(this);
this.showInviteUsersModalCallback = this.showInviteUsersModalCallback.bind(this);
this.closeInviteUsersModalCallback = this.closeInviteUsersModalCallback.bind(this);
this.showInviteUsersModalCallback = this.showInviteUsersModalCallback.bind(
this
);
this.closeInviteUsersModalCallback = this.closeInviteUsersModalCallback.bind(
this
);
this.hideModal = this.hideModal.bind(this);
}
@ -184,7 +188,7 @@ class TeamsMembers extends Component {
<FormattedMessage id="settings_page.single_team.members_panel_title" />
}
>
<Button bsStyle='primary' onClick={this.showInviteUsersModalCallback}>
<Button bsStyle="primary" onClick={this.showInviteUsersModalCallback}>
<Glyphicon glyph="plus" />
<FormattedMessage id="settings_page.single_team.add_members" />
</Button>

View file

@ -1,26 +1,26 @@
@import 'constants';
@import 'react-bootstrap-timezone-picker/dist/react-bootstrap-timezone-picker.min.css';
@import '~react-bootstrap-table/dist/react-bootstrap-table.min';
@import 'react-tagsinput/react-tagsinput.css';
@import "constants";
@import "react-bootstrap-timezone-picker/dist/react-bootstrap-timezone-picker.min.css";
@import "~react-bootstrap-table/dist/react-bootstrap-table.min";
@import "react-tagsinput/react-tagsinput.css";
body {
background-color: $color-concrete;
color: $color-emperor;
font-family: "Open Sans", Arial, Helvetica, sans-serif;
font-size: 13px;
background-color: $color-concrete;
color: $color-emperor;
font-family: "Open Sans", Arial, Helvetica, sans-serif;
font-size: 13px;
}
.label-primary {
background-color: $color-theme-primary;
background-color: $color-theme-primary;
}
.btn-primary {
background-color: $color-theme-secondary;
border-color: $primary-hover-color;
margin-right: 7px;
&:hover {
background-color: $primary-hover-color;
}
background-color: $color-theme-secondary;
border-color: $primary-hover-color;
margin-right: 7px;
&:hover {
background-color: $primary-hover-color;
}
}
// // fixes issue with dropdown in datatable

View file

@ -11,12 +11,14 @@ module ClientApi
raise ClientApi::CustomInvitationsError unless @emails && @team && @role
@emails && @emails.empty? { raise ClientApi::CustomInvitationsError }
@role && !UserTeam.roles.keys.include?(@role) { raise ClientApi::CustomInvitationsError }
if @role && !UserTeam.roles.keys.include?(@role)
raise ClientApi::CustomInvitationsError
end
end
def invitation
invite_results = []
@emails.each_with_index do |email, index|
result = {}
# Check invite users limit
@ -33,52 +35,13 @@ module ClientApi
# Check if user already exists
user = User.find_by_email(email) if User.exists?(email: email)
# User does not exist
if user.blank?
password = generate_user_password
# Validate the user data
error = !(Constants::BASIC_EMAIL_REGEX === email)
error = validate_user(email, email, password).count > 0 unless error
if !error
user = invite_new_user(email)
result[:status] = :user_created
result[:alert] = :success
result[:user] = user
# Invitation to team
if @team.present?
user_team = create_user_team_relation_and_notification(user)
result[:status] = :user_created_invited_to_team
result[:user_team] = user_team
end
else
# Return invalid status
result[:status] = :user_invalid
result[:alert] = :danger
end
# User exists
else
result[:status] = :"#{:user_exists}#{:_unconfirmed if !user.confirmed?}"
result[:alert] = :info
result[:user] = user
# Invitation to team
if @team.present?
user_team =
UserTeam.where(user: user, team: @team).first if UserTeam.exists?(user: user, team: @team)
if user_team.present?
result[:status] = :"#{:user_exists_and_in_team}#{:_unconfirmed if !user.confirmed?}"
else
user_team = create_user_team_relation_and_notification(user)
result[:status] = :"#{:user_exists_invited_to_team}#{:_unconfirmed if !user.confirmed?}"
end
result[:user_team] = user_team
end
end
result = if user.blank?
# User does not exist
handle_new_user(result, email, user)
else
# User exists
handle_existing_user(result, user)
end
invite_results << result
end
invite_results
@ -86,6 +49,61 @@ module ClientApi
private
def handle_new_user(result, email, user)
password = generate_user_password
# Validate the user data
error = (Constants::BASIC_EMAIL_REGEX !~ email)
error = validate_user(email, email, password).count > 0 unless error
if !error
# Invite new user
user = invite_new_user(email)
result[:status] = :user_created
result[:alert] = :success
result[:user] = user
# Invitation to team
if @team.present?
user_team = create_user_team_relation_and_notification(user)
result[:status] = :user_created_invited_to_team
result[:user_team] = user_team
end
else
# Return invalid status
result[:status] = :user_invalid
result[:alert] = :danger
end
result
end
def handle_existing_user(result, user)
result[:status] =
:"#{:user_exists}#{:_unconfirmed unless user.confirmed?}"
result[:alert] = :info
result[:user] = user
# Invitation to team
if @team.present?
if UserTeam.exists?(user: user, team: @team)
user_team = UserTeam.where(user: user, team: @team).first
end
if user_team.present?
result[:status] =
:"#{:user_exists_and_in_team}#{:_unconfirmed unless user
.confirmed?}"
else
user_team = create_user_team_relation_and_notification(user)
result[:status] =
:"#{:user_exists_invited_to_team}#{:_unconfirmed unless user
.confirmed?}"
end
result[:user_team] = user_team
end
result
end
def invite_new_user(email)
user = User.invite!(
full_name: email,
@ -140,4 +158,4 @@ module ClientApi
end
CustomInvitationsError = Class.new(StandardError)
end
end

View file

@ -2,7 +2,11 @@ json.invite_results invite_results do |invite_result|
json.status invite_result[:status]
json.alert invite_result[:alert]
json.email invite_result[:email]
json.user_role invite_result[:user_team].role if invite_result[:user_team].present?
json.invite_limit invite_result[:invite_user_limit] if invite_result[:invite_user_limit].present?
if invite_result[:user_team].present?
json.user_role invite_result[:user_team].role
end
if invite_result[:invite_user_limit].present?
json.invite_limit invite_result[:invite_user_limit]
end
end
json.team_name team.name if team.present?
json.team_name team.name if team.present?

View file

@ -83,7 +83,8 @@
</div>
<div class="panel-body">
<div class="col-xs-24 col-sm-12">
<a href="#" class="btn btn-primary pull-right row" data-trigger="invite-users" data-turbolinks="false" data-modal-id="team-invite-users-modal">
<a href="#" class="btn btn-primary pull-right row" data-trigger="invite-users"
data-turbolinks="false" data-modal-id="team-invite-users-modal">
<span class="glyphicon glyphicon-plus"></span>
<%= t("users.settings.teams.edit.add_user") %>
</a>