diff --git a/frontend_tests/casper_tests/05-subscriptions.js b/frontend_tests/casper_tests/05-subscriptions.js index b3acb60467..3169e1e670 100644 --- a/frontend_tests/casper_tests/05-subscriptions.js +++ b/frontend_tests/casper_tests/05-subscriptions.js @@ -28,20 +28,38 @@ casper.waitForSelector('.sub_unsub_button.checked', function () { casper.then(function () { casper.test.assertExists('#user-checkboxes [data-name="cordelia@zulip.com"]', 'Original user list contains Cordelia'); - casper.test.assertExists('#user-checkboxes [data-name="hamlet@zulip.com"]', 'Original user list contains King Hamlet'); + casper.test.assertExists('#user-checkboxes [data-name="othello@zulip.com"]', 'Original user list contains Othello'); }); + +casper.waitForSelector("#copy-from-stream-expand-collapse", function () { + casper.click('#copy-from-stream-expand-collapse'); +}); + +casper.waitUntilVisible("#stream-checkboxes", function () { + casper.test.assertExists('#stream-checkboxes [data-name="Scotland"]', 'Original stream list contains Scotland'); + casper.test.assertExists('#stream-checkboxes [data-name="Rome"]', 'Original stream list contains Rome'); +}); + casper.waitForSelector("form#stream_creation_form", function () { - casper.test.info("Filtering user list with keyword 'cor'"); - casper.fill('form#stream_creation_form', {user_list_filter: 'cor'}); + casper.test.info("Filtering with keyword 'ot'"); + casper.fill('form#stream_creation_form', {user_list_filter: 'ot'}); }); casper.waitForSelector(".subscriber-list", function () { casper.test.assertEquals(casper.visible('#user-checkboxes [data-name="cordelia@zulip.com"]'), - true, - "Cordelia is visible" - ); - casper.test.assertEquals(casper.visible('#user-checkboxes [data-name="hamlet@zulip.com"]'), false, - "King Hamlet is not visible" + "Cordelia is not visible" + ); + casper.test.assertEquals(casper.visible('#user-checkboxes [data-name="othello@zulip.com"]'), + true, + "Othello is visible" + ); + casper.test.assertEquals(casper.visible('#stream-checkboxes [data-name="Scotland"]'), + true, + "Scotland is visible" + ); + casper.test.assertEquals(casper.visible('#stream-checkboxes [data-name="Rome"]'), + false, + "Rome is not visible" ); }); casper.then(function () { @@ -53,15 +71,26 @@ casper.then(function () { true, "Cordelia is visible again" ); - casper.test.assertEquals(casper.visible('#user-checkboxes [data-name="hamlet@zulip.com"]'), + casper.test.assertEquals(casper.visible('#user-checkboxes [data-name="othello@zulip.com"]'), true, - "King Hamlet is visible again" + "Othello is visible again" + ); + casper.test.assertEquals(casper.visible('#stream-checkboxes [data-name="Scotland"]'), + true, + "Scotland is visible again" + ); + casper.test.assertEquals(casper.visible('#stream-checkboxes [data-name="Rome"]'), + true, + "Rome is visible again" ); }); casper.waitForSelector('#stream_creation_form', function () { - casper.test.assertTextExists('Add New Stream', 'New stream creation panel'); - casper.fill('form#stream_creation_form', {stream_name: 'Waseemio', stream_description: 'Oimeesaw'}); - casper.click('form#stream_creation_form button.btn.btn-primary'); + casper.test.assertTextExists('Add New Stream', 'New stream creation panel'); + casper.fill('form#stream_creation_form', {stream_name: 'Waseemio', stream_description: 'Oimeesaw'}); + casper.click('input[value="Scotland"] ~ span'); + casper.click('input[value="cordelia@zulip.com"] ~ span'); + casper.click('input[value="othello@zulip.com"] ~ span'); + casper.click('form#stream_creation_form button.btn.btn-primary'); }); casper.waitFor(function () { @@ -73,6 +102,10 @@ casper.then(function () { casper.test.info("User should be subscribed to stream Waseemio"); casper.test.assertSelectorHasText('.stream-name', 'Waseemio'); casper.test.assertSelectorHasText('.description', 'Oimeesaw'); + // Based on the selected checkboxes while creating stream, + // 4 users from Scotland are added. + // 1 user, Cordelia, is added. Othello (subscribed to Scotland) is not added twice. + casper.test.assertSelectorHasText('.subscriber-count-text', '5'); casper.fill('form#add_new_subscription', {stream_name: 'WASeemio'}); casper.click('#create_stream_button'); }); diff --git a/static/js/subs.js b/static/js/subs.js index a60e7a3781..7a18dc7079 100644 --- a/static/js/subs.js +++ b/static/js/subs.js @@ -762,7 +762,8 @@ function show_new_stream_modal() { $("#stream-creation").removeClass("hide"); $(".right .settings").hide(); $('#people_to_add').html(templates.render('new_stream_users', { - users: people.get_rest_of_realm() + users: people.get_rest_of_realm(), + streams: stream_data.get_streams_for_settings_page() })); // Make the options default to the same each time: @@ -864,10 +865,17 @@ $(function () { e.preventDefault(); update_announce_stream_state(); }); + $(document).on('click', '#copy-from-stream-expand-collapse', function (e) { + $('#stream-checkboxes').toggle(); + $("#copy-from-stream-expand-collapse .toggle").toggleClass('icon-vector-caret-right icon-vector-caret-down'); + e.preventDefault(); + update_announce_stream_state(); + }); - // Search People + // Search People or Streams $(document).on('input', '.add-user-list-filter', function (e) { var users = people.get_rest_of_realm(); + var streams = stream_data.get_streams_for_settings_page(); var user_list = $(".add-user-list-filter"); if (user_list === 0) { @@ -877,6 +885,23 @@ $(function () { var search_terms = search_term.toLowerCase().split(","); var filtered_users = people.filter_people_by_search_terms(users, search_terms); + _.each(streams, function (stream) { + var flag = true; + + flag = flag && (function () { + var sub_name = stream.name.toLowerCase(); + var matches_list = search_terms.indexOf(sub_name) > -1; + var matches_last_val = sub_name.match(search_terms[search_terms.length - 1]); + return matches_list || matches_last_val; + }()); + + if (flag) { + $("label[data-name='" + stream.name + "']").css("display", "block"); + } else { + $("label[data-name='" + stream.name + "']").css("display", "none"); + } + }); + // Hide users which aren't in filtered users _.each(users, function (user) { var display_type = filtered_users.hasOwnProperty(user.email)? "block" : "none"; @@ -929,6 +954,33 @@ $(function () { return $(elem).val(); } ); + + var checked_streams = _.map( + $("#stream_creation_form input:checkbox[name=stream]:checked"), + function (elem) { + return $(elem).val(); + } + ); + + var checked_stream_emails = []; + var stream_emails = []; + + _.each(checked_streams, function (checked_stream) { + stream_emails = []; + var subscriber_ids = stream_data.get_sub(checked_stream).subscribers.keys(); + _.each(subscriber_ids, function (subscriber_id) { + stream_emails.push(people.get_person_from_user_id(subscriber_id).email); + }); + checked_stream_emails = _.union(checked_stream_emails, stream_emails); + }); + + // If a stream was checked and the checkboxes are not visible, + // don't add checked streams + if ($('#stream-checkboxes').css('display') !== 'none') { + principals = _.union(principals, checked_stream_emails); + } + + // You are always subscribed to streams you create. principals.push(page_params.email); diff --git a/static/styles/zulip.css b/static/styles/zulip.css index ec16207ed8..e529278f24 100644 --- a/static/styles/zulip.css +++ b/static/styles/zulip.css @@ -2113,6 +2113,24 @@ div.floating_recipient { float: none; } +#stream-checkboxes { + margin-top: 10px; + display: none; +} + +#stream-checkboxes .checkbox { + display: block; +} + +#stream-checkboxes .checkbox input[type=checkbox] { + margin: 5px 0px; + float: none; +} + +#copy-from-stream-expand-collapse { + cursor: pointer; +} + .streams_popover .sp-container { background: white; cursor: pointer; diff --git a/static/templates/new_stream_users.handlebars b/static/templates/new_stream_users.handlebars index 546bd8a662..50d2218bd3 100644 --- a/static/templates/new_stream_users.handlebars +++ b/static/templates/new_stream_users.handlebars @@ -1,7 +1,22 @@ {{! Client-side Mustache template for rendering users in the stream creation modal.}} {{t "Check all" }} | {{t "Uncheck all" }} - + +
+ + + {{t "Copy from Stream" }} + +
+
+ {{#each streams}} + + {{/each}} +
{{#each users}}