From 4b75ad5110c98168a8e170a93085c4a70ea281c9 Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Mon, 5 Dec 2016 17:36:45 +0800 Subject: [PATCH 001/408] FIX: Don't raise error when admin access is invalid. --- lib/admin_constraint.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/admin_constraint.rb b/lib/admin_constraint.rb index 923e5e806c0..0ee711c2bca 100644 --- a/lib/admin_constraint.rb +++ b/lib/admin_constraint.rb @@ -10,6 +10,8 @@ class AdminConstraint return false if @require_master && RailsMultisite::ConnectionManagement.current_db != "default" provider = Discourse.current_user_provider.new(request.env) provider.current_user && provider.current_user.admin? + rescue Discourse::InvalidAccess + false end end From 46b3c2e9c5b6a052135e3d47cda7a9760d5ee866 Mon Sep 17 00:00:00 2001 From: Rafael dos Santos Silva Date: Mon, 5 Dec 2016 20:20:52 -0200 Subject: [PATCH 002/408] Use CSS animations for post and topic highlights --- .../discourse/components/discourse-topic.js.es6 | 12 +++--------- .../discourse/components/topic-list-item.js.es6 | 9 +++------ app/assets/stylesheets/common/base/discourse.scss | 9 +++++++++ app/assets/stylesheets/common/base/topic-post.scss | 2 +- app/assets/stylesheets/desktop/topic-list.scss | 2 +- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/app/assets/javascripts/discourse/components/discourse-topic.js.es6 b/app/assets/javascripts/discourse/components/discourse-topic.js.es6 index 5d5c6299337..17f40f3928b 100644 --- a/app/assets/javascripts/discourse/components/discourse-topic.js.es6 +++ b/app/assets/javascripts/discourse/components/discourse-topic.js.es6 @@ -5,16 +5,10 @@ import { selectedText } from 'discourse/lib/utilities'; import { observes } from 'ember-addons/ember-computed-decorators'; function highlight(postNumber) { - const $contents = $(`#post_${postNumber} .topic-body`), - origColor = $contents.data('orig-color') || $contents.css('backgroundColor'); + const $contents = $(`#post_${postNumber} .topic-body`); - $contents.data("orig-color", origColor) - .addClass('highlighted') - .stop() - .animate({ backgroundColor: origColor }, 2500, 'swing', function() { - $contents.removeClass('highlighted'); - $contents.css({'background-color': ''}); - }); + $contents.addClass('highlighted'); + $contents.on('animationend', () => $contents.removeClass('highlighted')); } export default Ember.Component.extend(AddArchetypeClass, Scrolling, { diff --git a/app/assets/javascripts/discourse/components/topic-list-item.js.es6 b/app/assets/javascripts/discourse/components/topic-list-item.js.es6 index a38dd9c8f4c..b7d47a3a15e 100644 --- a/app/assets/javascripts/discourse/components/topic-list-item.js.es6 +++ b/app/assets/javascripts/discourse/components/topic-list-item.js.es6 @@ -128,14 +128,11 @@ export default Ember.Component.extend(bufferedRender({ highlight(opts = { isLastViewedTopic: false }) { const $topic = this.$(); - const originalCol = $topic.css('backgroundColor'); $topic .addClass('highlighted') - .attr('data-islastviewedtopic', opts.isLastViewedTopic) - .stop() - .animate({ backgroundColor: originalCol }, 2500, 'swing', function() { - $topic.removeClass('highlighted'); - }); + .attr('data-islastviewedtopic', opts.isLastViewedTopic); + + $topic.on('animationend', () => $topic.removeClass('highlighted')); }, _highlightIfNeeded: function() { diff --git a/app/assets/stylesheets/common/base/discourse.scss b/app/assets/stylesheets/common/base/discourse.scss index 5bde52ae8ec..1429c55124e 100644 --- a/app/assets/stylesheets/common/base/discourse.scss +++ b/app/assets/stylesheets/common/base/discourse.scss @@ -339,3 +339,12 @@ body { span.relative-date { white-space:nowrap; } + +@keyframes background-fade-highlight { + 0% { + background-color: dark-light-diff($tertiary, $secondary, 85%, -65%); + } + 100% { + background-color: transparent; + } +} diff --git a/app/assets/stylesheets/common/base/topic-post.scss b/app/assets/stylesheets/common/base/topic-post.scss index 5cb0d0d54e8..03d244d096a 100644 --- a/app/assets/stylesheets/common/base/topic-post.scss +++ b/app/assets/stylesheets/common/base/topic-post.scss @@ -208,7 +208,7 @@ aside.quote { overflow: hidden; } &.highlighted { - background-color: dark-light-diff($tertiary, $secondary, 85%, -65%); + animation: background-fade-highlight 2.5s ease-out; } } diff --git a/app/assets/stylesheets/desktop/topic-list.scss b/app/assets/stylesheets/desktop/topic-list.scss index 0df2a15fe3b..49724880c79 100644 --- a/app/assets/stylesheets/desktop/topic-list.scss +++ b/app/assets/stylesheets/desktop/topic-list.scss @@ -56,7 +56,7 @@ > tbody > tr { &.highlighted { - background-color: dark-light-diff($tertiary, $secondary, 85%, -65%); + animation: background-fade-highlight 2.5s ease-out; } } button.bulk-select { From 77ef730f2060673047d0d1b0f05ae1fe8ea3ef39 Mon Sep 17 00:00:00 2001 From: Sawood Alam Date: Sat, 10 Dec 2016 18:57:48 -0500 Subject: [PATCH 003/408] RTL direction assigned to Urdu language --- app/helpers/application_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index d79ace3f343..09b86dc104b 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -135,7 +135,7 @@ module ApplicationHelper end def rtl? - ["ar", "fa_IR", "he"].include? I18n.locale.to_s + ["ar", "ur", "fa_IR", "he"].include? I18n.locale.to_s end def user_locale From 303282670f3fe3dc4173d4e4dbdf58731b4b4640 Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Tue, 13 Dec 2016 08:57:07 +0800 Subject: [PATCH 004/408] FIX: Skip callbacks when migrating. --- db/migrate/20161202034856_add_uploads_to_categories.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/db/migrate/20161202034856_add_uploads_to_categories.rb b/db/migrate/20161202034856_add_uploads_to_categories.rb index 93506dec18b..de35b1a0d0d 100644 --- a/db/migrate/20161202034856_add_uploads_to_categories.rb +++ b/db/migrate/20161202034856_add_uploads_to_categories.rb @@ -6,12 +6,12 @@ class AddUploadsToCategories < ActiveRecord::Migration transaction do Category.find_each do |category| logo_upload = Upload.find_by(url: category.logo_url) - category.uploaded_logo_id = logo_upload.id if logo_upload - background_upload = Upload.find_by(url: category.background_url) - category.uploaded_background_id = background_upload.id if background_upload - category.save! + category.update_columns( + uploaded_logo_id: logo_upload&.id, + uploaded_background_id: background_upload&.id + ) end end end From 53086fdb988d157cacbda1f52935f7ceda14812e Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Tue, 13 Dec 2016 09:10:27 +0800 Subject: [PATCH 005/408] FIX: Drop columns only after app has been deployed with updated code. --- db/fixtures/001_categories.rb | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/db/fixtures/001_categories.rb b/db/fixtures/001_categories.rb index 5e47dbcf2c1..b1bfc5cbb97 100644 --- a/db/fixtures/001_categories.rb +++ b/db/fixtures/001_categories.rb @@ -23,3 +23,27 @@ if uncat_id == -1 || !Category.exists?(uncat_id) Category.exec_sql "INSERT INTO site_settings(name, data_type, value, created_at, updated_at) VALUES ('uncategorized_category_id', 3, #{category_id}, now(), now())" end + +# 60 minutes after our migration runs we need to exectue this code... +duration = Rails.env.production? ? 60 : 0 +if Category.exec_sql(" + SELECT 1 FROM schema_migration_details + WHERE EXISTS( + SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_schema = 'public' AND table_name = 'categories' AND column_name = 'uploaded_logo_id' + ) AND + name = 'AddUploadsToCategories' AND + created_at < (current_timestamp at time zone 'UTC' - interval '#{duration} minutes') + ").to_a.length > 0 + + + Category.transaction do + STDERR.puts "Removing superflous category columns!" + %w[ + logo_url + background_url + ].each do |column| + Category.exec_sql("ALTER TABLE categories DROP column IF EXISTS #{column}") + end + end +end From 69330f8bc2199932f0cd91f2838ac6d70a5f3d6c Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Tue, 13 Dec 2016 11:26:26 +0800 Subject: [PATCH 006/408] Add user_updated event to webhooks. --- app/models/web_hook.rb | 2 +- app/services/user_updater.rb | 4 +++- config/locales/client.en.yml | 2 +- spec/models/web_hook_spec.rb | 5 ++++- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/app/models/web_hook.rb b/app/models/web_hook.rb index 9884bbf52eb..4eed81e6bef 100644 --- a/app/models/web_hook.rb +++ b/app/models/web_hook.rb @@ -71,7 +71,7 @@ class WebHook < ActiveRecord::Base WebHook.enqueue_topic_hooks(:topic_edited, post.topic) if post.is_first_post? && topic_changed end - %i(user_created user_approved).each do |event| + %i(user_created user_approved user_updated).each do |event| DiscourseEvent.on(event) do |user| WebHook.enqueue_hooks(:user, user_id: user.id, event_name: event.to_s) end diff --git a/app/services/user_updater.rb b/app/services/user_updater.rb index 524d97db1fb..762a0cd0b91 100644 --- a/app/services/user_updater.rb +++ b/app/services/user_updater.rb @@ -98,7 +98,9 @@ class UserUpdater update_muted_users(attributes[:muted_usernames]) end - (!save_options || user.user_option.save) && user_profile.save && user.save + saved = (!save_options || user.user_option.save) && user_profile.save && user.save + DiscourseEvent.trigger(:user_updated, user) if saved + saved end end diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index c5c9c3443b6..3e12e37f5cf 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -2573,7 +2573,7 @@ en: details: "When there is a new reply, edit, deleted or recovered." user_event: name: "User Event" - details: "When a user is created or approved." + details: "When a user is created, approved or updated." delivery_status: title: "Delivery Status" inactive: "Inactive" diff --git a/spec/models/web_hook_spec.rb b/spec/models/web_hook_spec.rb index 0022c0e9d6c..6c4c8fdfaa6 100644 --- a/spec/models/web_hook_spec.rb +++ b/spec/models/web_hook_spec.rb @@ -131,7 +131,7 @@ describe WebHook do PostDestroyer.new(user, post2).recover end - it 'should enqueue the right hooks for user creation events' do + it 'should enqueue the right hooks for user events' do WebHook.expects(:enqueue_hooks).once user @@ -140,6 +140,9 @@ describe WebHook do WebHook.expects(:enqueue_hooks).once user.approve(admin) + + WebHook.expects(:enqueue_hooks).once + UserUpdater.new(admin, user).update(username: 'testing123') end end end From b650a45b378ebeb5f517cbf3016d83fce896c508 Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 13 Dec 2016 14:50:05 +1100 Subject: [PATCH 007/408] tweak perf of rake and rails --- bin/docker/rails | 3 ++- bin/docker/rake | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/bin/docker/rails b/bin/docker/rails index 74b3e211034..1c35b782123 100755 --- a/bin/docker/rails +++ b/bin/docker/rails @@ -5,5 +5,6 @@ if [[ $# = 1 ]] && [[ "$1" =~ "s" ]]; then PARAMS="$PARAMS -b 0.0.0.0" fi -CMD="cd /src && RACK_HANDLER=puma RAILS_ENV=${RAILS_ENV:=development} rails $PARAMS" + +CMD="cd /src && RUBY_GLOBAL_METHOD_CACHE_SIZE=131072 LD_PRELOAD=/usr/lib/libjemalloc.so RACK_HANDLER=puma RAILS_ENV=${RAILS_ENV:=development} rails $PARAMS" docker exec -it -u discourse:discourse discourse_dev /bin/bash -c "$CMD" diff --git a/bin/docker/rake b/bin/docker/rake index 92abb3546b5..d70b45d7659 100755 --- a/bin/docker/rake +++ b/bin/docker/rake @@ -1,5 +1,5 @@ #!/bin/bash PARAMS="$@" -CMD="cd /src && RAILS_ENV=${RAILS_ENV:=development} rake $PARAMS" +CMD="cd /src && RUBY_GLOBAL_METHOD_CACHE_SIZE=131072 LD_PRELOAD=/usr/lib/libjemalloc.so RAILS_ENV=${RAILS_ENV:=development} rake $PARAMS" docker exec -it -u discourse:discourse discourse_dev /bin/bash -c "$CMD" From 6dab7f44638c1e5417315e66ba86f9e356f2a000 Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Tue, 13 Dec 2016 13:11:34 +0800 Subject: [PATCH 008/408] UX: Keep table styles consistent with topic list. --- app/assets/stylesheets/common/base/group.scss | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/app/assets/stylesheets/common/base/group.scss b/app/assets/stylesheets/common/base/group.scss index 146c5039ad2..1e88b25e7a9 100644 --- a/app/assets/stylesheets/common/base/group.scss +++ b/app/assets/stylesheets/common/base/group.scss @@ -56,7 +56,7 @@ table.group-members { width: 100%; table-layout: fixed; - th, tr { + tr { border-bottom: 1px solid dark-light-diff($primary, $secondary, 90%, -60%); } @@ -69,17 +69,23 @@ table.group-members { } th { + border-bottom: 3px solid dark-light-diff($primary, $secondary, 90%, -60%); + } + + th.sortable { text-align: left; - padding: 5px 0px; + padding: 5px 0px 5px 5px; + + color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + font-weight: normal; i { margin-left: 5px; } &:hover { - color: $tertiary; cursor: pointer; - text-decoration: underline; + background-color: dark-light-diff($primary, $secondary, 90%, -75%); } } From 5a5aea72b52eafecbda545277df1052db416e7a0 Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Tue, 13 Dec 2016 13:12:38 +0800 Subject: [PATCH 009/408] Remove icon from group tabs. --- app/assets/javascripts/discourse/controllers/group.js.es6 | 2 +- app/assets/javascripts/discourse/templates/group.hbs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/assets/javascripts/discourse/controllers/group.js.es6 b/app/assets/javascripts/discourse/controllers/group.js.es6 index 6f52622fa9a..8500a9d0fac 100644 --- a/app/assets/javascripts/discourse/controllers/group.js.es6 +++ b/app/assets/javascripts/discourse/controllers/group.js.es6 @@ -22,7 +22,7 @@ export default Ember.Controller.extend({ Tab.create({ name: 'mentions' }), Tab.create({ name: 'messages', requiresMembership: true }), Tab.create({ - name: 'logs', i18nKey: 'logs.title', icon: 'shield', + name: 'logs', i18nKey: 'logs.title', requiresMembership: true, requiresGroupAdmin: true }) ], diff --git a/app/assets/javascripts/discourse/templates/group.hbs b/app/assets/javascripts/discourse/templates/group.hbs index f7188592d2b..3b20460b2c8 100644 --- a/app/assets/javascripts/discourse/templates/group.hbs +++ b/app/assets/javascripts/discourse/templates/group.hbs @@ -41,7 +41,6 @@ {{#each getTabs as |tab|}}
  • {{#link-to tab.location model title=tab.message}} - {{#if tab.icon}}{{fa-icon tab.icon}}{{/if}} {{tab.message}} {{#if tab.count}}({{tab.count}}){{/if}} {{/link-to}} From 7bfabb029b1d8a90573af6aded889e62a8c02566 Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Tue, 13 Dec 2016 15:15:20 +0800 Subject: [PATCH 010/408] UX: Move editing group from into an individual tab. --- .../admin/controllers/admin-group.js.es6 | 52 -------------- .../javascripts/admin/templates/group.hbs | 20 +----- .../components/group-member.js.es6 | 0 .../components/group-members-input.js.es6 | 69 +++++++++++++++++++ .../{edit-group.js.es6 => group-edit.js.es6} | 13 ++-- .../discourse/controllers/group-index.js.es6 | 5 ++ .../discourse/controllers/group.js.es6 | 4 ++ .../discourse/routes/app-route-map.js.es6 | 1 + .../discourse/routes/group-edit.js.es6 | 15 ++++ .../javascripts/discourse/routes/group.js.es6 | 7 -- .../templates/components/group-member.hbs | 0 .../components/group-members-input.hbs | 23 +++++++ .../discourse/templates/group-edit.hbs | 46 +++++++++++++ .../discourse/templates/group-index.hbs | 15 +--- .../javascripts/discourse/templates/group.hbs | 6 -- .../discourse/templates/modal/edit-group.hbs | 34 --------- .../common/base/group-members-input.scss | 15 ++++ app/assets/stylesheets/common/base/group.scss | 29 ++++---- app/controllers/groups_controller.rb | 3 + config/locales/client.en.yml | 3 +- .../acceptance/group-edit-test.js.es6 | 33 +++++++++ ...ogs-test.js.es6 => group-logs-test.js.es6} | 0 .../javascripts/acceptance/groups-test.js.es6 | 29 ++------ 23 files changed, 242 insertions(+), 180 deletions(-) rename app/assets/javascripts/{admin => discourse}/components/group-member.js.es6 (100%) create mode 100644 app/assets/javascripts/discourse/components/group-members-input.js.es6 rename app/assets/javascripts/discourse/controllers/{edit-group.js.es6 => group-edit.js.es6} (51%) create mode 100644 app/assets/javascripts/discourse/routes/group-edit.js.es6 rename app/assets/javascripts/{admin => discourse}/templates/components/group-member.hbs (100%) create mode 100644 app/assets/javascripts/discourse/templates/components/group-members-input.hbs create mode 100644 app/assets/javascripts/discourse/templates/group-edit.hbs delete mode 100644 app/assets/javascripts/discourse/templates/modal/edit-group.hbs create mode 100644 app/assets/stylesheets/common/base/group-members-input.scss create mode 100644 test/javascripts/acceptance/group-edit-test.js.es6 rename test/javascripts/acceptance/{groups-logs-test.js.es6 => group-logs-test.js.es6} (100%) diff --git a/app/assets/javascripts/admin/controllers/admin-group.js.es6 b/app/assets/javascripts/admin/controllers/admin-group.js.es6 index 71cb2c70328..6c3da11d7f7 100644 --- a/app/assets/javascripts/admin/controllers/admin-group.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-group.js.es6 @@ -1,5 +1,4 @@ import { popupAjaxError } from 'discourse/lib/ajax-error'; -import { propertyEqual } from 'discourse/lib/computed'; import computed from 'ember-addons/ember-computed-decorators'; export default Ember.Controller.extend({ @@ -7,19 +6,6 @@ export default Ember.Controller.extend({ disableSave: false, savingStatus: '', - currentPage: function() { - if (this.get("model.user_count") === 0) { return 0; } - return Math.floor(this.get("model.offset") / this.get("model.limit")) + 1; - }.property("model.limit", "model.offset", "model.user_count"), - - totalPages: function() { - if (this.get("model.user_count") === 0) { return 0; } - return Math.floor(this.get("model.user_count") / this.get("model.limit")) + 1; - }.property("model.limit", "model.user_count"), - - showingFirst: Em.computed.lte("currentPage", 1), - showingLast: propertyEqual("currentPage", "totalPages"), - aliasLevelOptions: function() { return [ { name: I18n.t("groups.alias_levels.nobody"), value: 0 }, @@ -47,38 +33,6 @@ export default Ember.Controller.extend({ }, actions: { - next() { - if (this.get("showingLast")) { return; } - - const group = this.get("model"), - offset = Math.min(group.get("offset") + group.get("limit"), group.get("user_count")); - - group.set("offset", offset); - - return group.findMembers(); - }, - - previous() { - if (this.get("showingFirst")) { return; } - - const group = this.get("model"), - offset = Math.max(group.get("offset") - group.get("limit"), 0); - - group.set("offset", offset); - - return group.findMembers(); - }, - - removeMember(member) { - const self = this, - message = I18n.t("admin.groups.delete_member_confirm", { username: member.get("username"), group: this.get("model.name") }); - return bootbox.confirm(message, I18n.t("no_value"), I18n.t("yes_value"), function(confirm) { - if (confirm) { - self.get("model").removeMember(member); - } - }); - }, - removeOwner(member) { const self = this, message = I18n.t("admin.groups.delete_owner_confirm", { username: member.get("username"), group: this.get("model.name") }); @@ -95,12 +49,6 @@ export default Ember.Controller.extend({ this.set("model.ownerUsernames", null); }, - addMembers() { - if (Em.isEmpty(this.get("model.usernames"))) { return; } - this.get("model").addMembers(this.get("model.usernames")).catch(popupAjaxError); - this.set("model.usernames", null); - }, - save() { const group = this.get('model'), groupsController = this.get("adminGroupsType"), diff --git a/app/assets/javascripts/admin/templates/group.hbs b/app/assets/javascripts/admin/templates/group.hbs index f66e0ca8557..79ba4466969 100644 --- a/app/assets/javascripts/admin/templates/group.hbs +++ b/app/assets/javascripts/admin/templates/group.hbs @@ -33,26 +33,8 @@ {{/unless}}
    - -
    - - {{currentPage}}/{{totalPages}} - -
    -
    - {{#each model.members as |member|}} - {{group-member member=member automatic=model.automatic removeAction="removeMember"}} - {{/each}} -
    + {{group-members-input model=model}}
    - - {{#unless model.automatic}} -
    - - {{user-selector usernames=model.usernames placeholderKey="admin.groups.selector_placeholder" id="user-selector"}} - {{d-button action="addMembers" class="add" icon="plus" label="admin.groups.add"}} -
    - {{/unless}} {{/if}}
    diff --git a/app/assets/javascripts/admin/components/group-member.js.es6 b/app/assets/javascripts/discourse/components/group-member.js.es6 similarity index 100% rename from app/assets/javascripts/admin/components/group-member.js.es6 rename to app/assets/javascripts/discourse/components/group-member.js.es6 diff --git a/app/assets/javascripts/discourse/components/group-members-input.js.es6 b/app/assets/javascripts/discourse/components/group-members-input.js.es6 new file mode 100644 index 00000000000..1fb45f27bf6 --- /dev/null +++ b/app/assets/javascripts/discourse/components/group-members-input.js.es6 @@ -0,0 +1,69 @@ +import computed from 'ember-addons/ember-computed-decorators'; +import { popupAjaxError } from 'discourse/lib/ajax-error'; +import { propertyEqual } from 'discourse/lib/computed'; + +export default Ember.Component.extend({ + classNames: ["group-members-input"], + + @computed('model.limit', 'model.offset', 'model.user_count') + currentPage(limit, offset, userCount) { + if (userCount === 0) { return 0; } + + return Math.floor(offset / limit) + 1; + }, + + @computed('model.limit', 'model.user_count') + totalPages(limit, userCount) { + if (userCount === 0) { return 0; } + return Math.floor(userCount / limit) + 1; + }, + + @computed('model.usernames') + disableAddButton(usernames) { + return !usernames || !(usernames.length > 0); + }, + + showingFirst: Em.computed.lte("currentPage", 1), + showingLast: propertyEqual("currentPage", "totalPages"), + + actions: { + next() { + if (this.get("showingLast")) { return; } + + const group = this.get("model"); + const offset = Math.min(group.get("offset") + group.get("limit"), group.get("user_count")); + group.set("offset", offset); + + return group.findMembers(); + }, + + previous() { + if (this.get("showingFirst")) { return; } + + const group = this.get("model"); + const offset = Math.max(group.get("offset") - group.get("limit"), 0); + group.set("offset", offset); + + return group.findMembers(); + }, + + addMembers() { + if (Em.isEmpty(this.get("model.usernames"))) { return; } + this.get("model").addMembers(this.get("model.usernames")).catch(popupAjaxError); + this.set("model.usernames", null); + }, + + removeMember(member) { + const message = I18n.t("groups.edit.delete_member_confirm",{ + username: member.get("username"), + group: this.get("model.name") + }); + + return bootbox.confirm(message, I18n.t("no_value"), I18n.t("yes_value"), confirm => { + if (confirm) { + this.get("model").removeMember(member); + } + }); + } + } +}); diff --git a/app/assets/javascripts/discourse/controllers/edit-group.js.es6 b/app/assets/javascripts/discourse/controllers/group-edit.js.es6 similarity index 51% rename from app/assets/javascripts/discourse/controllers/edit-group.js.es6 rename to app/assets/javascripts/discourse/controllers/group-edit.js.es6 index 01db8c7ad47..32277430af5 100644 --- a/app/assets/javascripts/discourse/controllers/edit-group.js.es6 +++ b/app/assets/javascripts/discourse/controllers/group-edit.js.es6 @@ -1,16 +1,19 @@ import { popupAjaxError } from 'discourse/lib/ajax-error'; +import computed from 'ember-addons/ember-computed-decorators'; export default Ember.Controller.extend({ - saving: false, + @computed('saving') + savingText(saving) { + if (saving !== undefined) { + return saving ? I18n.t('saving') : I18n.t('saved'); + } + }, actions: { save() { this.set('saving', true); - this.get('model').save().then(() => { - this.transitionToRoute('group', this.get('model.name')); - this.send('closeModal'); - }).catch(error => { + this.get('model').save().catch(error => { popupAjaxError(error); }).finally(() => { this.set('saving', false); diff --git a/app/assets/javascripts/discourse/controllers/group-index.js.es6 b/app/assets/javascripts/discourse/controllers/group-index.js.es6 index ff1b32741e5..64ff33dc6d6 100644 --- a/app/assets/javascripts/discourse/controllers/group-index.js.es6 +++ b/app/assets/javascripts/discourse/controllers/group-index.js.es6 @@ -10,6 +10,7 @@ export default Ember.Controller.extend({ limit: null, offset: null, isOwner: Ember.computed.alias('model.is_group_owner'), + showActions: false, @observes('order', 'desc') refreshMembers() { @@ -28,6 +29,10 @@ export default Ember.Controller.extend({ }, actions: { + toggleActions() { + this.toggleProperty("showActions"); + }, + removeMember(user) { this.get('model').removeMember(user); }, diff --git a/app/assets/javascripts/discourse/controllers/group.js.es6 b/app/assets/javascripts/discourse/controllers/group.js.es6 index 8500a9d0fac..1e38992e8a2 100644 --- a/app/assets/javascripts/discourse/controllers/group.js.es6 +++ b/app/assets/javascripts/discourse/controllers/group.js.es6 @@ -21,6 +21,10 @@ export default Ember.Controller.extend({ Tab.create({ name: 'topics' }), Tab.create({ name: 'mentions' }), Tab.create({ name: 'messages', requiresMembership: true }), + Tab.create({ + name: 'edit', i18nKey: 'edit.title', + requiresMembership: true, requiresGroupAdmin: true + }), Tab.create({ name: 'logs', i18nKey: 'logs.title', requiresMembership: true, requiresGroupAdmin: true diff --git a/app/assets/javascripts/discourse/routes/app-route-map.js.es6 b/app/assets/javascripts/discourse/routes/app-route-map.js.es6 index 37733386f1f..4b23587a899 100644 --- a/app/assets/javascripts/discourse/routes/app-route-map.js.es6 +++ b/app/assets/javascripts/discourse/routes/app-route-map.js.es6 @@ -57,6 +57,7 @@ export default function() { this.route('mentions'); this.route('messages'); this.route('logs'); + this.route('edit'); }); // User routes diff --git a/app/assets/javascripts/discourse/routes/group-edit.js.es6 b/app/assets/javascripts/discourse/routes/group-edit.js.es6 new file mode 100644 index 00000000000..8608e6ecf3b --- /dev/null +++ b/app/assets/javascripts/discourse/routes/group-edit.js.es6 @@ -0,0 +1,15 @@ +export default Ember.Route.extend({ + titleToken() { + return I18n.t('groups.edit.title'); + }, + + model() { + return this.modelFor('group'); + }, + + setupController(controller, model) { + this.controllerFor('group-edit').setProperties({ model }); + this.controllerFor("group").set("showing", 'edit'); + model.findMembers(); + } +}); diff --git a/app/assets/javascripts/discourse/routes/group.js.es6 b/app/assets/javascripts/discourse/routes/group.js.es6 index 51731bccb68..232d898d794 100644 --- a/app/assets/javascripts/discourse/routes/group.js.es6 +++ b/app/assets/javascripts/discourse/routes/group.js.es6 @@ -17,12 +17,5 @@ export default Discourse.Route.extend({ setupController(controller, model) { controller.setProperties({ model, counts: this.get('counts') }); - }, - - actions: { - showGroupEditor() { - showModal('edit-group'); - this.controllerFor('edit-group').set('model', this.modelFor('group')); - } } }); diff --git a/app/assets/javascripts/admin/templates/components/group-member.hbs b/app/assets/javascripts/discourse/templates/components/group-member.hbs similarity index 100% rename from app/assets/javascripts/admin/templates/components/group-member.hbs rename to app/assets/javascripts/discourse/templates/components/group-member.hbs diff --git a/app/assets/javascripts/discourse/templates/components/group-members-input.hbs b/app/assets/javascripts/discourse/templates/components/group-members-input.hbs new file mode 100644 index 00000000000..c720c630f53 --- /dev/null +++ b/app/assets/javascripts/discourse/templates/components/group-members-input.hbs @@ -0,0 +1,23 @@ + +
    + + {{currentPage}}/{{totalPages}} + +
    +
    + {{#each model.members as |member|}} + {{group-member member=member automatic=model.automatic removeAction="removeMember"}} + {{/each}} +
    + +{{#unless model.automatic}} +
    + {{user-selector usernames=model.usernames}} + + {{d-button action="addMembers" + class="add" + icon="plus" + disabled=disableAddButton + label="groups.edit.add_members"}} +
    +{{/unless}} diff --git a/app/assets/javascripts/discourse/templates/group-edit.hbs b/app/assets/javascripts/discourse/templates/group-edit.hbs new file mode 100644 index 00000000000..136cd09ffa5 --- /dev/null +++ b/app/assets/javascripts/discourse/templates/group-edit.hbs @@ -0,0 +1,46 @@ +
    +
    +
    + + {{input type='text' name='title' value=model.title class='group-edit-title'}} +
    + +
    + + {{d-editor value=model.bio_raw class="group-edit-bio"}} +
    + +
    + {{group-members-input model=model}} +
    + +
    + {{group-flair-inputs model=model}} +
    + +
    + +
    + +
    + +
    + + {{d-button action="save" class="btn-primary" disabled=saving label="save"}} + {{savingText}} +
    +
    diff --git a/app/assets/javascripts/discourse/templates/group-index.hbs b/app/assets/javascripts/discourse/templates/group-index.hbs index cd01f61fa4d..ec00b5415ce 100644 --- a/app/assets/javascripts/discourse/templates/group-index.hbs +++ b/app/assets/javascripts/discourse/templates/group-index.hbs @@ -1,10 +1,5 @@ {{#if model}} - {{#if isOwner}} -
    - {{user-selector usernames=usernames placeholderKey="groups.selector_placeholder" id="user-search-selector" name="usernames"}} - {{d-button action="addMembers" class="add" icon="plus" label="groups.add"}} -
    - {{else if canJoinGroup}} + {{#if canJoinGroup}} {{#if model.is_group_user}} {{d-button action="leaveGroup" class="btn-danger group-index-leave" @@ -31,7 +26,6 @@ {{group-index-toggle order=order desc=desc field='username_lower' i18nKey='username'}} {{group-index-toggle order=order desc=desc field='last_posted_at' i18nKey='last_post'}} {{group-index-toggle order=order desc=desc field='last_seen_at' i18nKey='last_seen'}} - @@ -48,13 +42,6 @@ {{bound-date m.last_seen_at}} - - {{#if isOwner}} - {{#unless m.owner}} - - {{/unless}} - {{/if}} - {{/each}} diff --git a/app/assets/javascripts/discourse/templates/group.hbs b/app/assets/javascripts/discourse/templates/group.hbs index 3b20460b2c8..bacefc99715 100644 --- a/app/assets/javascripts/discourse/templates/group.hbs +++ b/app/assets/javascripts/discourse/templates/group.hbs @@ -20,12 +20,6 @@

    @{{model.name}}

    {{/if}} - - {{#if canEditGroup}} - - {{d-button action="showGroupEditor" label="groups.edit.title" class="group-edit-btn" icon="pencil"}} - - {{/if}}
    {{#if model.bio_cooked}} diff --git a/app/assets/javascripts/discourse/templates/modal/edit-group.hbs b/app/assets/javascripts/discourse/templates/modal/edit-group.hbs deleted file mode 100644 index 863f83dd3c3..00000000000 --- a/app/assets/javascripts/discourse/templates/modal/edit-group.hbs +++ /dev/null @@ -1,34 +0,0 @@ -{{#d-modal-body title="groups.edit.title" class="edit-group groups"}} -
    - - {{input type='text' name='title' value=model.title class='edit-group-title'}} - - - {{d-editor value=model.bio_raw class="edit-group-bio"}} - - {{group-flair-inputs model=model}} - - - - -
    -{{/d-modal-body}} - - diff --git a/app/assets/stylesheets/common/base/group-members-input.scss b/app/assets/stylesheets/common/base/group-members-input.scss new file mode 100644 index 00000000000..f8d41a5dc20 --- /dev/null +++ b/app/assets/stylesheets/common/base/group-members-input.scss @@ -0,0 +1,15 @@ +.group-members-input { + .ac-wrap { + width: 100% !important; + } + + .group-members-inputs-selector { + margin-top: 10px; + + .add { + margin-top: 7px; + } + } +} + + diff --git a/app/assets/stylesheets/common/base/group.scss b/app/assets/stylesheets/common/base/group.scss index 1e88b25e7a9..09c4eb28991 100644 --- a/app/assets/stylesheets/common/base/group.scss +++ b/app/assets/stylesheets/common/base/group.scss @@ -62,23 +62,22 @@ table.group-members { th:first-child { width: 60%; + text-align: left; } - th:last-child { + th.group-members-actions { width: 5%; } th { border-bottom: 3px solid dark-light-diff($primary, $secondary, 90%, -60%); + text-align: center; + padding: 5px 0px 5px 5px; + color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + font-weight: normal; } th.sortable { - text-align: left; - padding: 5px 0px 5px 5px; - - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); - font-weight: normal; - i { margin-left: 5px; } @@ -94,7 +93,12 @@ table.group-members { display: block; } + td:first-child { + text-align: left; + } + td { + text-align: center; color: dark-light-diff($primary, $secondary, 50%, -50%); padding: 0.8em 0; } @@ -128,14 +132,9 @@ table.group-members { } } -.group-edit { - float: right; -} - .form-horizontal { .group-flair-inputs { display: inline-block; - margin: 15px 0px; input[type="text"] { width: 80% !important; @@ -162,11 +161,7 @@ table.group-members { } } -.groups.edit-group .form-horizontal { - textarea { - width: 99%; - } - +.group-edit .form-horizontal { label { font-weight: bold; } diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 87137ed5937..42c6f466012 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -14,6 +14,9 @@ class GroupsController < ApplicationController render_serialized(find_group(:id), GroupShowSerializer, root: 'basic_group') end + def edit + end + def update group = Group.find(params[:id]) guardian.ensure_can_edit!(group) diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 3e12e37f5cf..099224f7f21 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -397,6 +397,8 @@ en: edit: title: 'Edit Group' group_title: 'Title' + add_members: "Add Members" + delete_member_confirm: "Remove '%{username}' from the '%{group}' group?" request_membership_pm: title: "Membership Request" body: "I would like to request membership in @%{groupName}." @@ -2499,7 +2501,6 @@ en: delete: "Delete" delete_confirm: "Delete this group?" delete_failed: "Unable to delete group. If this is an automatic group, it cannot be destroyed." - delete_member_confirm: "Remove '%{username}' from the '%{group}' group?" delete_owner_confirm: "Remove owner privilege for '%{username}'?" add: "Add" add_members: "Add members" diff --git a/test/javascripts/acceptance/group-edit-test.js.es6 b/test/javascripts/acceptance/group-edit-test.js.es6 new file mode 100644 index 00000000000..a5e8a513efb --- /dev/null +++ b/test/javascripts/acceptance/group-edit-test.js.es6 @@ -0,0 +1,33 @@ +import { acceptance } from "helpers/qunit-helpers"; + +acceptance("Editing Group", { + loggedIn: true +}); + +test("Editing group", () => { + visit("/groups/discourse/edit"); + + andThen(() => { + ok(find('.group-flair-inputs').length === 1, 'it should display avatar flair inputs'); + ok(find('.group-edit-bio').length === 1, 'it should display group bio input'); + ok(find('.group-edit-title').length === 1, 'it should display group title input'); + ok(find('.group-edit-public').length === 1, 'it should display group public input'); + ok(find('.group-edit-allow-membership-requests').length === 1, 'it should display group allow_membership_requets input'); + ok(find('.group-members-input .item').length === 7, 'it should display group members'); + ok(find('.group-members-input-selector').length === 1, 'it should display input to add group members'); + ok(find('.group-members-input-selector .add[disabled]').length === 1, 'add members button should be disabled'); + }); + + click('.group-edit-public'); + + andThen(() => { + ok(find('.group-edit-allow-membership-requests[disabled]').length === 1, 'it should disable group allow_membership_requets input'); + }); + + click('.group-edit-public'); + click('.group-edit-allow-membership-requests'); + + andThen(() => { + ok(find('.group-edit-public[disabled]').length === 1, 'it should disable group public input'); + }); +}); diff --git a/test/javascripts/acceptance/groups-logs-test.js.es6 b/test/javascripts/acceptance/group-logs-test.js.es6 similarity index 100% rename from test/javascripts/acceptance/groups-logs-test.js.es6 rename to test/javascripts/acceptance/group-logs-test.js.es6 diff --git a/test/javascripts/acceptance/groups-test.js.es6 b/test/javascripts/acceptance/groups-test.js.es6 index c3578e43d74..6152326f52c 100644 --- a/test/javascripts/acceptance/groups-test.js.es6 +++ b/test/javascripts/acceptance/groups-test.js.es6 @@ -27,8 +27,9 @@ test("Browsing Groups", () => { visit("/groups/discourse/messages"); andThen(() => { - ok(find(".nav-stacked li a[title='Messages']").length === 0, 'it should not show messages tab if user is admin'); - ok(find(".nav-stacked li a[title='Logs']").length === 0, 'it should not show Logs tab if user is admin'); + ok(find(".nav-stacked li a[title='Messages']").length === 0, 'it should not show messages tab if user is not admin'); + ok(find(".nav-stacked li a[title='Edit Group']").length === 0, 'it should not show messages tab if user is not admin'); + ok(find(".nav-stacked li a[title='Logs']").length === 0, 'it should not show Logs tab if user is not admin'); ok(count('.user-stream .item') > 0, "it lists stream items"); }); }); @@ -41,31 +42,9 @@ test("Admin Browsing Groups", () => { andThen(() => { ok(find(".nav-stacked li a[title='Messages']").length === 1, 'it should show messages tab if user is admin'); + ok(find(".nav-stacked li a[title='Edit Group']").length === 1, 'it should show edit group tab if user is admin'); ok(find(".nav-stacked li a[title='Logs']").length === 1, 'it should show Logs tab if user is admin'); equal(find('.group-title').text(), 'Awesome Team', 'it should display the group title'); equal(find('.group-name').text(), '@discourse', 'it should display the group name'); }); - - click('.group-edit-btn'); - - andThen(() => { - ok(find('.group-flair-inputs').length === 1, 'it should display avatar flair inputs'); - ok(find('.edit-group-bio').length === 1, 'it should display group bio input'); - ok(find('.edit-group-title').length === 1, 'it should display group title input'); - ok(find('.edit-group-public').length === 1, 'it should display group public input'); - ok(find('.edit-group-allow-membership-requests').length === 1, 'it should display group allow_membership_requets input'); - }); - - click('.edit-group-public'); - - andThen(() => { - ok(find('.edit-group-allow-membership-requests[disabled]').length === 1, 'it should disable group allow_membership_requets input'); - }); - - click('.edit-group-public'); - click('.edit-group-allow-membership-requests'); - - andThen(() => { - ok(find('.edit-group-public[disabled]').length === 1, 'it should disable group public input'); - }); }); From 0e934a2f2dbd5634e30f21cddc11c13b2374d502 Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Tue, 13 Dec 2016 15:17:14 +0800 Subject: [PATCH 011/408] Make eslint happy. --- app/assets/javascripts/discourse/routes/group.js.es6 | 1 - 1 file changed, 1 deletion(-) diff --git a/app/assets/javascripts/discourse/routes/group.js.es6 b/app/assets/javascripts/discourse/routes/group.js.es6 index 232d898d794..3df20f794bf 100644 --- a/app/assets/javascripts/discourse/routes/group.js.es6 +++ b/app/assets/javascripts/discourse/routes/group.js.es6 @@ -1,5 +1,4 @@ import Group from 'discourse/models/group'; -import showModal from 'discourse/lib/show-modal'; export default Discourse.Route.extend({ From f4af74b7b33dd3a2f29ca5a3c06924920637c4eb Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Tue, 13 Dec 2016 15:20:59 +0800 Subject: [PATCH 012/408] FIX: Incorrect class. --- app/assets/stylesheets/common/base/group-members-input.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/common/base/group-members-input.scss b/app/assets/stylesheets/common/base/group-members-input.scss index f8d41a5dc20..6313010435e 100644 --- a/app/assets/stylesheets/common/base/group-members-input.scss +++ b/app/assets/stylesheets/common/base/group-members-input.scss @@ -3,7 +3,7 @@ width: 100% !important; } - .group-members-inputs-selector { + .group-members-input-selector { margin-top: 10px; .add { From fbd3727c7d8493964d32dbe40b04b5252fcf35ed Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Tue, 13 Dec 2016 15:26:16 +0800 Subject: [PATCH 013/408] UX: Improve group edit style for mobile. --- app/assets/stylesheets/mobile/group.scss | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/assets/stylesheets/mobile/group.scss b/app/assets/stylesheets/mobile/group.scss index c254518732c..b465ba77612 100644 --- a/app/assets/stylesheets/mobile/group.scss +++ b/app/assets/stylesheets/mobile/group.scss @@ -32,6 +32,18 @@ background-color: $quaternary; } +.form-horizontal { + .control-group { + margin-bottom: 10px; + } + + .group-flair-inputs { + .group-flair-right { + margin: 0px + } + } +} + table.group-logs { width: 130%; } From 5a2794a0c78d3fbea1e04482eaaa1097c0259953 Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Tue, 13 Dec 2016 15:53:04 +0800 Subject: [PATCH 014/408] FIX: `Discourse.ajax` is deprecated. --- .../javascripts/discourse/controllers/avatar-selector.js.es6 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/discourse/controllers/avatar-selector.js.es6 b/app/assets/javascripts/discourse/controllers/avatar-selector.js.es6 index c59c632e38a..ab30fef1572 100644 --- a/app/assets/javascripts/discourse/controllers/avatar-selector.js.es6 +++ b/app/assets/javascripts/discourse/controllers/avatar-selector.js.es6 @@ -1,5 +1,6 @@ import computed from "ember-addons/ember-computed-decorators"; import ModalFunctionality from "discourse/mixins/modal-functionality"; +import { ajax } from 'discourse/lib/ajax'; import { allowsImages } from 'discourse/lib/utilities'; @@ -34,8 +35,7 @@ export default Ember.Controller.extend(ModalFunctionality, { refreshGravatar() { this.set("gravatarRefreshDisabled", true); - return Discourse - .ajax(`/user_avatar/${this.get("username")}/refresh_gravatar.json`, { method: "POST" }) + return ajax(`/user_avatar/${this.get("username")}/refresh_gravatar.json`, { method: "POST" }) .then(result => this.setProperties({ gravatar_avatar_template: result.gravatar_avatar_template, gravatar_avatar_upload_id: result.gravatar_upload_id, From 43ee9f884e7a9d2e2bb8e47ff006250996643de5 Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Tue, 13 Dec 2016 16:16:26 +0800 Subject: [PATCH 015/408] FEATURE: Add `Group#full_name`. --- app/assets/javascripts/admin/templates/group.hbs | 5 +++++ app/assets/javascripts/discourse/controllers/group.js.es6 | 6 +++--- app/assets/javascripts/discourse/models/group.js.es6 | 3 ++- app/assets/javascripts/discourse/templates/group-edit.hbs | 4 ++-- app/assets/javascripts/discourse/templates/group.hbs | 2 +- app/controllers/admin/groups_controller.rb | 3 ++- app/controllers/groups_controller.rb | 2 +- app/models/group.rb | 1 + app/serializers/basic_group_serializer.rb | 3 ++- config/locales/client.en.yml | 2 +- db/migrate/20161213073938_add_full_name_to_groups.rb | 5 +++++ spec/controllers/admin/groups_controller_spec.rb | 3 ++- spec/integration/groups_spec.rb | 4 ++-- test/javascripts/acceptance/group-edit-test.js.es6 | 2 +- 14 files changed, 30 insertions(+), 15 deletions(-) create mode 100644 db/migrate/20161213073938_add_full_name_to_groups.rb diff --git a/app/assets/javascripts/admin/templates/group.hbs b/app/assets/javascripts/admin/templates/group.hbs index 79ba4466969..9f4f2a941ce 100644 --- a/app/assets/javascripts/admin/templates/group.hbs +++ b/app/assets/javascripts/admin/templates/group.hbs @@ -11,6 +11,11 @@ {{#if model.id}} {{#unless model.automatic}} +
    + + {{input type='text' name='full_name' value=model.full_name class='group-edit-full-name'}} +
    +
    {{d-editor value=model.bio_raw}} diff --git a/app/assets/javascripts/discourse/controllers/group.js.es6 b/app/assets/javascripts/discourse/controllers/group.js.es6 index 1e38992e8a2..2686d92b470 100644 --- a/app/assets/javascripts/discourse/controllers/group.js.es6 +++ b/app/assets/javascripts/discourse/controllers/group.js.es6 @@ -36,9 +36,9 @@ export default Ember.Controller.extend({ return !automatic && isGroupOwner; }, - @computed('model.name', 'model.title') - groupName(name, title) { - return (title || name).capitalize(); + @computed('model.name', 'model.full_name') + groupName(name, fullName) { + return (fullName || name).capitalize(); }, @computed('model.name', 'model.flair_url', 'model.flair_bg_color', 'model.flair_color') diff --git a/app/assets/javascripts/discourse/models/group.js.es6 b/app/assets/javascripts/discourse/models/group.js.es6 index c4cd97ddf00..d3ca40baef7 100644 --- a/app/assets/javascripts/discourse/models/group.js.es6 +++ b/app/assets/javascripts/discourse/models/group.js.es6 @@ -134,7 +134,8 @@ const Group = Discourse.Model.extend({ flair_color: this.get('flairHexColor'), bio_raw: this.get('bio_raw'), public: this.get('public'), - allow_membership_requests: this.get('allow_membership_requests') + allow_membership_requests: this.get('allow_membership_requests'), + full_name: this.get('full_name') }; }, diff --git a/app/assets/javascripts/discourse/templates/group-edit.hbs b/app/assets/javascripts/discourse/templates/group-edit.hbs index 136cd09ffa5..03122d2abaa 100644 --- a/app/assets/javascripts/discourse/templates/group-edit.hbs +++ b/app/assets/javascripts/discourse/templates/group-edit.hbs @@ -1,8 +1,8 @@
    - - {{input type='text' name='title' value=model.title class='group-edit-title'}} + + {{input type='text' name='full_name' value=model.full_name class='group-edit-full-name'}}
    diff --git a/app/assets/javascripts/discourse/templates/group.hbs b/app/assets/javascripts/discourse/templates/group.hbs index bacefc99715..68204c0276c 100644 --- a/app/assets/javascripts/discourse/templates/group.hbs +++ b/app/assets/javascripts/discourse/templates/group.hbs @@ -16,7 +16,7 @@ {{groupName}} - {{#if model.title}} + {{#if model.full_name}}

    @{{model.name}}

    {{/if}} diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb index ed96b325100..3b5445f0704 100644 --- a/app/controllers/admin/groups_controller.rb +++ b/app/controllers/admin/groups_controller.rb @@ -69,6 +69,7 @@ class Admin::GroupsController < Admin::AdminController group.flair_color = group_params[:flair_color].presence group.public = group_params[:public] if group_params[:public] group.bio_raw = group_params[:bio_raw] if group_params[:bio_raw] + group.full_name = group_params[:full_name] if group_params[:full_name] if group.save Group.reset_counters(group.id, :group_users) @@ -145,7 +146,7 @@ class Admin::GroupsController < Admin::AdminController :name, :alias_level, :visible, :automatic_membership_email_domains, :automatic_membership_retroactive, :title, :primary_group, :grant_trust_level, :incoming_email, :flair_url, :flair_bg_color, - :flair_color, :bio_raw, :public, :allow_membership_requests + :flair_color, :bio_raw, :public, :allow_membership_requests, :full_name ) end end diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 42c6f466012..058aa8851bc 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -239,7 +239,7 @@ class GroupsController < ApplicationController :flair_bg_color, :flair_color, :bio_raw, - :title, + :full_name, :public, :allow_membership_requests ) diff --git a/app/models/group.rb b/app/models/group.rb index 1adbaa4a292..d1fbf372e28 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -514,6 +514,7 @@ end # bio_cooked :text # public :boolean default(FALSE), not null # allow_membership_requests :boolean default(FALSE), not null +# full_name :string # # Indexes # diff --git a/app/serializers/basic_group_serializer.rb b/app/serializers/basic_group_serializer.rb index 264013cbdaf..b37184e7232 100644 --- a/app/serializers/basic_group_serializer.rb +++ b/app/serializers/basic_group_serializer.rb @@ -18,7 +18,8 @@ class BasicGroupSerializer < ApplicationSerializer :bio_raw, :bio_cooked, :public, - :allow_membership_requests + :allow_membership_requests, + :full_name def include_incoming_email? staff? diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 099224f7f21..cacf9fd0abc 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -396,7 +396,7 @@ en: to: "To" edit: title: 'Edit Group' - group_title: 'Title' + full_name: 'Full Name' add_members: "Add Members" delete_member_confirm: "Remove '%{username}' from the '%{group}' group?" request_membership_pm: diff --git a/db/migrate/20161213073938_add_full_name_to_groups.rb b/db/migrate/20161213073938_add_full_name_to_groups.rb new file mode 100644 index 00000000000..655b89551b5 --- /dev/null +++ b/db/migrate/20161213073938_add_full_name_to_groups.rb @@ -0,0 +1,5 @@ +class AddFullNameToGroups < ActiveRecord::Migration + def change + add_column :groups, :full_name, :string + end +end diff --git a/spec/controllers/admin/groups_controller_spec.rb b/spec/controllers/admin/groups_controller_spec.rb index f9ee82f9ee6..dacdde2568e 100644 --- a/spec/controllers/admin/groups_controller_spec.rb +++ b/spec/controllers/admin/groups_controller_spec.rb @@ -41,7 +41,8 @@ describe Admin::GroupsController do "bio_raw"=>nil, "bio_cooked"=>nil, "public"=>false, - "allow_membership_requests"=>false + "allow_membership_requests"=>false, + "full_name"=>group.full_name }]) end diff --git a/spec/integration/groups_spec.rb b/spec/integration/groups_spec.rb index 0a53cee2f89..7d4b2a0644c 100644 --- a/spec/integration/groups_spec.rb +++ b/spec/integration/groups_spec.rb @@ -54,7 +54,7 @@ describe "Groups" do flair_color: 'BBB', flair_url: 'fa-adjust', bio_raw: 'testing', - title: 'awesome team', + full_name: 'awesome team', public: true, allow_membership_requests: true } } @@ -68,7 +68,7 @@ describe "Groups" do expect(group.flair_color).to eq('BBB') expect(group.flair_url).to eq('fa-adjust') expect(group.bio_raw).to eq('testing') - expect(group.title).to eq('awesome team') + expect(group.full_name).to eq('awesome team') expect(group.public).to eq(true) expect(group.allow_membership_requests).to eq(true) expect(GroupHistory.last.subject).to eq('allow_membership_requests') diff --git a/test/javascripts/acceptance/group-edit-test.js.es6 b/test/javascripts/acceptance/group-edit-test.js.es6 index a5e8a513efb..234505277d6 100644 --- a/test/javascripts/acceptance/group-edit-test.js.es6 +++ b/test/javascripts/acceptance/group-edit-test.js.es6 @@ -10,7 +10,7 @@ test("Editing group", () => { andThen(() => { ok(find('.group-flair-inputs').length === 1, 'it should display avatar flair inputs'); ok(find('.group-edit-bio').length === 1, 'it should display group bio input'); - ok(find('.group-edit-title').length === 1, 'it should display group title input'); + ok(find('.group-edit-full-name').length === 1, 'it should display group full name input'); ok(find('.group-edit-public').length === 1, 'it should display group public input'); ok(find('.group-edit-allow-membership-requests').length === 1, 'it should display group allow_membership_requets input'); ok(find('.group-members-input .item').length === 7, 'it should display group members'); From 2686ee5ab29b33edc1fcb32811421c703bd60ff6 Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Tue, 13 Dec 2016 16:39:44 +0800 Subject: [PATCH 016/408] FIX: Admin can't add/remove public group users. --- app/controllers/groups_controller.rb | 8 +++-- spec/integration/groups_spec.rb | 47 ++++++++++++++++++---------- 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 058aa8851bc..e4b5fc14331 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -132,7 +132,9 @@ class GroupsController < ApplicationController raise Discourse::NotFound if users.blank? if group.public - raise Discourse::InvalidAccess unless current_user == users.first + if !guardian.can_log_group_changes?(group) && current_user != users.first + raise Discourse::InvalidAccess + end unless current_user.staff? RateLimiter.new(current_user, "public_group_membership", 3, 1.minute).performed! @@ -183,7 +185,9 @@ class GroupsController < ApplicationController raise Discourse::NotFound unless user if group.public - raise Discourse::InvalidAccess unless current_user == user + if !guardian.can_log_group_changes?(group) && current_user != user + raise Discourse::InvalidAccess + end unless current_user.staff? RateLimiter.new(current_user, "public_group_membership", 3, 1.minute).performed! diff --git a/spec/integration/groups_spec.rb b/spec/integration/groups_spec.rb index 7d4b2a0644c..fbf620c4a38 100644 --- a/spec/integration/groups_spec.rb +++ b/spec/integration/groups_spec.rb @@ -237,20 +237,6 @@ describe "Groups" do expect(group_history.target_user).to eq(user2) end - it "can make incremental deletes" do - expect do - xhr :delete, "/groups/#{group.id}/members", username: user.username - end.to change { group.users.count }.by(-1) - - expect(response).to be_success - - group_history = GroupHistory.last - - expect(group_history.action).to eq(GroupHistory.actions[:remove_user_from_group]) - expect(group_history.acting_user).to eq(admin) - expect(group_history.target_user).to eq(user) - end - it "cannot add members to automatic groups" do group.update!(automatic: true) @@ -296,6 +282,22 @@ describe "Groups" do group.update!(public: true) end + context 'admin' do + it "can make incremental adds" do + expect do + xhr :put, "/groups/#{group.id}/members", usernames: other_user.username + end.to change { group.users.count }.by(1) + + expect(response).to be_success + + group_history = GroupHistory.last + + expect(group_history.action).to eq(GroupHistory.actions[:add_user_to_group]) + expect(group_history.acting_user).to eq(admin) + expect(group_history.target_user).to eq(other_user) + end + end + it 'should allow a user to join the group' do sign_in(other_user) @@ -305,7 +307,9 @@ describe "Groups" do expect(response).to be_success end - it 'should not allow a user to add another user to a group' do + it 'should not allow an underprivilege user to add another user to a group' do + sign_in(user) + xhr :put, "/groups/#{group.id}/members", usernames: other_user.username expect(response).to be_forbidden @@ -364,6 +368,15 @@ describe "Groups" do group.update!(public: true) end + context "admin" do + it "removes by username" do + expect { xhr :delete, "/groups/#{group.id}/members", username: other_user.username } + .to change { group.users.count }.by(-1) + + expect(response).to be_success + end + end + it 'should allow a user to leave a group' do sign_in(other_user) @@ -373,7 +386,9 @@ describe "Groups" do expect(response).to be_success end - it 'should not allow a user to leave a group for another user' do + it 'should not allow a underprivilege user to leave a group for another user' do + sign_in(user) + xhr :delete, "/groups/#{group.id}/members", username: other_user.username expect(response).to be_forbidden From eefd286f71d6bab0c8519b113a3e72917b8a5120 Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Tue, 13 Dec 2016 16:45:49 +0800 Subject: [PATCH 017/408] Fix JS tests. --- test/javascripts/fixtures/group-fixtures.js.es6 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/javascripts/fixtures/group-fixtures.js.es6 b/test/javascripts/fixtures/group-fixtures.js.es6 index 55041ac96f4..84b472251fc 100644 --- a/test/javascripts/fixtures/group-fixtures.js.es6 +++ b/test/javascripts/fixtures/group-fixtures.js.es6 @@ -4,7 +4,7 @@ export default { "id":47, "automatic":false, "name":"discourse", - "title":"Awesome Team", + "full_name":"Awesome Team", "user_count":8, "alias_level":0, "visible":true, From 911629735c81468be5e96f965afd26dc4a28b52c Mon Sep 17 00:00:00 2001 From: Sawood Alam Date: Tue, 13 Dec 2016 09:26:11 -0500 Subject: [PATCH 018/408] Removed the fallback mailcatcher install command --- bin/docker/mailcatcher | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/docker/mailcatcher b/bin/docker/mailcatcher index e25dc7c3642..f1f258d70ff 100755 --- a/bin/docker/mailcatcher +++ b/bin/docker/mailcatcher @@ -1,4 +1,4 @@ #!/bin/bash -CMD="mailcatcher --http-ip 0.0.0.0 -f || (apt-get install -y libsqlite3-dev && gem install mailcatcher && mailcatcher --http-ip 0.0.0.0 -f)" +CMD="mailcatcher --http-ip 0.0.0.0 -f" docker exec -it discourse_dev /bin/bash -c "$CMD" From 67a0a36362f6eb7a62f59b0e832a32759069ae85 Mon Sep 17 00:00:00 2001 From: Neil Lalonde Date: Mon, 12 Dec 2016 14:35:31 -0500 Subject: [PATCH 019/408] FIX: topic lists on mobile should include featured links --- .../discourse/templates/mobile/list/topic-list-item.raw.hbs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/assets/javascripts/discourse/templates/mobile/list/topic-list-item.raw.hbs b/app/assets/javascripts/discourse/templates/mobile/list/topic-list-item.raw.hbs index e630889d492..731c080bd8c 100644 --- a/app/assets/javascripts/discourse/templates/mobile/list/topic-list-item.raw.hbs +++ b/app/assets/javascripts/discourse/templates/mobile/list/topic-list-item.raw.hbs @@ -10,6 +10,9 @@