.organization form#item_form(onsubmit="return editOrganization()") | {% if can_edit %} .input-group label(for='org-name-field') Name input.item-name.input-transparent#org-name-field( name="name", type="text", placeholder="Organization's name", value="{{ organization.name | hide_none }}") .input-group label(for='org-description-field') Description textarea.item-description.input-transparent#org-description-field( name="description", type="text", rows=1, placeholder="Organization's description") {{ organization.description | hide_none }} .input-group label(for='org-website-field') Website input.item-website.input-transparent#org-website-field( name="website", type="text", placeholder="Organization's website", value="{{ organization.website | hide_none }}") .input-group label(for='org-location-field') Location input.item-location.input-transparent#org-location-field( name="location", type="text", placeholder="Organization's location", value="{{ organization.location | hide_none }}") .input-group label(for='org-ip-ranges-field') IP Ranges textarea.item-ip-ranges#org-ip-ranges-field( name="ip_ranges", type="text", rows="{{ organization.human_ip_ranges|length|default(1, true) }}", placeholder="IP ranges in CIDR notation, one on each line", title="IPv6 like 2a01:1234:abcd:3b::0/64\nIPv4 like 172.16.0.0/16") | {{ organization.human_ip_ranges | join('\n') }} p.help-block. Clients with a matching IP address will be considered part of the organization. IPv4 ranges are always normalized to IPv6 prior to saving. Changes to IP Ranges take up to an hour to affect already logged-in users. | {% if can_super_edit %} .input-group label(for='org-seat-count-field') Seat count input.item-location.input-transparent#org-seat-count-field( name="seat_count", type="text", placeholder="Seat count", value="{{ organization.seat_count | hide_none }}") p.help-block Users covered by the IP Ranges do not count towards the seat count. .input-group label(for='org-roles-field') Roles input.item-location.input-transparent#org-roles-field( name="org_roles", type="text", placeholder="Organization roles", value="{{ organization.org_roles | hide_none | sort | join(' ') }}") | {% endif %} .input-group.org-admin label.item-admin-user Organization Administrator p#admin-name a(href='javascript:showAdminPicker()', title='Click to choose another administrator') {{ admin_user.full_name }} p#admin-picker input#admin-select.form-control( name='admin_user', type='text', placeholder='Search for a new administrator') a(href='javascript:hideAdminPicker()') span.text-danger Cancel script $('#admin-picker').hide(); .input-group button#item-save.btn.btn-outline-success.btn-block(type='submit') i.pi-check | Save Changes | {% else %} p.item-name {{ organization.name | hide_none }} | {% if organization.description %} p.item-description {{ organization.description | hide_none }} | {% endif %} | {% if organization.website %} p.item-website a(href='{{ organization.website }}', target='_blank') {{ organization.website }} | {% endif %} | {% if organization.location %} p.item-location {{ organization.location | hide_none }} | {% endif %} p.item-admin-user Organization administrator: {{ admin_user.full_name }} | {% endif %} hr 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 | hide_none | sort | join(', ') }} hr h4 Organization members .members | {% if can_edit %} .sharing-users-search .form-group input#user-select.form-control( name='contacts', type='text', placeholder='Add member by name') | {% endif %} 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 or current_user.objectid == member['_id'] %} | {% 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 %} | {% else %} li p.text-warning No members yet | {% endfor %} | {% if organization.unknown_members %} hr p Pending Members (i.e. 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 %} | {% endif %} | {% if can_edit %} br p Batch-add members by email address: form#batch_add_form(onsubmit="return batchAddUsers()") .input-group textarea.item-description( name="emails", type="text", rows=1, placeholder="Enter the email addresses separated by space or a new line") .input-group button.btn.btn-info.btn-block(type='submit') i.pi-plus | Add Members | {% endif %} .action-result-panel | {% if config.DEBUG %} #item-view-feed .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. $(document).ready(function() { $('#user-select').userSearch( 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() { $existing.removeClass('active'); $existing.dequeue(); }); toastr.info('User is already member of this organization'); } else { addUser(hit.objectID); } } ); $('#admin-select').userSearch( function (event, hit, dataset) { setAdmin(hit.objectID, hit.full_name); } ); }); function addUser(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); toastr.success('Member added to this organization!'); // 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); }); }; function setAdmin(user_id, full_name) { if (!user_id || user_id.length == 0) { toastr.error('Please select a user from the list'); return; } if (!confirm("Are you sure you want to transfer administrator privileges to " + full_name + "?")) { hideAdminPicker(); toastr.warning('Cancelled administrative transfer.'); return; } patchOrganization({ op: 'assign-admin', user_id: user_id }) .done(function (data) { window.location.reload(); toastr.info('Updated admin user'); }) .fail(function (err) { var msg = xhrErrorResponseMessage(err); toastr.error('Could not add member: ' + msg); }); }; function editOrganization() { var $form = $('#item_form'); var new_name = $form.find('*[name="name"]').val(); {% if can_super_edit %} var org_roles_str = $form.find('*[name="org_roles"]').val().trim(); var org_roles = Array(); if (org_roles_str.length) org_roles = org_roles_str.split(/\s/); {% endif %} var ipr = $form.find('*[name="ip_ranges"]').val().trim(); if (ipr == '') ip_ranges = Array(); else ip_ranges = ipr.split(/\s/); 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(), ip_ranges: ip_ranges, {% if can_super_edit %} seat_count: parseInt($form.find('*[name="seat_count"]').val()), org_roles: org_roles, {% endif %} }) .done(function() { $('span.organization-name-{{ organization._id }}').text(new_name); item_open('{{ organization._id }}', false); }) .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; } function showAdminPicker() { $('#admin-name').hide(); $('#admin-picker').show(function() { $(this).find('input').focus(); }); } function hideAdminPicker() { $('#admin-picker').hide(); $('#admin-name').show(); } | {% 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 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; } 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(); if ('{{ current_user.user_id }}' == user_id) { // User removed self, so we cannot open this organization again. $('#organization-{{ organization._id}}').remove(); toastr.success('You left the organization.') } else { 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); }); } | {% endblock %}