Basic Functionality

This commit is contained in:
Robin Ward 2016-03-10 17:37:58 -05:00
commit 9b20e633c0
9 changed files with 310 additions and 0 deletions

102
.eslintrc Normal file
View File

@ -0,0 +1,102 @@
{
"env": {
"jasmine": true,
"node": true,
"mocha": true,
"browser": true,
"builtin": true
},
ecmaVersion: 7,
"globals":
{"Ember":true,
"jQuery":true,
"$":true,
"RSVP":true,
"Discourse":true,
"Em":true,
"PreloadStore":true,
"Handlebars":true,
"I18n":true,
"bootbox":true,
"module":true,
"moduleFor":true,
"moduleForComponent":true,
"Pretender":true,
"sandbox":true,
"controllerFor":true,
"test":true,
"ok":true,
"not":true,
"expect":true,
"equal":true,
"visit":true,
"andThen":true,
"click":true,
"currentPath":true,
"currentRouteName":true,
"currentURL":true,
"fillIn":true,
"keyEvent":true,
"triggerEvent":true,
"count":true,
"exists":true,
"visible":true,
"invisible":true,
"asyncRender":true,
"selectDropdown":true,
"asyncTestDiscourse":true,
"fixture":true,
"find":true,
"sinon":true,
"moment":true,
"start":true,
"_":true,
"alert":true,
"containsInstance":true,
"deepEqual":true,
"notEqual":true,
"define":true,
"require":true,
"requirejs":true,
"hasModule":true,
"Blob":true,
"File":true},
"rules": {
"block-scoped-var": 2,
"dot-notation": 0,
"eqeqeq": [
2,
"allow-null"
],
"guard-for-in": 2,
"no-bitwise": 2,
"no-caller": 2,
"no-cond-assign": 0,
"no-debugger": 2,
"no-empty": 0,
"no-eval": 2,
"no-extend-native": 2,
"no-extra-parens": 0,
"no-irregular-whitespace": 2,
"no-iterator": 2,
"no-loop-func": 2,
"no-multi-str": 2,
"no-new": 2,
"no-plusplus": 0,
"no-proto": 2,
"no-script-url": 2,
"no-sequences": 2,
"no-shadow": 2,
"no-undef": 2,
"no-unused-vars": 2,
"no-with": 2,
"semi": 2,
"strict": 0,
"valid-typeof": 2,
"wrap-iife": [
2,
"inside"
]
},
"parser": "babel-eslint"
}

View File

@ -0,0 +1,28 @@
import { default as computed, on } from 'ember-addons/ember-computed-decorators';
import { popupAjaxError } from 'discourse/lib/ajax-error';
export default Ember.Controller.extend({
newNote: null,
saving: false,
@on('init')
reset() {
this.setProperties({ newNote: null, saving: false });
},
@computed('newNote', 'saving')
attachDisabled(newNote, saving) {
return saving || !newNote || (newNote.length === 0);
},
actions: {
attachNote() {
const note = this.store.createRecord('staff-note');
this.set('saving', true);
note.save({ raw: this.get('newNote'), user_id: this.get('userId') }).then(() => {
this.set('newNote', '');
this.get('model').pushObject(note);
}).catch(popupAjaxError).finally(() => this.set('saving', false));
}
}
});

View File

@ -0,0 +1,30 @@
import { withPluginApi } from 'discourse/lib/plugin-api';
import showModal from 'discourse/lib/show-modal';
export default {
name: 'enable-staff-notes',
initialize(container) {
const siteSettings = container.lookup('site-settings:main');
if (!siteSettings.staff_notes_enabled) { return; }
withPluginApi('0.2', api => {
api.decorateWidget('poster-name:after', dec => dec.attach('staff-notes-icon'));
api.createWidget('staff-notes-icon', {
tagName: 'span.staff-notes-icon',
html() {
return this.attach('emoji', { name: 'pencil' });
},
click() {
return this.store.find('staff-note', { user_id: this.attrs.user_id }).then(model => {
const controller = showModal('staff-notes', { model, title: 'staff_notes.title' });
controller.reset();
controller.set('userId', this.attrs.user_id);
});
}
});
});
},
};

