Added web interface for organizations.
It looks like crap, but it allows you to edit the details and the members.
This commit is contained in:
parent
64eab850c5
commit
e9cb235640
@ -313,6 +313,20 @@ class OrgManager:
|
|||||||
'$pull': {'unknown_members': member_email}
|
'$pull': {'unknown_members': member_email}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
def org_members(self, member_sting_ids: typing.Iterable[str]) -> typing.List[dict]:
|
||||||
|
"""Returns the user documents of the organization members.
|
||||||
|
|
||||||
|
This is a workaround to provide membership information for
|
||||||
|
organizations without giving 'mortal' users access to /api/users.
|
||||||
|
"""
|
||||||
|
from pillar.api.utils import str2id
|
||||||
|
|
||||||
|
member_ids = [str2id(uid) for uid in member_sting_ids]
|
||||||
|
users_coll = current_app.db('users')
|
||||||
|
users = users_coll.find({'_id': {'$in': member_ids}},
|
||||||
|
projection={'_id': 1, 'full_name': 1, 'email': 1})
|
||||||
|
return list(users)
|
||||||
|
|
||||||
|
|
||||||
def setup_app(app):
|
def setup_app(app):
|
||||||
from . import patch, hooks
|
from . import patch, hooks
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
def setup_app(app):
|
def setup_app(app):
|
||||||
from . import main, users, projects, nodes, notifications, redirects, subquery
|
from . import main, users, projects, nodes, notifications, organizations, redirects, subquery
|
||||||
main.setup_app(app, url_prefix=None)
|
main.setup_app(app, url_prefix=None)
|
||||||
users.setup_app(app, url_prefix=None)
|
users.setup_app(app, url_prefix=None)
|
||||||
redirects.setup_app(app, url_prefix='/r')
|
redirects.setup_app(app, url_prefix='/r')
|
||||||
@ -7,3 +7,4 @@ def setup_app(app):
|
|||||||
nodes.setup_app(app, url_prefix='/nodes')
|
nodes.setup_app(app, url_prefix='/nodes')
|
||||||
notifications.setup_app(app, url_prefix='/notifications')
|
notifications.setup_app(app, url_prefix='/notifications')
|
||||||
subquery.setup_app(app)
|
subquery.setup_app(app)
|
||||||
|
organizations.setup_app(app, url_prefix='/orgs')
|
||||||
|
5
pillar/web/organizations/__init__.py
Normal file
5
pillar/web/organizations/__init__.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
from .routes import blueprint
|
||||||
|
|
||||||
|
|
||||||
|
def setup_app(app, url_prefix=None):
|
||||||
|
app.register_blueprint(blueprint, url_prefix=url_prefix)
|
83
pillar/web/organizations/routes.py
Normal file
83
pillar/web/organizations/routes.py
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
import attr
|
||||||
|
from flask import Blueprint, render_template, request, jsonify
|
||||||
|
import flask_wtf.csrf
|
||||||
|
import werkzeug.exceptions as wz_exceptions
|
||||||
|
|
||||||
|
from pillarsdk import User
|
||||||
|
|
||||||
|
import pillar.flask_extra
|
||||||
|
from pillar import current_app
|
||||||
|
from pillar.api.utils import authorization, str2id, gravatar
|
||||||
|
from pillar.web.system_util import pillar_api
|
||||||
|
from pillar.api.utils.authentication import current_user
|
||||||
|
|
||||||
|
|
||||||
|
from pillarsdk import Organization
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
blueprint = Blueprint('pillar.web.organizations', __name__, url_prefix='/organizations')
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route('/', endpoint='index')
|
||||||
|
def index(organization_id: str = None):
|
||||||
|
api = pillar_api()
|
||||||
|
|
||||||
|
organizations = Organization.all(api=api)
|
||||||
|
|
||||||
|
if not organization_id and organizations['_items']:
|
||||||
|
organization_id = organizations['_items'][0]._id
|
||||||
|
|
||||||
|
can_create_organization = current_user().has_cap('create-organization')
|
||||||
|
|
||||||
|
return render_template('organizations/index.html',
|
||||||
|
can_create_organization=can_create_organization,
|
||||||
|
organizations=organizations,
|
||||||
|
open_organization_id=organization_id)
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route('/<organization_id>')
|
||||||
|
@pillar.flask_extra.vary_xhr()
|
||||||
|
def view_embed(organization_id: str):
|
||||||
|
if not request.is_xhr:
|
||||||
|
return index(organization_id)
|
||||||
|
|
||||||
|
api = pillar_api()
|
||||||
|
|
||||||
|
organization: Organization = Organization.find(organization_id, api=api)
|
||||||
|
|
||||||
|
om = current_app.org_manager
|
||||||
|
organization_oid = str2id(organization_id)
|
||||||
|
|
||||||
|
members = om.org_members(organization.members)
|
||||||
|
for member in members:
|
||||||
|
member['avatar'] = gravatar(member.get('email'))
|
||||||
|
member['_id'] = str(member['_id'])
|
||||||
|
|
||||||
|
can_edit = om.user_is_admin(organization_oid)
|
||||||
|
|
||||||
|
csrf = flask_wtf.csrf.generate_csrf()
|
||||||
|
|
||||||
|
return render_template('organizations/view_embed.html',
|
||||||
|
organization=organization,
|
||||||
|
members=members,
|
||||||
|
can_edit=can_edit,
|
||||||
|
seats_used=len(members) + len(organization.unknown_members),
|
||||||
|
csrf=csrf)
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route('/create-new', methods=['POST'])
|
||||||
|
@authorization.require_login(require_cap='create-organization')
|
||||||
|
def create_new():
|
||||||
|
"""Creates a new Organization, owned by the currently logged-in user."""
|
||||||
|
|
||||||
|
user_id = current_user().user_id
|
||||||
|
log.info('Creating new organization for user %s', user_id)
|
||||||
|
|
||||||
|
name = request.form['name']
|
||||||
|
seat_count = int(request.form['seat_count'], 10)
|
||||||
|
|
||||||
|
org_doc = current_app.org_manager.create_new_org(name, user_id, seat_count)
|
||||||
|
|
||||||
|
return jsonify({'_id': org_doc['_id']}), 201
|
182
src/templates/organizations/index.jade
Normal file
182
src/templates/organizations/index.jade
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
| {% extends 'layout.html' %}
|
||||||
|
| {% block bodyattrs %}{{ super() }} data-context='organizations'{% endblock %}
|
||||||
|
| {% block page_title %}Organizations{% endblock %}
|
||||||
|
|
||||||
|
| {% block body %}
|
||||||
|
#col_main.organization-index
|
||||||
|
#col_main-content
|
||||||
|
.col_header.item-list-header
|
||||||
|
i.pi-cloud
|
||||||
|
| Your organizations
|
||||||
|
|
||||||
|
.item-list.col-scrollable
|
||||||
|
.table
|
||||||
|
.table-head
|
||||||
|
.table-row
|
||||||
|
.table-cell.item-name
|
||||||
|
span.collapser(title="Collapse name column") Name
|
||||||
|
.table-cell.item-priority
|
||||||
|
span.collapser(title="Collapse priority column") Members
|
||||||
|
.table-cell.item-priority
|
||||||
|
span.collapser(title="Collapse priority column") Unknown Members
|
||||||
|
|
||||||
|
.table-body
|
||||||
|
| {% for organization in organizations['_items'] %}
|
||||||
|
| {% set link_url = url_for('pillar.web.organizations.view_embed', organization_id=organization._id) %}
|
||||||
|
.table-row(id="organization-{{ organization._id }}")
|
||||||
|
.table-cell.item-name
|
||||||
|
a(data-organization-id="{{ organization._id }}",
|
||||||
|
href="{{ link_url }}",
|
||||||
|
class="organization-link")
|
||||||
|
span(class="organization-name-{{ organization._id }}") {{ organization.name }}
|
||||||
|
.table-cell.item-members
|
||||||
|
a(data-organization-id="{{ organization._id }}",
|
||||||
|
href="{{ link_url }}",
|
||||||
|
class="organization-link")
|
||||||
|
span(class="organization-projects-{{ organization._id }}") {{ organization.members|hide_none|count }}
|
||||||
|
.table-cell.item-unknown-members
|
||||||
|
a(data-organization-id="{{ organization._id }}",
|
||||||
|
href="{{ link_url }}",
|
||||||
|
class="organization-link")
|
||||||
|
span(class="organization-projects-{{ organization._id }}") {{ organization.unknown_members|hide_none|count }}
|
||||||
|
| {% endfor %}
|
||||||
|
|
||||||
|
#item-action-panel
|
||||||
|
| {% if can_create_organization %}
|
||||||
|
button.btn(onclick='createNewOrganization(this)') Create new organization (max {{max_organizations}})
|
||||||
|
#create_organization_result_panel
|
||||||
|
| {% endif %}
|
||||||
|
|
||||||
|
#col_right
|
||||||
|
.col_header
|
||||||
|
span.header_text
|
||||||
|
#item-details.col-scrollable
|
||||||
|
.item-details-empty
|
||||||
|
| Select an organization
|
||||||
|
| {% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
| {% block footer_scripts %}
|
||||||
|
script(src="{{ url_for('static_pillar', filename='assets/js/vendor/jquery.typeahead-0.11.1.min.js')}}")
|
||||||
|
script(src="{{ url_for('static_pillar', filename='assets/js/vendor/algoliasearch-3.19.0.min.js')}}")
|
||||||
|
script(src="{{ url_for('static_pillar', filename='assets/js/vendor/jquery.autocomplete-0.22.0.min.js') }}", async=true)
|
||||||
|
|
||||||
|
script.
|
||||||
|
|
||||||
|
/* Returns a more-or-less reasonable message given an error response object. */
|
||||||
|
function xhrErrorResponseMessage(err) {
|
||||||
|
if (typeof err.responseJSON == 'undefined')
|
||||||
|
return err.statusText;
|
||||||
|
|
||||||
|
if (typeof err.responseJSON._error != 'undefined' && typeof err.responseJSON._error.message != 'undefined')
|
||||||
|
return err.responseJSON._error.message;
|
||||||
|
|
||||||
|
if (typeof err.responseJSON._message != 'undefined')
|
||||||
|
return err.responseJSON._message
|
||||||
|
|
||||||
|
return err.statusText;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open an organization in the #item-details div.
|
||||||
|
*/
|
||||||
|
function item_open(item_id, pushState)
|
||||||
|
{
|
||||||
|
if (item_id === undefined ) {
|
||||||
|
throw new ReferenceError("item_open(" + item_id + ") called.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Style elements starting with item_type and dash, e.g. "#job-uuid"
|
||||||
|
var clean_classes = 'active processing';
|
||||||
|
var current_item = $('#organization-' + item_id);
|
||||||
|
|
||||||
|
$('[id^="organization-"]').removeClass(clean_classes);
|
||||||
|
current_item
|
||||||
|
.removeClass(clean_classes)
|
||||||
|
.addClass('processing');
|
||||||
|
|
||||||
|
var item_url = '/orgs/' + item_id;
|
||||||
|
statusBarSet('default', 'Loading organization…');
|
||||||
|
|
||||||
|
$.get(item_url, function(item_data) {
|
||||||
|
statusBarClear();
|
||||||
|
$('#item-details').html(item_data);
|
||||||
|
$('#col_right .col_header span.header_text').text('Organization details');
|
||||||
|
|
||||||
|
current_item
|
||||||
|
.removeClass(clean_classes)
|
||||||
|
.addClass('active');
|
||||||
|
|
||||||
|
}).fail(function(xhr) {
|
||||||
|
if (console) {
|
||||||
|
console.log('Error fetching organization', item_id, 'from', item_url);
|
||||||
|
console.log('XHR:', xhr);
|
||||||
|
}
|
||||||
|
|
||||||
|
current_item.removeClass(clean_classes);
|
||||||
|
statusBarSet('error', 'Failed to open organization', 'pi-warning');
|
||||||
|
|
||||||
|
if (xhr.status) {
|
||||||
|
$('#item-details').html(xhr.responseText);
|
||||||
|
} else {
|
||||||
|
$('#item-details').html('<p class="text-danger">Opening ' + item_type + ' failed. There possibly was ' +
|
||||||
|
'an error connecting to the server. Please check your network connection and ' +
|
||||||
|
'try again.</p>');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Determine whether we should push the new state or not.
|
||||||
|
pushState = (typeof pushState !== 'undefined') ? pushState : true;
|
||||||
|
if (!pushState) return;
|
||||||
|
|
||||||
|
// Push the correct URL onto the history.
|
||||||
|
var push_state = {itemId: item_id};
|
||||||
|
|
||||||
|
window.history.pushState(
|
||||||
|
push_state,
|
||||||
|
'Organization: ' + item_id,
|
||||||
|
item_url
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
{% if open_organization_id %}
|
||||||
|
$(function() { item_open('{{ open_organization_id }}', false); });
|
||||||
|
{% endif %}
|
||||||
|
{% if can_create_organization %}
|
||||||
|
function createNewOrganization(button) {
|
||||||
|
$(button)
|
||||||
|
.attr('disabled', 'disabled')
|
||||||
|
.fadeTo(200, 0.1);
|
||||||
|
$('#create_organization_result_panel').html('');
|
||||||
|
|
||||||
|
// TODO: create a form to get the initial info from the user.
|
||||||
|
$.post(
|
||||||
|
'{{ url_for('pillar.organizations.create_new') }}',
|
||||||
|
{
|
||||||
|
name: 'New Organization',
|
||||||
|
seat_count: 1,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.done(function() {
|
||||||
|
var $p = $('<p>').text('organization created, reloading list.')
|
||||||
|
$('#create_organization_result_panel').html($p);
|
||||||
|
|
||||||
|
window.location.reload();
|
||||||
|
})
|
||||||
|
.fail(function(err) {
|
||||||
|
var msg = xhrErrorResponseMessage(err);
|
||||||
|
$('#create_organization_result_panel').html('Error creating organization: ' + msg);
|
||||||
|
|
||||||
|
$(button)
|
||||||
|
.fadeTo(1000, 1.0)
|
||||||
|
.queue(function() {
|
||||||
|
$(this)
|
||||||
|
.removeAttr('disabled')
|
||||||
|
.dequeue()
|
||||||
|
;
|
||||||
|
})
|
||||||
|
})
|
||||||
|
;
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
| {% endblock %}
|
325
src/templates/organizations/view_embed.jade
Normal file
325
src/templates/organizations/view_embed.jade
Normal file
@ -0,0 +1,325 @@
|
|||||||
|
.flamenco-box.organization
|
||||||
|
form#item_form(onsubmit="return editOrganization()")
|
||||||
|
| {% if can_edit %}
|
||||||
|
.input-group
|
||||||
|
input.item-name.input-transparent(
|
||||||
|
name="name",
|
||||||
|
type="text",
|
||||||
|
placeholder="Organization's name",
|
||||||
|
value="{{ organization.name | hide_none }}")
|
||||||
|
.input-group
|
||||||
|
textarea.item-description.input-transparent(
|
||||||
|
name="description",
|
||||||
|
type="text",
|
||||||
|
rows=1,
|
||||||
|
placeholder="Organization's description") {{ organization.description | hide_none }}
|
||||||
|
.input-group
|
||||||
|
input.item-website.input-transparent(
|
||||||
|
name="website",
|
||||||
|
type="text",
|
||||||
|
placeholder="Organization's website",
|
||||||
|
value="{{ organization.website | hide_none }}")
|
||||||
|
.input-group
|
||||||
|
input.item-location.input-transparent(
|
||||||
|
name="location",
|
||||||
|
type="text",
|
||||||
|
placeholder="Organization's location",
|
||||||
|
value="{{ organization.location | hide_none }}")
|
||||||
|
.input-group
|
||||||
|
button#item-save.btn.btn-default.btn-block(type='submit')
|
||||||
|
i.pi-check
|
||||||
|
| Save Organization
|
||||||
|
| {% else %}
|
||||||
|
p.item-name {{ organization.name | hide_none }}
|
||||||
|
| {% if organization.description %}
|
||||||
|
p.item-description {{ organization.description | hide_none }}
|
||||||
|
p.item-website {{ organization.website | hide_none }}
|
||||||
|
p.item-location {{ organization.location | hide_none }}
|
||||||
|
| {% endif %}
|
||||||
|
| {% endif %}
|
||||||
|
|
||||||
|
h4 Properties
|
||||||
|
.table.item-properties
|
||||||
|
.table-body
|
||||||
|
.table-row.properties-last-updated
|
||||||
|
.table-cell Last Updated
|
||||||
|
.table-cell(title='Unable to edit, set by Organization')
|
||||||
|
| {{ organization._updated | hide_none | pretty_date_time }}
|
||||||
|
.table-row.properties-seat-count
|
||||||
|
.table-cell Seat Count
|
||||||
|
.table-cell(title='Unable to edit, determined by subscription')
|
||||||
|
| {{ organization.seat_count }} ({{ seats_used }} used)
|
||||||
|
.table-row.properties-org-roles
|
||||||
|
.table-cell User roles
|
||||||
|
.table-cell(title='Unable to edit, determined by subscription')
|
||||||
|
| {{ organization.org_roles | sort | join(', ') }}
|
||||||
|
|
||||||
|
|
||||||
|
.flamenco-box.manager
|
||||||
|
h4 Organization members
|
||||||
|
| {% if can_edit %}
|
||||||
|
.row
|
||||||
|
.sharing-users-search
|
||||||
|
.form-group
|
||||||
|
input#user-select.form-control(
|
||||||
|
name='contacts',
|
||||||
|
type='text',
|
||||||
|
placeholder='Add member by name')
|
||||||
|
| {% endif %}
|
||||||
|
.row
|
||||||
|
ul.sharing-users-list
|
||||||
|
| {% for member in members %}
|
||||||
|
li.sharing-users-item(
|
||||||
|
data-user-id="{{ member['_id'] }}",
|
||||||
|
class="{% if current_user.objectid == member['_id'] %}self{% endif %}")
|
||||||
|
.sharing-users-avatar
|
||||||
|
img(src="{{ member['avatar'] }}")
|
||||||
|
.sharing-users-details
|
||||||
|
span.sharing-users-name
|
||||||
|
| {{ member['full_name'] }}
|
||||||
|
| {% if current_user.objectid == member['_id'] %}
|
||||||
|
small (You)
|
||||||
|
| {% endif %}
|
||||||
|
| {% if organization['admin_uid'] == member['_id'] %}
|
||||||
|
small (admin)
|
||||||
|
| {% endif %}
|
||||||
|
span.sharing-users-extra {{ member['username'] }}
|
||||||
|
.sharing-users-action
|
||||||
|
| {% if can_edit %}
|
||||||
|
| {% if current_user.objectid == member['_id'] %}
|
||||||
|
button.user-remove(title="Leave as member of this organization")
|
||||||
|
i.pi-trash
|
||||||
|
| {% else %}
|
||||||
|
button.user-remove(title="Remove this user from this organization")
|
||||||
|
i.pi-trash
|
||||||
|
| {% endif %}
|
||||||
|
| {% endif %}
|
||||||
|
| {% endfor %}
|
||||||
|
.row
|
||||||
|
h5 Users without Blender Cloud account
|
||||||
|
ul.sharing-users-list.unknown-members
|
||||||
|
| {% for email in organization.unknown_members %}
|
||||||
|
li.sharing-users-item.unknown-member(data-user-email='{{ email }}')
|
||||||
|
.sharing-users-avatar
|
||||||
|
img(src="{{ email | gravatar }}")
|
||||||
|
.sharing-users-details
|
||||||
|
span.sharing-users-email {{ email }}
|
||||||
|
.sharing-users-action
|
||||||
|
| {% if can_edit %}
|
||||||
|
button.user-remove(title="Remove this user from this organization")
|
||||||
|
i.pi-trash
|
||||||
|
| {% endif %}
|
||||||
|
| {% endfor %}
|
||||||
|
|
||||||
|
| {% if can_edit %}
|
||||||
|
h5 Batch-add members by email address
|
||||||
|
form#batch_add_form(onsubmit="return batchAddUsers()")
|
||||||
|
.input-group
|
||||||
|
textarea.item-description.input-transparent(
|
||||||
|
name="emails",
|
||||||
|
type="text",
|
||||||
|
rows=1,
|
||||||
|
placeholder="Email addresses, separated by space/enter")
|
||||||
|
.input-group
|
||||||
|
button.btn.btn-default.btn-block(type='submit')
|
||||||
|
i.pi-check
|
||||||
|
| Add members
|
||||||
|
| {% endif %}
|
||||||
|
|
||||||
|
.action-result-panel
|
||||||
|
|
||||||
|
#item-view-feed
|
||||||
|
| {% if config.DEBUG %}
|
||||||
|
.debug-info
|
||||||
|
a.debug-info-toggle(role='button',
|
||||||
|
data-toggle='collapse',
|
||||||
|
href='#debug-content-organization',
|
||||||
|
aria-expanded='false',
|
||||||
|
aria-controls='debug-content-organization')
|
||||||
|
i.pi-info
|
||||||
|
| Debug Info
|
||||||
|
#debug-content-organization.collapse
|
||||||
|
pre.
|
||||||
|
{{ organization.to_dict() | pprint }}
|
||||||
|
| {% endif %}
|
||||||
|
|
||||||
|
| {% block footer_scripts %}
|
||||||
|
|
||||||
|
| {% if can_edit %}
|
||||||
|
script.
|
||||||
|
|
||||||
|
function patchOrganization(patch) {
|
||||||
|
if (typeof patch == 'undefined') {
|
||||||
|
throw 'patchOrganization(undefined) called';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (console) console.log('patchOrganization', patch);
|
||||||
|
|
||||||
|
var promise = $.ajax({
|
||||||
|
url: '/api/organizations/{{ organization._id }}',
|
||||||
|
method: 'PATCH',
|
||||||
|
contentType: 'application/json',
|
||||||
|
data: JSON.stringify(patch),
|
||||||
|
})
|
||||||
|
.fail(function(err) {
|
||||||
|
if (console) console.log('Error patching: ', err);
|
||||||
|
})
|
||||||
|
;
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
var APPLICATION_ID = '{{config.ALGOLIA_USER}}'
|
||||||
|
var SEARCH_ONLY_API_KEY = '{{config.ALGOLIA_PUBLIC_KEY}}';
|
||||||
|
var INDEX_NAME = '{{config.ALGOLIA_INDEX_USERS}}';
|
||||||
|
var client = algoliasearch(APPLICATION_ID, SEARCH_ONLY_API_KEY);
|
||||||
|
var index = client.initIndex(INDEX_NAME);
|
||||||
|
|
||||||
|
$('#user-select').autocomplete({hint: false}, [
|
||||||
|
{
|
||||||
|
source: function (q, cb) {
|
||||||
|
index.search(q, {hitsPerPage: 5}, function (error, content) {
|
||||||
|
if (error) {
|
||||||
|
cb([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cb(content.hits, content);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
displayKey: 'full_name',
|
||||||
|
minLength: 2,
|
||||||
|
limit: 10,
|
||||||
|
templates: {
|
||||||
|
suggestion: function (hit) {
|
||||||
|
var suggestion = hit.full_name + ' (' + hit.username + ')';
|
||||||
|
var $p = $('<p>').text(suggestion);
|
||||||
|
return $p.html();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]).on('autocomplete:selected', function (event, hit, dataset) {
|
||||||
|
var $existing = $('li.sharing-users-item[data-user-id="' + hit.objectID + '"]');
|
||||||
|
if ($existing.length) {
|
||||||
|
$existing
|
||||||
|
.addClass('active')
|
||||||
|
.delay(1000)
|
||||||
|
.queue(function() {
|
||||||
|
console.log('no');
|
||||||
|
$existing.removeClass('active');
|
||||||
|
$existing.dequeue();
|
||||||
|
});
|
||||||
|
toastr.info('User is already member of this organization');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
addUser('{{ organization["_id"] }}', hit.objectID);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
function addUser(organizationId, userId){
|
||||||
|
if (!userId || userId.length == 0) {
|
||||||
|
toastr.error('Please select a user from the list');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
patchOrganization({
|
||||||
|
op: 'assign-user',
|
||||||
|
user_id: userId
|
||||||
|
})
|
||||||
|
.done(function (data) {
|
||||||
|
setTimeout(function(){ $('.sharing-users-item').removeClass('added');}, 350);
|
||||||
|
statusBarSet('success', 'Member added to this organization!', 'pi-grin');
|
||||||
|
|
||||||
|
// TODO fsiddi: avoid the reloading of the entire page?
|
||||||
|
window.location.reload();
|
||||||
|
})
|
||||||
|
.fail(function (err) {
|
||||||
|
var msg = xhrErrorResponseMessage(err);
|
||||||
|
toastr.error('Could not add member: ' + msg);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
| {% endif %}
|
||||||
|
script.
|
||||||
|
$(document).ready(function() {
|
||||||
|
$('body').off('click', '.user-remove'); // remove previous handlers.
|
||||||
|
$('body').on('click', '.user-remove', function(e) {
|
||||||
|
var user_id = $(this).closest('*[data-user-id]').data('user-id');
|
||||||
|
var user_email = $(this).closest('*[data-user-email]').data('user-email');
|
||||||
|
removeUser(user_id, user_email);
|
||||||
|
});
|
||||||
|
|
||||||
|
function removeUser(user_id, email) {
|
||||||
|
if (typeof user_id == 'undefined' && typeof email == 'undefined') {
|
||||||
|
throw "removeUser(undefined, undefined) called";
|
||||||
|
}
|
||||||
|
var organization_id = '{{ organization._id }}';
|
||||||
|
var patch = {op: 'remove-user'};
|
||||||
|
|
||||||
|
if (typeof user_id !== 'undefined') {
|
||||||
|
patch.user_id = user_id;
|
||||||
|
}
|
||||||
|
if (typeof email !== 'undefined') {
|
||||||
|
patch.email = String(email);
|
||||||
|
}
|
||||||
|
|
||||||
|
patchOrganization(patch)
|
||||||
|
.done(function() {
|
||||||
|
$("ul.sharing-users-list").find("[data-user-id='" + user_id + "']").remove();
|
||||||
|
item_open('{{ organization._id }}', false);
|
||||||
|
toastr.success('User removed from this organization');
|
||||||
|
}).fail(function (data) {
|
||||||
|
var msg = xhrErrorResponseMessage(data);
|
||||||
|
toastr.error('Error removing user: ' + msg);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function editOrganization() {
|
||||||
|
var $form = $('#item_form');
|
||||||
|
var new_name = $form.find('*[name="name"]').val();
|
||||||
|
|
||||||
|
patchOrganization({
|
||||||
|
op: 'edit-from-web',
|
||||||
|
name: new_name,
|
||||||
|
description: $form.find('*[name="description"]').val(),
|
||||||
|
website: $form.find('*[name="website"]').val(),
|
||||||
|
location: $form.find('*[name="location"]').val(),
|
||||||
|
})
|
||||||
|
.done(function() {
|
||||||
|
$('span.organization-name-{{ organization._id }}').text(new_name);
|
||||||
|
})
|
||||||
|
.fail(function(err) {
|
||||||
|
var msg = xhrErrorResponseMessage(err);
|
||||||
|
toastr.error('Error editing organization: ' + msg);
|
||||||
|
})
|
||||||
|
;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function batchAddUsers() {
|
||||||
|
var $form = $('#batch_add_form');
|
||||||
|
var emails = $form.find('*[name="emails"]').val().split(/\s/);
|
||||||
|
console.log(emails);
|
||||||
|
|
||||||
|
patchOrganization({
|
||||||
|
op: 'assign-users',
|
||||||
|
emails: emails,
|
||||||
|
})
|
||||||
|
.done(function() {
|
||||||
|
item_open('{{ organization._id }}', false);
|
||||||
|
})
|
||||||
|
.fail(function(err) {
|
||||||
|
var msg = xhrErrorResponseMessage(err);
|
||||||
|
toastr.error('Error adding members: ' + msg);
|
||||||
|
})
|
||||||
|
;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
| {% endblock %}
|
Loading…
x
Reference in New Issue
Block a user