From 4cea92c4e9fa0bab3df06ae5a710da341b8230bd Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 17 Apr 2013 17:08:21 +1000 Subject: [PATCH] work in progress add support for groups --- .../controllers/admin_groups_controller.js | 10 ++ app/assets/javascripts/admin/models/group.js | 24 +++ .../admin/routes/admin_groups_routes.js | 9 ++ .../javascripts/admin/routes/admin_routes.js | 2 + .../admin/templates/admin.js.handlebars | 1 + .../admin/templates/groups.js.handlebars | 23 +++ .../admin/views/admin_groups_view.js | 4 + .../discourse/models/selectable_array.js | 19 +++ .../templates/composer.js.handlebars | 2 +- .../discourse/views/composer_view.js | 44 +----- .../discourse/views/user_selector_view.js | 65 ++++++++ .../stylesheets/application/compose.css.scss | 145 +++++++++--------- app/controllers/admin/flags_controller.rb | 2 - app/controllers/admin/groups_controller.rb | 4 + app/models/group.rb | 5 + app/models/group_user.rb | 2 + config/locales/client.en.yml | 4 + config/routes.rb | 1 + db/migrate/20130416004607_create_groups.rb | 8 + db/migrate/20130416004933_group_users.rb | 11 ++ 20 files changed, 268 insertions(+), 117 deletions(-) create mode 100644 app/assets/javascripts/admin/controllers/admin_groups_controller.js create mode 100644 app/assets/javascripts/admin/models/group.js create mode 100644 app/assets/javascripts/admin/routes/admin_groups_routes.js create mode 100644 app/assets/javascripts/admin/templates/groups.js.handlebars create mode 100755 app/assets/javascripts/admin/views/admin_groups_view.js create mode 100644 app/assets/javascripts/discourse/models/selectable_array.js create mode 100644 app/assets/javascripts/discourse/views/user_selector_view.js mode change 100644 => 100755 app/assets/stylesheets/application/compose.css.scss create mode 100644 app/controllers/admin/groups_controller.rb create mode 100644 app/models/group.rb create mode 100644 app/models/group_user.rb create mode 100644 db/migrate/20130416004607_create_groups.rb create mode 100644 db/migrate/20130416004933_group_users.rb diff --git a/app/assets/javascripts/admin/controllers/admin_groups_controller.js b/app/assets/javascripts/admin/controllers/admin_groups_controller.js new file mode 100644 index 00000000000..e0b75168d2a --- /dev/null +++ b/app/assets/javascripts/admin/controllers/admin_groups_controller.js @@ -0,0 +1,10 @@ +Discourse.AdminGroupsController = Ember.ArrayController.extend({ + itemController: 'adminGroup', + edit: function(action){ + this.get('content').select(action); + } +}); + +Discourse.AdminGroupController = Ember.ObjectController.extend({ + +}); diff --git a/app/assets/javascripts/admin/models/group.js b/app/assets/javascripts/admin/models/group.js new file mode 100644 index 00000000000..70e13815b2a --- /dev/null +++ b/app/assets/javascripts/admin/models/group.js @@ -0,0 +1,24 @@ +Discourse.Group = Discourse.Model.extend({ + +}); + +Discourse.Group.reopenClass({ + findAll: function(){ + var list = Discourse.SelectableArray.create(); + + list.addObject(Discourse.Group.create({id: 1, name: "all mods", members: ["A","b","c"]})); + list.addObject(Discourse.Group.create({id: 2, name: "other mods", members: ["A","b","c"]})); + + return list; + }, + + find: function(id) { + var promise = new Em.Deferred(); + + setTimeout(function(){ + promise.resolve(Discourse.Group.create({id: 1, name: "all mods", members: ["A","b","c"]})); + }, 1000); + + return promise; + } +}); diff --git a/app/assets/javascripts/admin/routes/admin_groups_routes.js b/app/assets/javascripts/admin/routes/admin_groups_routes.js new file mode 100644 index 00000000000..a0f7022d8ea --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin_groups_routes.js @@ -0,0 +1,9 @@ +Discourse.AdminGroupsRoute = Discourse.Route.extend({ + model: function() { + return Discourse.Group.findAll(); + }, + renderTemplate: function() { + this.render('admin/templates/groups',{into: 'admin/templates/admin'}); + } +}); + diff --git a/app/assets/javascripts/admin/routes/admin_routes.js b/app/assets/javascripts/admin/routes/admin_routes.js index 4dc179dc30c..67493ca2681 100644 --- a/app/assets/javascripts/admin/routes/admin_routes.js +++ b/app/assets/javascripts/admin/routes/admin_routes.js @@ -25,6 +25,8 @@ Discourse.Route.buildRoutes(function() { this.route('old', { path: '/old' }); }); + this.route('groups', {path: '/groups'}); + this.resource('adminUsers', { path: '/users' }, function() { this.resource('adminUser', { path: '/:username' }); this.resource('adminUsersList', { path: '/list' }, function() { diff --git a/app/assets/javascripts/admin/templates/admin.js.handlebars b/app/assets/javascripts/admin/templates/admin.js.handlebars index 8fa2bf43b39..e14a1389fe1 100644 --- a/app/assets/javascripts/admin/templates/admin.js.handlebars +++ b/app/assets/javascripts/admin/templates/admin.js.handlebars @@ -7,6 +7,7 @@
  • {{#linkTo 'admin.site_settings'}}{{i18n admin.site_settings.title}}{{/linkTo}}
  • {{#linkTo 'adminSiteContents'}}{{i18n admin.site_content.title}}{{/linkTo}}
  • {{#linkTo 'adminUsersList.active'}}{{i18n admin.users.title}}{{/linkTo}}
  • +
  • {{#linkTo 'admin.groups'}}{{i18n admin.groups.title}}{{/linkTo}}
  • {{#linkTo 'admin.email_logs'}}{{i18n admin.email_logs.title}}{{/linkTo}}
  • {{#linkTo 'adminFlags.active'}}{{i18n admin.flags.title}}{{/linkTo}}
  • {{#linkTo 'admin.customize'}}{{i18n admin.customize.title}}{{/linkTo}}
  • diff --git a/app/assets/javascripts/admin/templates/groups.js.handlebars b/app/assets/javascripts/admin/templates/groups.js.handlebars new file mode 100644 index 00000000000..eb05ffab957 --- /dev/null +++ b/app/assets/javascripts/admin/templates/groups.js.handlebars @@ -0,0 +1,23 @@ +
    +
    +

    {{i18n admin.groups.edit}}

    + +
    + +
    + {{#if content.active}} + {{#with content.active}} + {{name}} + {{view Discourse.UserSelector id="private-message-users" class="span8" placeholderKey="admin.groups.selector_placeholder" tabindex="1" usernamesBinding="usernames"}} + {{/with}} + {{else}} + nothing here yet + {{/if}} +
    +
    diff --git a/app/assets/javascripts/admin/views/admin_groups_view.js b/app/assets/javascripts/admin/views/admin_groups_view.js new file mode 100755 index 00000000000..09243fcb42a --- /dev/null +++ b/app/assets/javascripts/admin/views/admin_groups_view.js @@ -0,0 +1,4 @@ +Discourse.AdminGroupsView = Discourse.View.extend({ +}); + + diff --git a/app/assets/javascripts/discourse/models/selectable_array.js b/app/assets/javascripts/discourse/models/selectable_array.js new file mode 100644 index 00000000000..733ea685630 --- /dev/null +++ b/app/assets/javascripts/discourse/models/selectable_array.js @@ -0,0 +1,19 @@ +// this allows you to track the selected item in an array, ghetto for now +Discourse.SelectableArray = Em.ArrayProxy.extend({ + content: [], + selectIndex: function(index){ + this.select(this[index]); + }, + select: function(selected){ + this.content.each(function(item){ + if(item === selected){ + Em.set(item, "active", true) + } else { + if (item.get("active")) { + Em.set(item, "active", false) + } + } + }); + this.set("active", selected); + } +}); diff --git a/app/assets/javascripts/discourse/templates/composer.js.handlebars b/app/assets/javascripts/discourse/templates/composer.js.handlebars index 46021916ebc..0b0e3c204e3 100644 --- a/app/assets/javascripts/discourse/templates/composer.js.handlebars +++ b/app/assets/javascripts/discourse/templates/composer.js.handlebars @@ -28,7 +28,7 @@ {{#if content.editTitle}}
    {{#if content.creatingPrivateMessage}} - {{view Discourse.TextField id="private-message-users" class="span8" placeholderKey="composer.users_placeholder" tabindex="1"}} + {{view Discourse.UserSelector topicIdBinding="controller.controllers.topic.content.id" excludeCurrentUser="true" id="private-message-users" class="span8" placeholderKey="composer.users_placeholder" tabindex="1" usernamesBinding="content.targetUsernames"}} {{/if}} {{view Discourse.TextField valueBinding="content.title" tabindex="2" id="reply-title" maxlength="255" class="span8" placeholderKey="composer.title_placeholder"}} {{#unless content.creatingPrivateMessage}} diff --git a/app/assets/javascripts/discourse/views/composer_view.js b/app/assets/javascripts/discourse/views/composer_view.js index 7e72edb53b3..8bd50e417c1 100644 --- a/app/assets/javascripts/discourse/views/composer_view.js +++ b/app/assets/javascripts/discourse/views/composer_view.js @@ -167,17 +167,7 @@ Discourse.ComposerView = Discourse.View.extend({ $LAB.script(assetPath('defer/html-sanitizer-bundle')); Discourse.ComposerView.trigger("initWmdEditor"); - template = Handlebars.compile("
    " + - "" + - "
    "); + template = Discourse.UserSelector.templateFunction(); transformTemplate = Handlebars.compile("{{avatar this imageSize=\"tiny\"}} {{this.username}}"); $wmdInput.data('init', true); @@ -193,38 +183,6 @@ Discourse.ComposerView = Discourse.View.extend({ transformComplete: function(v) { return v.username; } }); - selected = []; - $('#private-message-users').val(this.get('content.targetUsernames')).autocomplete({ - template: template, - - dataSource: function(term) { - return Discourse.UserSearch.search({ - term: term, - topicId: _this.get('controller.controllers.topic.content.id'), - exclude: selected.concat([Discourse.get('currentUser.username')]) - }); - }, - - onChangeItems: function(items) { - items = $.map(items, function(i) { - if (i.username) { - return i.username; - } else { - return i; - } - }); - _this.set('content.targetUsernames', items.join(",")); - selected = items; - }, - - transformComplete: transformTemplate, - - reverseTransform: function(i) { - return { username: i }; - } - - }); - topic = this.get('topic'); this.editor = editor = Discourse.Markdown.createEditor({ lookupAvatar: function(username) { diff --git a/app/assets/javascripts/discourse/views/user_selector_view.js b/app/assets/javascripts/discourse/views/user_selector_view.js new file mode 100644 index 00000000000..9d484061f83 --- /dev/null +++ b/app/assets/javascripts/discourse/views/user_selector_view.js @@ -0,0 +1,65 @@ +Discourse.UserSelector = Discourse.TextField.extend({ + + didInsertElement: function(){ + var _this = this; + var selected = []; + var transformTemplate = Handlebars.compile("{{avatar this imageSize=\"tiny\"}} {{this.username}}"); + var template = Discourse.UserSelector.templateFunction(); + + $(this.get('element')).val(this.get('usernames')).autocomplete({ + template: template, + + dataSource: function(term) { + var exclude = selected; + if (_this.get('excludeCurrentUser')){ + exclude = exclude.concat([Discourse.get('currentUser.username')]); + } + return Discourse.UserSearch.search({ + term: term, + topicId: _this.get('topicId'), + exclude: exclude + }); + }, + + onChangeItems: function(items) { + items = $.map(items, function(i) { + if (i.username) { + return i.username; + } else { + return i; + } + }); + _this.set('usernames', items.join(",")); + selected = items; + }, + + transformComplete: transformTemplate, + + reverseTransform: function(i) { + return { username: i }; + } + + }); + + } + +}); + + +Discourse.UserSelector.reopenClass({ + // I really want to move this into a template file, but I need a handlebars template here, not an ember one + templateFunction: function(){ + this.compiled = this.compiled || Handlebars.compile("
    " + + "" + + "
    "); + return this.compiled; + } +}); diff --git a/app/assets/stylesheets/application/compose.css.scss b/app/assets/stylesheets/application/compose.css.scss old mode 100644 new mode 100755 index 1ccb3b50e2f..1d6b8e948f1 --- a/app/assets/stylesheets/application/compose.css.scss +++ b/app/assets/stylesheets/application/compose.css.scss @@ -54,6 +54,40 @@ margin-left: 10px; } + +.autocomplete { + z-index: 9999; + position: absolute; + width: 200px; + background-color: $white; + border: 1px solid $gray; + ul { + list-style: none; + padding: 0; + margin: 0; + li { + border-bottom: 1px solid $light_gray; + a[href] { + padding: 5px; + display: block; + span.username { + color: lighten($black, 20); + } + span.name { + font-size: 11px; + } + &.selected { + background-color: $light_gray; + } + @include hover { + background-color: $light_gray; + text-decoration: none; + } + } + } + } +} + #reply-control { .requirements-not-met { background-color: rgba(255, 0, 0, 0.12); @@ -181,38 +215,7 @@ margin-right: auto; float: none; } - .autocomplete { - z-index: 999; - position: absolute; - width: 200px; - background-color: $white; - border: 1px solid $gray; - ul { - list-style: none; - padding: 0; - margin: 0; - li { - border-bottom: 1px solid $light_gray; - a[href] { - padding: 5px; - display: block; - span.username { - color: lighten($black, 20); - } - span.name { - font-size: 11px; - } - &.selected { - background-color: $light_gray; - } - @include hover { - background-color: $light_gray; - text-decoration: none; - } - } - } - } - } + // When the post is new (new topic) the sizings are different &.edit-title { &.open { @@ -309,50 +312,50 @@ margin-bottom: 10px; } -#main #reply-control { - div.ac-wrap { - background-color: $white; - border: 1px solid #cccccc; - padding: 4px 10px; - @include border-radius-all(3px); - div.item { - float: left; - margin-right: 10px; - span { - padding-left: 5px; - height: 22px; - display: inline-block; - line-height: 22px; - vertical-align: bottom; - } - a { - margin-left: 4px; - font-size: 10px; - line-height: 10px; - padding: 2px 1px 1px 3px; - border-radius: 10px; - width: 10px; - display: inline-block; - border: 1px solid rgba(255, 255, 255, 0); - &:hover { - background-color: lighten($red, 45); - border: 1px solid lighten($red, 20); - text-decoration: none; - color: $white; - } - } + +div.ac-wrap { + background-color: $white; + border: 1px solid #cccccc; + padding: 4px 10px; + @include border-radius-all(3px); + div.item { + float: left; + margin-right: 10px; + span { + padding-left: 5px; + height: 22px; + display: inline-block; + line-height: 22px; + vertical-align: bottom; } - input[type="text"] { - float: left; - margin-top: 5px; - border: 0; - padding: 0; - margin: 4px 0 0; - box-shadow: none; + a { + margin-left: 4px; + font-size: 10px; + line-height: 10px; + padding: 2px 1px 1px 3px; + border-radius: 10px; + width: 10px; + display: inline-block; + border: 1px solid rgba(255, 255, 255, 0); + &:hover { + background-color: lighten($red, 45); + border: 1px solid lighten($red, 20); + text-decoration: none; + color: $white; + } } } + input[type="text"] { + float: left; + margin-top: 5px; + border: 0; + padding: 0; + margin: 4px 0 0; + box-shadow: none; + } } + #reply-control.edit-title.private-message { .wmd-controls { top: 140px; diff --git a/app/controllers/admin/flags_controller.rb b/app/controllers/admin/flags_controller.rb index eb0cbe71e0c..2dad84a3d7e 100644 --- a/app/controllers/admin/flags_controller.rb +++ b/app/controllers/admin/flags_controller.rb @@ -1,5 +1,3 @@ -require_dependency 'sql_builder' - class Admin::FlagsController < Admin::AdminController def index diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb new file mode 100644 index 00000000000..12702c4de59 --- /dev/null +++ b/app/controllers/admin/groups_controller.rb @@ -0,0 +1,4 @@ +class Admin::GroupsController < Admin::AdminController + def show + end +end diff --git a/app/models/group.rb b/app/models/group.rb new file mode 100644 index 00000000000..8e660a9e752 --- /dev/null +++ b/app/models/group.rb @@ -0,0 +1,5 @@ +class Group < ActiveRecord::Base + def self.builtin + Enum.new(:moderators, :admins, :trust_level_1, :trust_level_2) + end +end diff --git a/app/models/group_user.rb b/app/models/group_user.rb new file mode 100644 index 00000000000..ed0c77ce23f --- /dev/null +++ b/app/models/group_user.rb @@ -0,0 +1,2 @@ +class GroupUser < ActiveRecord::Base +end diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 91bcf3708a3..c8b23714545 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -860,6 +860,10 @@ en: delete_title: "delete post (if its the first post delete topic)" flagged_by: "Flagged by" error: "Something went wrong" + + groups: + title: "Groups" + edit: "Edit Groups" api: title: "API" diff --git a/config/routes.rb b/config/routes.rb index 4139f2c3fea..1312a046280 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -27,6 +27,7 @@ Discourse::Application.routes.draw do resources :site_settings get 'reports/:type' => 'reports#show' + resources :groups resources :users, id: USERNAME_ROUTE_FORMAT do collection do get 'list/:query' => 'users#index' diff --git a/db/migrate/20130416004607_create_groups.rb b/db/migrate/20130416004607_create_groups.rb new file mode 100644 index 00000000000..48495d1cebe --- /dev/null +++ b/db/migrate/20130416004607_create_groups.rb @@ -0,0 +1,8 @@ +class CreateGroups < ActiveRecord::Migration + def change + create_table :groups do |t| + t.string :name, null: false + t.timestamps + end + end +end diff --git a/db/migrate/20130416004933_group_users.rb b/db/migrate/20130416004933_group_users.rb new file mode 100644 index 00000000000..688673e9f46 --- /dev/null +++ b/db/migrate/20130416004933_group_users.rb @@ -0,0 +1,11 @@ +class GroupUsers < ActiveRecord::Migration + def change + create_table :group_users do |t| + t.integer :group_id, null: false + t.integer :user_id, null: false + t.timestamps + end + + add_index :group_users, [:group_id, :user_id], unique: true + end +end