View File

@ -0,0 +1,16 @@
<div class="modal-body staff-notes-modal">
{{#each model as |n|}}
<div class='staff-note'>
<div class='posted-by'>
{{avatar n.created_by imageSize="tiny"}}
</div>
<div class='cooked'>{{cook-text n.raw}}</div>
<div class='clearfix'></div>
</div>
{{/each}}
{{textarea value=newNote}}
{{d-button action="attachNote" label="staff_notes.attach" class="btn-primary" disabled=attachDisabled}}
</div>

View File

@ -0,0 +1,23 @@
.staff-notes-modal {
height: 300px;
.posted-by {
width: 30px;
float: left;
}
.cooked {
float: left;
p {
margin: 0 0 1em 0;
}
}
.staff-note {
margin-bottom: 1em;
}
}
.staff-notes-icon {
cursor: pointer;
}

View File

@ -0,0 +1,5 @@
en:
js:
staff_notes:
title: "Staff Notes"
attach: "Attach Note"

View File

@ -0,0 +1,3 @@
en:
site_settings:
staff_notes_enabled: "Allow staff users to attach notes to users"

4
config/settings.yml Normal file
View File

@ -0,0 +1,4 @@
plugins:
staff_notes_enabled:
default: false
client: true

99
plugin.rb Normal file
View File

@ -0,0 +1,99 @@
# name: staff-notes
# about: Gives the ability for staff members to attach notes to users
# version: 0.0.1
# authors: Robin Ward
enabled_site_setting :staff_notes_enabled
register_asset 'stylesheets/staff_notes.scss'
after_initialize do
require_dependency 'user'
module ::DiscourseStaffNotes
class Engine < ::Rails::Engine
engine_name "discourse_staff_notes"
isolate_namespace DiscourseStaffNotes
end
def self.key_for(user_id)
"notes:#{user_id}"
end
def self.notes_for(user_id)
PluginStore.get('staff_notes', key_for(user_id)) || []
end
def self.add_note(user_id, raw, created_by)
notes = notes_for(user_id)
record = { id: SecureRandom.hex(16), raw: raw, created_by: created_by, created_at: Time.now }
notes << record
::PluginStore.set("staff_notes", key_for(user_id), notes)
record
end
end
require_dependency 'application_serializer'
class ::StaffNoteSerializer < ApplicationSerializer
attributes :id, :raw, :created_by, :created_at
def id
object[:id]
end
def raw
object[:raw]
end
def created_by
user = User.where(id: object[:created_by]).first
return nil if user.blank?
BasicUserSerializer.new(user, scope: scope, root: false)
end
def created_at
object[:created_at]
end
end
require_dependency 'application_controller'
class DiscourseStaffNotes::StaffNotesController < ::ApplicationController
before_filter :ensure_logged_in
before_filter :ensure_staff
def index
user = User.where(id: params[:user_id]).first
raise Discourse::NotFound if user.blank?
notes = ::DiscourseStaffNotes.notes_for(params[:user_id])
render json: {
extras: { username: user.username },
staff_notes: serialize_data(notes, ::StaffNoteSerializer)
}
end
def create
user_id = params[:staff_note][:user_id]
user = User.where(id: user_id).first
raise Discourse::NotFound if user.blank?
staff_note = ::DiscourseStaffNotes.add_note(user.id, params[:staff_note][:raw], current_user.id)
render json: serialize_data(staff_note, ::StaffNoteSerializer)
end
end
DiscourseStaffNotes::Engine.routes.draw do
get '/' => 'staff_notes#index'
post '/' => 'staff_notes#create'
end
Discourse::Application.routes.append do
mount ::DiscourseStaffNotes::Engine, at: "/staff_notes"
end
end