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